背景:

  • docker inspect:对接 Docker daemon(dockerd),读取 Docker 自己的元数据存储
  • crictl inspect:对接 CRI runtime(containerd/cri-o),是 K8s 标准 CRI 接口工具,直接调用容器运行时,不走 dockerd

在 K8s 环境,现在绝大多数节点底层是 containerd;即使节点装了 docker,k8s 也不经过 dockerd,而是直接调用 containerd。

1、核心本质区别

  1. docker inspect:Docker 专属 API,输出 Docker 完整元数据(镜像、容器、HostConfig、GraphDriver、docker 网络、docker volume 等 docker 内部概念)。
  2. crictl inspect:CRI 标准接口输出,面向 K8s 的 Pod/Container 模型,输出 CRI 定义的结构体,没有 docker 那套 GraphDriver、HostConfig、Mounts 完整语义,字段集合不一样。

注意:crictl inspect 有两个对象:

  • crictl inspect <container-id> → 查看容器
  • crictl inspectp <pod-id> → 查看 pod sandbox(沙箱,pause 容器)

2、输出结构对比

1)docker inspect(你前面看过的 JSON)

输出完整 docker 模型,包含大类:

  • IdCreatedState(PID、OOMKilled、ExitCode)
  • Config:镜像原始配置 Env/Cmd/Entrypoint/Volumes
  • HostConfig:docker run 参数,资源限制、端口映射、重启策略
  • GraphDriver:overlay2 LowerDir / UpperDir / MergedDir / WorkDir(宿主机磁盘路径)
  • Mounts:完整挂载列表 Source/Destination/Mode
  • NetworkSettings:docker 网络、IP、PortBindings、SandboxKey
  • RootFS镜像分层 diffID

✅ 特点:Docker 的全部内部细节都能看到,docker 的概念全部保留。

2)crictl inspect <containerID>(CRI 输出)

CRI 把对象分成两层:PodSandbox (pause 沙箱) + 业务 Container

json

{
  "status": {
    "id": "容器ID",
    "pid": 1234,
    "state": { },
    "createdAt": "...",
    "sandboxId": "pause容器ID", // 归属哪个pod沙箱
  },
  "info": {
    "config": { /* CRI容器配置:image、env、command、mounts、resource限制 */ },
    "image": { /* 镜像信息 */ },
    "sandboxConfig": { /* pod沙箱配置:pod namespace、hostname、dns、pod annotation */ }
  }
}
crictl 缺失 docker 独有的字段(重点)
  • 没有 HostConfig:CRI 模型不使用 docker HostConfig 这套结构;资源限制直接放在config.resources
  • 没有 GraphDriver看不到 LowerDir、UpperDir、MergedDir 宿主机 overlay 磁盘路径!(这是最常踩坑点)
  • 没有 docker 风格 Mounts完整结构体;挂载在config.mounts,只有简单source,destination,readonly,缺少 docker 的 volume 驱动信息
  • 没有 NetworkSettings:网络信息放在 inspectp(pod sandbox) 里面,业务容器本身不带网络信息;网络属于 Pod 沙箱 pause 容器。

📌K8s 中网络、hostname、dns、pid/uts/net 命名空间全部归属于PodSandbox(pause),业务容器共享沙箱的命名空间。 所以看 Pod IP、网络 namespace 要执行:crictl inspectp <pod-sandbox-id>,而不是看业务 container。


3、关键概念映射表(containerd 环境)

表格

功能docker inspectcrictl inspect / inspectp
进程状态、PID、退出码Statestatus
容器启动命令、环境变量Config.{Env,Cmd,Entrypoint}info.config.{env,command}
CPU / 内存资源限制HostConfiginfo.config.resources
overlay2 磁盘路径 (Lower/UpperDir)GraphDriver.Data❌获取不到,CRI 接口不暴露
镜像分层 RootFSRootFS❌CRI 不返回 RootFS diffID
挂载卷信息Mounts[]info.config.mounts[],信息精简
Pod / 容器 IP、网络命名空间NetworkSettingscrictl inspectp sandboxID(pause 沙箱),业务容器无网络
主机名、DNS 配置容器内 Config归属 PodSandbox inspectp
对象模型单个容器(容器自带网络)PodSandbox (pause) + 多个业务容器分离模型

重要:

  1. crictl拿不到 overlay2 的 UpperDir 等底层存储路径;如果需要看 containerd 底层磁盘路径,需要去读 containerd 内部元数据,CRI 接口不给。
  2. K8s 节点底层是 containerd,即使节点装了 docker,docker ps / docker inspect 看不到 k8s 的 pod 容器;dockerd 和 containerd 是两套完全独立容器存储。

4、实操示例(K8s containerd 节点)

bash

# crictl 列出pod沙箱
crictl pods

# 查看pod沙箱(pause):Pod IP、dns、网络命名空间,用 inspectp
crictl inspectp <pod-sandbox-id>

# 查看pod里面的业务容器:进程、env、command、挂载、资源
crictl inspect <container-id>

# crictl 看不到overlay UpperDir!!如果想找,只能查containerd内部数据库

什么时候用哪个

  1. docker inspect
  • 纯 Docker 环境(非 K8s),排查 docker 容器、看 overlay 磁盘路径、docker volume、docker 端口映射。
  1. crictl inspect / inspectp
  • K8s 节点,底层 containerd/cri‑o;排查 Pod 沙箱状态、容器进程、env、命令、挂载、资源;
  • 缺点:拿不到底层存储驱动路径 RootFS/GraphDriver

补充: containerd 环境,如果非要拿到容器 overlay2 UpperDir/LowerDir,CRI 工具 crictl 做不到;需要调用 containerd 原生工具ctr

bash

ctr c info <container-id>

ctr是 containerd 原生 CLI,不是 CRI 标准工具,可以拿到存储层路径。


5、容易混淆区分小结

澄清误区:ctr c info 并不会直接输出 UpperDir/LowerDir 绝对路径字符串;只是可以拿到 snapshotKey,再手动拼接宿主机快照目录,间接找到 overlay 文件。之前表格标记✅容易产生误导。

表格

工具后端接口适用对象是否可直接输出 Overlay UpperDir/LowerDir 绝对路径是否返回 snapshotKey是否可查看 OCI mounts (容器挂载)
docker inspectdockerd (Docker 专属 API)docker run 创建的容器✅ 直接在 GraphDriver.Data 返回完整路径❌ docker 体系无 snapshotKey 概念Mounts字段
crictl inspectCRI‑API (高层接口)K8s Pod 容器 (containerd/cri‑o)❌ CRI 接口屏蔽快照底层信息info.runtimeSpec.mounts
nerdctl inspect(默认 dockercompat 模式)containerd Native APIK8s‑CRI/nerdctl 创建容器❌ K8s 容器 Mounts=null
nerdctl container inspect --mode=nativecontainerd Native APIK8s‑CRI/nerdctl 创建容器❌ nerdctl 不输出 snapshotKeyspec.mounts
ctr c infocontainerd Native API(底层)K8s‑CRI/ctr 创建容器❌(间接获取,需 snapshotKey 拼接路径✅ 返回 .snapshotKeyspec.mounts

关键文字说明(消除误导)

  1. docker inspect dockerd 直接把 overlay 的 UpperDir/LowerDir 完整绝对路径放在 JSON 返回值,开箱即用,不需要二次查找。

  2. ctr c info 返回字段:.snapshotKey(快照唯一标识) 不会返回 /var/lib/containerd/.../upper 这种完整路径。 你需要手动拼接快照根目录:

/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/${snapshotKey}/

进入该目录后,才可见 upper、lower、work、merged。

所以:ctr → 有钥匙 (snapshotKey),但不给你开好门(完整路径);docker inspect 直接把门打开给你完整路径。

一句话记忆: docker inspect = docker 私有完整元数据; crictl inspect = K8s CRI 标准化精简输出,Pod 和容器分离,屏蔽底层存储细节; ctr = containerd 底层原生工具,可以看到更多底层细节。

6、nerdctl 命令

ctr ≠ nerdctl,二者是两套完全独立的客户端,只不过都可以调用 containerd‑native API。

containerd容器+nerdctl工具 需要注意model参数:

nerdctl -n k8s.io inspect  xxx  看不到挂载点

nerdctl -n k8s.io   inspect --mode=native  xxx 可以看到挂载点

7、nerdctl 看不到 snapshotKey,ctr c info 可以,那么是工具的问题,还是 后面容器的问题?

完全是工具 (nerdctl) 本身的输出裁剪问题,和容器、containerd 后台没有任何关系。 你这个 K8s 容器本身确实存在 snapshotKey;containerd‑Native API 也返回了 snapshotKey;只是 nerdctl 主动选择不打印这个字段

  • 不是容器问题:Kubelet/CRI 创建出来的 Pod 容器,100% 都会分配 snapshotKey;
  • 不是权限、命名空间 (k8s.io) 问题nerdctlctr 使用完全一样的 /run/containerd/containerd.sock 访问后端。

只是由于某些字段是OCI标准字段和非标准字段,导致工具实现不同,containerd 原生 API,获取容器信息返回两块独立数据:

  1. OCI Runtime Spec(.spec
    • 进程、环境变量、mounts、namespace、cgroup
    • 属于 OCI 标准--mode=native 会完整输出这块内容
  2. containerd 扩展字段(非 OCI 标准)
    • .snapshotKey.task.shim、创建快照信息
    • 属于 containerd 内部私有元数据,不属于 OCI 规范
  • nerdctl container inspect --mode=native 的设计定位:只输出 OCI‑spec 那一块标准内容,不输出 containerd 私有扩展字段GitHub。
  • ctr c info 是 containerd 底层调试工具,把 OCI spec + containerd 私有元数据全部打印出来,所以你能看到 .snapshotKey

更多推荐