Docker引擎分层解析

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: 当我们拉取Docker Image时,如果仔细观察的话,你就会发现:它被拉成不同的层。另外,当然,我们创建自己的Docker Image时,也会创建多个层。在本文中,我们将尝试更好地去探究Docker层次的秘密。

      当我们拉取Docker Image时,如果仔细观察的话,你就会发现:它被拉成不同的层。另外,当然,我们创建自己的Docker Image时,也会创建多个层。在本文中,我们将尝试更好地去探究Docker层次的秘密。

      Docker Image由多层组成。每层都对应于Dockerfile中的某些指令。以下说明创建了一个图层:RUN、COPY、ADD。 让我们来看一个例子,我们将使用预先创建的Spring Boot MVC应用程序,并在Maven构建中创建Docker镜像。Dockerfile文件如下:


[administrator@JavaLangOutOfMemory luga ]% vi Dockerfile
FROM openjdk:10-jdk
VOLUME /tmp
RUN useradd -d /home/luga -m -s /bin/bash luga
USER luga
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"]

      使用docker build 进行构建Docker镜像,当然,也可以使用mvn clean install命令行进行操作。如下图所示:(因涉及项目中信息,故对部分数据进行参数化处理)


[administrator@JavaLangOutOfMemory luga ]% docker build -f Dockerfile -t luga-demo-springboot .
[+] Building 2718.8s (9/9) FINISHED                                                                                                                                   
 => [internal] load build definition from Dockerfile                                                                                                             0.0s
 => => transferring dockerfile: 358B                                                                                                                             0.0s
 => [internal] load .dockerignore                                                                                                                                0.0s
 => => transferring context: 2B                                                                                                                                  0.0s
 => [internal] load metadata for docker.io/library/openjdk:10-jdk                                                                                               13.2s
 => [auth] library/openjdk:pull token for registry-1.docker.io                                                                                                   0.0s
 => [1/3] FROM docker.io/library/openjdk:10-jdk@sha256:9f17c917630d5e95667840029487b6561b752f1be6a3c4a90c4716907c1aad65                                       2703.4s
 => => resolve docker.io/library/openjdk:10-jdk@sha256:9f17c917630d5e95667840029487b6561b752f1be6a3c4a90c4716907c1aad65                                          0.0s
 => => sha256:9f17c917630d5e95667840029487b6561b752f1be6a3c4a90c4716907c1aad65 2.37kB / 2.37kB                                                                   0.0s
 => => sha256:923d074ef1f4f0dceef68d9bad8be19c918d9ca8180a26b037e00576f24c2cb4 2.00kB / 2.00kB                                                                   0.0s
 => => sha256:b11e88dd885d8b2621d448f3d2099068d181c5c778c2ab0cf0f61b573fa429b7 4.99kB / 4.99kB                                                                   0.0s
 => => sha256:16e82e17faef9e90ceefcd8175e9899edce768aa6008cc16dd1e3fe7d3b88bb8 49.26MB / 49.26MB                                                                39.8s
 => => sha256:117dc02416a34c62e28a030f27828f2f31af6b8b1f02c85b009a1ffb390d01dc 7.38MB / 7.38MB                                                                  21.3s
 => => sha256:7e4c717259ac9c550efbbf41c6fe0dc9598046f4bfd4b398deb63f7a0c19cb3f 9.78MB / 9.78MB                                                                  51.9s
 => => sha256:7a518b8f48be0323544739e175909b24be833b4a2bf39939f91fbcc2ab0e48a4 50.64MB / 50.64MB                                                                42.5s
 => => sha256:add32d44f708eff65a3ad250f4c87b8c3c912f0240c8a0acd76a88df6cd3ebdc 883.70kB / 883.70kB                                                              44.2s
 => => sha256:a0158fa0854313538e2807a9098aa49227ea46a44c3fc9120e9dc74e45baa408 237B / 237B                                                                      43.4s
 => => extracting sha256:16e82e17faef9e90ceefcd8175e9899edce768aa6008cc16dd1e3fe7d3b88bb8                                                                       12.9s
 => => sha256:9eb8cb7aab2603e0ca5015d7f512794de6fdf25375be1f135a3b890c5a17a298 131B / 131B                                                                      44.2s
 => => sha256:a9448aba0bc3432cb23b86d5c0fe808e4f0c8b7fdbe46fa26cdada8bcd582aa9 378.28MB / 378.28MB                                                            2654.1s
 => => extracting sha256:117dc02416a34c62e28a030f27828f2f31af6b8b1f02c85b009a1ffb390d01dc                                                                        2.1s
 => => extracting sha256:7e4c717259ac9c550efbbf41c6fe0dc9598046f4bfd4b398deb63f7a0c19cb3f                                                                        3.3s
 => => extracting sha256:7a518b8f48be0323544739e175909b24be833b4a2bf39939f91fbcc2ab0e48a4                                                                       14.2s
 => => extracting sha256:add32d44f708eff65a3ad250f4c87b8c3c912f0240c8a0acd76a88df6cd3ebdc                                                                        0.4s
 => => extracting sha256:a0158fa0854313538e2807a9098aa49227ea46a44c3fc9120e9dc74e45baa408                                                                        0.0s
 => => extracting sha256:9eb8cb7aab2603e0ca5015d7f512794de6fdf25375be1f135a3b890c5a17a298                                                                        0.0s
 => => extracting sha256:a9448aba0bc3432cb23b86d5c0fe808e4f0c8b7fdbe46fa26cdada8bcd582aa9                                                                       47.3s
 => [internal] load build context                                                                                                                                0.3s
 => => transferring context: 1.36MB                                                                                                                              0.2s
 => [2/3] RUN useradd -d /home/luga -m -s /bin/bash luga                                                                                                         1.6s
 => [3/3] COPY  app.jar                                                                                                                                          0.1s
 => exporting to image                                                                                                                                           0.1s
 => => exporting layers                                                                                                                                          0.1s
 => => writing image sha256:6bc6d7e6e0b2deaca0451ebd5d78fcbd43036ddf917dc17e29a741565d9070ff                                                                     0.0s
 => => naming to docker.io/library/luga-demo-springboot                                                                                                          0.0s

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


[administrator@JavaLangOutOfMemory luga ] % docker image ls
REPOSITORY                  TAG                                              IMAGE ID       CREATED         SIZE
luga-demo-springboot        latest                                           6bc6d7e6e0b2   21 hours ago    989MB
nacos/nacos-server          latest                                           9c0b55a5ab2c   34 hours ago    935MB
grafana/grafana             latest                                           13afb861111c   2 days ago      187MB
mysql                       5.7                                              cc8775c0fe94   4 days ago      449MB
prom/prometheus             latest                                           53fd5ed1cd48   10 days ago     173MB
openzipkin/zipkin           latest                                           9b4acc3eb019   3 weeks ago     150MB
redis                       latest                                           ef47f3b6dc11   5 weeks ago     104MB
tomee                       latest                                           9e19954eefcc   5 weeks ago     336MB
... ...

[administrator@JavaLangOutOfMemory luga ] % docker histoty 6bc6d7e6e0b2
IMAGE           CREATED               CREATED BY                                      SIZE    COMMENT
6bc6d7e6e0b2   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 luga                  0B
04f6b2716819    2 minutes ago         /bin/sh -c useradd -d /home/luga -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
...

     从上面的命令行,我们注意到,中间容器的大小确实为0B,与预期的一样。Dockerfile中只有RUN和COPY命令会影响Docker镜像的大小。openjdk:10-jdkimage的层也被列出,并由丢失的关键字识别。这仅意味着这些层建立在不同的系统上,并且在本地不可用。

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


Image will be built as luga-demo-springboot:latest
Step 1/8 : FROM openjdk:10-jdk
Pulling from library/openjdk
t: 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 luga-demo-springboot:latest


     我们注意到第一层与我们先前的版本相同。图层ID相同。在日志中,我们注意到图层是从缓存中提取的。在步骤7中,使用新ID创建新层。我们确实创建了一个新的JAR文件,Docker将其解释为一个新文件,因此创建了一个新层。在步骤8中,还创建了一个新层,因为它是建立在新层之上的。

      让我们再次列出Docker镜像:


[administrator@JavaLangOutOfMemory luga ] %docker image ls
REPOSITORY                              TAG               IMAGE ID        CREATED           SIZE
luga-demo-springboot                  latest              d7u9b72aad85    13 seconds ago    1GB
<none>                          <none>        8e2b049f9783    5 minutes ago     1GB
openjdk                                 10-jdk            b11e88dd885d    2 months ago      987MB


     我们的标签latest已收到我们上一个版本的图片ID。我们的旧图片ID的存储库和标签已删除,使用none关键字表示。这称为悬空图像。我们将在本文结尾处对此进行更详细的说明。

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


[administrator@JavaLangOutOfMemory luga ] %docker history d7u9b72aad85
IMAGE           CREATED           CREATED BY                                      SIZE     COMMENT
d7u9b72aad85    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文件。


[administrator@JavaLangOutOfMemory luga ] %docker image ls
REPOSITORY                              TAG               IMAGE ID        CREATED           SIZE
luga-demo-springboot                    latest            eced642d4f5c    30 seconds ago    1GB
<none>                            <none>      d7u9b72aad85    3 minutes ago     1GB
<none>                            <none>      8e2b049f9783    8 minutes ago     1GB
openjdk                                 10-jdk            b11e88dd885d    2 months ago      987MB
[administrator@JavaLangOutOfMemory luga ] %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
...

      现在大小如何?

      让我们仔细看看docker image ls命令的最新输出。我们注意到两个悬挂的图像,大小为1 GB。但这对存储真正意味着什么?首先,我们需要知道图像数据的存储位置。使用以下命令以检索存储位置:


[administrator@JavaLangOutOfMemory luga ] %ocker 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目录的大小,以了解分配的存储空间:


[administrator@JavaLangOutOfMemory luga ] %du -sh -

m

overlay2

1059 overlay2

     openjdk:10-jdk镜像为987 MB。我们镜像中的JAR文件为17.4 MB。总大小应约为987 MB + 3 * 17.4 MB(两个悬挂的镜像和一个真实的镜像)。这大约是1,040 MB。我们可以得出结论,Docker镜像有某种智能存储,我们不能简单地添加所有Docker镜像的大小来检索实际存储大小。差异是由于存在中间图像。这些可以显示如下:


[administrator@JavaLangOutOfMemory luga ] %docker images -a
REPOSITORY                                      TAG                   IMAGE ID        CREATED       SIZE
luga-demo-springboot                          latest                  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  987MB

      我们如何优化这些悬而未决的镜像呢? 我们不再需要它们,它们仅分配存储空间。我们可以使用docker rmi命令删除经常不用的镜像,或者,可以使用docker image prune命令执行此操作,以将其释放。

      在本文中,我们试图更好地理解Docker层。我们注意到,如果我们不定期清洁中间层,则会继续创建中间,并且悬挂的镜像仍然保留在我们的系统中。

相关文章
|
14天前
|
Linux iOS开发 Docker
Docker:容器化技术的领航者 —— 从基础到实践的全面解析
在云计算与微服务架构日益盛行的今天,Docker作为容器化技术的佼佼者,正引领着一场软件开发与部署的革命。它不仅极大地提升了应用部署的灵活性与效率,还为持续集成/持续部署(CI/CD)提供了强有力的支撑。
192 69
|
6天前
|
Cloud Native 持续交付 Docker
深入解析Docker容器化技术及其在生产环境中的应用
深入解析Docker容器化技术及其在生产环境中的应用
10 0
|
2月前
|
图形学 C#
超实用!深度解析Unity引擎,手把手教你从零开始构建精美的2D平面冒险游戏,涵盖资源导入、角色控制与动画、碰撞检测等核心技巧,打造沉浸式游戏体验完全指南
【8月更文挑战第31天】本文是 Unity 2D 游戏开发的全面指南,手把手教你从零开始构建精美的平面冒险游戏。首先,通过 Unity Hub 创建 2D 项目并导入游戏资源。接着,编写 `PlayerController` 脚本来实现角色移动,并添加动画以增强视觉效果。最后,通过 Collider 2D 组件实现碰撞检测等游戏机制。每一步均展示 Unity 在 2D 游戏开发中的强大功能。
84 6
|
2月前
|
存储 安全 虚拟化
深入解析:Docker的架构与组件
【8月更文挑战第27天】
132 5
|
2月前
|
缓存 运维 监控
打造稳定高效的数据引擎:数据库服务器运维最佳实践全解析
打造稳定高效的数据引擎:数据库服务器运维最佳实践全解析
|
2月前
|
开发者 图形学 API
从零起步,深度揭秘:运用Unity引擎及网络编程技术,一步步搭建属于你的实时多人在线对战游戏平台——详尽指南与实战代码解析,带你轻松掌握网络化游戏开发的核心要领与最佳实践路径
【8月更文挑战第31天】构建实时多人对战平台是技术与创意的结合。本文使用成熟的Unity游戏开发引擎,从零开始指导读者搭建简单的实时对战平台。内容涵盖网络架构设计、Unity网络API应用及客户端与服务器通信。首先,创建新项目并选择适合多人游戏的模板,使用推荐的网络传输层。接着,定义基本玩法,如2D多人射击游戏,创建角色预制件并添加Rigidbody2D组件。然后,引入网络身份组件以同步对象状态。通过示例代码展示玩家控制逻辑,包括移动和发射子弹功能。最后,设置服务器端逻辑,处理客户端连接和断开。本文帮助读者掌握构建Unity多人对战平台的核心知识,为进一步开发打下基础。
68 0
|
2月前
|
图形学 机器学习/深度学习 人工智能
颠覆传统游戏开发,解锁未来娱乐新纪元:深度解析如何运用Unity引擎结合机器学习技术,打造具备自我进化能力的智能游戏角色,彻底改变你的游戏体验——从基础设置到高级应用全面指南
【8月更文挑战第31天】本文探讨了如何在Unity中利用机器学习增强游戏智能。作为领先的游戏开发引擎,Unity通过ML-Agents Toolkit等工具支持AI代理的强化学习训练,使游戏角色能自主学习完成任务。文章提供了一个迷宫游戏示例及其C#脚本,展示了环境观察、动作响应及奖励机制的设计,并介绍了如何设置训练流程。此外,还提到了Unity与其他机器学习框架(如TensorFlow和PyTorch)的集成,以实现更复杂的游戏玩法。通过这些技术,游戏的智能化程度得以显著提升,为玩家带来更丰富的体验。
40 0
|
2月前
|
监控 API 开发者
深入解析Docker容器的生命周期
【8月更文挑战第24天】
28 0
|
2月前
|
NoSQL Linux MongoDB
Docker 解析:使用 Dockerfile 自动构建镜像
Docker 解析:使用 Dockerfile 自动构建镜像
72 0
|
2月前
|
Ubuntu 应用服务中间件 nginx
Docker 解析:如何将 Nginx 容器化并用作代理
Docker 解析:如何将 Nginx 容器化并用作代理
59 0

热门文章

最新文章

下一篇
无影云桌面