crictl —— 容器运行时命令行(CRI 层)
一句话定义
crictl 是 K8s 节点上容器运行时(containerd / CRI-O)的命令行工具。它直接和 CRI 接口对话,绕开 kubectl 和 apiserver——当 kubelet 出问题、容器没起来、kubectl 看不到东西时,crictl 是最后的诊断工具。
典型场景
- kubelet 起不来 / 节点 NotReady:
crictl ps看节点上有什么容器在跑 - ImagePullBackOff:
crictl images看镜像有没有、crictl pull手动拉 - 容器一直 crash:
crictl logs <id>直接看容器日志(不经 kubectl) - 磁盘满了:
crictl rmi --prune清没用的镜像 - 排查"kubectl 看到 Running,节点上是不是真的":
crictl ps
crictl 与 kubectl / docker 的关系
你 ─► kubectl ─► apiserver ─► kubelet ─► containerd / CRI-O ◄─ crictl
│
└─► containers
- kubectl 走 apiserver,看的是 K8s 视角的 pod
- crictl 直接连容器运行时 socket,看的是节点本地的真实容器
Docker 时代的
docker ps在 K8s 节点上不再适用(K8s 1.24+ 已经移除 Docker 作 runtime)。crictl 是它的合法继承者。
配置:连接哪个 runtime
crictl 默认配置在 /etc/crictl.yaml:
runtime-endpoint: unix:///run/containerd/containerd.sock
image-endpoint: unix:///run/containerd/containerd.sock
timeout: 10
debug: false
第一次跑 crictl ps 报错 "no runtime endpoint" 就是没配。
修复:
cat > /etc/crictl.yaml <<'EOF'
runtime-endpoint: unix:///run/containerd/containerd.sock
image-endpoint: unix:///run/containerd/containerd.sock
timeout: 10
EOF
或者命令行 crictl --runtime-endpoint unix:///run/containerd/containerd.sock ps。
核心命令一览
| crictl 命令 | docker 等价 | 用途 |
|---|---|---|
crictl ps | docker ps | 看容器 |
crictl ps -a | docker ps -a | 含已退出的 |
crictl pods | (无) | 看 Pod sandbox |
crictl images | docker images | 镜像列表 |
crictl pull <img> | docker pull | 手动拉镜像 |
crictl rmi <img> | docker rmi | 删镜像 |
crictl rmi --prune | docker image prune | 清没用的 |
crictl logs <ctr> | docker logs | 容器日志 |
crictl exec -it <ctr> sh | docker exec | 进容器 |
crictl inspect <ctr> | docker inspect | 看容器详情 |
crictl stop <ctr> | docker stop | 停容器(kubelet 会拉起新的) |
crictl rm <ctr> | docker rm | 删容器 |
crictl info | docker info | runtime 信息 |
crictl stats | docker stats | 实时资源 |
crictl version | docker version | 版本 |
记住一条:crictl 是从节点本地视角看的。kubectl 是从集群视角看的。两者对得上才正常。
crictl ps —— 看容器
$ crictl ps
CONTAINER IMAGE CREATED STATE NAME POD NAMESPACE
abc123... nginx:1.25 5 minutes ago Running nginx nginx-abc default
def456... etcd:3.5.10 1 day ago Running etcd etcd-m1 kube-system
...
字段:
- CONTAINER —— 容器 ID(前缀,完整 ID 用
-v) - POD —— 这个容器属于哪个 pod(K8s 一个 pod 可能有多个容器)
- NAMESPACE —— K8s namespace(不是 Linux ns)
常用过滤
crictl ps -a # 含已退出
crictl ps -a --name nginx # 按名字
crictl ps --state Exited # 按状态
crictl ps -a --label io.kubernetes.pod.name=nginx-abc # 按 K8s label
# 简化输出(脚本用)
crictl ps -q # 只输出 ID
crictl ps -q --name nginx | head -1 # 拿第一个 nginx 容器 ID
crictl pods —— 看 Pod sandbox
每个 K8s pod 在 CRI 层是一个 sandbox(pause 容器 + 一组应用容器)。
$ crictl pods
POD ID CREATED STATE NAME NAMESPACE
xyz789... 5 minutes ago Ready nginx-abc default
...
crictl pods 列 sandbox,crictl ps 列其中的具体容器。
排查 Pod 起不来的真实方法
K8s pod 显示 ContainerCreating 或 CrashLoopBackOff 时 —— 节点上发生了什么?
# 1. 在 kubectl 那边看
kubectl describe pod my-pod
# 找出 Pod 调度到了哪个节点:Node: m4
# 2. 登节点
ssh m4
# 3. 看真实容器状态
crictl ps -a | grep my-pod
# CONTAINER IMAGE CREATED STATE NAME
# abc... nginx ... Exited nginx ← 退出了!
# def... pause ... Running ...sandbox...
# 4. 看那个 Exited 容器的日志
crictl logs abc...
# 看到容器 panic / 启动失败
# 5. 看为啥 Exited
crictl inspect abc... | jq '.status'
# {
# "exitCode": 1,
# "reason": "Error",
# "message": "configmap mounting failed: ...",
# "startedAt": "...",
# "finishedAt": "..."
# }
看 kubelet 怎么管这个 pod 的
journalctl -u kubelet --since "10 min ago" | grep my-pod
# 显示 pod sync 失败 / image pull 失败 / cgroup 创建失败 等具体原因
镜像管理:images / pull / rmi
crictl images # 列所有镜像
crictl images | grep nginx
crictl images --quiet # 只 ID
crictl pull nginx:1.25 # 手动拉
crictl pull --auth user:pass private-registry.com/nginx:1.25 # 带认证
crictl rmi nginx:1.21 # 删一个镜像
crictl rmi --prune # 删所有没被容器引用的镜像(**节点磁盘满救命**)
"ImagePullBackOff" 排查
kubectl describe pod my-pod | grep -A 10 Events
# Failed to pull image "private.example.com/myapp:v1": rpc error: code = Unknown desc = ...
# 登节点手动拉
ssh m4
crictl pull private.example.com/myapp:v1
# 看具体错:DNS 失败 / 401 / TLS / ...
常见原因和修法:
| 错误 | 原因 | 修 |
|---|---|---|
connection refused / DNS | 节点 DNS 配错 | /etc/resolv.conf |
401 Unauthorized | 没配 imagePullSecret | 创建 secret + pod spec 引用 |
x509: certificate signed by unknown | 私有 registry 自签 cert | containerd 配置加 ca |
manifest unknown | tag 错 | 看 registry 列表 |
私有 registry 配 containerd(Day7 必会):
# /etc/containerd/config.toml
[plugins."io.containerd.grpc.v1.cri".registry.configs."private.example.com".auth]
username = "user"
password = "pass"
改完 systemctl restart containerd。
crictl logs 和 kubectl logs 的区别
| 维度 | kubectl logs | crictl logs |
|---|---|---|
| 来源 | apiserver → kubelet → 容器 stdout | 节点本地 |
| 需要 | apiserver 可用 | 只需 runtime 可用 |
--previous | 支持(上次崩溃日志) | 不直接支持,要找上一个 container ID |
-f 跟随 | 支持 | 支持 |
-c 选容器 | 选 K8s pod 内多容器 | 直接指定 container ID |
crictl logs abc... # 整体
crictl logs -f abc... # 跟随
crictl logs --tail=100 abc...
crictl logs --since=10m abc...
kubectl logs 不工作时 crictl logs 是替代方案:apiserver 挂了、kubelet 重启中、网络隔离。
crictl exec —— 进容器
crictl exec -it abc... sh
crictl exec abc... ls /var/log
和 kubectl exec 等价,但不经过 apiserver。
crictl stats —— 实时资源
crictl stats
# CONTAINER CPU % MEM DISK INODES
# abc123 5.2% 120MB 1.5MB 234
# def456 0.3% 50MB 0.5MB 102
crictl stats -a # 含已停止
看节点上真实的容器资源。kubectl top 依赖 metrics-server,crictl stats 直接问 runtime。
crictl info —— Runtime 健康
$ crictl info
{
"status": {
"conditions": [
{"type": "RuntimeReady", "status": true, "reason": "RuntimeReady"},
{"type": "NetworkReady", "status": true, "reason": "NetworkReady"}
]
},
"cniconfig": { ... },
"config": { ... }
}
排查节点 NotReady:
crictl info | jq '.status.conditions'
# 哪个 condition false 就是哪里坏了
NetworkReady=false —— CNI 没起来。 RuntimeReady=false —— containerd 本身有问题。
训练营节点排错套路
Pod 起不来 / 节点 NotReady 排错链
# 1. 节点状态
kubectl get nodes
kubectl describe node m4 | tail -30
# 2. 登节点
ssh m4
# 3. runtime 健康
crictl info | jq '.status.conditions'
systemctl status containerd
# 4. 当前容器
crictl ps -a | head
# 5. 看具体崩了的
crictl logs <container-id>
crictl inspect <container-id> | jq '.status'
# 6. kubelet 日志
journalctl -u kubelet --since "5 min ago"
# 7. 磁盘 / 内存
df -h
free -h
磁盘满了的快速救援
# 节点磁盘报警
df -h /var/lib/containerd
# 清没引用的镜像
crictl rmi --prune
# 通常能清几个 GB
# 清 containerd 缓存(小心,要重启)
ctr -n k8s.io content prune # 用 ctr 不是 crictl
# journal 也清
journalctl --vacuum-size=200M
crictl vs ctr 的区别
容器运行时两个命令行工具容易混:
| 工具 | 走的接口 | 用途 |
|---|---|---|
| crictl | CRI(K8s 标准接口) | K8s 视角的容器、镜像操作 |
| ctr | containerd 原生 API | containerd 内部细节 |
例子:
crictl images # 只显示 K8s 用的镜像(k8s.io namespace)
ctr images list # 显示**所有** containerd 镜像(含其它 namespace)
ctr -n k8s.io images list # 等价 crictl images
ctr -n moby images list # 看其它(如 docker 兼容层)
99% 场景用 crictl。ctr 用在 containerd 自身排错。
常见踩坑
坑 1:no runtime endpoint configured
$ crictl ps
FATA[0000] no runtime endpoint configured
/etc/crictl.yaml 没配。见上面"配置"段。
坑 2:用旧版 crictl 配新 containerd
crictl --version
# crictl version 1.21 ← 太老
crictl 版本最好和 K8s minor 版本一致(K8s 1.28 → crictl 1.28)。
VERSION=v1.28.0
curl -L https://github.com/kubernetes-sigs/cri-tools/releases/download/$VERSION/crictl-$VERSION-linux-amd64.tar.gz \
| tar -xz -C /usr/local/bin
坑 3:crictl ps 没看到 pod 容器
kubectl get pods # 显示 nginx Running
ssh m4 'crictl ps | grep nginx' # 找不到
可能 pod 在别的节点上跑。kubectl get pods -o wide 看 NODE 列。
或者节点上 runtime 出问题、Pod 还显示 Running 但实际没跑。这种"幽灵 Pod"是 kubelet bug,需要 restart kubelet。
坑 4:crictl 删容器但 kubelet 立刻拉起
crictl stop abc...
crictl rm abc...
crictl ps # 几秒后又有一个新的
这是正常的——K8s 的 desired state 是要这个 pod 跑着,kubelet 看到容器没了会拉起新的。要真停:
kubectl delete pod my-pod -n my-ns
# 或者把 deployment 缩到 0
kubectl scale deploy nginx --replicas=0
直接 crictl 操作只在调试时用,不能"代替 kubectl"管 K8s 状态。
坑 5:root 才能跑 crictl
$ crictl ps
FATA[0000] connect failed: ... permission denied
containerd.sock 默认 root only。用 sudo 或者 ssh root。
把用户加到组也行:
chgrp myuser /run/containerd/containerd.sock # 不推荐(生产)
坑 6:crictl logs --previous 不存在
kubectl logs --previous 看上次挂的日志,但 crictl logs 不支持。
替代:
crictl ps -a --name my-container # 看所有同名容器(含已 Exited)
# 拿到上一个的 container ID
crictl logs <previous-id>
坑 7:镜像名带 / 让 grep 麻烦
crictl images | grep "registry.k8s.io/etcd"
# 因为 / 在某些 shell 里要转义
OK 一般 grep 不需要转义 /,但写 sed 时要。
关联命令
- kubectl —— 集群视角,crictl 是节点视角
- kubeadm —— kubeadm 不直接用 crictl,但安装/排错时常配合
- systemctl ——
systemctl status containerd/restart containerd - journalctl ——
journalctl -u containerd看 runtime 日志 ctr—— containerd 原生 CLI,看 namespace 细节用nerdctl—— containerd 的"docker-like" CLI(更易用,适合本地测试)