Docker镜像大小优化的方法
利用Squash功能 (1.13 后新增的试验功能)
- Docker Daemon 进程启动前,要将 experimental 参数设置为 true;
docker v20版本是通过修改/etc/docker/daemon.json开启:{ "experimental": true }
- 编写的dockerfile没有与之前没有差异
- 编译时,增加--squash参数
docker build --squash -t xxx:xxx .
squash功能一方面压缩了镜像的大小,另一方面保存了镜像的构建信息,但是该方法属于实验特性,需要谨慎使用。
使用较小的基础镜像
- 系统镜像使用Ubuntu、CentOs、Alpine、scratch、buybox等
- 许多官网镜像都提供slim版本
上述方式的缺点在于,基础镜像由于体积小,可能会缺失我们需要的依赖或工具,需要逐步补充,耗费的工作量较大。
Dockerfile指令优化
指令拼接
定义 Dockerfile 时,多次使用RUN指令会生成多个镜像层,使得镜像臃肿。应该将多个指令拼接合并为一个RUN(通过运算符&&和/来实现).
如果在 RUN 命令中执行 apt、apk 或者 yum ,可以借助指令自身的配置选项减少镜像层数量及镜像大小。
- 执行
apt-get install -y
时增加选项--no-install-recommends
,可以不用安装建议性(非必须)的依赖,也可以在执行apk add
时添加选项--no-cach
e达到同样效果; - 执行
yum install -y
时候, 同时安装多个工具,比如yum install -y gcc gcc-c++ make
; - 组件的安装和清理要串联在一条指令里面,如
apt-get install zip && rm -rf /var/cache/apk/*
利用export和import指令
# 启动一个容器
docker run -d --name test test:2.0
# 利用export和import将容器导出变为镜像
docker export test | docker import - test:3.0
该方法也能有效降低镜像大小,但会丢失镜像的构建信息。
多阶段构建
Docker 17.05 版本后,支持多阶段构建 ,Dockerfile 中编写多条 FROM 指令,构建期间会生成临时的“中间层镜像”,然后使用 COPY --from 从中间层镜像中拷贝数据, 只保留最终的镜像,这样可以构建出层数少,体积小的镜像。
比如我在原始大小 21.5M 的 Redis 镜像的基础上添加一个 18M 的 tar 包,然后把包解压到 /tmp 目录,我如果按照你这种写法,先 COPY 然后解 tar 包再删除 tar 包的话,那么最终镜像体积将增大 36M:
[root@centos7 docker]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
redis 3.2-alpine e34cb61ff391 10 months ago 21.5MB
[root@centos7 docker]# pwd
/tmp/docker
[root@centos7 docker]# ll
总用量 17604
-rw-r--r-- 1 root root 138 5月 22 10:48 Dockerfile
-rw-r--r-- 1 root root 18022400 5月 22 10:31 test.tar
[root@centos7 docker]# du -sh *
4.0K Dockerfile
18M test.tar
[root@centos7 docker]# cat Dockerfile
FROM redis:3.2-alpine
COPY . /tmp
RUN tar -xvf /tmp/test.tar -C /tmp \
&& rm -rf /tmp/test.tar
[root@centos7 docker]# docker build -t test:1.0 ./
[root@centos7 docker]# docker images -a
REPOSITORY TAG IMAGE ID CREATED SIZE
test 1.0 ad75bb647078 35 minutes ago 57.6MB
<none> <none> 552c9ac7b32b 35 minutes ago 39.5MB
redis 3.2-alpine e34cb61ff391 10 months ago 21.5MB
由于 Docker 镜像采用联合文件系统,这里 COPY/RUN 每一个指令都在上面一条指令的基础上添加一个新层,新的一层并不能改变前面一层的大小,所以尽管我在 RUN 指令后面加了 rm -rf /tmp/test.tar 但是可以看到,最终构建出来的镜像是 57.6M,还是增加了 18M * 2 的大小,COPY test.tar 这条指令导致增加的体积没能减小。
现在改写 Dockerfile 采用多阶段构建,最终镜像体积只增大 18M:
[root@centos7 docker]# cat Dockerfile
FROM redis:3.2-alpine as build
COPY . /tmp
RUN tar -xvf /tmp/test.tar -C /tmp
FROM redis:3.2-alpine
COPY --from=build /tmp/var /tmp/var
[root@centos7 docker]# docker build -t test:2.0 ./
[root@centos7 docker]# docker images -a
REPOSITORY TAG IMAGE ID CREATED SIZE
test 2.0 fb453b6b8b7e 20 seconds ago 39.5MB
<none> <none> c5939eabcf76 21 seconds ago 57.6MB
<none> <none> 14cb09455b18 22 seconds ago 57.6MB
test 1.0 ad75bb647078 35 minutes ago 57.6MB
<none> <none> 552c9ac7b32b 35 minutes ago 39.5MB
redis 3.2-alpine e34cb61ff391 10 months ago 21.5MB
这里可以看到 Dockerfile 里面包含了多个 FROM 指令,然后第一个 FROM 作为 构建使用取了个别名 as "build", 把 COPY/RUN 这些构建阶段的指令都写在这里,然后我在第二阶段使用 COPY --from=build 只要拷贝解 tar 后的内容到需要的镜像里(示例中解 tar 出的文件名是 var),最终镜像大小 39.5M,只增加了解 tar 包多出的 18M,第一阶段临时镜像的大小增加多大都没关系了。
所以,你这里只需要在原来的基础上新增 COPY --from 指令把 make install 后的可执行文件以及所需的动态链接库拷贝到最终镜像中就行了。
本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。