理解映像檔層

說明

正如您在 什麼是映像檔? 中所學到的,容器映像檔是由多個「層」(layers)組成的。這些層一旦建立,就是不可變的(immutable)。但這到底是什麼意思?這些層又是如何被用來建立容器可用的檔案系統的呢?

映像檔層 (Image layers)

映像檔中的每一層都包含一組檔案系統的變更——新增、刪除或修改。讓我們來看一個理論上的映像檔:

  1. 第一層新增了基本指令和一個套件管理器,例如 apt。
  2. 第二層安裝了 Python 執行環境和用於依賴管理的 pip。
  3. 第三層複製了應用程式特定的 requirements.txt 檔案。
  4. 第四層安裝了該應用程式特定的依賴套件。
  5. 第五層複製了應用程式的實際原始程式碼。

這個範例看起來可能像這樣:

screenshot of the flowchart showing the concept of the image layers

這種做法是有益的,因為它允許在多個映像檔之間重複使用層。例如,想像您想要建立另一個 Python 應用程式。由於分層機制,您可以利用相同的 Python 基礎層。這將加快建構速度,並減少發佈映像檔所需的儲存空間和頻寬。映像檔分層看起來可能類似於下圖:

screenshot of the flowchart showing the benefits of the image layering

分層讓您可以透過重複使用他人的基礎層來擴充映像檔,從而只需新增應用程式真正需要的資料。

堆疊層

分層是透過內容定址儲存(content-addressable storage)和聯合檔案系統(union filesystems)實現的。雖然這會變得比較技術性,但其運作原理如下:

  1. 每一層下載後,都會解壓縮到主機檔案系統中屬於它自己的目錄中。
  2. 當您從映像檔執行容器時,會建立一個聯合檔案系統,將各層堆疊在一起,形成一個新的統一視圖。
  3. 當容器啟動時,其根目錄會使用 chroot 設定為該統一目錄的位置。

在建立聯合檔案系統時,除了映像檔層之外,還會專門為正在執行的容器建立一個目錄。這允許容器進行檔案系統變更,同時保持原始映像檔層不被更動。這使得您可以從同一個底層映像檔執行多個容器。

試試看

在本實作指南中,您將使用 docker container commit 指令手動建立新的映像檔層。請注意,您很少會用這種方式建立映像檔,因為通常您會 使用 Dockerfile。但這樣做可以讓您更容易理解它的運作原理。

建立基礎映像檔

在第一步中,您將建立自己的基礎映像檔,並將其用於後續步驟。

  1. 下載並安裝 Docker Desktop。

  2. 在終端機中,執行以下指令來啟動一個新容器:

    $ docker run --name=base-container -ti ubuntu
    

    一旦映像檔下載完成且容器啟動後,您應該會看到一個新的 Shell 提示符號。這表示您正在容器內執行。它看起來會像下面這樣(容器 ID 會有所不同):

    root@d8c5ca119fcd:/#
    
  3. 在容器內,執行以下指令來安裝 Node.js:

    $ apt update && apt install -y nodejs
    

    當此指令執行時,它會在容器內下載並安裝 Node。在聯合檔案系統的背景下,這些檔案系統變更發生在該容器專屬的目錄內。

  4. 執行以下指令來驗證 Node 是否已安裝:

    $ node -e 'console.log("Hello world!")'
    

    接著,您應該會在控制台中看到「Hello world!」出現。

  5. 現在您已經安裝了 Node,您可以準備將所做的變更儲存為一個新的映像檔層,並以此啟動新的容器或建構新的映像檔。為此,您將使用 docker container commit 指令。在新的終端機視窗中執行以下指令:

    $ docker container commit -m "Add node" base-container node-base
    
  6. 使用 docker image history 指令查看映像檔的層:

    $ docker image history node-base
    

    您將看到類似下面的輸出:

    IMAGE          CREATED          CREATED BY                                      SIZE      COMMENT
    d5c1fca2cdc4   10 seconds ago   /bin/bash                                       126MB     Add node
    2b7cc08dcdbb   5 weeks ago      /bin/sh -c #(nop)  CMD ["/bin/bash"]            0B
    <missing>      5 weeks ago      /bin/sh -c #(nop) ADD file:07cdbabf782942af0…   69.2MB
    <missing>      5 weeks ago      /bin/sh -c #(nop)  LABEL org.opencontainers.…   0B
    <missing>      5 weeks ago      /bin/sh -c #(nop)  LABEL org.opencontainers.…   0B
    <missing>      5 weeks ago      /bin/sh -c #(nop)  ARG LAUNCHPAD_BUILD_ARCH     0B
    <missing>      5 weeks ago      /bin/sh -c #(nop)  ARG RELEASE                  0B
    

    請注意頂部一行的「Add node」註解。這一層包含了您剛剛安裝的 Node.js。

  7. 為了證明您的映像檔已安裝 Node,您可以使用這個新映像檔啟動一個新容器:

    $ docker run node-base node -e "console.log('Hello again')"
    

    執行後,您應該會在終端機中收到「Hello again」的輸出,這顯示 Node 已成功安裝並運作正常。

  8. 現在您已建立好基礎映像檔,可以移除該容器:

    $ docker rm -f base-container
    

基礎映像檔定義

基礎映像檔是建構其他映像檔的基石。雖然可以使用任何映像檔作為基礎映像檔,但有些映像檔是刻意設計為建構區塊的,為應用程式提供基礎或起點。

在此範例中,您可能不會部署這個 node-base 映像檔,因為它目前還沒有實質功能。但它是一個您可以運用於其他建構的基礎。

建構應用程式映像檔

現在您有了基礎映像檔,您可以擴充該映像檔來建構額外的映像檔。

  1. 使用剛建立的 node-base 映像檔啟動一個新容器:

    $ docker run --name=app-container -ti node-base
    
  2. 在此容器內,執行以下指令來建立一個 Node 程式:

    $ echo 'console.log("Hello from an app")' > app.js
    

    若要執行此 Node 程式,您可以使用以下指令,並在螢幕上看到印出的訊息:

    $ node app.js
    
  3. 在另一個終端機中,執行以下指令將此容器的變更儲存為新映像檔:

    $ docker container commit -c "CMD node app.js" -m "Add app" app-container sample-app
    

    此指令不僅會建立一個名為 sample-app 的新映像檔,還會將額外的設定加入映像檔中,以設定啟動容器時的預設指令。在本例中,您將其設定為自動執行 node app.js

  4. 在容器外的終端機中,執行以下指令來查看更新後的層:

    $ docker image history sample-app
    

    您將看到類似以下的輸出。請注意,頂層註解為「Add app」,下一層為「Add node」:

    IMAGE          CREATED              CREATED BY                                      SIZE      COMMENT
    c1502e2ec875   About a minute ago   /bin/bash                                       33B       Add app
    5310da79c50a   4 minutes ago        /bin/bash                                       126MB     Add node
    2b7cc08dcdbb   5 weeks ago          /bin/sh -c #(nop)  CMD ["/bin/bash"]            0B
    <missing>      5 weeks ago          /bin/sh -c #(nop) ADD file:07cdbabf782942af0…   69.2MB
    <missing>      5 weeks ago          /bin/sh -c #(nop)  LABEL org.opencontainers.…   0B
    <missing>      5 weeks ago          /bin/sh -c #(nop)  LABEL org.opencontainers.…   0B
    <missing>      5 weeks ago          /bin/sh -c #(nop)  ARG LAUNCHPAD_BUILD_ARCH     0B
    <missing>      5 weeks ago          /bin/sh -c #(nop)  ARG RELEASE                  0B
    
  5. 最後,使用全新的映像檔啟動一個新容器。由於您已經指定了預設指令,您可以使用以下指令:

    $ docker run sample-app
    

    您應該會看到您的問候語出現在終端機中,這來自您的 Node 程式。

  6. 現在您已完成容器的操作,可以使用以下指令移除它們:

    $ docker rm -f app-container
    

其他資源

如果您想深入了解所學內容,請查看以下資源:

下一步

正如先前所暗示的,大多數映像檔建構並不會使用 docker container commit。相反地,您會使用 Dockerfile,它能為您自動化這些步驟。

編寫 Dockerfile
© . This site is unofficial and not affiliated with Kubernetes or Docker Inc.