利用Squash功能 (1.13 后新增的试验功能)

  1. Docker Daemon 进程启动前,要将 experimental 参数设置为 true;
    docker v20版本是通过修改/etc/docker/daemon.json开启:
    {
    "experimental": true
    }
  2. 编写的dockerfile没有与之前没有差异
  3. 编译时,增加--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-cache达到同样效果;
  • 执行 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 后的可执行文件以及所需的动态链接库拷贝到最终镜像中就行了。