本文發表於一年多前。舊文章可能包含過時內容。請檢查頁面中的資訊自發布以來是否已變得不正確。

Kubernetes 中的容器取證檢查點

取證式容器檢查點技術基於使用者空間中的檢查點/恢復(Checkpoint/Restore In Userspace,CRIU),允許在容器不知情的情況下為其建立有狀態的副本。容器的副本可以在沙盒環境中被多次分析和恢復,而原始容器對此毫不知情。取證式容器檢查點技術在 Kubernetes v1.25 中作為 Alpha 功能引入。

它是如何工作的?

藉助 CRIU,可以對容器進行檢查點操作和恢復。CRIU 已被整合到 runc、crun、CRI-O 和 containerd 中,Kubernetes 中實現的取證式容器檢查點功能正是使用了這些現有的 CRIU 整合。

為什麼它很重要?

藉助 CRIU 及其相應的整合,可以將執行中容器的所有資訊和狀態儲存到磁碟,以供日後進行取證分析。取證分析對於在不停止或影響可疑容器的情況下對其進行檢查可能非常重要。如果容器確實受到攻擊,攻擊者可能會檢測到檢查容器的嘗試。建立一個檢查點並在沙盒環境中分析容器,提供了一種在原始容器乃至攻擊者可能不知情的情況下檢查容器的可能性。

除了取證式容器檢查點的用例之外,還可以在不丟失內部狀態的情況下將容器從一個節點遷移到另一個節點。特別是對於初始化時間較長的有狀態容器,從檢查點恢復可以在重啟後節省時間,或實現更快的啟動時間。

我該如何使用容器檢查點功能?

該功能受特性門控保護,因此請確保啟用 ContainerCheckpoint 門控後才能使用這項新功能。

容器執行時也必須支援容器檢查點功能

  • containerd:支援正在討論中。更多細節請參見 containerd 的 PR #6965

  • CRI-O:v1.25 版本支援取證式容器檢查點。

使用 CRI-O 的示例

要在 CRI-O 中使用取證式容器檢查點功能,執行時需要使用命令列選項 --enable-criu-support=true 啟動。對於 Kubernetes,你需要啟用 ContainerCheckpoint 特性門控來執行你的叢集。由於檢查點功能由 CRIU 提供,因此也需要安裝 CRIU。通常 runc 或 crun 依賴於 CRIU,因此它會被自動安裝。

同樣需要指出的是,在撰寫本文時,檢查點功能在 CRI-O 和 Kubernetes 中應被視為 Alpha 級別的特性,其安全影響仍在評估中。

一旦容器和 Pod 開始執行,就可以建立檢查點了。檢查點功能目前僅在 kubelet 級別公開。要對一個容器設定檢查點,你可以在該容器執行的節點上執行 curl,並觸發一個檢查點操作。

curl -X POST "https://:10250/checkpoint/namespace/podId/container"

對於一個位於 default 名稱空間中名為 counters 的 Pod 內,名為 counter 的容器,其 kubelet API 端點地址為:

curl -X POST "https://:10250/checkpoint/default/counters/counter"

為了完整起見,以下 curl 命令列選項是必需的,以便讓 curl 接受 kubelet 的自簽名證書並授權使用 kubeletcheckpoint API。

--insecure --cert /var/run/kubernetes/client-admin.crt --key /var/run/kubernetes/client-admin.key

觸發這個 kubelet API 將會向 CRI-O 請求建立一個檢查點。CRI-O 會向你的底層執行時(例如,runc)請求一個檢查點。收到該請求後,runc 會呼叫 criu 工具來執行實際的檢查點操作。

一旦檢查點操作完成,檢查點檔案應該位於 /var/lib/kubelet/checkpoints/checkpoint-<pod-name>_<namespace-name>-<container-name>-<timestamp>.tar

然後,你可以使用該 tar 歸檔檔案在其他地方恢復容器。

在 Kubernetes 之外恢復已設定檢查點的容器(使用 CRI-O)

利用檢查點 tar 歸檔檔案,可以在 Kubernetes 之外的一個沙盒化的 CRI-O 例項中恢復容器。為了在恢復過程中獲得更好的使用者體驗,我建議你使用 CRI-O GitHub 主分支的最新版本。如果你使用的是 CRI-O v1.25,則需要在啟動容器之前手動建立一些 Kubernetes 會建立的目錄。

在 Kubernetes 之外恢復容器的第一步是使用 crictl 建立一個 Pod 沙箱:

crictl runp pod-config.json

然後,你可以將之前建立檢查點的容器恢復到新建立的 Pod 沙箱中:

crictl create <POD_ID> container-config.json pod-config.json

container-config.json 中,你需要指定之前建立的檢查點歸檔檔案的路徑,而不是指定映象倉庫中的容器映象:

{
  "metadata": {
      "name": "counter"
  },
  "image":{
      "image": "/var/lib/kubelet/checkpoints/<checkpoint-archive>.tar"
  }
}

接下來,執行 crictl start <CONTAINER_ID> 來啟動該容器,然後一個之前被設定檢查點的容器的副本就應該在運行了。

在 Kubernetes 內部恢復已設定檢查點的容器

要在 Kubernetes 中直接恢復之前設定檢查點的容器,需要將檢查點歸檔檔案轉換為一個可以推送到映象倉庫的映象。

一種將本地檢查點歸檔檔案進行轉換的可行方法包括以下步驟,藉助 buildah 完成:

newcontainer=$(buildah from scratch)
buildah add $newcontainer /var/lib/kubelet/checkpoints/checkpoint-<pod-name>_<namespace-name>-<container-name>-<timestamp>.tar /
buildah config --annotation=io.kubernetes.cri-o.annotations.checkpoint.name=<container-name> $newcontainer
buildah commit $newcontainer checkpoint-image:latest
buildah rm $newcontainer

生成的映象並非標準化格式,僅能與 CRI-O 配合使用。請將此映象格式視為預 Alpha 階段。目前正在進行討論,以標準化此類檢查點映象的格式。需要記住的重要一點是,這種尚未標準化的映象格式只有在 CRI-O 啟動時帶有 --enable-criu-support=true 引數時才能工作。啟動支援 CRIU 的 CRI-O 所帶來的安全影響尚不明確,因此應謹慎使用該功能及映象格式。

現在,你需要將該映象推送到一個容器映象倉庫。例如:

buildah push localhost/checkpoint-image:latest container-image-registry.example/user/checkpoint-image:latest

要恢復這個檢查點映象(container-image-registry.example/user/checkpoint-image:latest),該映象需要在 Pod 的規約中列出。下面是一個示例清單:

apiVersion: v1
kind: Pod
metadata:
  namePrefix: example-
spec:
  containers:
  - name: <container-name>
    image: container-image-registry.example/user/checkpoint-image:latest
  nodeName: <destination-node>

Kubernetes 將新的 Pod 排程到一個節點上。該節點上的 kubelet 指示容器執行時(本例中為 CRI-O)基於指定的映象 registry/user/checkpoint-image:latest 建立並啟動一個容器。CRI-O 檢測到 registry/user/checkpoint-image:latest 是對檢查點資料的引用,而不是一個容器映象。然後,CRI-O 不會執行建立和啟動容器的常規步驟,而是獲取檢查點資料並從該指定的檢查點恢復容器。

該 Pod 中的應用程式會繼續執行,就好像沒有進行過檢查點操作一樣;在容器內部,應用程式看起來和行為上都像任何其他正常啟動而不是從檢查點恢復的容器一樣。

透過這些步驟,可以將一個節點上執行的 Pod 替換為另一個節點上執行的新的等效 Pod,而不會丟失該 Pod 中容器的狀態。

我如何參與?

你可以透過多種方式聯絡 SIG Node

進一步閱讀

關於如何分析容器檢查點的詳細資訊,請參閱後續文章取證式容器分析