瞭解映像檔分層

說明

如同您在 什麼是映像檔? 中所學到的,容器映像檔是由多個分層組成。而且每個分層一旦建立,就無法變更。但是,這實際上是什麼意思?以及如何使用這些分層來建立容器可以使用的檔案系統?

映像檔分層

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

  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

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

堆疊分層

透過內容可定址儲存和聯集檔案系統,分層才得以實現。雖然這將變得比較技術性,但以下是它的運作方式

  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 image history` 指令檢視映像檔的分層

    您將看到類似以下的輸出

    請注意頂行上的「新增 node」註解。此分層包含您剛才安裝的 Node.js。

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

    這樣,您應該會在終端機中看到「Hello again」輸出,顯示 Node 已安裝且正常運作。

  • 現在您已完成建立基礎映像檔,您可以移除該容器

  • 基礎映像檔定義

    基礎映像是建置其他映像檔的基礎。可以使用任何映像檔作為基礎映像檔。但是,有些映像檔是特意建立為建構區塊,為應用程式提供基礎或起點。

    在此範例中,您可能不會部署此 `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
      

      接著您會看到類似以下的輸出。請注意,最上層的註釋為「新增應用程式」,而下一層的註釋為「新增 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,它會自動執行這些步驟。