Reprint: http://www.dockerinfo.net/3328.html
introduce
Some time ago, Netease Honeycomb launched the Honeycomb Logo T-shirt, which was made with Docker image. The most amazing thing is that its final image size is only 585 bytes.
$ docker images | grep hub.c.163.com/public/logo REPOSITORY TAG IMAGE ID CREATED SIZE hub.c.163.com/public/logo latest 6fbdd13cd20411 days ago 585 B
A lot of techniques for reducing mirroring are used, especially for the optimization and simplification of C programs. But we usually develop more than C language, and even some images are not packaged by ourselves (such as downloading public images), so is there some general way to simplify Docker images? The answer is yes, and even some mirrors can be reduced by 98%. The benefits of reducing the image size are self-evident. It not only saves storage space, but also saves bandwidth and speeds up transmission. Well, then please follow me to learn how to simplify the Docker image step by step.
Layers
Before starting to make a mirror, first understand the principle of mirroring, and the most important concept is the mirror layer (Layers). The mirror layer relies on a series of underlying technologies, such as filesystems, copy-on-write, union mounts, etc. Fortunately, you can learn these technologies in many places, The technical details will not be repeated here.
In general, you need to keep this in mind:
In a Dockerfile, each instruction creates an image layer, which in turn increases the overall image size.
for example:
FROM busybox RUN mkdir /tmp/foo RUN dd if=/dev/zero of=/tmp/foo/bar bs=1048576 count=100 RUN rm /tmp/foo/bar
The above Dockerfile does the following things:
- Based on an official base image busybox (only more than 1M)
- Create a folder (/tmp/foo) and a file (bar)
- 100M size is allocated for the file
- delete this large file
In fact, it didn't do anything in the end, let's build it into an image to see (build can refer to the first issue):
docker build -t busybox:test .
再让我们来对比下原生的 busybox 镜像大小和我们生成的镜像大小:
$ docker images | grep busyboxbusybox test 896c63dbdb96 2 seconds ago 106 MB busybox latest 2b8fd9751c4c 9 weeks ago 1.093 MB
出乎意料的是,却生成了 106 MB 的镜像。
多出了 100 M,这是为何?这点和 git 类似(都用到了Copy-On-Write技术),我用 git 做了如下两次提交(添加了又删除),请问 A_VERY_LARGE_FILE 还在 git 仓库中吗?
$ git add A_VERY_LARGE_FILE
$ git commit
$ git rm A_VERY_LARGE_FILE
$ git commit
答案是: 在的 ,并且会占用仓库的大小。Git 会保存每一次提交的文件版本,而 Dockerfile 中每一条指令都可能增加整体镜像的大小,即使它最终什么事情都没做。
精简步骤
了解了镜像层知识,有助于我们接下来制作精简镜像。这里开始,以最常用的开源缓存软件 Redis 为例,从一步步试验,来介绍如何制作更精简的 Docker 镜像。
步骤 1:初始化构建 Redis 镜像
直接上 Dockerfile :
FROM ubuntu:trusty ENV VER 3.0.0 ENV TARBALL http://download.redis.io/releases/redis-$VER.tar.gz # ==> Install curl and helper tools... RUN apt-get update RUN apt-get install -y curl make gcc # ==> Download, compile, and install... RUN curl -L $TARBALL | tar zxv WORKDIR redis-$VER RUN make RUN make install #... # ==> Clean up... WORKDIR / RUN apt-get remove -y --auto-remove curl make gcc RUN apt-get clean RUN rm -rf /var/lib/apt/lists/* /redis-$VER #... CMD ["redis-server"]
结合注释,读起来并不困难,用到的都是常规的几个命令,简要介绍如下:
- FROM:顶头写,指定一个基础镜像,此处基于 ubuntu:trusty
- ENV:设置环境变量,这里设置了 VER 和 TARBALL 两个环境变量
- RUN:最常用的 Dockerfile 指令,用于运行各种命令,这里调用了 8 次 RUN 指令
- WORKDIR:指定工作目录,相当于指令 cd
- CMD:指定镜像默认执行的命令,此处默认执行 redis-server 命令来启动 redis
执行构建:
$ docker build -t redis:lab-1 .
注:国内网络,更新下载可能会较慢
查看大小:
动辄就有 300多 M 的大小,不能忍,下面我们开始一步步优化。
步骤 2: 优化基础镜像
方法:选用更小的基础镜像。
常用的 Linux 系统镜像一般有 ubuntu、centos、debian,其中debian 更轻量,而且够用,对比如下:
REPOSITORY TAG IMAGE ID VIRTUAL SIZE --------------- ------ ------------ ------------ centos 7 214a4932132a 215.7 MB centos 6 f6808a3e4d9e 202.6 MB ubuntu trusty d0955f21bf24 188.3 MB ubuntu precise 9c5e4be642b7 131.9 MB debian jessie 65688f7c61c4 122.8 MB debian wheezy 1265e16d0c28 84.96 MB
替换 debian:jessie 作为我们的基础镜像。
优化 Dockerfile:
FROM debian:jessie #...
执行构建:
$ docker build -t redis:lab-2 .
查看大小:
减少了42M,稍有成效,但并不明显。细心的同学应该发现,只有 122 MB 的 debian 基础镜像,构建后增加到了 305 MB,看来这里面肯定有优化的空间,如何优化就要用到我们开头说到的 Image Layer 知识了。
步骤 3:串联 Dockerfile 指令
方法: 串联你的 Dockerfile 指令(一般是 RUN 指令)。
Dockerfile 中的 RUN 指令通过 && 和 / 支持将命令串联在一起,有时能达到意想不到的精简效果。
优化 Dockerfile:
FROM debian:jessie ENV VER 3.0.0 ENV TARBALL http://download.redis.io/releases/redis-$VER.tar.gz RUN echo "==> Install curl and helper tools..." && \ apt-get update && \ apt-get install -y curl make gcc && \ \ echo "==> Download, compile, and install..." && \ curl -L $TARBALL | tar zxv && \ cd redis-$VER && \ make && \ make install && \ ... echo "==> Clean up..." && \ apt-get remove -y --auto-remove curl make gcc && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* /redis-$VER #... CMD ["redis-server"]
构建:
$ docker build -t redis:lab-3 .
查看大小:
哇!一下子减少了 50%,效果明显啊!这是最常用的一个精简手段了。
步骤 4:压缩你的镜像
方法:试着用命令或工具压缩你的镜像。
docker 自带的一些命令还能协助压缩镜像,比如 export 和 import
$ docker run -d redis:lab-3 $ docker export 71b1c0ad0a2b | docker import - redis:lab-4
但麻烦的是需要先将容器运行起来,而且这个过程中你会丢失镜像原有的一些信息,比如:导出端口,环境变量,默认指令。
所以一般通过命令行来精简镜像都是实验性的,那么这里再推荐一个小工具: docker-squash。用起来更简单方便,并且不会丢失原有镜像的自带信息。
下载安装:
https://github.com/jwilder/docker-squash#installation(复制此链接到浏览器打开)
压缩操作:
$ docker save redis:lab-3 \ | sudo docker-squash -verbose -t redis:lab-4 \ | docker load
注: 该工具在 Mac 下并不好使,请在 Linux 下使用
对比大小:
Well, it doesn't look like much from here, so all I can say is try and don't expect too much.