Docker層和虛懸鏡像(dangling image)介紹

原文發表於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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章