如何构建优质的 Docker 镜像
1.优质镜像的判断标准
-
功能完备 例如:
- Android SDK 镜像应能直接编译项目,而无需先更新容器。
- MySQL 容器应提供初始化数据库和用户的途径。
-
极简
容器的优势在于隔离应用(即便不为安全,也能避免污染主机文件系统)。与其在主机安装 Node.js 或 Java 开发工具包,不如牺牲少量磁盘空间或性能换取隔离性。因此,镜像应仅包含必要内容,这样既扩展性强又减少故障点。
- 透明
即公开 Dockerfile,便于审查构建过程并自定义。
遗憾的是,Docker 注册中心难以发现或评估”优质”镜像。用户常需先执行 docker pull <...>
,然后困惑为何 10MB 的 Node 二进制文件需要 10 个文件系统层,最终占用 700MB 虚拟环境。
2.构建优质 Docker 镜像的实践
-
基于 Debian 构建镜像 Ubuntu:14.04 镜像为 195MB,而 debian:wheezy 仅 85MB,且多余的 Ubuntu 空间并无显著价值。极端情况下甚至可基于 2MB 的 busybox 构建,如 progrium/logspout 镜像(仅 14MB)。
-
避免无谓安装构建工具 构建工具体积庞大且编译缓慢。若仅需安装现成软件,应优先使用二进制包(如 Node.js 官方预编译包)或系统包管理器(如 Redis)。仅在以下情况考虑安装构建工具:
- 需要特定版本(如 Debian 仓库中的 Redis 版本过旧)
- 需自定义编译选项
- 需通过 npm 安装二进制模块 第三种情况建议单独创建基于最小 Node 镜像的 “npm 安装器” 镜像。
-
及时清理临时文件 以下 Dockerfile 生成 109MB 镜像:
FROM debian:wheezyRUN apt-get update && apt-get install -y wgetRUN wget http://cachefly.cachefly.net/10mb.testRUN rm 10mb.test
而等效的以下写法仅 99MB:
FROM debian:wheezyRUN apt-get update && apt-get install -y wgetRUN 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 -
- 清理包管理器缓存
apt-get update
会生成冗余的/var/lib/apt/lists/
数据。清理后可节省数 MB 空间:
FROM debian:wheezyRUN apt-get update && apt-get install -y wget && rm -rf /var/lib/apt/lists/*
- 固定软件包版本 虽然镜像不可变,但 Dockerfile 可能因外部依赖变化而产生不同输出。通过固定版本降低影响:
Package: redis-serverVersions:2:2.4.14-1...
- 合并关联命令
将关联命令合并为单个
RUN
指令,既能提升构建缓存效率,又能减少文件系统层数。使用\
增强可读性:
RUN apt-get update && \ apt-get install -y \ wget=1.13.4-3+deb7u1 \ ca-certificates=20130119 \ ...
- 使用环境变量避免重复
参考官方 Node.js 镜像的 Dockerfile,通过
ENV
定义变量并在后续指令中引用:
ENV NODE_VERSION 0.10.32RUN 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.进阶思考
- 基于 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。
- 编写测试脚本
这一实践源自 Aptible 团队。通过将测试套件集成到 Dockerfile 中,可确保镜像具备预期功能。虽然个人使用场景可能无需此步骤,但对于公开镜像或团队协作项目,它能显著提升可维护性和明确性。
测试示例(摘自 jbergknoff/sass 仓库):
#!/bin/shecho --- Tests ---echo -n "验证是否安装 sassc 3.2.1... "sass -v | grep sassc | grep "3.2.1" > /dev/null[ "$?" -ne 0 ] && echo 失败 && exit 1echo 通过
测试失败将导致构建中断。如需测试框架,推荐轻量级的 bats(支持跳过测试、setup/teardown 等特性)。
- 善用脚本封装逻辑
当 RUN 指令因清理操作变得冗长时,可将逻辑拆解为独立脚本。例如 jbergknoff/sass 的 build.sh:
#!/bin/sh
apk --update add git build-basegit clone https://github.com/sass/sassccd sassc && git clone https://github.com/sass/libsassSASS_LIBSASS_PATH=/sassc/libsass make
mv bin/sassc /usr/bin/sassapk del git build-baserm -rf /sassc /var/cache/apk/*
通过单条 RUN 指令执行脚本,既能保持镜像精简(避免中间层残留文件),又能提升 Dockerfile 可读性。此技巧尤其适用于源码编译场景。
4.总结
这些实践进一步优化了镜像的轻量化与可维护性,延续了前文”最小化”与”透明化”的核心原则。Docker 生态的演进不断验证着这些基础理念的价值。