Kube.NETes v1.25 引入了容器檢查點 API 作為 alpha 特性。這提供了一種在不停止容器的情況下備份和恢復運行在 Pod 中的容器的方式。此功能主要用于調試分析,但任何 Kubernetes 用戶都可以利用常規備份和恢復功能。
接下來,讓我們來看看這個特性,并了解如何在我們的集群中啟用它,并利用它進行備份和恢復或調試分析。
安裝
在我們開始對任何容器進行檢查點處理之前,我們需要一個 playgroud,在這個 playgroud 上我們可以操作 kubelet 和它的工作負載。為此,我們將需要一個支持容器檢查點處理的 v1.25+ 版本的 Kubernetes集 群和容器運行時環境。
這里我們將使用在 Vagrant 中構建的虛擬機內使用 kubeadm 創建一個集群,我們將相關配置文件放到了 Github 倉庫:https://github.com/MartinHeinz/kubeadm-vagrant-playground/tree/contAIner-checkpoint-api 中,只需執行 vagrant up
即可快速啟動該集群。
如果你想搭建自己的集群,請確保集群必須啟用 ContainerCheckpoint
功能標志。對于 kubeadm 使用以下配置:
# kubeadm-config.yaml
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
featureGates:
ContainerCheckpoint: true
---
apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
kubernetesVersion: v1.25.0
apiServer:
extraArgs:
feature-gates: "ContainerCheckpoint=true"
controllerManager:
extraArgs:
feature-gates: "ContainerCheckpoint=true"
scheduler:
extraArgs:
feature-gates: "ContainerCheckpoint=true"
networking:
podSubnet: 10.244.0.0/16
這將向集群組件傳遞 --feature-gates
標志。此外,我們還需要使用支持檢查點的容器運行時。撰寫本文時,僅 CRI-O
支持它,而 Containerd 可能很快也會支持(https://github.com/containerd/containerd/pull/6965),最新版本的 crictl
已經支持通過 crictl checkpoint
創建檢查點。
要使用 CRI-O 配置集群,請按照文檔中的說明安裝它,或者使用上述存儲庫中的腳本(你應該在虛擬機而不是本地運行此腳本)。
另外,我們還需要為 CRI-O 啟用 CRIU
,這是在后臺執行實際檢查點的工具。要啟用它,我們需要設置 --enable-criu-support=true
標志。上面的腳本可以為你做到這一點。
另外,如果你打算將其恢復到 Pod 中,還需要將 --drop-infra-ctr
設置為 false,否則您將收到 CreateContainerError
并顯示如下消息:
kubelet Error: pod level PID namespace requested for the container, ...
... but pod sandbox was not similarly configured, and does not have an infra container
在安裝了 CRI-O 之后,我們還需要告訴 kubeadm 使用它的 sock 文件,下面的配置將會處理這個問題:
apiVersion: kubeadm.k8s.io/v1beta3
kind: InitConfiguration
localAPIEndpoint:
advertiseAddress: 192.168.56.2
bindPort: 6443
nodeRegistration:
criSocket: "unix:///var/run/crio/crio.sock"
---
然后我們就可以使用以下命令快速啟動集群:
kubeadm init --config=.../kubeadm-config.yaml --upload-certs | tee kubeadm-init.out
這將給我們提供一個單節點集群,如下(注意容器運行時版本):
$ kubectl get nodes -o wide
NAME STATUS ROLES AGE VERSION ... OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
kubemaster Ready control-plane 82s v1.25.4 ... Ubuntu 20.04.5 LTS 5.4.0-125-generic cri-o://1.25.0
Checkpointing
集群安裝完成后,我們可以嘗試創建一個檢查點。在 Kubernetes 上通常可以使用 kubectl 或者運行 curl 命令來執行常規操作,訪問集群 APIServer。然而,在這里這樣做是行不通的,因為檢查點 API 只暴露在每個集群節點上的 kubelet 上。因此,我們必須前往節點上并直接與 kubelet 交互:
$ vagrant ssh kubemaster
$ sudo su -
# Check if it's running...
$ systemctl status kubelet
kubelet.service - kubelet: The Kubernetes Node Agent
Loaded: loaded (/lib/systemd/system/kubelet.service; enabled; vendor preset: enabled)
Drop-In: /etc/systemd/system/kubelet.service.d
└─10-kubeadm.conf
Active: active (running) since Sat 2022-11-12 10:25:29 UTC; 30s ago
Docs: https://kubernetes.io/docs/home/
Main PID: 29501 (kubelet)
Tasks: 14 (limit: 2339)
Memory: 34.7M
CGroup: /system.slice/kubelet.service
└─29501 /usr/bin/kubelet --bootstrap-kubeconfig=... --kubeconfig=...
為了創建檢查點,我們還需要一個正在運行的 Pod。讓我們在 default 命名空間中創建一個 Nginx Pod:
$ kubectl taint nodes --all node-role.kubernetes.io/control-plane-
$ kubectl run webserver --image=nginx -n default
$ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE
webserver 1/1 Running 0 27s 10.85.0.4 kubemaster
這里我們從節點中刪除了污點,這樣即使它是控制平面,我們也可以在節點上調度工作負載。
接下來,讓我們向 kubelet 發出一個示例 API 請求,來查看是否正常:
$ curl -skv -X GET "https://localhost:10250/pods"
--key /etc/kubernetes/pki/apiserver-kubelet-client.key
--cacert /etc/kubernetes/pki/ca.crt
--cert /etc/kubernetes/pki/apiserver-kubelet-client.crt
{
"kind": "PodList",
"apiVersion": "v1",
"metadata": {},
"items": [
{
"metadata": {
"name": "webserver",
"namespace": "default",
...
}
}
...
}
kubelet 默認運行在端口 10250 上,因此我們使用 curl 命令并請求其所有的 Pod。我們還需要指定 CA 證書、客戶端證書和密鑰進行身份驗證。
接下來就可以創建一個檢查點了:
$ curl -sk -X POST "https://localhost:10250/checkpoint/default/webserver/webserver"
--key /etc/kubernetes/pki/apiserver-kubelet-client.key
--cacert /etc/kubernetes/pki/ca.crt
--cert /etc/kubernetes/pki/apiserver-kubelet-client.crt
# Response:
# {"items":["/var/lib/kubelet/checkpoints/checkpoint-webserver_default-webserver-2022-11-12T10:28:13Z.tar"]}
# Check the directory:
$ ls -l /var/lib/kubelet/checkpoints/
total 3840
-rw------- 1 root root 3931136 Nov 12 10:28 checkpoint-webserver_default-webserver-2022-11-12T10:28:13Z.tar
# Verify that original container is still running:
$ crictl ps --name webserver
CONTAINER IMAGE CREATED STATE NAME ATTEMPT POD ID POD
880ee7ddff7f3 Docker.io/library/nginx@sha256:... 48 seconds ago Running webserver 0 d584446dd8d5e webserver
檢查點 API 位于 .../checkpoint/${NAMESPACE}/${POD}/${CONTAINER}
,這里我們使用之前創建的 Pod,此請求在 /var/lib/kubelet/checkpoints/checkpoint-<pod>_<namespace>-<container>-<timestamp>.tar
中創建了一個存檔。
運行上述 curl 后,您可能會收到如下錯誤:
checkpointing of default/webserver/webserver failed (CheckpointContainer is only supported in the CRI v1 runtime API)
# or
checkpointing of default/webserver/webserver failed (rpc error: code = Unknown desc = checkpoint/restore support not available)
這意味著您的容器運行時尚不支持檢查點功能,或者未正確啟用。
分析
我們現在有了一個檢查點容器存檔,所以讓我們看看里面有什么:
$ cd /var/lib/kubelet/checkpoints/
# Rename because "tar" doesn't like ":" in names
$ mv "checkpoint-webserver_default-webserver-2022-11-12T10:28:13Z.tar" webserver.tar
# View contents:
$ tar --exclude="*/*" -tf webserver.tar
dump.log
checkpoint/
config.dump
spec.dump
rootfs-diff.tar
io.kubernetes.cri-o.LogPath
# Extract:
$ tar -xf checkpoint-webserver_default-webserver-2022-09-04T10:15:37Z.tar
$ ls checkpoint/
cgroup.img fdinfo-4.img ids-31.img mountpoints-13.img pages-2.img tmpfs-dev-139.tar.gz.img
core-1.img files.img inventory.img netns-10.img pages-3.img tmpfs-dev-140.tar.gz.img
core-30.img fs-1.img ipcns-var-11.img pagemap-1.img pages-4.img tmpfs-dev-141.tar.gz.img
core-31.img fs-30.img memfd.img pagemap-30.img pstree.img tmpfs-dev-142.tar.gz.img
descriptors.json fs-31.img mm-1.img pagemap-31.img seccomp.img utsns-12.img
fdinfo-2.img ids-1.img mm-30.img pagemap-shmem-94060.img timens-0.img
fdinfo-3.img ids-30.img mm-31.img pages-1.img tmpfs-dev-136.tar.gz.img
$ cat config.dump
{
"id": "880ee7ddff7f3ce11ee891bd89f8a7356c97b23eb44e0f4fbb51cb7b94ead540",
"name": "k8s_webserver_webserver_default_91ad1757-424e-4195-9f73-349b332cbb7a_0",
"rootfsImageName": "docker.io/library/nginx:latest",
"runtime": "runc",
"createdTime": "2022-11-12T10:27:56.460946241Z"
}
$ tar -tf rootfs-diff.tar
var/cache/nginx/proxy_temp/
var/cache/nginx/scgi_temp/
var/cache/nginx/uwsgi_temp/
var/cache/nginx/client_temp/
var/cache/nginx/fastcgi_temp/
etc/mtab
run/nginx.pid
run/secrets/kubernetes.io/
run/secrets/kubernetes.io/serviceaccount/
如果您不需要一個正在運行的 Pod/容器進行分析,那么提取并閱讀上面顯示的一些文件可能會為您提供必要的信息。
恢復
雖然 Checkpointing API 目前更加注重于調試分析,但它仍然可以用于從存檔中恢復 Pod/容器。最簡單的方法是從檢查點存檔創建一個鏡像:
FROM scratch
# Need to use ADD because it extracts archives
ADD webserver.tar .
這里我們使用一個空(scratch)鏡像,然后向其添加歸檔文件。這里需要使用 ADD 命令,因為它會自動解壓縮歸檔文件。接下來,我們使用 docker 或 buildah 構建它。
$ cd /var/lib/kubelet/checkpoints/
# Or docker build ...
$ buildah bud
--annotation=io.kubernetes.cri-o.annotations.checkpoint.name=webserver
-t restore-webserver:latest
Dockerfile .
$ buildah push localhost/restore-webserver:latest docker.io/martinheinz/restore-webserver:latest
在上面,我們還添加了一個注解,描述了容器的原始可讀名稱,然后我們將其推送到一些倉庫,以便 Kubernetes 可以拉取它。最后,我們創建一個Pod,指定之前推送的鏡像。
# pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: restore-webserver
labels:
App: nginx
spec:
containers:
- name: webserver
image: docker.io/martinheinz/restore-webserver:latest
nodeName: kubemaster
為了測試是否成功,我們可以通過 Service 將 Pod 暴露出來,并使用 curl 命令訪問其IP地址。
$ kubectl expose pod restore-webserver --port=80 --target-port=80
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 14m
restore-webserver ClusterIP 10.104.30.90 <none> 80/TCP 17s
$ curl http://10.104.30.90
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
</html>
可以看到生效了,我們成功地在不停止它的情況下備份了并恢復一個正在運行的 Pod。
總結
Kubernetes 的檢查點功能是增強容器化應用程序容錯性和彈性的強大工具。通過實施良好規劃的檢查點策略,你可以將停機時間降至最低,改善資源利用情況,并簡化應用程序遷移。
優點
-
增強的容錯性——檢查點功能使應用程序能夠在故障發生時從最后一個已知檢查點恢復,減少停機時間,并確保應用程序保持高可用性。 -
簡化遷移——檢查點功能使將正在運行的應用程序移動到不同主機變得更加容易。通過保存應用程序的狀態,您可以將其遷移到不同的節點,而不會丟失進度或造成中斷。如果您的應用程序需要很長時間的預熱,您可以利用這個功能。這將大大減少應用程序的啟動時間。 -
改進的擴展性——通過檢查點功能,您可以輕松地擴展應用程序以滿足波動的需求。如果一個節點過載了,您可以將應用程序遷移到擁有更多資源的另一個節點,確保最佳性能。 -
高效的資源使用——檢查點功能允許您暫停長時間運行的應用程序,釋放資源給其他任務使用。當再次需要應用程序時,可以從檢查點恢復。
Kubernetes 檢查點的最佳實踐
-
定期創建檢查點——根據應用程序的要求,定期創建檢查點,以最小化在故障發生時的數據丟失。 -
監控和管理資源——檢查點功能可能消耗大量系統資源,尤其是內存。監控集群的資源使用情況,并根據需要調整檢查點策略,以避免性能問題。 -
測試您的檢查點策略——定期測試您的檢查點過程,確保其按預期工作,并能在故障發生時恢復應用程序。 -
自動化檢查點管理——使用像 cron 作業或 Kubernetes Operator 這樣的自動化工具,在預定的時間表上創建和管理檢查點,確保您的應用程序始終受到保護。
參考文檔
-
https://martinheinz.dev/blog/85 -
https://faun.pub/kubernetes-checkpointing-a-definitive-guide-33dd1a0310f6 -
https://github.com/containerd/containerd/pull/6965 -
https://github.com/MartinHeinz/kubeadm-vagrant-playground/tree/container-checkpoint-api