bigywater.tech
Kubernetes · 78

containerd 与 crictl:节点上看到的容器

kubectl 里的一个 Pod 在节点上到底是什么?crictl 和 ctr 看到的是同一批东西吗?日志文件长什么样?这篇在 kind 节点里把 Pod、沙箱、容器、进程、日志逐层对应起来。

1 + 2
一个 Pod 在节点上:1 个沙箱(pause)+ 2 个业务容器,同属一个 shim
10 = 5 + 5
ctr -n k8s.io containers 数 = crictl 的容器数 + 沙箱数
0.7 秒
crictl stop 之后 kubelet 重启容器的用时

Pod、沙箱、容器

实测 · 本机执行输出
  kubectl get pod hello:2 个容器(main、side)
  crictl pods:1 个沙箱(sandbox)id=47b11aa565a7… state=SANDBOX_READY;labels 里 io.kubernetes.pod.name=hello io.kubernetes.pod.namespace=k72 io.kubernetes.pod.uid=d5916cff…(= kubectl 的 uid:True)
  crictl ps --pod 47b11aa565a7…:[('side', 'RUNNING', 'f537a1e347d6'), ('main', 'RUNNING', 'befb0f6daaab')]
  kubectl 里 status.containerStatuses[main].containerID=containerd://befb0f6…;和 crictl 里 main 的 id 一致:True
  节点上(宿主机视角)这个 Pod 的三个进程 {'沙箱(pause)': 214064, 'main': 214089, 'side': 214118}:
    PID PPID COMMAND
     214064 214038 /pause
     214089 214038 sh -c i=0; while true; do echo line-$i; echo err-$i >&2; i=$((i+1)); sleep 1; done
     214118 214038 sleep 3600
  main 的父进程是 containerd-shim(每个沙箱一个 shim,管着 pause 和这个 Pod 的所有容器):214038       1 /usr/local/bin/containerd-shim-runc-v2 -namespace k8s.io -id 47b11aa565a7f86950b49644

kubectl 里 Pod hello 有 2 个容器。在它所在的 worker 节点上,crictl pods 有 1 个沙箱,labels 里 io.kubernetes.pod.name=hello、io.kubernetes.pod.namespace=k72,pod.uid 和 kubectl 的 uid 一致;crictl ps --pod 列出 side 和 main,kubectl 里 containerID 就是 crictl 的容器 id。宿主机视角,沙箱的 /pause 进程和 main(sh -c …)、side(sleep 3600)的父进程是同一个 containerd-shim-runc-v2。

containerd 里的对象

实测 · 本机执行输出
  ctr namespaces ls:['k8s.io'](kubelet 用的 containerd 命名空间是 k8s.io)
  ctr -n k8s.io containers ls 共 10 行;crictl ps -a 共 5 个容器 + crictl pods 共 5 个沙箱——沙箱在 containerd 眼里也是一个容器(pause),所以两者加起来对得上:True
  crictl inspect main:pid=214089;runtime=io.containerd.runc.v2;snapshotter=overlayfs;image=docker.io/library/busybox:1.36;cgroupsPath=kubelet-kubepods-burstable-podd5916cff_d7b7_45ca_a8e2_40b1d1044566.sli;memory.limit=67108864;cpu.quota/period=10000/100000
    memLimit=67108864 即 64Mi,cpuQuota=10000(微秒/100000 周期)即 100m——kubelet 把 resources 翻译成 OCI 运行时配置

ctr namespaces ls 只有 k8s.io——kubelet 用的 containerd 命名空间。ctr -n k8s.io containers ls 共 10 行,正好等于 crictl ps -a 的 5 个容器加 crictl pods 的 5 个沙箱(沙箱在 containerd 眼里也是一个容器)。crictl inspect 里:runtime=io.containerd.runc.v2、snapshotter=overlayfs、cgroupsPath 是 kubelet-kubepods-burstable-pod…slice、memory.limit=67108864(64Mi)、cpu.quota/period=10000/100000(100m)——kubelet 把 Pod 的 resources 翻译成了 OCI 运行时配置。

日志文件

实测 · 本机执行输出
  /var/log/pods/k72_hello_<uid>/main/:0.log
  0.log 的格式(时间戳 流 标记 内容):
    2026-10-10T16:50:22.795662756Z stderr F err-2
    2026-10-10T16:50:22.795727006Z stdout F line-2
    2026-10-10T16:50:23.796919506Z stdout F line-3
    2026-10-10T16:50:23.79693759Z stderr F err-3
  /var/log/containers/ 里的符号链接(kubectl logs 最终读的就是它指向的文件):
    hello_k72_main-befb0f6daaabf70ce66ee0a99… -> …/k72_hello_<uid>/main/0.log
    hello_k72_side-f537a1e347d6aeb9ea92dc3a3… -> …/k72_hello_<uid>/side/0.log
  crictl logs --tail 2:['line-4', 'err-4']

日志在 /var/log/pods/k72_hello_<uid>/main/0.log,每行是“时间戳 流 标记 内容”,如 2026-10-10T16:50:22.795727006Z stdout F line-2(F 表示完整的一行,stdout 与 stderr 交错)。/var/log/containers/ 下是符号链接(hello_k72_main-<容器id>….log 指向上面的文件),日志采集器通常读这里;crictl logs 和 kubectl logs 读到的是同一份内容。

crictl 的运维操作

实测 · 本机执行输出
  crictl exec:在 main 容器里: hostname=hello uid=0 pid1=sh
  crictl stats main:cpu 累计 29 ms;内存 working set 1168 KiB
  crictl images(节选):
    IMAGE TAG IMAGE ID SIZE
    docker.io/kindest/kindnetd v20260820-69b56db7 8e13ccc0aaa68 35.7MB
    docker.io/kindest/local-path-helper v20260131-7181c60a 8f375e9f93513 2.74MB
    docker.io/kindest/local-path-provisioner v20260820-69b56db7 0771a8a05619f 14.2MB
  用 crictl stop 停掉 main:kubelet 0.7 秒内就重启了它(restartCount=1),新的 containerID 以 7fc8b3ff124e 开头,和原来的 befb0f6daaab 不同

crictl exec 能直接在容器里执行命令(得到 hostname=hello uid=0 pid1=sh);crictl stats 显示 main 的累计 CPU(29 ms)和内存 working set(1168 KiB);crictl images 列出节点上的镜像。用 crictl stop 停掉 main,kubelet 0.7 秒内就把它重启了(restartCount=1),新的容器 id 与原来不同——crictl 改的是实际状态,kubelet 会按 restartPolicy 把它拉回期望状态。

分步演示

containerd 与 crictl
本步的对象

    本文用到的工具与边界

    • 本机 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);Pod 固定在 worker 节点;crictl、ctr、ps 都是在节点里用 docker exec 运行;进程 PID 取自 crictl inspect / inspectp 的 info.pid;重启用时是 0.3 秒间隔轮询,单次测量;每种情形各一次
    • 没有验证:CRI 的 gRPC 接口本身(下一篇);containerd 配置文件 config.toml 与插件;镜像拉取与 snapshotter 目录;containerd 与 Docker 的关系;其他运行时(CRI-O、gVisor、Kata);日志轮转(containerLogMaxSize)
    京ICP备2026067502号-1京公网安备11010502063487号