缩减 Docker 镜像的体积对于简化开发流程、加速构建速度和缩短部署时间至关重要,同时还能节省宝贵的存储空间。基于我的实际经验,我发现了一些既优化 Docker 镜像,又能提升性能和效率的有效策略。以下是我推荐并经常使用的最佳实践,以保持精简高效的 Docker 镜像。
1. 使用最小的镜像选择最小的基础镜像是最有效的方法之一。最小的基础镜像,如alpine
、scratch
或debian-slim
,要小得多,因为它们只包含必要的内容。
看看基于 ubuntu
的 Python 镜像和基于 alpine
的那个之间的大小差异
使用 Ubuntu 作为基础镜像:
FROM python:3.11-slim
# 从Python 3.11-slim镜像开始
- 图像大小:大约 60 MB,基于 Python 3.11 和 Ubuntu 的基础镜像
使用 Alpine 作为基础镜像,就像...
FROM python:3.11-alpine # 使用Python 3.11-alpine基础镜像
- 图像大小:大约 23 MB,基于 Alpine 基础镜像的 Python 3.11
基于 Alpine 的镜像大约是基于 Ubuntu 的镜像的三分之一大小。这种显著的大小减少是因为 Alpine Linux 是一个特别设计用于 Docker 环境的最小化发行版。使用最小基础镜像不仅能减少镜像的大小,还能减小攻击面,还能增强安全性。
2. 多阶段构建流程多阶段构建允许你将构建环境与运行时环境隔离开,确保最终镜像仅包含必要文件。这有助于排除运行时不需要的构建工具和依赖等,从而减小最终 Docker 镜像的体积。
使用 Python 的例子考虑一个使用 Python 开发的应用程序,你希望采用多阶段构建技术来让最终镜像保持精简:
用于多阶段构建的Dockerfile:
# 构建阶段:
FROM python:3.11-slim AS builder
WORKDIR /app
# 安装构建所需的依赖项
COPY requirements.txt .
RUN pip install --user -r requirements.txt
# 复制应用代码:
COPY . .
# 最终阶段:
FROM python:3.11-slim
WORKDIR /app
# 安装运行时所需的依赖项
COPY --from=builder /root/.local /root/.local
COPY . .
# 设置环境变量以包含用户安装的包路径
ENV PATH=/root/.local/bin:$PATH
CMD ["python", "app.py"]
大小对比
- 如果没有使用多阶段构建:如果你使用单阶段Dockerfile,最终生成的镜像将同时包含构建依赖和应用代码:
## 使用 Python 3.11 版本的 slim 镜像
FROM python:3.11-slim
## 设置工作目录为 /app
WORKDIR /app
## 复制 requirements.txt 文件到当前目录
COPY requirements.txt .
## 安装 requirements.txt 文件中列出的依赖包
RUN pip install -r requirements.txt
## 复制当前目录下的所有文件到容器中
COPY . .
## 设置容器启动时运行的命令
CMD ["python", "app.py"]
- 图像大小:约 150 MB(包含构建和运行所需依赖项)。
使用多阶段构建法:通过使用提供的多阶段构建示例来,最终镜像更小。
- 图片大小:大约 60 MB,(仅包含运行时所需的依赖和应用程序代码)。
清理无用的文件,比如缓存、临时文件和构建依赖项,是减小Docker镜像大小的重要一步。这一做法确保你的镜像只包含运行应用所需的必要组件,同时最小化镜像的体积并减少潜在的攻击面。
Python 示例代码这里是一个如何在Python应用的Dockerfile中移除不必要的文件的例子。
清理前的状况。
FROM python:3.11-slim
WORKDIR /app
# 安装构建所需的依赖
COPY requirements.txt .
RUN pip install -r requirements.txt
# 复制应用的代码
COPY . .
CMD ["python", "app.py"]
清理模式:
FROM python:3.11-slim
WORKDIR /app
# 安装构建依赖项
COPY requirements.txt .
RUN pip install -r requirements.txt \
# 清理临时文件和缓存数据
&& rm -rf /root/.cache/pip
# 复制应用程序代码
COPY . .
CMD ["python", "app.py"]
未进行清理:在一个没有清理的Dockerfile中,因为未清理的缓存和临时文件,镜像可能会变得更大。
- 图片大小约为150MB(包括构建缓存和其他不需要的文件)。
使用清理命令,比如:使用清理命令,如 rm -rf /root/.cache/pip
来清除缓存和临时文件可以减小最终镜像的大小:
- 图片大小: 在清理了缓存和临时文件之后,大约 120 MB 左右。
.dockerignore
文件配置
.dockerignore
文件类似于 .gitignore
文件,但用于 Docker 构建。它指定了哪些文件和目录不应该被包含在 Docker 构建上下文内容中。这有助于缩小构建上下文内容的大小,从而加快构建速度,生成更小的 Docker 镜像文件。
.dockerignore
的优点
- 减少构建上下文大小:通过排除不必要的文件,你可以减少发送给Docker守护程序的数据量,从而加快构建速度。
- 更小的Docker镜像:在最终镜像中排除不需要的文件可以防止它们被包含,从而有助于减小镜像的大小。
- 提高构建效率:较小的构建上下文意味着Docker可以更有效地缓存层,从而加快重新构建的过程。
.dockerignore
文件的例子
这里有一个简单的 .dockerignore
文件例子:
.git
node_modules
*.log
.DS_Store
请注意,以上文件路径和模式不需要翻译,它们保持原样。
如果没有.dockerignore
文件:
- 当不必要的文件被包含在 Docker 构建上下文中时,这些文件会被发送给 Docker 守护进程,并最终成为 Docker 镜像的一部分,即使它们在最终镜像中并没有被使用。
- 例如,包含
.git
目录或node_modules
文件夹可能会显著增加构建上下文的大小。.git
目录可能包含几百兆字节的版本历史,而node_modules
可能会增加另外几百兆字节的依赖文件,这些依赖文件在生产镜像中不需要。 - 对构建上下文大小的影响:排除最终镜像不需要的文件和目录,有助于减小构建上下文的大小,否则,构建上下文的大小可能会增加到几个 GB,具体取决于被排除的文件的数量和大小。如果构建上下文包含一个大型的
.git
目录、node_modules
文件夹以及其他不需要的文件,其大小可能达到 1 GB 左右。
使用**.dockerignore**
:
- 通过使用
.dockerignore
文件排除不必要的文件,你可以将构建范围限制为仅包含应用程序所需的文件。 - 排除
.git
、node_modules
和其他大型目录后,构建范围的大小可以从几个吉字节缩小到仅几兆字节。 - 对镜像大小的影响是:虽然
.dockerignore
文件本身并不会直接减小最终 Docker 镜像的大小,但它可以防止不必要的文件被添加到构建范围内。这使得构建过程更加高效,并有助于通过确保仅包含相关文件来创建更精简的最终镜像。在排除这些不必要的文件后,构建上下文可能减少到 50 MB 左右,这可以显著减少构建时间,使最终 Docker 镜像更加高效。
在 Docker 中,每个 RUN
、COPY
和 ADD
指令都会在生成的镜像中创建一个新的层。这些层会增大 Docker 镜像的大小并影响构建的速度。将多个命令合并成一个 RUN
指令有助于减少层数,从而生成更高效和更紧凑的 Docker 镜像。
无层次简化:
- 每个单独的指令(如
RUN
、COPY
等)都会在 Docker 镜像中创建一个新的层。这些层层层叠加会导致镜像大小变大,因为中间文件、临时数据和额外的元数据都会被包含在内。 - 例如,使用单独的
RUN
指令会产生多个层,每个层都会有自己的元数据和开销,这可能会导致最终镜像大小变大。 - 这将影响镜像的大小:如果你使用多个
RUN
指令,比如:
RUN 更新软件包列表
RUN 安装 curl -y
RUN 清理软件包缓存
使用多个 RUN
指令会导致镜像大小大约为 150 MB,每一层都会增加一些额外的大小。因此尽量减少使用多个 RUN
指令。
最小化层次:
- 将多个命令合并到一个
RUN
指令中可以减少层数,并有助于将更改合并到较少的、更精简的层中。 -
例如,将命令合并到一个
RUN
指令中,比如这样: RUN command1 && command2 && command3
RUN apt-get update && apt-get install -y curl && apt-get clean
# 更新包列表并安装curl,然后清理缓存
将命令组合成一个 RUN
指令可以将镜像的大小减少到大约 130 MB。这样做的方法是将更改合并到更少的层中,并尽量减少多余的中间数据。
而不是复制整个目录到你的Docker镜像,使用具体的COPY
命令,只包含你需要的文件。这种方法避免了传输不必要的文件,减少了镜像的大小。
例子:比如说
复制 package.json 到当前目录
复制 src/ 目录到当前目录
- 大小影响:通过仅复制特定的文件和目录,您可以避免包含不必要的文件,这些文件可能会使镜像文件变大。比如说,排除开发文件或构建工件,从而减少镜像文件的大小数兆字节。
创建多架构的Docker镜像确保了与各种环境(如ARM和x86)的兼容性。这种方法可以更好地适应各种硬件平台。
例子
- 大小影响:多架构镜像针对特定架构进行了优化,这会减少在不同平台上使用的镜像的大小。这有助于避免那些臃肿的镜像,这些镜像包含了不必要的多架构支持,实际上只需要支持一个架构。
这些最佳实践可以让Docker镜像更加高效、安全和快速,从而帮助您更高效地管理和部署容器。