基础镜像 | 再谈 Docker 瘦身
Docker
镜像是 Docker 运维的基本单元。 优化镜像体积,能够:
- 缩短部署时的下载时间;
- 提升安全性,因为可供攻击的目标更少;
- 减少故障恢复时间;
- 节省存储开销。
正确认识分层和共享
认清与理解 Docker 镜像的层次结构,是进行镜像优化的前提和基础。
分层
docker 镜像的存储结构是分层次的。 无论底层的文件系统(可选可配置)是基于快照还是分块。
镜像构建的过程里,每步操作都产生一个只读的层:可重用,可复制,但是不可修改。 层次叠加,形成了包含历史(history)结构的镜像。 镜像启动后,产生了容器(container)。容器也是一个单独的层,可读写,但是不持久化,易丢失。
共享
共享,以分层为基础。 以一个镜像为祖先,分别用多种方式做不同操作,各自生成新层,则形成多个新的镜像,祖先镜像则成为共享的部分。 继承自共同的祖先,实现了对共同内容的利用。
镜像优化
对单个镜像体积做优化,采取方式有两类思路:
- 选择尽可能小的基础镜像
- 打包尽可能少的内容入镜像
- 如:去除或减少编译、测试等中间步骤内容
- 使用单行命令
工程实践中,还要考虑多镜像复用,尽量:
- 将能复用的部分放进模版镜像。
选择最小化基础镜像,创建模版镜像
基础镜像需要满足的基本条件:
- 正确的 init 系统。(discuss)
- 日志。Docker 的应用程序日志一般默认在标准输出,而系统进程仍然会往 /dev/log 写数据。需要有日志处理程序 syslog
- 后台任务如 cron
- 工具进程如 sshd(慎重选择)
这些需求,与在传统的服务器和虚拟机上做部署,是相似的。
如果仅仅基于这种相似性,就选择了使用 CentOS/Debian/Ubuntu 做为基础镜像,那么就有问题了。 首先,这些传统发行版的 Docker 镜像并不能符合基础需求; 其次,这些发行版的体积太大。
所有,仍然需要选择较小的发行版制作初始镜像。
市面上可选的有:
- phusion/baseimage
- baseimage-amzn
- Alpine
- busybox
- …
使用共同的模版镜像
如果有多个业务需要运维,则在公共基础镜像基础上,构建私有的模版镜像。 共同的模版镜像,能够:
- 实现镜像共享
- 减少重复工作。解决边缘问题
- 减少开发时间。专注于上层应用
- 减少编译时间。
- 减少部署时间。基于镜像的层共享
模版镜像的目标是抽取业务的公共部分。
持续的审查和优化
上线后,需要经常关注这些问题:
- 某个组件放在模版镜像中,现在已经很少用到了?或者反之。
- 新业务使用到某个公共镜像,效果还不错,要不要推而广之?
解决这些问题,还需要经常复审镜像服用的效率。 简单的方案,可以对各类环境中的模版使用情况进行统计,尽量合并相似的镜像,将使用率较高的组件吸收进模版镜像。
常用考评指标:
- 开发效率
- 编译效率
- 传输效率
- 存储效率
要不要使用 --flatten
--flatten
是现在还是一个测试特性,可以减少镜像总体积,然而与复用的原则有冲突。
需要更加实际情况去做考虑。
为什么选择最小的基础镜像(如alpine)之后,应用镜像反而变大了?
这是因为应用程序需要一系列依赖。 如果使用最小的基础镜像,在制作应用镜像时,比如需要去安装依赖。 安装步骤可能会引入更多不必要的元素。 所以反而体积会超过已包含所需依赖的公共镜像。
centos -> app
alpine -> dependencies -> app # Too much dependencies