Docker入门系列 10. Docker 镜像的制作 优化 精简


1. Docker镜像制作

前面几篇文章已经给大家介绍了 Docker 的基本概念,相信大家也会使用 Docker 运行自己想要的容器了。但是只有学会制作镜像,才能将 Docker 应用到我们的项目中去。

multilayer

docker镜像是除了docker的核心技术之外,也是应用发布的标准格式。一个完整的docker镜像可以支撑一个docker容器的运行,在docker的整个使用过程中,进入一个已经定型的容器之后,就可以在容器中进行操作,最常见的操作就是在容器中安装应用服务,如果要把已经安装的服务进行迁移,就需要把环境及搭建的服务生成新的镜像。

创建镜像的方法有三种,分别是基于已有镜像创建、基于本地模板创建及基于dockerfile创建。接下来会将这几种方法依次写下来。

1.1 镜像制作 - 基于已有镜像创建

基于已有镜像创建主要是使用docker commit命令,本质上就是把一个容器里面运行的程序及该程序的运行环境打包起来生成新的镜像。

命令格式如下:

docker  commit  [选项]  容器ID/名称  仓库名称:[标签]

常用选项如下:

  • -m:说明信息;
  • -a:作者信息;
  • -p:生成过程中停止容器的运行

基于已有镜像创建新的镜像举例:

启动一个镜像,在容器里做修改,然后将修改后的容器提交为新的镜像,需要记住该容器的ID号,如下:

[root@localhost ~]# docker ps -a                #查看当前运行的容器
CONTAINER ID        IMAGE                      COMMAND                  CREATED             STATUS              PORTS                       NAMES
c037e7a5734b        docker.io/sameersbn/bind   "/sbin/entrypoint...."   19 seconds ago      Up 7 seconds        53/tcp, 10000/tcp, 53/udp   hopeful_clarke
[root@localhost ~]# docker exec -it c037e7a5734b /bin/bash            #进入该容器
root@c037e7a5734b:/# echo 11111111111 > /etc/a.txt               #随便写入一个文件
root@c037e7a5734b:/# exit                      #退出该容器
exit
[root@localhost ~]# docker commit -m "newnamed" -a "ljz" c037e7a5734b docker:mynamed
#使用docker commit命令创建一个新的镜像
sha256:e178f320e4821642bed66d0e61e8a85eedd841cb8a3a84db3d38e7d92d844eae
[root@localhost ~]# docker images | grep mynamed          #查看新创建的镜像
docker                        mynamed             e178f320e482        11 seconds ago      323 MB
[root@localhost ~]# docker create -it docker:mynamed /bin/bash       #基于新创建的镜像创建一个容器
dc37cf2d6ef754200aea067d7a15c83713f2488dac0913013373809633266f07
[root@localhost ~]# docker ps -a               #获取刚刚创建的容器ID号,下面标红的就是
CONTAINER ID        IMAGE                      COMMAND                  CREATED             STATUS              PORTS                       NAMES
'dc37cf2d6ef7'        docker:mynamed             /sbin/entrypoint....   11 seconds ago      Created                                         pensive_williams
c037e7a5734b        docker.io/sameersbn/bind   /sbin/entrypoint....   6 minutes ago       Up 5 minutes        53/tcp, 10000/tcp, 53/udp   hopeful_clarke
[root@localhost ~]# docker start dc37cf2d6ef7            #启动刚创建的容器
dc37cf2d6ef7
[root@localhost ~]# docker exec -it dc37cf2d6ef7 /bin/bash          #查看在之前容器里写入的文件是否存在
root@dc37cf2d6ef7:/# cat /etc/a.txt 
11111111111
#可以看到新创建的容器中,有在之前容器中创建的文件,说明镜像更改成功

1.2 镜像制作 - 基于本地模板创建

通过导入操作系统模板文件可以生成镜像,模板可以从OPENVZ开源项目下载,或者 https://wiki.openvz.org/Download/template/precreated ,优先使用OPENVZ开源项目那个链接;

基于本地模板创建举例:

下载centos 7的迷你版系统模板,使用docker导入命令导入为本地镜像:

[root@localhost ~]# wget https://download.openvz.org/template/precreated/centos-7-x86_64-minimal.tar.gz             #下载centos7的模板压缩包
                           .................. #省略部分内容
[root@localhost ~]# ls | grep centos*                  #确认已经下载centos7的模板压缩包
centos-7-x86_64-minimal.tar.gz
[root@localhost ~]# cat centos-7-x86_64-minimal.tar.gz | docker import - docker:new  
#使用docker导入命令导入为本地镜像
sha256:c065d5c0571df48eba3b95b1302494b596cf9f67c24eacc82ff75a9e9c2b7622
[root@localhost ~]# docker images | grep new         #查看导入的镜像
docker                        new                 c065d5c0571d        56 minutes ago      435 MB
#至此,可使用这个镜像创建容器并部署需要的功能,进行使用了。

1.3 镜像制作 - 基于dockerfile创建

1.3.1 Dockerfile 概念

我们使用 Dockerfile 定义镜像,依赖镜像来运行容器,因此 Dockerfile 是镜像和容器的关键,Dockerfile 可以非常容易的定义镜像内容。Docker 通过读取 Dockerfile 中的指令自动构建图像。

Dockerfile 由一系列命令和参数构成的脚本,这些命令应用于基础镜像并最终创建一个新的镜像。它们简化了从头到尾的流程并极大的简化了部署工作。Dockerfile 从 FROM 命令开始,紧接着跟随者各种方法,命令和参数。其产出为一个新的可以用于创建容器的镜像。

  • Docker 镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。镜像不包含任何动态数据,其内容在构建之后也不会被改变。

  • 镜像的定制实际上就是定制每一层所添加的配置、文件。如果我们可以把每一层修改、安装、构建、操作的命令都写入一个脚本,用这个脚本来构建、定制镜像,可以解决构建过程无法重复的问题、镜像构建透明性的问题、体积的问题。

  • Dockerfile 是一个文本文件,其内包含了一条条的指令(Instruction),每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。有了 Dockerfile,当我们需要定制自己额外的需求时,只需在 Dockerfile 上添加或者修改指令,重新生成 image 即可,省去了敲命令的麻烦。

1.3.2 Dockerfile 文件格式

Dockerfile 分为四部分:基础镜像信息、维护者信息、镜像操作指令、容器启动执行指令。一开始必须要指明所基于的镜像名称,接下来一般会说明维护者信息;后面则是镜像操作指令,例如 RUN 指令。每执行一条RUN 指令,镜像添加新的一层,并提交;最后是 CMD 指令,来指明运行容器时的操作命令。

Dockerfile文件格式如下:

## Dockerfile文件格式
# This dockerfile uses the ubuntu image
# VERSION 2 - EDITION 1
# Author: docker_user
# Command format: Instruction [arguments / command] ..
# 1、第一行必须指定 基础镜像信息
FROM ubuntu
# 2、维护者信息
MAINTAINER docker_user docker_user@email.com
# 3、镜像操作指令
RUN echo "deb http://archive.ubuntu.com/ubuntu/ raring main universe">>/etc/apt/sources.list
RUN apt-get update && apt-get install -y nginx
RUN echo "\ndaemon off;">>/etc/nginx/nginx.conf
# 4、容器启动执行指令
CMD /usr/sbin/nginx

在编写dockerfile时,有严格的格式需要遵循:第一行必须使用FROM指令指明所基于的镜像名称;之后使用MAINTAINER指令说明维护该镜像的用户信息;然后是镜像操作相关指令,如RUN指令,每运行一条指令,都会给基础镜像添加新的一层;最后使用CMD指令来指定启动容器时要运行的命令操作。

dockerfile有十几条命令可用于构建镜像,其中常见的指令如下:

指令含义
FROM 镜像指定新镜像所基于的镜像,第一条指令必须为FROM 指令,每创建一个镜像就需要一条FROM指令
MAINTAINER 名字说明镜像的维护人员信息
RUN 命令在所基于的镜像上执行命令,并提交到新镜像中
CMD[“要运行的程序”,”参数1”,”参数2”]指令启动容器时要运行的命令或脚本,Dockerfile只能有一条CMD命令,如果指定多条则只能最后一条被执行
EXPOSE 端口号指定新镜像加载到Docker时要开启的端口
ENV 环境变量变量值设置一个环境变量的值,会被后面的RUN使用
ADD 源文件/目录目标文件/目录将源文件复制到目标文件,源文件要与Dockerfile位于相同目录中,或者是一个URL
COPY 源文件/目录目标文件/目录将本地主机上的文件/目录复制到目标地点,源文件/目录要与Dockerfile在相同的目录中
VOLUME[“目录”]在容器中创建一个挂载点
USER 用户名/UID指定运行容器的用户
WORKDIR 路径为后续的RUN、CMD、ENTRYPOINT指定工作目录
ONBUILD 命令指定所生成的镜像作为一个基础镜像时所要运行的命令

1.3.3 构建镜像

docker build 命令会根据 Dockerfile 文件及上下文构建新 Docker 镜像。构建上下文是指 Dockerfile 所在的本地路径或一个URL(Git仓库地址)。构建上下文环境会被递归处理,所以构建所指定的路径还包括了子目录,而URL还包括了其中指定的子模块。

将当前目录做为构建上下文时,可以像下面这样使用docker build命令构建镜像:

docker build .
Sending build context to Docker daemon 6.51 MB
...

说明:构建会在 Docker 后台守护进程(daemon)中执行,而不是 CLI中。构建前,构建进程会将全部内容(递归)发送到守护进程。大多情况下,应该将一个空目录作为构建上下文环境,并将 Dockerfile 文件放在该目录下。

在构建上下文中使用的 Dockerfile 文件,是一个构建指令文件。为了提高构建性能,可以通过 .dockerignore文件排除上下文目录下不需要的文件和目录。

在 Docker 构建镜像的第一步,docker CLI 会先在上下文目录中寻找 .dockerignore文件,根据 .dockerignore 文件排除上下文目录中的部分文件和目录,然后把剩下的文件和目录传递给 Docker 服务。

Dockerfile 一般位于构建上下文的根目录下,也可以通过 -f指定该文件的位置:

docker build -f /path/to/a/Dockerfile.

构建时,还可以通过 -t参数指定构建成镜像的仓库、标签。

1.3.4 镜像标签

docker build -t nginx/v3 .

如果存在多个仓库下,或使用多个镜像标签,就可以使用多个 -t参数:

docker build -t nginx/v3:1.0.2-t nginx/v3:latest .

在 Docker 守护进程执行 Dockerfile 中的指令前,首先会对 Dockerfile 进行语法检查,有语法错误时会返回:

docker build -t nginx/v3 .
Sending build context to Docker daemon 2.048 kB
Error response from daemon:Unknown instruction: RUNCMD

1.3.5 缓存

Docker 守护进程会一条一条的执行 Dockerfile 中的指令,而且会在每一步提交并生成一个新镜像,最后会输出最终镜像的ID。生成完成后,Docker 守护进程会自动清理你发送的上下文。 Dockerfile文件中的每条指令会被独立执行,并会创建一个新镜像,RUN cd /tmp等命令不会对下条指令产生影响。 Docker 会重用已生成的中间镜像,以加速docker build的构建速度。以下是一个使用了缓存镜像的执行过程:

$ docker build -t svendowideit/ambassador .
Sending build context to Docker daemon 15.36 kB
Step1/4: FROM alpine:3.2
--->31f630c65071
Step2/4: MAINTAINER SvenDowideit@home.org.au
--->Using cache
--->2a1c91448f5f
Step3/4: RUN apk update && apk add socat && rm -r /var/cache/
--->Using cache
--->21ed6e7fbb73
Step4/4: CMD env | grep _TCP=|(sed 's/.*_PORT_\([0-9]*\)_TCP=tcp:\/\/\(.*\):\(.*\)/socat -t 100000000 TCP4-LISTEN:\1,fork,reuseaddr TCP4:\2:\3 \&/'&& echo wait)| sh
--->Using cache
--->7ea8aef582cc
Successfully built 7ea8aef582cc

构建缓存仅会使用本地父生成链上的镜像,如果不想使用本地缓存的镜像,也可以通过 –cache-from指定缓存。指定后将不再使用本地生成的镜像链,而是从镜像仓库中下载。

1.3.6 寻找缓存的逻辑

Docker 寻找缓存的逻辑其实就是树型结构根据 Dockerfile 指令遍历子节点的过程。下图可以说明这个逻辑。

FROM base_image:version Dockerfile:
+----------+ FROM base_image:version
|base image| RUN cmd1 -->use cache because we found base image
+-----X----+ RUN cmd11 -->use cache because we found cmd1
/ \
/ \
RUN cmd1 RUN cmd2 Dockerfile:
+------++------+ FROM base_image:version
|image1||image2| RUN cmd2 -->use cache because we found base image
+---X--++------+ RUN cmd21 -->notuse cache because there's no child node
/ \ running cmd21, so we build a new image here
/ \
RUN cmd11 RUN cmd12
+-------+ +-------+
|image11| |image12|
+-------+ +-------+

大部分指令可以根据上述逻辑去寻找缓存,除了 ADD 和 COPY 。这两个指令会复制文件内容到镜像内,除了指令相同以外,Docker 还会检查每个文件内容校验(不包括最后修改时间和最后访问时间),如果校验不一致,则不会使用缓存。

除了这两个命令,Docker 并不会去检查容器内的文件内容,比如 RUN apt-get -y update,每次执行时文件可能都不一样,但是 Docker 认为命令一致,会继续使用缓存。这样一来,以后构建时都不会再重新运行 apt-get -y update。

如果 Docker 没有找到当前指令的缓存,则会构建一个新的镜像,并且之后的所有指令都不会再去寻找缓存。

1.3.6 简单示例

接下来用一个简单的示例来感受一下 Dockerfile 是如何用来构建镜像启动容器。我们以定制 nginx 镜像为例,在一个空白目录中,建立一个文本文件,并命名为 Dockerfile:

mkdir mynginx
cd mynginx
vi Dockerfile

构建一个 Dockerfile 文件内容为:

FROM nginx
RUN echo '<h1>Hello, Docker!</h1>'>/usr/share/nginx/html/index.html
vi Dockerfile

这个 Dockerfile 很简单,一共就两行涉及到了两条指令:FROM 和 RUN,FROM 表示获取指定基础镜像,RUN 执行命令,在执行的过程中重写了 nginx 的默认页面信息,将信息替换为:Hello, Docker!。

在 Dockerfile 文件所在目录执行:

docker build -t nginx:v1 .

命令最后有一个. 表示当前目录

构建完成之后,使用 docker images 命令查看所有镜像,如果存在 REPOSITORY 为 nginx 和 TAG 是 v1 的信息,就表示构建成功。

docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx v1 8c92471de2cc6 minutes ago 108.6 MB

接下来使用 docker run 命令来启动容器

docker run --name docker_nginx_v1 -d -p 80:80 nginx:v1

这条命令会用 nginx 镜像启动一个容器,命名为 docker_nginx_v1,并且映射了 80 端口,这样我们可以用浏览器去访问这个 nginx 服务器: http://192.168.0.54/,页面返回信息:

这样一个简单使用 Dockerfile 构建镜像,运行容器的示例就完成了!

2. Docker镜像的精简与优化

精简Docker镜像的好处很多,不仅可以节省存储空间和带宽,还能减少安全隐患。优化镜像大小的手段多种多样,因服务所使用的基础开发语言不同而有差异。我们将介绍精简Docker镜像的几种通用方法。

精简Docker镜像大小的必要性

Docker镜像由很多镜像层(Layers)组成(最多127层),镜像层依赖于一系列的底层技术,比如文件系统(filesystems)、写时复制(copy-on-write)、联合挂载(union mounts)等技术,你可以查看Docker社区文档以了解更多有关Docker存储驱动的内容,这里就不再赘述技术细节。总的来说,Dockerfile中的每条指令都会创建一个镜像层,继而会增加整体镜像的尺寸。

下面是精简Docker镜像尺寸的好处:

  • 减少构建时间
  • 减少磁盘使用量
  • 减少下载时间
  • 因为包含文件少,攻击面减小,提高了安全性
  • 提高部署速度

2.1 优化基础镜像

优化基础镜像的方法就是选用合适的更小的基础镜像,常用的 Linux 系统镜像一般有 Ubuntu、CentOs、Alpine,其中Alpine更推荐使用。大小对比如下:

➜ docker images
REPOSITORY         TAG             IMAGE ID            CREATED             SIZE
ubuntu             latest        74f8760a2a8b        8 days ago          82.4MB
alpine             latest        11cd0b38bc3c        2 weeks ago         4.41MB
centos               7           49f7960eb7e4        7 weeks ago         200MB
debian             latest        3bbb526d2608        8 days ago          101MB

Alpine是一个高度精简又包含了基本工具的轻量级Linux发行版,基础镜像只有4.41M,各开发语言和框架都有基于Alpine制作的基础镜像,强烈推荐使用它。

查看上面的镜像尺寸对比结果,你会发现最小的镜像也有4.41M,那么有办法构建更小的镜像吗?答案是肯定的,例如 gcr.io/google_containers/pause-amd64:3.1 镜像仅有742KB。为什么这个镜像能这么小?在为大家解密之前,再推荐两个基础镜像:

1. scratch镜像

scratch是一个空镜像,只能用于构建其他镜像,比如你要运行一个包含所有依赖的二进制文件,如Golang程序,可以直接使用scratch作为基础镜像。现在给大家展示一下上文提到的Google pause镜像Dockerfile:

FROM scratch
ARG ARCH
ADD bin/pause-${ARCH} /pause
ENTRYPOINT ["/pause"]

Google pause镜像使用了scratch作为基础镜像,这个镜像本身是不占空间的,使用它构建的镜像大小几乎和二进制文件本身一样大,所以镜像非常小。当然在我们的Golang程序中也会使用。对于一些Golang/C程序,可能会依赖一些动态库,你可以使用自动提取动态库工具,比如ldd、linuxdeployqt等提取所有动态库,然后将二进制文件和依赖动态库一起打包到镜像中。

2. busybox镜像

scratch是个空镜像,如果希望镜像里可以包含一些常用的Linux工具,busybox镜像是个不错选择,镜像本身只有1.16M,非常便于构建小镜像。

2.2 串联 Dockerfile 指令

大家在定义Dockerfile时,如果太多的使用RUN指令,经常会导致镜像有特别多的层,镜像很臃肿,而且甚至会碰到超出最大层数(127层)限制的问题,遵循 Dockerfile 最佳实践,我们应该把多个命令串联合并为一个 RUN(通过运算符&&和/ 来实现),每一个 RUN 要精心设计,确保安装构建最后进行清理,这样才可以降低镜像体积,以及最大化的利用构建缓存。

下面是一个优化前Dockerfile:

FROM ubuntu

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"]

构建镜像,名称叫 test/test:0.1。

我们对Dockerfile做优化,优化后Dockerfile:

FROM ubuntu

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"]

构建镜像,名称叫 test/test:0.2。

对比两个镜像大小:

root@k8s-master:/tmp/iops#docker images
REPOSITORY       TAG           IMAGE ID            CREATED             SIZE
test/test        0.2         58468c0222ed        2 minutes ago       98.1MB
test/test        0.1         e496cf7243f2        6 minutes ago       307MB
root@k8s-master:/tmp/iops#

可以看到,将多条RUN命令串联起来构建的镜像大小是每条命令分别RUN的三分之一。

提示:为了应对镜像中存在太多镜像层,Docker 1.13版本以后,提供了一个压扁镜像功能,即将 Dockerfile 中所有的操作压缩为一层。这个特性还处于实验阶段,Docker默认没有开启,如果要开启,需要在启动Docker时添加-experimental 选项,并在Docker build 构建镜像时候添加 –squash 。我们不推荐使用这个办法,请在撰写 Dockerfile 时遵循最佳实践编写,不要试图用这种办法去压缩镜像。

2.3 使用多阶段构建

Dockerfile中每条指令都会为镜像增加一个镜像层,并且你需要在移动到下一个镜像层之前清理不需要的组件。实际上,有一个Dockerfile用于开发(其中包含构建应用程序所需的所有内容)以及一个用于生产的瘦客户端,它只包含你的应用程序以及运行它所需的内容。这被称为“建造者模式”。Docker 17.05.0-ce版本以后支持多阶段构建。使用多阶段构建,你可以在Dockerfile中使用多个FROM语句,每条FROM指令可以使用不同的基础镜像,这样您可以选择性地将服务组件从一个阶段COPY到另一个阶段,在最终镜像中只保留需要的内容。

下面是一个使用COPY –from 和 FROM … AS … 的Dockerfile:

# Compile
FROM golang:1.9.0 AS builder
WORKDIR /go/src/v9.git...com/.../k8s-monitor
COPY . .
WORKDIR /go/src/v9.git...com/.../k8s-monitor
RUN make build
RUN mv k8s-monitor /root
Package
Use scratch image
FROM scratch
WORKDIR /root/
COPY --from=builder /root .
EXPOSE 8080
CMD ["/root/k8s-monitor"] 

构建镜像,你会发现生成的镜像只有上面COPY 指令指定的内容,镜像大小只有2M。这样在以前使用两个Dockerfile(一个Dockerfile用于开发和一个用于生产的瘦客户端),现在使用多阶段构建就可以搞定。

2.4 构建业务服务镜像技巧

Docker在build镜像的时候,如果某个命令相关的内容没有变化,会使用上一次缓存(cache)的文件层,在构建业务镜像的时候可以注意下面两点:

  • 不变或者变化很少的体积较大的依赖库和经常修改的自有代码分开;

  • 因为cache缓存在运行Docker build命令的本地机器上,建议固定使用某台机器来进行Docker build,以便利用cache。

下面是构建Spring Boot应用镜像的例子,用来说明如何分层。其他类型的应用,比如Java WAR包,Nodejs的npm模块等,可以采取类似的方式。

  1. 在Dockerfile所在目录,解压缩maven生成的jar包。

    unzip <path-to-app-jar>.jar -d app
  2. Dockerfile我们把应用的内容分成4个部分COPY到镜像里面:其中前面3个基本不变,第4个是经常变化的自有代码。最后一行是解压缩后,启动spring boot应用的方式。

    FROM openjdk:8-jre-alpine
    
    LABEL maintainer "opl-xws@xiaomi.com"
    COPY app/BOOT-INF/lib/ /app/BOOT-INF/lib/
    COPY app/org /app/org
    COPY app/META-INF /app/META-INF
    COPY app/BOOT-INF/classes /app/BOOT-INF/classes
    EXPOSE 8080
    CMD ["/usr/bin/java", "-cp", "/app", "org.springframework.boot.loader.JarLauncher"]

    这样在构建镜像时候可大大提高构建速度。

2.5 其他优化办法

1、RUN命令中执行apt、apk或者yum类工具技巧

如果在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 …。将所有yum install 任务放在一条RUN命令上执行,从而减少镜像层的数量;

  • 组件的安装和清理要串联在一条指令里面,如 apk –update add php7 && rm -rf /var/cache/apk/* ,因为Dockerfile的每条指令都会产生一个文件层,如果将apk add … 和 rm -rf … 命令分开,清理无法减小apk命令产生的文件层的大小。 Ubuntu或Debian可以使用 rm -rf /var/lib/apt/lists/* 清理镜像中缓存文件;CentOS等系统使用yum clean all 命令清理。

2、压缩镜像

Docker自带的一些命令还能协助压缩镜像,比如export和import

$ docker run -d test/test:0.2
$ docker export 747dc0e72d13 | docker import - test/test:0.3

使用这种方式需要先将容器运行起来,而且这个过程中会丢失镜像原有的一些信息,比如:导出端口,环境变量,默认指令。

查看这两个镜像history信息,如下,可以看到test/test:0.3 丢失了所有的镜像层信息:

root@k8s-master:/tmp/iops# docker history test/test:0.3
IMAGE               CREATED             CREATED BY          SIZE                COMMENT
6fb3f00b7a72        15 seconds ago                          84.7MB              Imported from -
root@k8s-master:/tmp/iops# docker history test/test:0.2
IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
58468c0222ed        2 hours ago         /bin/sh -c #(nop)  CMD ["redis-server"]         0B       
1af7ffe3d163        2 hours ago         /bin/sh -c echo "==> Install curl and helper…   15.7MB   
8bac6e733d54        2 hours ago         /bin/sh -c #(nop)  ENV TARBALL=http://downlo…   0B       
793282f3ef7a        2 hours ago         /bin/sh -c #(nop)  ENV VER=3.0.0                0B       
74f8760a2a8b        8 days ago          /bin/sh -c #(nop)  CMD ["/bin/bash"]            0B       
<missing>           8 days ago          /bin/sh -c mkdir -p /run/systemd && echo 'do…   7B
<missing>           8 days ago          /bin/sh -c sed -i 's/^#\s*\(deb.*universe\)$…   2.76kB
<missing>           8 days ago          /bin/sh -c rm -rf /var/lib/apt/lists/*          0B
<missing>           8 days ago          /bin/sh -c set -xe   && echo '#!/bin/sh' > /…   745B    
<missing>           8 days ago          /bin/sh -c #(nop) ADD file:5fabb77ea8d61e02d…   82.4MB   
root@k8s-master:/tmp/iops#

文章作者: Baymax
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Baymax !
评论
 上一篇
Flutter 学习计划 1. Flutter 简介与开发环境 Flutter 学习计划 1. Flutter 简介与开发环境
一、 Flutter简介Flutter 是 Google推出并开源的移动应用开发框架,主打跨平台、高保真、高性能。开发者可以通过 Dart语言开发 App,一套代码同时运行在 iOS 和 Android平台。 Flutter提供了丰富的组件
2020-07-30
下一篇 
Docker入门系列  9. Docker 容器的数据管理  数据卷 绑定挂载 临时挂载 Docker入门系列 9. Docker 容器的数据管理 数据卷 绑定挂载 临时挂载
因为容器技术,重新构建并运行应用变得容易且快速,容器化应用基本是无需进一步配置的(已经打包好所有依赖并且已配置),只需PULL,然后Deploy(也许还需要一份docker-compose.yml)就好。我们无需太担心硬件,OS,库以及应用
2020-05-25
  目录