你一定不知道!这个将 Docker 镜像缩小 99% 的隐藏技术

Dipanshu 2024-11-04 11:52:58

 
 

你是否一样厌倦了与臃肿的 Docker 镜像进行抗争?这些镜像是否会占用磁盘空间并影响部署速度?值得注意的是:顶级 DevOps 团队已经将镜像大小削减了 99%。在本指南中,我将揭开他们一直保密的技术。

 
 

 

臃肿的 Docker 镜像的隐性成本

 

在我们深入研究解决方案之前,让我们先讨论一下为什么这很重要。过大的 Docker 镜像不仅会带来麻烦,还会让你付出代价:
 
  • 时间:构建和部署周期较慢
  • 金钱:增加存储和带宽成本
  • 性能:应用程序响应速度降低

 

从 1.2GB 到 8MB 的历程:案例研究

 

为了说明这些技术的威力,让我们看一个真实的例子。我们采用了一个基于 Python 的标准机器学习应用程序,初始 Docker 镜像大小为 1.2GB,并将其优化至仅 8MB,优化步骤如下:
 
  • 多阶段构建
  • 图层优化
  • 最少的基础镜像(包括 Scratch)
  • 高级技术,例如 distroless 镜像
  • 安全最佳实践
 
起点:臃肿的镜像
 
这通常是你可能遇到的典型 Dockerfile
 
 
  •  
  •  
  •  
  •  
  •  
  •  
FROM python:3.9WORKDIR /appCOPY requirements.txt .RUN pip install -r requirements.txtCOPY . .CMD ["python", "main.py"]
 
 
该 Dockerfile 虽然有效,但以下原因会导致镜像尺寸较大:
 
  • 使用完整的Python镜像
  • 包括不必要的构建工具和依赖项
  • 层缓存效率低下
  • 可能包含不必要的文件
 
现在一起看看可以优化镜像的技术:
 

多阶段构建:游戏规则改变者

 

多阶段构建是一种强大的技术,可以显著减少最终 Docker 镜像的大小,它使我们能够将构建时依赖项与运行时依赖项分开。

 

  • 使用最小的基础镜像

 

根据你的用例,将完整的 python 版本替换为 slim 或 alpine 版本

 

  •  
FROM python:3.9-slim AS builder

 

 

单阶段 Dockerfile

 
  •  
  •  
#  an official Python runtime as a parent imageFROM python:3.9-slim
 
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
# Install necessary build dependenciesRUN apt-get update && apt-get install -y --no-install-recommends \    build-essential \    gcc \    && rm -rf /var/lib/apt/lists/*# Set the working directoryWORKDIR /app# Copy the requirements file and install dependenciesCOPY requirements.txt ./RUN pip install --no-cache-dir -r requirements.txt# Copy the rest of the application codeCOPY . .# Compile the model (if necessary)RUN python compile_model.py# Run the inference scriptCMD ["python", "inference.py"]
 
构建此镜像大约需要 1.2GB,这么大的尺寸是由于这包含了所有构建工具和开发库。
 
 

多阶段 Dockerfile

 
第一阶段:构建阶段
 
  • 我们将设置工作目录
  • 安装必要的构建工具
  • 复制并安装我们的 python 依赖项
  • 复制应用程序代码
  • 使用 PyInstaller 创建独立的可执行文件
 
  •  
  •  
# Stage 1: BuildFROM python:3.9-slim AS builder
 
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
# Install necessary build dependenciesRUN apt-get update && apt-get install -y --no-install-recommends \    build-essential \    gcc \    && rm -rf /var/lib/apt/lists/*# Set the working directoryWORKDIR /app# Copy the requirements file and install dependenciesCOPY requirements.txt ./RUN pip install --no-cache-dir -r requirements.txt# Copy the application codeCOPY . .# Compile the model (if necessary)RUN python compile_model.py# Install PyInstallerRUN pip install pyinstaller# Create a standalone executableRUN pyinstaller --onefile inference.py
 
第二阶段:生产阶段
 
  • 从scratch镜像开始 - 一个完全空的镜像
  • 仅复制必要的文件Build stage
  • 我们设置编译应用程序的入口点
 
  •  
  •  
  •  
  •  
  •  
  •  
  •  
# Stage 2: ProductionFROM scratch# Set the working directoryWORKDIR /app# Copy only the necessary files from the build stageCOPY --from=builder /app/dist/inference /app/inferenceCOPY --from=builder /app/model /app/model# Run the inference executableENTRYPOINT ["/app/inference"]
 
构建此多阶段 Dockerfile 的镜像大小约为85 MB — 减少了90%以上

 

层优化:每个字节都很重要

 

你在 Dockerfile 中写入的每条指令都会在 Docker 镜像中创建一个新层。例如 RUN、COPY 和 ADD 命令各自添加一个层,并且每个层都会添加镜像大小以及构建时间
 
  • 最小化层:将多个运行命令组合成单个 RUN 指令,这种方法可以最大限度地减少冗余层并保持镜像更干净。例如:你应该将它们组合起来,而不是用于安装包和清理临时文件的单独 RUN 命令。
 
  • 使用&&链接命令并在同一层进行清理。
 
 
  •  
  •  
  •  
RUN apt-get update && apt-get install -y python3-pip python3-dev && \pip3 install numpy pandas && \apt-get clean && rm -rf /var/lib/apt/lists/*
 

 

从头开始的最小基础镜像:少即是多

 

这是创建 Docker 镜像的最强大、但也是最具挑战性的方法。从头开始创建镜像意味着使用从头开始的基础镜像(https://hub.docker.com/_/scratch),无底层操作系统、没有依赖性、没有预先存在的数据或应用程序。把它想象成一个空的存储光盘,必须用数据填充它,因为里面什么也没有。
 
你放入其中的任何内容都会影响其大小,也意味着,如果你需要任何依赖项或支持应用程序或工具,你需要自己将它们安装在镜像上。
 
它主要在两种情况下有用:
 
  • 创建自己的基础镜像时。当创建了自己的 Linux 发行版,不想把它放在另一个基础镜像之上,比如 ubuntu 之类的,就可以可以使用临时镜像,然后将你自己的 Linux 发行版放在上面。
 
  • 拥有独立的可执行应用程序时。例如,对于基于 Python 的独立 ML/DL 应用程序(例如处理预测的模型服务器),可以使用 PyInstaller 等工具将代码编译为可执行文件。编译后,将可执行文件放入临时 Docker 镜像中。由于此镜像缺少操作系统或库,因此只需手动添加必要的依赖项(例如 TensorFlow、PyTorch、模型文件或配置文件)。这可以使镜像最小化并针对部署进行优化。
 
 
  •  
  •  
  •  
  •  
# syntax=docker/dockerfile:1FROM scratchADD myapp /CMD ["/myapp"]
 

 

先进技术

 
 

Distroless 镜像

 
想象一下你正在收拾行李去旅行,你有三个选择:
 
  • 收拾好你的整个衣柜(Full Distribution Image)
  • 不带任何东西,在目的地购买所有东西(Scratch Image)
  • 仅打包你实际需要的内容(Distroless Image)
 
Google 的 distroless 镜像介于完整发行版和临时镜像之间,这是“恰到好处”的选项,它仅包含运行应用程序所需的内容。
 
Distroless 镜像特点如下:
 
  • 比完整分布镜像小
  • 更安全,因为不必要的组件更少
  • 仍然包含 SSL 证书和时区数据等重要内容
 
  •  
  •  
  •  
  •  
  •  
FROM gcr.io/distroless/python3-debian10COPY --from=builder /app/dist/main /app/mainCOPY --from=builder /app/model /app/modelCOPY --from=builder /app/config.yml /app/config.ymlENTRYPOINT ["/app/main"]
 
使用 Docker 构建工具包
 
Docker BuildKit 为构建 Docker 镜像提供了改进的性能、安全性和更灵活的缓存失效。启用它
 
  •  
DOCKER_BUILDKIT=1 docker build -t myapp .
 
 

其他技术

 
  • 消除不必要的文件:不要保留任何应用程序,镜像内的数据将直接增加镜像大小。相反,将容器连接到外部存储卷并将数据存储在那里,以便应用程序可以访问它,也可以不会使镜像膨胀。或者,你的应用程序还可以连接到外部数据存储(例如 MySQL 或 AWS S3)并从那里访问数据。
     
  • 使用.dockerignore文件: .dockerignore类似于.gitignore。它允许从最终镜像中排除特定文件和目录,我们可以将 .dockerignore 文件添加到项目的根目录。例如,可以将大型数据文件、虚拟环境、日志、模型检查点和临时文件添加到. dockerignore 文件。
 
  •  
  •  
# Exclude large datasetsdata/
 
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
# Exclude virtual environmentvenv/# Exclude cache, logs, and temporary files__pycache__/*.log*.tmp*.pyc*.pyo*.pyd.pytest_cache.git.gitignoreREADME.md# Exclude model training checkpoints and tensorboard logscheckpoints/runs/
 
  • 利用镜像分析工具:诸如 Docker slim 之类的镜像压缩工具Dive也非常强大。它们将让你分析每个镜像层,包括层大小和内部文件,并可以帮助你找出自重在哪里以及你可以删除什么。
  • Unikernels:更小的镜像,包含你的应用程序和底层操作系统,旨在直接运行,但需要更深入的理解才能有效实现。(我个人还没有尝试过,但它们可以比典型的 Docker 镜像小 80%)

 

安全精简 Docker 镜像的基本安全实践

 
  • 使用受信任的官方基础镜像,避免来自未知来源的未经验证的镜像
  • 始终以非 root 用户身份运行容器
 
  •  
  •  
RUN adduser --disabled-password --gecos "" appuserUSER appuser
 
  • 通过限制端口和 IP 地址来限制容器的网络暴露
 
  •  
docker run -p 127.0.0.1:8080:8080 myimage
 
定期扫描 Docker 镜像是否存在已知漏洞。(考虑使用Trivy等工具定期扫描镜像是否存在漏洞)
 
  •  
docker scan your-image:tag
 
  • 避免将 API 密钥或密码等敏感信息直接硬编码到 Dockerfile 或环境变量中,而是使用 Docker 机密或由编排工具管理的环境变量等方法
     
  • 为容器启用日志记录和监控以跟踪任何可疑活动
 

结论

 
实施这些技术后,我们取得了以下成果:
 
  • 镜像大小:从 1.2GB 减少到 8MB(减少 99.33%)
  • 部署时间:缩短 85%
  • 云成本:降低 60%
 
请记住,关键是从最小的基础镜像开始,使用多阶段构建将构建环境与运行时环境分开,并不断优化层和依赖项。通过这些方法,可以显着减小 Docker 镜像的大小!
 
鼓励大家在自己的项目中尝试这些优化技术,从多阶段构建开始,然后逐步应用其他技术,看看可以将 Docker 镜像制作得有多小、有多高效。Happy optimizing :)
 
 
作者丨Dipanshu    编译丨Rio
来源丨网址:https://medium.com/aws-in-plain-english/docker-pros-are-shrinking-images-by-99-the-hidden-techniques-you-cant-afford-to-miss-a70ee26b4cbf

 

*本文为dbaplus社群编译整理,如需转载请取得授权并标明出处!欢迎广大技术人员投稿,投稿邮箱:editor@dbaplus.cn

 

最新评论
访客 2024年04月08日

如果字段的最大可能长度超过255字节,那么长度值可能…

访客 2024年03月04日

只能说作者太用心了,优秀

访客 2024年02月23日

感谢详解

访客 2024年02月20日

一般干个7-8年(即30岁左右),能做到年入40w-50w;有…

访客 2023年08月20日

230721

活动预告