如何构建优质的 Docker 镜像

2025年8月18日

1.优质镜像的判断标准

容器的优势在于隔离应用(即便不为安全,也能避免污染主机文件系统)。与其在主机安装 Node.js 或 Java 开发工具包,不如牺牲少量磁盘空间或性能换取隔离性。因此,镜像应仅包含必要内容,这样既扩展性强又减少故障点。

即公开 Dockerfile,便于审查构建过程并自定义。

遗憾的是,Docker 注册中心难以发现或评估”优质”镜像。用户常需先执行 docker pull <...>,然后困惑为何 10MB 的 Node 二进制文件需要 10 个文件系统层,最终占用 700MB 虚拟环境。

2.构建优质 Docker 镜像的实践

  1. 基于 Debian 构建镜像 Ubuntu:14.04 镜像为 195MB,而 debian:wheezy 仅 85MB,且多余的 Ubuntu 空间并无显著价值。极端情况下甚至可基于 2MB 的 busybox 构建,如 progrium/logspout 镜像(仅 14MB)。

  2. 避免无谓安装构建工具 构建工具体积庞大且编译缓慢。若仅需安装现成软件,应优先使用二进制包(如 Node.js 官方预编译包)或系统包管理器(如 Redis)。仅在以下情况考虑安装构建工具:

    • 需要特定版本(如 Debian 仓库中的 Redis 版本过旧)
    • 需自定义编译选项
    • 需通过 npm 安装二进制模块 第三种情况建议单独创建基于最小 Node 镜像的 “npm 安装器” 镜像。
  3. 及时清理临时文件 以下 Dockerfile 生成 109MB 镜像:

FROM debian:wheezy
RUN apt-get update && apt-get install -y wget
RUN wget http://cachefly.cachefly.net/10mb.test
RUN rm 10mb.test

而等效的以下写法仅 99MB:

FROM debian:wheezy
RUN apt-get update && apt-get install -y wget
RUN wget http://cachefly.cachefly.net/10mb.test && rm 10mb.test

此外,可通过管道避免临时文件,例如:

wget -O - http://nodejs.org/dist/v0.10.32/node-v0.10.32-linux-x64.tar.gz | tar zxf -
  1. 清理包管理器缓存 apt-get update 会生成冗余的 /var/lib/apt/lists/ 数据。清理后可节省数 MB 空间:
FROM debian:wheezy
RUN apt-get update && apt-get install -y wget && rm -rf /var/lib/apt/lists/*
  1. 固定软件包版本 虽然镜像不可变,但 Dockerfile 可能因外部依赖变化而产生不同输出。通过固定版本降低影响:
Package: redis-server
Versions:
2:2.4.14-1
...
  1. 合并关联命令 将关联命令合并为单个 RUN 指令,既能提升构建缓存效率,又能减少文件系统层数。使用 \ 增强可读性:
RUN apt-get update && \
apt-get install -y \
wget=1.13.4-3+deb7u1 \
ca-certificates=20130119 \
...
  1. 使用环境变量避免重复 参考官方 Node.js 镜像的 Dockerfile,通过 ENV 定义变量并在后续指令中引用:
ENV NODE_VERSION 0.10.32
RUN curl -SLO "http://nodejs.org/dist/v
$NODE_VERSION/node-v$
NODE_VERSION-linux-x64.tar.gz" \
&& tar -xzf "node-v$NODE_VERSION-linux-x64.tar.gz" -C /usr/local --strip-components=1 \
&& rm "node-v$NODE_VERSION-linux-x64.tar.gz"

3.进阶思考

  1. 基于 Alpine 构建镜像

Alpine Linux 镜像(Docker Hub 链接)仅 5MB,却包含维护良好的现代化软件仓库(通过 apk 管理)。它是构建微型应用容器的理想选择(如 6MB 的 Redis 镜像、17MB 的 Node.js 0.10 镜像、6MB 的 PostgreSQL 客户端镜像)。

但需注意:Alpine 使用 musl libc,多数动态链接的 Linux 二进制文件无法直接运行。如需自行编译,可安装 Alpine 的 build-base 包(功能类似 Debian 的 build-essential)。默认 shell 为 ash,但可通过包管理器安装 bash。

  1. 编写测试脚本

这一实践源自 Aptible 团队。通过将测试套件集成到 Dockerfile 中,可确保镜像具备预期功能。虽然个人使用场景可能无需此步骤,但对于公开镜像或团队协作项目,它能显著提升可维护性和明确性。

测试示例(摘自 jbergknoff/sass 仓库):

#!/bin/sh
echo --- Tests ---
echo -n "验证是否安装 sassc 3.2.1... "
sass -v | grep sassc | grep "3.2.1" > /dev/null
[ "$?" -ne 0 ] && echo 失败 && exit 1
echo 通过

测试失败将导致构建中断。如需测试框架,推荐轻量级的 bats(支持跳过测试、setup/teardown 等特性)。

  1. 善用脚本封装逻辑

当 RUN 指令因清理操作变得冗长时,可将逻辑拆解为独立脚本。例如 jbergknoff/sass 的 build.sh:

#!/bin/sh
apk --update add git build-base
git clone https://github.com/sass/sassc
cd sassc && git clone https://github.com/sass/libsass
SASS_LIBSASS_PATH=/sassc/libsass make
mv bin/sassc /usr/bin/sass
apk del git build-base
rm -rf /sassc /var/cache/apk/*

通过单条 RUN 指令执行脚本,既能保持镜像精简(避免中间层残留文件),又能提升 Dockerfile 可读性。此技巧尤其适用于源码编译场景。

4.总结

这些实践进一步优化了镜像的轻量化与可维护性,延续了前文”最小化”与”透明化”的核心原则。Docker 生态的演进不断验证着这些基础理念的价值。

准备好开始您的 AI 之旅了吗?

读完这篇文章,想必您对 AI 技术有了更深的了解。现在就来体验共绩算力,让您的想法快速变成现实。

✓ 已有 10 万 + 开发者在使用

✓ 99.9% 服务可用性

✓ 开箱即用的容器托管