让我们创建一个更小的Docker镜像
我将我的博客文章进行了交叉发布:https://www.ianlewis.org/jp/small-docker-images
最近,容器技术很流行,我对各种工具都感兴趣地去探索。其中最受欢迎的是大家都喜爱的Docker。Docker可以通过docker run轻松创建容器的执行环境,同时可以通过docker build轻松构建容器镜像。Docker的镜像构建是基于类似于Makefile的Dockerfile文件,按照其中的命令顺序进行构建。
以中国为例,
FROM debian:jessie
RUN apt-get update
RUN apt-get install -y python
RUN mkdir -p /data
VOLUME ["/data"]
WORKDIR /data
EXPOSE 8000
CMD [ "python", "-m", "SimpleHTTPServer", "8000" ]
这是一个非常简单的Docker镜像,但是实际构建并查看镜像的大小后:
VIRTUAL SIZE
167.4 MB
这个应用本来很简单,可是挺大的呢。
为什么这么大?
Dockerfile中的第一行写着FROM debian:jessie,这是为了将整个Debian 8.x映像包含进去,所以映像变得很大。此外,如果Dockerfile中需要构建gcc或g++之类的工具,则会将构建所需的工具和库添加到映像中,尽管这些在运行应用程序时是不必要的,这会增加相当大的体积。
那么,如果我们要创建一个Redis的图像,最清晰、概念最好的方法就是这样做。
FROM debian:jessie
RUN apt-get update
RUN apt-get install -y gcc libc6-dev make
RUN curl -sSL "http://download.redis.io/releases/redis-3.0.5.tar.gz" -o
redis.tar.gz
RUN mkdir -p /usr/src/redis
RUN tar -xzf redis.tar.gz -C /usr/src/redis
RUN make -C /usr/src/redis
RUN make -C /usr/src/redis install
# 全部削除
RUN rm -f redis.tar.gz
RUN rm -f /usr/src/redis
RUN apt-get purge -y --auto-remove gcc libc6-dev make
...
每次执行Docker的RUN命令时,都会保存图像的”层”,并将其状态缓存到图像中。即使最后删除了这些层,但由于在中途提交了层,最终图像的大小仍然保持不变。
如何将其缩小?
由于每次执行RUN命令都会进行提交,所以必须用一个RUN命令来执行所有的操作。
以下是从实际的Redis Dockerfile中提取出的代码片段。
ENV REDIS_VERSION 3.0.5
ENV REDIS_DOWNLOAD_URL http://download.redis.io/releases/redis-3.0.5.tar.gz
ENV REDIS_DOWNLOAD_SHA1 ad3ee178c42bfcfd310c72bbddffbbe35db9b4a6
# for redis-sentinel see: http://redis.io/topics/sentinel
RUN buildDeps='gcc libc6-dev make' \
&& set -x \
&& apt-get update && apt-get install -y $buildDeps --no-install-recommends \
&& rm -rf /var/lib/apt/lists/* \
&& mkdir -p /usr/src/redis \
&& curl -sSL "$REDIS_DOWNLOAD_URL" -o redis.tar.gz \
&& echo "$REDIS_DOWNLOAD_SHA1 *redis.tar.gz" | sha1sum -c - \
&& tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1 \
&& rm redis.tar.gz \
&& make -C /usr/src/redis \
&& make -C /usr/src/redis install \
&& rm -r /usr/src/redis \
&& apt-get purge -y --auto-remove $buildDeps
用这个运行命令,可以安装、构建、清理建筑用库,所有操作都在一个命令中完成。这样做可以避免镜像变得过大。
依存关系的地狱
大部分的Docker映像是从Debian或Ubuntu映像继承而来的,它们变得很大的原因是,当我们在容器中执行应用程序时,如果该应用程序所依赖的动态链接库没有全部包含在内,那么就会自然崩溃。因此,在构建映像时使用apt-get等工具会很方便。
在谷歌中,所有应用程序都以容器方式运行。为了执行容器应用程序,将不必要的内容放入容器之外并构建为静态二进制文件,只将该二进制文件和必要的文件放入镜像中,这是谷歌的做法。这种方式使镜像更小,对依赖库的管理更加简单,我认为这相当聪明。这也是Go默认编译为静态二进制文件的原因之一。