返回文章列表

Docker架構與Containerd深度解析

本文探討 Docker 架構與 Containerd 運作機制,解析 Docker daemon 與 Containerd 的關係,並剖析 Containerd 架構中的關鍵元件,如 Bundle Service、Runtime Service 以及與 OCI 標準的整合。此外,文章還比較了 Podman 與

容器技術 系統管理

Docker 的核心元件 Docker daemon 與 Containerd 緊密合作,共同管理容器的生命週期。Docker daemon 負責接收使用者指令,而 Containerd 則負責處理容器的實際執行。Containerd 的架構包含多個子系統,例如 Bundle Service 負責從映像檔中提取 bundle,Runtime Service 負責執行 bundle 並建立容器。Containerd 與 OCI 標準相容,透過 Distribution Controller、Bundle Controller 和 Runtime Controller 協同工作,完成容器的提取、解封裝和執行。此外,Podman 作為無守護程式的容器引擎,直接與容器執行時互動,簡化了管理流程並提升了安全性。Podman 使用 libpod 函式函式倉管理容器生命週期,並透過 CNI 外掛支援容器網路。libpod 提供了豐富的 API,涵蓋容器映像管理、生命週期控制、Pod 管理以及資源隔離等功能。在容器執行環境方面,Podman 支援 OCI 相容的 runc 和 crun,其中 crun 以其 C 語言的實作和輕量級特性而著稱。

Docker 架構與 Containerd 詳解

Docker 架構的核心在於 Docker daemon,它是整個系統的關鍵元件。本文將探討 Docker daemon 與相關元件的運作方式。

Docker Daemon 與 Containerd 的關係

首先,檢查 Docker daemon 的狀態:

[root@fedora34 ~]# systemctl status docker
● docker.service - Docker Application Container Engine
Loaded: loaded (/usr/lib/systemd/system/docker.service; disabled; vendor preset: disabled)
Active: active (running) since Tue 2021-08-31 19:46:57 UTC; 1h 39min ago
TriggeredBy: ● docker.socket
Docs: https://docs.docker.com
Main PID: 20258 (dockerd)
Tasks: 12
Memory: 31.1M
CPU: 1.946s
CGroup: /system.slice/docker.service
└─20258 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock

內容解密:

  • systemctl status docker 用於檢查 Docker daemon 的執行狀態。
  • Active: active (running) 表示 Docker daemon 正在執行。
  • Main PID: 20258 (dockerd) 表示主行程 ID 為 20258,執行中的程式為 dockerd
  • -H fd:// 指定 Docker daemon 的監聽方式。
  • --containerd=/run/containerd/containerd.sock 表示 Docker daemon 與 Containerd 的通訊介面。

Docker daemon 並非單獨運作,其伴侶元件 Containerd 也扮演著重要角色。檢查 Containerd 的狀態:

[root@fedora34 ~]# systemctl status containerd
● containerd.service - containerd container runtime
Loaded: loaded (/usr/lib/systemd/system/containerd.service; disabled; vendor preset: disabled)
Active: active (running) since Wed 2021-08-25 12:48:17 UTC; 6 days ago
Docs: https://containerd.io
Process: 4267 ExecStartPre=/sbin/modprobe overlay (code=exited, status=0/SUCCESS)
Main PID: 4268 (containerd)
Tasks: 43
Memory: 44.1M
CPU: 8min 36.291s
CGroup: /system.slice/containerd.service
├─4268 /usr/bin/containerd
├─20711 /usr/bin/containerd-shim-runc-v2 -namespace moby -id 3901d2600732ae1f2681cde0074f290c1839b1a4b0c63ac9aaccdba4f646e06a -address /run/containerd/containerd.sock
├─20864 /usr/bin/containerd-shim-runc-v2 -namespace moby -id 78dc2eeb321433fc67cf910743c0c53e54d9f45cfee8d18319d03a622dc56666 -address /run/containerd/containerd.sock
└─21015 /usr/bin/containerd-shim-runc-v2 -namespace moby -id 7433c0613412349833b927efa79a4f589916b12c942003cd616d45ed7611fc31 -address /run/containerd/containerd.sock

內容解密:

  • systemctl status containerd 用於檢查 Containerd 的執行狀態。
  • Containerd 啟動了三個子行程 /usr/bin/containerd-shim-runc-v2,對應三個正在執行的容器。
  • containerd-shim-runc-v2 是 Containerd 用於管理容器的執行環境。

接下來,使用 Docker CLI 檢視正在執行的容器:

[root@fedora34 ~]# docker ps
CONTAINER ID   IMAGE                           COMMAND                  CREATED         STATUS         PORTS                                         NAMES
7433c0613412   centos/httpd-24-centos7:latest   "container-entrypoin..."   26 minutes ago   Up 26 minutes   8080/tcp, 8443/tcp                            funny_goodall
78dc2eeb3214   centos/httpd-24-centos7:latest   "container-entrypoin..."   26 minutes ago   Up 26 minutes   8080/tcp, 8443/tcp                            wonderful_rubin
3901d2600732   centos/httpd-24-centos7:latest   "container-entrypoin..."   26 minutes ago   Up 26 minutes   8080/tcp, 8443/tcp                            relaxed_heisenberg

內容解密:

  • docker ps 命令列出目前系統中所有正在執行的容器。
  • 每個容器由 containerd-shim-runc-v2 管理,並透過 Docker daemon 組態。

Containerd 架構解析

Containerd 的架構由多個子系統組成,主要包括:

  • Bundle Service: 從磁碟映像中提取 bundle。
  • Runtime Service: 執行 bundle,建立執行中的容器。

主要模組包括:

  • Executor: 負責實作容器執行時的模組,在架構圖中對應 Runtimes 部分。
  • Supervisor: 負責監控並報告容器狀態,在架構圖中對應 Containers 部分。
  • Snapshotter: 管理檔案系統快照。
  • Events: 負責收集並處理事件。
  • Metrics: 提供多項指標的 API 介面。

Containerd 將容器執行過程簡化為以下步驟:

  1. 透過 Distribution Controller 提取後設資料和內容。
  2. 使用 Bundle Controller 解封裝並建立快照,形成 bundle。
  3. 透過 Runtime Controller 執行容器。
@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultTextAlignment center
skinparam rectangleBackgroundColor #F5F5F5
skinparam rectangleBorderColor #333333
skinparam arrowColor #333333

title Containerd 架構解析

rectangle "提取資料" as node1
rectangle "解封裝" as node2
rectangle "建立快照" as node3
rectangle "執行容器" as node4

node1 --> node2
node2 --> node3
node3 --> node4

@enduml

此圖示展示了 Containerd 的資料流動過程。

詳細解析:

Containerd 的設計使其能夠與 Docker daemon 無縫協作,同時保持對 OCI(Open Container Initiative)標準的相容性。這種架構設計不僅提高了容器的執行效率,也增強了系統的可擴充套件性和穩定性。透過上述解析,我們能夠更深入地理解 Docker 生態系統中各元件之間的協作關係。

Podman 與 Docker 的比較:深入解析 Podman 無守護程式架構

在前面的章節中,我們已經詳細介紹了 Docker 容器引擎的核心功能和設計原則,特別是其以守護程式為中心的架構。現在,我們將進一步分析 Podman 的無守護程式架構。

Podman 無守護程式架構

Podman(全稱 POD MANager)是一種無需守護程式的容器引擎,允許使用者管理容器、映像檔及其相關資源,如儲存卷或網路資源。第一次安裝 Podman 的使用者很快就會發現,安裝完成後不需要啟動任何服務!Podman 不需要背景執行的守護程式來執行容器。

安裝完成後,Podman 二進位制檔案同時作為命令列介面(CLI)和容器引擎,協調容器執行時的執行。以下子章節將探討 Podman 的行為和建構模組的詳細資訊。

Podman 命令和 REST API

Podman CLI 提供了一組不斷增長的命令。精選的命令列表可在 https://docs.podman.io/en/latest/Commands.html 找到。

以下列表探討了一些最常用的命令:

  • build:從 Containerfile 或 Dockerfile 建立映像檔
  • cp:在容器和本地檔案系統之間複製檔案/資料夾
  • exec:在正在執行的容器中執行命令
  • events:顯示 Podman 事件
  • generate:產生結構化資料,如 Kubernetes YAML 或 systemd 單元
  • images:列出本地快取的映像檔
  • inspect:傳回容器或映像檔的低階資訊
  • kill:終止一個或多個正在執行的容器
  • load:從容器 TAR 封存或標準輸入載入映像檔
  • login:登入容器註冊中心
  • logs:取得容器的日誌
  • pod:管理 pod
  • ps:列出正在執行的容器
  • pull:從註冊中心提取映像檔或儲存函式庫
  • push:將映像檔或儲存函式庫推播到註冊中心
  • restart:重新啟動一個或多個容器
  • rm:刪除一個或多個容器
  • rmi:刪除一個或多個映像檔
  • run:在新容器中執行命令
  • save:將一個或多個映像檔儲存到 TAR 封存(預設輸出到標準輸出)
  • start:啟動一個或多個已停止的容器
  • stop:停止一個或多個正在執行的容器
  • system:管理 Podman(磁碟使用情況、容器遷移、REST API 服務、儲存管理、修剪)
  • tag:建立參照 SOURCE_IMAGE 的 TARGET_IMAGE 標籤
  • unshare:在修改的使用者名稱空間中執行命令
  • volume:管理容器卷(列出、修剪、建立、檢查)

在後續章節中,我們將更詳細地介紹上述命令,並瞭解如何使用它們來管理完整的容器生命週期。

已經使用過 Docker 的使用者會立即發現 Podman CLI 命令與 Docker CLI 命令相容,有助於在兩種工具之間實作平滑過渡。

與 Docker 不同,Podman 不需要執行中的 Docker 守護程式監聽 Unix 通訊端來執行上述命令。使用者仍然可以選擇執行 Podman 服務並使其監聽 Unix 通訊端以公開原生 REST API。

執行以下命令,Podman 將在首選路徑上建立通訊端端點並監聽 API 呼叫:

$ podman system service --time 0 unix:///tmp/podman.sock

如果未提供,預設通訊端端點為 unix:///run/podman/podman.sock(適用於 rootful 服務)和 unix:///run/user/<UID>/podman/podman.sock(適用於 rootless 容器)。

REST API 使用範例與解析

使用者隨後可以對通訊端端點發出 REST API 呼叫。以下範例查詢 Podman 以取得可用的本地映像檔:

curl --unix-socket /tmp/podman.sock http://d/v3.0.0/libpod/images/json | jq .

此命令透過 Unix 通訊端與 Podman 通訊,查詢本地映像檔資訊,並使用 jq 對 JSON 輸出進行格式化處理,以提高可讀性。

Podman 專案維護著符合 OpenAPI 規範的 REST API 檔案,可在 https://docs.podman.io/en/latest/_static/api.html 查閱。

REST API 的應用與優勢

Podman 的 REST API 為開發者和系統管理員提供了強大的介面,能夠以程式設計方式管理容器和映像檔。這種靈活性使得自動化指令碼、CI/CD 管道以及自定義工具能夠無縫整合 Podman 的功能,從而提高工作效率並簡化容器管理工作流程。

Podman 建構模組

Podman 盡可能遵循開放標準;因此,大多數執行時、建置、儲存和網路元件都依賴於社群專案和標準。以下列表中描述的元件可以被視為主要的 Podman 建構模組:

1. Libpod 與容器生命週期管理

  • 容器生命週期由 libpod 函式函式倉管理,該函式庫已包含在 Podman 主儲存函式庫中:https://github.com/containers/podman/tree/main/libpod。
  • libpod 提供了一套完整的 API,用於管理容器的建立、執行、暫停和刪除等操作,確保了容器生命週期的可控性和可管理性。

2. OCI 相容的容器執行時

  • 容器執行時根據 OCI 規範,由符合 OCI 標準的執行時(如 crun 和 runc)實作。
  • 本章後續將探討容器執行時的工作原理以及上述執行時之間的關鍵差異。

3. 映像檔管理與 Containers/Image 函式庫

  • 同時,映像檔管理採用了 containers/image 函式庫(https://github.com/containers/image)。
  • 這是一組用於容器引擎和容器註冊中心的 Go 語言函式庫,用於處理映像檔的提取、上傳和驗證等操作。

4. 儲存管理與 Containers/Storage 函式庫

  • 容器和映像檔的儲存實作採用了 containers/storage 函式庫(https://github.com/containers/storage)。
  • 這是另一個用於管理檔案系統層、容器映像檔和容器卷的 Go 語言函式庫,為容器的持久化和資料管理提供了基礎。

5. Buildah 與映像檔建置

  • 映像檔建置由 Buildah(https://github.com/containers/buildah)實作,它既是一個二進位制工具,也是一個用於建置 OCI 映像檔的函式庫。
  • 本文後續章節將對 Buildah 進行詳細介紹,包括其使用方法和最佳實踐。

6. Conmon 與容器執行時監控

  • 容器執行時監控和與引擎的通訊由 Conmon(一種用於監控 OCI 執行時的工具)實作,被 Podman 和 CRI-O 使用(https://github.com/containers/conmon)。
  • Conmon 負責監控容器的執行狀態,並提供日誌收集和健康檢查等功能,確保了容器的穩定執行。

Podman 無守護程式架構深度解析

Podman 的無守護程式架構是其最核心的特色之一,這種設計不僅提升了系統的安全性,也簡化了容器管理的複雜度。本章節將探討 Podman 的架構組成、關鍵技術以及與 Docker 的比較。

容器網路支援與 CNI 規範

Podman 的容器網路支援是透過 Kubernetes Container Network Interface (CNI) 規範來實作的。這種外掛導向的方法使得 Podman 的網路組態具備高度的彈性。預設情況下,Podman 使用基本的 bridge CNI 外掛,但使用者可以擴充套件更多的網路功能,相關的外掛列表可在 Containernetworking plugins 儲存函式庫中找到。

libpod 函式庫的核心角色

Podman 的核心基礎建立在 libpod 函式庫之上,這個函式庫也被其他開源專案如 CRI-O 所採用。libpod 包含了管理容器生命週期所需的所有邏輯,可以說是 Podman 專案誕生的關鍵。該函式庫使用 Go 語言編寫,提供高階的引擎功能,其範圍包括:

  • 管理容器映像格式(OCI 和 Docker 映像)
  • 容器生命週期管理(建立、執行、停止、刪除等)
  • 管理簡單容器和 Pod(一組分享名稱空間的沙箱化容器)
  • 支援無根容器和 Pod
  • 管理容器資源隔離(CGroup)
  • 提供與 Docker 相容的 CLI 和 REST API

#### 內容解密:libpod 核心功能解析

libpod 的主要功能涵蓋了容器管理的各個方面。首先,它支援 OCI 和 Docker 映像格式的管理,包括驗證、提取、儲存和推播映像。其次,它提供了完整的容器生命週期管理,從建立到刪除容器的各個階段。此外,libpod 還支援 Pod 的管理,Pod 是一組分享特定名稱空間(如 UTC、IPC、Network 和 Pid)的容器。最後,libpod 提供了資源隔離的功能,允許使用者在執行容器時管理記憶體、CPU 和儲存裝置的讀寫速率。

OCI 容器執行環境:runc 和 crun

容器引擎負責容器的高階協調,而低階的容器建立和執行則由容器執行環境負責。OCI Runtime Specification 已成為業界標準,runc 是目前最廣泛採用的 OCI 容器執行環境。runc 專案包含了 libcontainer 套件,這是一個用於建立具有名稱空間、cgroups 和檔案系統存取控制的容器的 Go 套件。

#### 內容解密:runc 與 libcontainer 的關係

runc 使用 libcontainer 來完成以下任務:

  1. 使用 Podman 提供的容器掛載點和中繼資料。
  2. 與核心互動,使用 clone()unshare() 系統呼叫來啟動容器並執行隔離程式。
  3. 設定 CGroup 資源保留。
  4. 設定 SELinux 原則、Seccomp 和 App Armor 規則。

libcontainer 負責處理容器的初始化、名稱空間和檔案描述符的建立、容器 rootFS 和繫結掛載的建立,以及安全限制的管理。

crun:一個新的 OCI 容器執行環境

除了 runc 之外,crun 是另一個值得注意的 OCI 容器執行環境。crun 完全使用 C 語言編寫,具有快速和低記憶體佔用的特點。由於 runc 和 crun 都符合 OCI Runtime Specification,因此它們可以被容器引擎互換使用。

#### 內容解密:crun 的優勢

crun 的設計理念是提供一個更乾淨、更輕量級的 OCI 容器執行環境。它利用 C 語言的設計優勢,在效能和資源利用率方面進行了最佳化。對於需要高效能和低資源佔用的場景,crun 提供了一個可行的替代方案。