kubelet 和容器运行时到底是怎么“对话”的?CRI 里最基本的几个调用是什么?运行时的网络没就绪会怎样?这篇在节点上自己扮演一次 kubelet,把 CRI 的调用顺序和 kubelet 的对账行为都现场做一遍。
kubelet 配置 /var/lib/kubelet/config.yaml:containerRuntimeEndpoint: unix:///run/containerd/containerd.sock
crictl 配置 /etc/crictl.yaml:runtime-endpoint: unix:///run/containerd/containerd.sock
这个 socket 是 gRPC 服务端:srw-rw---- 1 root root 0 Oct 10 08:03 /run/containerd/contai
crictl version(通过 CRI 的 Version 调用拿到):['Version: 0.1.0', 'RuntimeName: containerd', 'RuntimeVersion: v2.3.4', 'RuntimeApiVersion: v1']
kubectl get node 的 status.nodeInfo.containerRuntimeVersion=containerd://2.3.4(kubelet 把 CRI Version 的结果写进了 Node 对象)
crictl info 的运行时状态 conditions:[('RuntimeReady', True), ('NetworkReady', True), ('ContainerdHasNoDeprecationWarnings', True)]
CRI 里的两个服务(从 containerd 的 cri 插件看):runc io.containerd.runc.v2 /etc/cni/net.dkubelet 配置里 containerRuntimeEndpoint: unix:///run/containerd/containerd.sock,crictl 的 /etc/crictl.yaml 指向同一个 socket——都是通过本地 unix socket 说 gRPC。crictl version(CRI 的 Version 调用)得到 containerd v2.3.4 和 RuntimeApiVersion: v1,kubectl 里 Node 的 containerRuntimeVersion=containerd://2.3.4 就是 kubelet 写进去的。crictl info 的 conditions 有 RuntimeReady、NetworkReady、ContainerdHasNoDeprecationWarnings,当时都是 true。
crictl runp(RunPodSandbox)→ 沙箱 ad3bb1801d57…,用时 0.13 秒;crictl create(CreateContainer)→ 容器 d74ab8f0c425…,0.06 秒;crictl start(StartContainer),0.09 秒
刚建好的沙箱:state=SANDBOX_READY;IP=10.244.1.204(CNI 已经配好)
kubelet 在运行时,这个沙箱存活了 0.9 秒后变成 SANDBOX_NOTREADY——API Server 里没有这个 Pod,kubelet 把运行时里它不认识的 Pod 当作孤儿清掉
现在先停掉 kubelet(systemctl stop kubelet),再来一遍:
8 秒后沙箱仍是 SANDBOX_READY,IP=10.244.1.205;容器日志 ['hello-from-cri', 'kx-main-worker', 'hello-from-cri', 'kx-main-worker'](第二行是 hostname,沙箱没设时取节点名)
kubectl 看得到它吗:看不到,API Server 里没有这个 Pod;crictl pods 里:manual
重新启动 kubelet 后 2.3 秒,这个沙箱被清掉了(状态 SANDBOX_NOTREADY)
清理后 crictl pods 里还有 manual 吗:False按 CRI 的顺序手动调用:crictl runp(RunPodSandbox,0.13 秒,CNI 配好 IP 10.244.1.204)→ crictl create(CreateContainer,0.06 秒)→ crictl start(StartContainer,0.09 秒),容器日志输出 hello-from-cri 和主机名(沙箱没设 hostname 时取节点名;日志文件沿用同一个 main.log,所以第二轮里出现两遍)。但 kubelet 在运行时,这个沙箱只活了 0.9 秒就变成 NOTREADY:API Server 里没有这个 Pod,kubelet 把运行时里它不认识的沙箱当孤儿清掉。先 systemctl stop kubelet 再做一遍,8 秒后沙箱仍是 READY(IP 10.244.1.205),kubectl 看不到它,crictl pods 看得到;重新启动 kubelet 2.3 秒后它就被清掉了。kubelet 是在做对账:以 API Server 为准,清理运行时里多出来的东西。
把 CNI 配置文件挪走后 0.1 秒:crictl info 的 NetworkReady=false;[('NetworkReady', 'NetworkPluginNotReady', 'Network plugin returns error: cni plugin not initialized')]
节点 Ready 条件变成 False 用了 8.6 秒(从挪走配置算起):reason=KubeletNotReady;message=container runtime network not ready: NetworkReady=false reason:NetworkPluginNotReady message:Network plugin returns error: cni plugin not in
这时该节点上已经在运行的系统 Pod:[('kindnet-bhd99', 'Running'), ('kube-proxy-btrjt', 'Running'), ('metrics-server-59b55d8c77-md5qj', 'Running')](已有的容器不受影响,只是新 Pod 起不来)
用 mv 把配置文件放回去,20 秒后 NetworkReady=[('NetworkReady', False)](containerd 的配置目录监听没有发现“移入”的文件,仍是 false)
改成重新写入文件内容(cat > 原文件):0.1 秒后 NetworkReady=true,9.8 秒后节点恢复 Ready把 CNI 配置文件挪走:0.1 秒后 crictl info 的 NetworkReady=false(NetworkPluginNotReady: cni plugin not initialized);8.6 秒后节点 Ready 变 False,reason=KubeletNotReady,message 里有 container runtime network not ready。节点上已有的系统 Pod 仍然 Running,新 Pod 才创建不了。恢复时有个坑:用 mv 把文件放回去,20 秒后 NetworkReady 仍是 false(containerd 的监听没发现被“移入”的文件);改成 cat > 原文件 重新写入内容,0.1 秒后恢复,9.8 秒后节点回到 Ready。