Docker引擎分层解析

本文涉及的产品
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 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层。我们注意到,如果我们不定期清洁中间层,则会继续创建中间,并且悬挂的镜像仍然保留在我们的系统中。

相关文章
|
23天前
|
机器学习/深度学习 安全 大数据
揭秘!企业级大模型如何安全高效私有化部署?全面解析最佳实践,助你打造智能业务新引擎!
【10月更文挑战第24天】本文详细探讨了企业级大模型私有化部署的最佳实践,涵盖数据隐私与安全、定制化配置、部署流程、性能优化及安全措施。通过私有化部署,企业能够完全控制数据,确保敏感信息的安全,同时根据自身需求进行优化,提升计算性能和处理效率。示例代码展示了如何利用Python和TensorFlow进行文本分类任务的模型训练。
68 6
|
5天前
|
运维 持续交付 虚拟化
深入解析Docker容器化技术的核心原理
深入解析Docker容器化技术的核心原理
22 1
|
19天前
|
Kubernetes Cloud Native 调度
云原生批量任务编排引擎Argo Workflows发布3.6,一文解析关键新特性
Argo Workflows是CNCF毕业项目,最受欢迎的云原生工作流引擎,专为Kubernetes上编排批量任务而设计,本文主要对最新发布的Argo Workflows 3.6版本的关键新特性做一个深入的解析。
|
1月前
|
存储 应用服务中间件 云计算
深入解析:云计算中的容器化技术——Docker实战指南
【10月更文挑战第14天】深入解析:云计算中的容器化技术——Docker实战指南
55 1
|
1月前
|
缓存 网络协议 网络安全
docker中DNS解析问题
【10月更文挑战第6天】
173 6
|
1月前
|
网络协议 Docker 容器
docker中的DNS配置
【10月更文挑战第5天】
298 1
|
1月前
|
存储 缓存 数据处理
深度解析:Hologres分布式存储引擎设计原理及其优化策略
【10月更文挑战第9天】在大数据时代,数据的规模和复杂性不断增加,这对数据库系统提出了更高的要求。传统的单机数据库难以应对海量数据处理的需求,而分布式数据库通过水平扩展提供了更好的解决方案。阿里云推出的Hologres是一个实时交互式分析服务,它结合了OLAP(在线分析处理)与OLTP(在线事务处理)的优势,能够在大规模数据集上提供低延迟的数据查询能力。本文将深入探讨Hologres分布式存储引擎的设计原理,并介绍一些关键的优化策略。
101 0
|
1月前
|
网络安全 Docker 容器
【Bug修复】秒杀服务器异常,轻松恢复网站访问--从防火墙到Docker服务的全面解析
【Bug修复】秒杀服务器异常,轻松恢复网站访问--从防火墙到Docker服务的全面解析
26 0
|
1月前
|
云计算 开发者 Docker
揭秘云计算中的容器化技术——Docker的深度解析
【10月更文挑战第6天】揭秘云计算中的容器化技术——Docker的深度解析
|
2月前
|
Cloud Native 持续交付 Docker
深入解析Docker容器化技术及其在生产环境中的应用
深入解析Docker容器化技术及其在生产环境中的应用
37 0
下一篇
无影云桌面