本文发表已超过一年。较旧的文章可能包含过时的内容。请检查页面中的信息自发布以来是否已变得不正确。
Kubernetes 中的容器取证检查点
容器取证检查点基于 Checkpoint/Restore In Userspace (CRIU),允许在容器不知情的情况下创建正在运行的容器的状态副本。容器副本可以被分析并在沙盒环境中多次还原,而原始容器并不会知晓。容器取证检查点作为 Kubernetes v1.25 中的一个 Alpha 功能引入。
它是如何工作的?
借助 CRIU,可以对容器进行检查点和还原。CRIU 集成在 runc、crun、CRI-O 和 containerd 中,而 Kubernetes 中实现的容器取证检查点使用了这些现有的 CRIU 集成。
为什么它很重要?
借助 CRIU 和相应的集成,可以将正在运行的容器的所有信息和状态获取到磁盘上,以便进行后续的取证分析。取证分析对于检查可疑容器而无需停止或影响它可能很重要。如果容器真的受到攻击,攻击者可能会检测到检查容器的尝试。拍摄检查点并在沙盒环境中分析容器提供了检查容器的可能性,而原始容器以及可能的攻击者并不会知晓该检查。
除了容器取证检查点用例之外,还可以将容器从一个节点迁移到另一个节点,而不会丢失其内部状态。特别是对于具有较长初始化时间的有状态容器,从检查点还原可以节省重启后的时间或实现更快的启动时间。
我如何使用容器检查点?
该功能位于功能门控后面,因此请确保在可以使用新功能之前启用 ContainerCheckpoint
门控。
运行时也必须支持容器检查点
containerd:目前正在讨论中。有关更多详细信息,请参阅 containerd pull request #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://127.0.0.1:10250/checkpoint/namespace/podId/container"
对于名为 counter 的容器(在名为 counters 的 Pod 中,命名空间名为 default),kubelet API 端点可在
curl -X POST "https://127.0.0.1:10250/checkpoint/default/counters/counter"
为了完整起见,以下 curl
命令行选项是必要的,以使 curl
接受 kubelet 的自签名证书并授权使用 kubelet 的 checkpoint
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 沙盒实例中还原容器。为了在还原期间获得更好的用户体验,我建议您使用 main CRI-O GitHub 分支中的最新版本的 CRI-O。如果您使用的是 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
深入阅读
请参阅后续文章容器取证分析,以了解如何分析容器检查点的详细信息。