原文发表于kubernetes中文社区,为作者原创 原文地址

当你拉取Docker镜像时,你会注意到它被拉取成不同的层。另外,当你创建自己的Docker镜像时,也会创建多个层。在本文中,我们将更好地理解Docker层。

1.什么是Docker层?

Docker镜像由几层组成。每层都对应 Dockerfile中的特定指定。Docker层创建指令有: RUNCOPYADD。其他指令将创建中间层,并且不会影响镜像的大小。

我们看一个例子:创建一个Spring Boot MVC应用程序, 并且在Maven构建中创建Docker镜像。这些资源可从 GitHub获得。

我们使用的 feature/dockerbenchsecurity 分支,它是 master 分支中更安全的版本。

Dockerfile如下:

FROM openjdk:10-jdk
VOLUME /tmp
RUN useradd -d /home/appuser -m -s /bin/bash appuser
USER appuser
HEALTHCHECK --interval=5m --timeout=3s CMD curl -f http://localhost:8080/actuator/health/ || exit 1
ARG JAR_FILE
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]

我们使用mvn clean install 命令构建的应用程序,还将创建Docker镜像。为了简洁起见,我们没有列出openjdk:10-jdk镜像中所有图层的拉取 。

Image will be built as mydeveloperplanet/mykubernetesplanet:0.0.3-SNAPSHOT
Step 1/8 : FROM openjdk:10-jdk
Pulling from library/openjdk
Image 16e82e17faef: Pulling fs layer
...
Image a9448aba0bc3: Pull complete
Digest: sha256:9f17c917630d5e95667840029487b6561b752f1be6a3c4a90c4716907c1aad65
Status: Downloaded newer image for openjdk:10-jdk---> b11e88dd885d
Step 2/8 : VOLUME /tmp---> Running in 21329898c3a6
Removing intermediate container 21329898c3a6---> b6f9ca000de6
Step 3/8 : RUN useradd -d /home/appuser -m -s /bin/bash appuser---> Running in 82645047e6e7
Removing intermediate container 82645047e6e7---> 04f6b2716819
Step 4/8 : USER appuser---> Running in 697b663dadbb
Removing intermediate container 697b663dadbb---> eaf6b8af5709
Step 5/8 : HEALTHCHECK --interval=5m --timeout=3s CMD curl -f http://localhost:8080/actuator/health/ || exit 1---> Running in f420b9d060c5
Removing intermediate container f420b9d060c5---> 77f95436a3ff
Step 6/8 : ARG JAR_FILE---> Running in 60b9d25ad2ac
Removing intermediate container 60b9d25ad2ac---> 135fa7df95ac
Step 7/8 : COPY ${JAR_FILE} app.jar---> 63c18567012b
Step 8/8 : ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]---> Running in 79203446934a
Removing intermediate container 79203446934a---> 8e2b049f9783
Successfully built 8e2b049f9783
Successfully tagged mydeveloperplanet/mykubernetesplanet:0.0.3-SNAPSHOT

镜像构建发生了什么?我们注意到创建了多个docker层,并且大多数层都被删除(删除中间容器)。那么,为什么说删除中间容器而不是删除中间层呢?这是因为构建步骤是在中间容器中执行的。完成构建步骤后,旧可以删除中间容器。除此之外,层是只读的。一层包含前一层和当前层之间的差异。在这些层的顶层,有一个可写层(当前层),被称为容器层。如前所述,只有特定的指令会创建一个新层。

我们来看一下我们的Docker镜像:

$ docker image ls
REPOSITORY                              TAG               IMAGE ID        CREATED               SIZE
mydeveloperplanet/mykubernetesplanet    0.0.3-SNAPSHOT    8e2b049f9783    About a minute ago    1GB
openjdk

看看我们的mykubernetesplanet 镜像的构建历史记录 :

$ docker history 8e2b049f9783
IMAGE           CREATED               CREATED BY                                      SIZE    COMMENT
8e2b049f9783    About a minute ago    /bin/sh -c #(nop) ENTRYPOINT ["java" "-Djav…    0B
63c18567012b    About a minute ago    /bin/sh -c #(nop) COPY file:2a5b71774c60e0f6…   17.4MB
135fa7df95ac    About a minute ago    /bin/sh -c #(nop) ARG JAR_FILE                  0B
77f95436a3ff    2 minutes ago         /bin/sh -c #(nop) HEALTHCHECK &{["CMD-SHELL…    0B
eaf6b8af5709    2 minutes ago         /bin/sh -c #(nop) USER appuser                  0B
04f6b2716819    2 minutes ago         /bin/sh -c useradd -d /home/appuser -m -s /b…   399kB
b6f9ca000de6    2 minutes ago         /bin/sh -c #(nop) VOLUME [/tmp]                 0B
b11e88dd885d    2 months ago          /bin/sh -c #(nop) CMD ["jshell"]                0B
<missing>       2 months ago          /bin/sh -c set -ex; if [ ! -d /usr/share/m…     697MB
<missing>       2 months ago          /bin/sh -c #(nop) ENV JAVA_DEBIAN_VERSION=1…

在这里我们注意到,与预期一致,中间容器的大小确实为0B。Dockerfile中只有 RUN and COPY 指令会影响Docker镜像的大小。openjdk:10-jdk镜像的各层 也被列举出,显示为missing ,这意味着这些层可以建立在不同的系统上,并且在本地不可用。

2.重新创建Docker镜像

如果在不对源代码进行任何更改的情况下,再次运行Maven构建,会发生什么情况?

Image will be built as mydeveloperplanet/mykubernetesplanet:0.0.3-SNAPSHOT
Step 1/8 : FROM openjdk:10-jdk
Pulling from library/openjdk
Digest: sha256:9f17c917630d5e95667840029487b6561b752f1be6a3c4a90c4716907c1aad65
Status: Image is up to date for openjdk:10-jdk
---> b11e88dd885d
Step 2/8 : VOLUME /tmp
---> Using cache
---> b6f9ca000de6
...
Step 6/8 : ARG JAR_FILE
---> Using cache
---> 135fa7df95ac
Step 7/8 : COPY ${JAR_FILE} app.jar
---> 409f2fee0cde
Step 8/8 : ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
---> Running in 75f07955bbc8
Removing intermediate container 75f07955bbc8
---> e5d7b72aad05
Successfully built e5d7b72aad05
Successfully tagged mydeveloperplanet/mykubernetesplanet:0.0.3-SNAPSHOT

从上面我们注意到,镜像第一层与我们先前的版本相同–层ID相同。在日志中,我们注意到docker层是从缓存中提取的。

在步骤7中,docker使用新ID创建了一个新层。因为我们确实创建了一个新的JAR文件,Docker也识别它是一个新文件,因此创建了一个新层。

在步骤8中,还创建了一个新层,因为它是建立在新层之上的。

让我们再次列出Docker镜像:

$ docker image ls
REPOSITORY                              TAG               IMAGE ID        CREATED           SIZE
mydeveloperplanet/mykubernetesplanet    0.0.3-SNAPSHOT    e5d7b72aad05    13 seconds ago    1GB
<none>                                  <none>            8e2b049f9783    5 minutes ago     1GB
openjdk                                 10-jdk            b11e88dd885d    2 months ag

0.0.3-SNAPSHOT 中可以看到上次docker构建的镜像ID。而且,上个版本镜像ID和标签都已被删除,并以none 关键字表示 。这称为 虚悬镜像(dangling image) 。我们将在本文结尾处对虚悬镜像(dangling image) 进行更详细的说明。

当我们查看新创建的镜像的历史时,我们注意到两个顶层是新的,就像docker构建日志中一样:

$ docker history e5d7b72aad05
IMAGE           CREATED           CREATED BY                                      SIZE     COMMENT
e5d7b72aad05    38 seconds ago    /bin/sh -c #(nop) ENTRYPOINT ["java" "-Djav…    0B
409f2fee0cde    42 seconds ago    /bin/sh -c #(nop) COPY file:4b04c6500d340c9e…   17.4MB
135fa7df95ac    6 minutes ago     /bin/sh -c #(nop) ARG JAR_FILE                  0B
...

当我们更改应用源代码时,结果是相同的,因为在这种情况下,还会生成一个新的JAR文件。

$ docker image ls
REPOSITORY                              TAG               IMAGE ID        CREATED           SIZE
mydeveloperplanet/mykubernetesplanet    0.0.3-SNAPSHOT    eced642d4f5c    30 seconds ago    1GB
<none>                                  <none>            e5d7b72aad05    3 minutes ago     1GB
<none>                                  <none>            8e2b049f9783    8 minutes ago     1GB
openjdk                                 10-jdk            b11e88dd885d    2 months ago      987MB
$ docker history eced642d4f5c
IMAGE           CREATED               CREATED BY                                      SIZE    COMMENT
eced642d4f5c    About a minute ago    /bin/sh -c #(nop) ENTRYPOINT ["java" "-Djav…    0B
44a9097b8bad    About a minute ago    /bin/sh -c #(nop) COPY file:1d5276778b53310e…   17.4MB
135fa7df95ac    9 minutes ago         /bin/sh -c #(nop) ARG JAR_FILE                  0B
...

3.镜像有多大?

通过docker image ls命令的输出 ,我们注意到两个虚悬镜像(dangling image),大小为1 GB。这对存储有什么影响?

首先,我们需要知道镜像数据的存储位置。使用以下命令可以检索存储位置:

$ docker image inspect eced642d4f5c..."GraphDriver": {"Data": {"LowerDir": "/var/lib/docker/overlay2/655be8bea8e54c31ebb7e3adf05db227d194a49c1e2f95552d593d623e024b92/diff:/var/lib/docker/overlay2/993f77b91a487e19b3696836efee23c8a17791d71096d348c54c38fba3dc8478/diff:/var/lib/docker/overlay2/d62d6ca8ce1960d057e11d163d458563628e5a337de06455e714900f72005589/diff:/var/lib/docker/overlay2/cabdf4de81557a8047e3670bd2eecb5449de7de8fe9dfd4ad0c81d7dd2c61e9d/diff:/var/lib/docker/overlay2/062bf99d6a563ee2ef7824ec02ff5cd09fb8721cb23f6a55f8927edc2607f9c1/diff:/var/lib/docker/overlay2/ba024c24b20771dbf409f501423273e13225cf675f30896720cadace1c7be000/diff:/var/lib/docker/overlay2/d15f4477b53508127bebd1224c9ea09cd767f7db7429ffb1e8aa79b01ab77506/diff:/var/lib/docker/overlay2/ea434348d6625bc49875d0aba886b24ff0e1e204a350099981dcfc4029bc688d/diff:/var/lib/docker/overlay2/05e003c0522c7049110aa3ce09814ff2167da1e53ec83481fef03324011ce6e6/diff","MergedDir": "/var/lib/docker/overlay2/205b55ee2f0e06394b6d17067338845410609887ccd18f53bf0646ff60452ffb/merged","UpperDir": "/var/lib/docker/overlay2/205b55ee2f0e06394b6d17067338845410609887ccd18f53bf0646ff60452ffb/diff","WorkDir": "/var/lib/docker/overlay2/205b55ee2f0e06394b6d17067338845410609887ccd18f53bf0646ff60452ffb/work"},"Name": "overlay2"},...

可以看到,我们的Docker镜像存储在 /var/lib/docker/overlay2。我们可以通过查看overlay2 目录的大小,来了解它占用的存储空间:

$ du -sh -m overlay2
1059 overlay2

openjdk:10-jdk 镜像大小是987 MB,JAR文件为17.4 MB,总大小应约为987 MB + 3 * 17.4 MB(两个虚悬镜像(dangling image) 和一个真实的镜像)。这大约是1,040 MB。

可以看出,我们不能简单地添加所有Docker镜像的大小来确认实际存储大小。

其中的差异是由于存在中间镜像。

这些可以显示如下:

$ docker images -a
REPOSITORY                              TAG               IMAGE ID        CREATED       SIZE
mydeveloperplanet/mykubernetesplanet    0.0.3-SNAPSHOT    eced642d4f5c    7 days ago    1GB
<none>                                  <none>            44a9097b8bad    7 days ago    1GB
<none>                                  <none>            e5d7b72aad05    7 days ago    1GB
<none>                                  <none>            409f2fee0cde    7 days ago    1GB
<none>                                  <none>            8e2b049f9783    7 days ago    1GB
<none>                                  <none>            63c18567012b    7 days ago    1GB
<none>                                  <none>            135fa7df95ac    7 days ago    987MB
<none>                                  <none>            77f95436a3ff    7 days ago    987MB
<none>                                  <none>            eaf6b8af5709    7 days ago    987MB
<none>                                  <none>            04f6b2716819    7 days ago    987MB
<none>                                  <none>            b6f9ca000de6    7 days ago    987MB
openjdk                                 10-jdk            b11e88dd885d    2 months ago

4. 如何去掉虚悬镜像(dangling image)

虚悬镜像(dangling image) ,我们不需要它们,它们还占用存储空间。我们如何去掉呢?

首先,列出悬空的镜像:

$ docker images -f dangling=true
REPOSITORY    TAG     IMAGE ID        CREATED       SIZE
<none>        <none>  e5d7b72aad05    7 days ago    1GB
<none>        <none>  8e2b049f9783    7 days ago    1GB

我们可以使用以下 docker rmi 命令删除镜像:

$ docker rmi e5d7b72aad05
Deleted: sha256:e5d7b72aad054100d142d99467c218062a2ef3bc2a0994fb589f9fc7ff004afe
Deleted: sha256:409f2fee0cde9b5144f8e92887b61e49f3ccbd2b0e601f536941d3b9be32ff47
Deleted: sha256:2162a2af22ee26f7ac9bd95c39818312dc9714b8fbfbeb892ff827be15c7795b

或者,你可以使用 docker image prune 命令来执行此操作。

现在已经删除了虚悬镜像(dangling image),让我们看一下overlay2 目录的大小 :

$ du -sh -m overlay2
1026 overlay2

我们节省了33 MB。

似乎还不算什么,但是当你经常构建Docker镜像时,随着时间的流逝,它会显著增长。

5.总结

在本文中,我们尝试更好地理解Docker层。我们注意到构建Docker镜像时会创建中间层,如果我们不定期清理中间层,那么虚悬镜像(dangling image)会一直保留在我们的系统中。我们还查看了系统上Docker镜像的大小。

译文链接: https://dzone.com/articles/docker-layers-explained

Docker层和虚悬镜像(dangling image)介绍相关推荐

  1. 【Docker】虚悬镜像(Dangling Image)介绍和处理方法

    本期目录 1. 虚悬镜像介绍 2. 查看本地所有虚悬镜像 3. 删除全部虚悬镜像 4. 人为构建虚悬镜像 专栏精选文章 1. 虚悬镜像介绍 虚悬镜像 (Dangling Image) 指的是仓库名 ( ...

  2. Docker help命令和镜像命令、虚悬镜像dangling images的创建和删除

    目录 1. help命令 1.1 docker help命令 1.2 docker具体命令的help示例 2. 镜像命令 2.1 docker images---查看本地镜像 2.2 docker s ...

  3. Docker虚悬镜像

    什么是虚悬镜像 仓库名.标签都是 none的镜像就是虚悬镜像. 造成原因 在镜像服务运行时 ,直接强制删除该镜像,就会造成docker中出现虚悬镜像 如何删除 1.查找虚悬镜像进程ID:docker ...

  4. linux 上删除docker 虚悬镜像

    场景 使用Dockerfile定制镜像-定制Tomcat为例: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/99093026 虚 ...

  5. docker 虚悬镜像 ( 悬空镜像 ) :镜像没有仓库名或没有标签

    前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家.点击跳转到教程. 1. 我们在build镜像的过程中,可能会产生一些临时的不具有名称也没有作用的镜像他们的名称一般都是 ...

  6. Docker容器 - 虚悬镜像

    目录 是什么 怎么查看 删除 在上一篇DockerFile解析的试验过程中,瞎捣鼓出来了一个虚悬镜像,这篇来讲讲这是个什么东西. 是什么 名称和TAG都为 <none> 的镜像被称为虚悬镜 ...

  7. 清除Docker中所有为<none>的镜像(虚悬镜像)

    docker images | grep none | awk '{print $3}' | xargs docker rmi 或 docker image prune

  8. Docker学习——三大组件【镜像、容器、仓库】的应用(二)

    2019独角兽企业重金招聘Python工程师标准>>> 一.使用 Docker 镜像 Docker 运行容器前需要本地存在对应的镜像,如果本地不存在该镜像,Docker 会从镜像仓库 ...

  9. Docker这些none:none的镜像,难道就不配拥有名字吗

    1 前言 欢迎访问南瓜慢说 www.pkslow.com获取更多精彩文章! 搞容器开发一段时间后,想看看都有哪些镜像,执行了一下docker images -a,蒙圈了,有一堆<none> ...

最新文章

  1. 人力资源中最常见的7张报表
  2. 算法_bitmap算法
  3. android 自定义搜索框edittext,Android编程自定义搜索框实现方法【附demo源码下载】...
  4. java多线程 生产者消费者_java多线程之生产者消费者经典问题 - 很不错的范例
  5. 一名全栈工程师的必备“百宝箱”
  6. 生成jsp验证码的代码详解(servlet版)
  7. php中接口验证失败,支付宝手机接口,服务端PHP验证失败,求助
  8. python抓取网站内容_python抓取网站内容详细
  9. java web 精仿微博_【Java】盘点 Github 上的高仿 app 项目,B站 微博 微信等等
  10. 精心备战30天,三天斩获阿里offer,揭秘面试流程及我的学习方向
  11. 你真的知道如何使用Target.Count吗?
  12. 4.4 Kali与windows xp之间搭建TFTP服务
  13. 大数据-什么是云计算技术,云技术用什么语言开发
  14. c语言tft屏浮点数显示,编个可显示中文的TFT程序
  15. 槐香拂过,你如期而至
  16. python语言通俗理解_慢步学习,python语言编程,来扯扯语言的学习理解
  17. Tomcat提示指定的服务未安装Unable to open the service 'tomcat'
  18. 花生壳内网穿透(Linux版)
  19. 跑分软件测试的游戏是,性能跑分 常规软件测试解析
  20. SQL server2008安装教程(详细)

热门文章

  1. 关于音乐歌词下载到MP3里乱码问题的解决
  2. 付费专栏-付费课程-【购买须知】
  3. python中的argv和argc
  4. openstack创建的云主机不能访问外网,不能ping www.baidu.com
  5. python求是不是完数_python求完数
  6. UI设计就业前景广阔无边,入门正当时!
  7. 四级地址库 国家标准的行政区划代码 省市区街道
  8. vue里面的model
  9. 使用beecloud和easychat 做微信H5 微信公众号支付遇到的坑
  10. ArcFace[2019-CVPR]