几个月之前,在执行一个关键部署时,我们遇到了一个意想不到的问题:部署过程耗时极长,令人头疼。罪魁祸首竟然是臃肿的 Docker 镜像。这不仅令人沮丧,还导致了我们无法承受的系统停机。
这段经历教会了我一个重要的教训:小小的改变也能带来巨大的不同。通过优化 Docker 镜像,我们成功地将部署时间缩短了一半,节约了存储成本,并提升了 CI/CD 管道的整体效率。今天,我将分享我们实现这一转变所使用的策略。
……
为什么要优化 Docker 镜像?如果你曾经经历过构建速度慢、部署时间长或镜像仓库中充斥着体积过大的镜像,你不是唯一有这种经历的人。为什么减少镜像大小至关重要:
- 更快的构建: 您的开发周期变得更短,您可以专注于更重要的事情。
- 高效的存储: 更小的镜像节省了您Docker仓库和机器上的磁盘空间。
- 更快的部署: 通过网络部署更小的镜像更快。
- 增强的安全性: 组件较少意味着漏洞也较少。
...
或使用省略号(...)来表示暂停或段落间的过渡。
(翻译为:...)
我们让Docker镜像瘦身的那天我记得我们在优化努力之后第一次使用 docker images
命令时的情景。看到优化前后的镜像大小,感觉就像健身几周后上秤称重一样,能感受到差异,很有成就感。
以下是我們遵循的确切步骤,以完成那次转变:
(注:这里的"exact steps"是专有名词或特定表述,建议保留英文原词,因此翻译为:"exact steps")
此处省略
优化 Docker 镜像的7个有效手段1. 选择一个最小的基础图像
我们不再以 ubuntu:latest
或其他大型镜像为起点,而是改用 alpine
。这改动将镜像大小从 800MB 减至不到 30MB。
例子
在Dockerfile中使用最新版本的alpine镜像:
FROM alpine:latest
切换到全屏 退出全屏
……
2. 使用多步构建。
在许多项目中,例如一个 React 应用程序,我们可能有一些构建依赖项(例如 Node.js 和 npm 这样的工具),这些依赖项仅在构建过程中需要,而在生产镜像中并不需要。通过采用多阶段构建,我们可以把构建环境和运行时环境区分开来,从而生成一个更小的容器镜像。
示例:
在此示例中,我们将为 React 应用程序使用多步构建。
# 构建阶段
FROM node:16 AS builder
WORKDIR /app
COPY package.json package-lock.json ./ # 将 package.json 和 package-lock.json 复制到工作目录下
RUN npm install # 运行 npm install 命令安装依赖
COPY . . # 将项目文件复制到工作目录下
RUN npm run build # 运行 npm run build 命令构建项目
# 运行时阶段
FROM nginx:alpine # 从 nginx:alpine 镜像创建一个新的容器层
COPY --from=builder /app/build /usr/share/nginx/html # 将构建阶段生成的文件复制到 nginx 的静态文件目录
CMD ["nginx", "-g", "daemon off;"] # 启动 nginx 并设置不使用守护进程
全屏 退出全屏
在上面的 Dockerfile 中
-
使用官方的node:16镜像版本来安装依赖,构建React应用,生成静态资源。
- 我们用较小的 nginx:alpine 镜头来运行构建完成的 React 程序。
这种多阶段构建流程确保只有必要的构建工件(如构建目录)会被包含在最终的镜像中,从而使镜像的大小最小化并优化生产环境。
……
3. 删除不需要的文件
在调试时,我们经常会不小心将临时文件加入到构建过程中。通过添加一个 .dockerignore
文件,我们就能防止这些临时文件被意外打包到镜像中。这样就能确保这些临时文件不会意外地出现在最终的镜像中。
示例 .dockerignore 文件:
# 以下文件不需要版本控制
node_modules # 忽略的文件模式
*.log # 忽略的文件模式
.git # 忽略的文件模式
全屏 退出全屏
省略了内容
4. 合并及减少图层
每个 Dockerfile
中的指令(例如 RUN
、COPY
、ADD
)都会在 Docker 镜像中创建一个新的层。过多的层可能会使你的镜像变得臃肿。通过将多个指令合并到一个 RUN
指令中,你就可以减少层数,从而优化镜像。
示例:
而不是写:
RUN apt-get update # 更新软件包列表
RUN apt-get install -y curl vim # 安装curl和vim
RUN apt-get clean # 清理不必要的下载包
RUN rm -rf /var/lib/apt/lists/* # 清除缓存的软件包列表
进入全屏,退出全屏
把它们合并成一个试试看:
RUN # 更新软件包列表并安装curl和vim,然后清理缓存
apt-get update && apt-get install -y curl vim \
&& apt-get clean && rm -rf /var/lib/apt/lists/*
全屏显示 关闭全屏
这种方法尽可能地减少了层数,并确保临时文件,如缓存,在同层移除,这样就让镜像更小、更干净。
……此处省略……
5、避免安装不必要的依赖
最初,我们的Docker镜像包含了一些额外的库“以防万一。”随着时间的推移,我们发现这导致了镜像过大并增加了安全风险。我们发现,通过只指定运行时依赖项,从而保持了镜像的精简和安全性,这使镜像更符合实际需要。
例如,我们没有为每个项目中的安装大量的库文件,而是专注于最少的依赖并避免使用没必要的包。
此处省略
6. 使用 docker-slim
命令。
对我们流程来说,docker-slim
是一个改变游戏规则的工具。这款工具会自动分析你的镜像,并通过移除不必要的部分,如未使用的文件、二进制文件和库等,来减小其大小,确保功能不受影响。
我们使用docker-slim
将图像大小最多减少了80%,使其成为我们优化策略中不可或缺的工具。
指令以缩小图像:
docker-slim build <image-name> (请替换为实际的镜像名称)
``` 使用docker-slim构建镜像。
全屏切换(进入), 全屏切换(退出)
* * *
### 7. 定期检查并清理镜像
Docker 镜像会随时间积累,未使用的镜像或层会占用宝贵的磁盘空间,这可以清理。定期进行清理未使用的镜像有助于保持环境整洁。
你可以通过运行下面的命令来移除不需要的图像和图层:
**删除未使用镜像的命令:**
运行这条命令 docker system prune -f
可以强制清理所有未使用的 Docker 数据。
全屏模式 退出全屏
**清理所有未使用的图片的命令:**
可以运行以下命令来删除所有未使用的Docker镜像(强制执行):
docker image prune -a -f
进入全屏模式,退出全屏
把定期剪枝纳入你的工作流程,你可以让 Docker 环境保持简洁高效。
* * *
## 如何衡量成功
在实施这些优化之后,我们使用`docker images`命令来比较大小。结果令人惊讶。
* **之前大约是:** 1.2GB。
* **之后大约是:** 250MB。
不仅我们的部署变快了,云存储成本也大幅减少了。
* * *
## 结尾
优化 Docker 镜像可能看起来像是一个小任务,但这能给你的工作流程带来巨大的好处。无论是你是一个单打独斗的开发者,还是一个大型团队的一员,这些策略都能真正产生影响。
所以,还等什么呢?跳进你的 `Dockerfile` 里,开始优化工作,享受更精简、更快的部署带来的好处,享受更精简、更快部署的好处,享受更精简、更快的部署带来的好处。
(注意:为了避免重复,最终翻译为:)
所以,还等什么呢?跳进你的 `Dockerfile` 里,开始优化工作,享受更精简、更快的部署带来的好处,
* * *
## 参考资料:
1. [Docker 官方文档页面](https://docs.docker.com)
2. [Dockerfile 编写的最佳实践](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/)