bigywater.tech
Kubernetes · 79

CRI:kubelet 与容器运行时的接口

kubelet 和容器运行时到底是怎么“对话”的?CRI 里最基本的几个调用是什么?运行时的网络没就绪会怎样?这篇在节点上自己扮演一次 kubelet,把 CRI 的调用顺序和 kubelet 的对账行为都现场做一遍。

0.9 秒
手动创建的沙箱被 kubelet 当作孤儿清掉所用的时间
8.6 秒
挪走 CNI 配置后节点 Ready 变 False 所用的时间
20 秒仍 false
用 mv 把配置放回去,containerd 没有重新加载;改成写入内容才恢复

找到运行时

实测 · 本机执行输出
  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.d

kubelet 配置里 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。

自己扮演 kubelet

实测 · 本机执行输出
  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。

分步演示

CRI
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / kind(Docker 里的单节点或多节点真实集群,节点是 Docker Desktop 虚拟机里的容器)/ kubectl;用空的 kubeconfig 之外的独立 kubeconfig,不碰用户自己的集群;计时用 date +%s%N / kubectl 事件时间戳,数字每次运行会有小幅抖动。三节点 kind 集群 kx-main(Kubernetes v1.37.0,containerd v2.3.4,systemd cgroup 驱动),在 worker 节点上操作;手动起 Pod 用 crictl runp / create / start 并给沙箱设置 cgroup_parent: system.slice(systemd 驱动要求);停 kubelet 用 systemctl stop / start;挪走与恢复 CNI 配置只影响这一个 worker,实验后已恢复;每种情形各一次(实验中途曾因 mv 放回无效手动修复过一次)
    • 没有验证:CRI 的 gRPC 报文本身(没有抓包,只通过 crictl 的行为和命令输出间接观察);containerd 配置文件监听 CNI 目录的实现细节(mv 无效是实测现象,原因推测是只监听写入事件);ImageService 的 PullImage / ListImages;CRI streaming(exec / attach / port-forward 通过 kubelet 代理)的细节;RuntimeClass 对应的多运行时配置;kubelet 清理孤儿沙箱的具体策略和代码路径
    京ICP备2026067502号-1京公网安备11010502063487号