跳到主要内容

k8s-pod-containercreating


K8S 生产排查台账:Pod 卡在 ContainerCreating(FailedCreatePodSandBox 通用排查指南)

01 前置理论与核心机制

1. PodSandbox 的生命周期与 CNI 交互机制

  • 基础架构容器:Pod 在 K8S 中以多容器共享环境形式存在。在拉起业务容器前,容器运行时(CRI,如 containerd)会优先调用底层的 pause 镜像拉起基础架构容器,初始化并持有专用的 Linux Namespace(Network、IPC、UTS 等)。
  • CNI 调用标准流程
    1. CRI 接收到 Kubelet 的 RunPodSandbox RPC 请求。
    2. CRI 在宿主机创建 Network Namespace。
    3. CRI 按照标准 CNI 规范,解析配置并同步调用网络插件二进制(执行 ADD 动作)。
    4. CNI 插件分配 IP、配置网卡对(veth pair)、设置宿主机与容器内的路由规则。
    5. CNI 将网络配置结果(IP、DNS、路由)以标准 JSON 返回给 CRI,沙箱创建完成。
  • 报错本质FailedCreatePodSandBox 代表在上述调用链中,系统命名空间创建、CRI 内部流程或 CNI 插件的 ADD 执行失败,导致沙箱未就绪,业务容器因此无法进入拉取镜像与启动流程。

2. Node Ready 状态与网络健康的判定边界

  • 节点状态上报链路kubelet 标记节点为 Ready,核心考量是节点的系统基础健康状况(心跳上报、未触碰 DiskPressure/MemoryPressure/PIDPressure 驱逐线,以及 CRI 守护进程处于连接可用状态)。
  • 为什么 CNI 异常时节点通常仍维持 Ready
    • 在主流 CRI 架构下,节点的网络就绪判定(NetworkReady)主要由底层的 CRI 插件通过检查网络配置目录(如 /etc/cni/net.d/)是否存在合法配置,向 Kubelet 进行状态同步与汇报。
    • 在常规心跳周期中,运行时与 Kubelet 通常不会主动调用 CNI 二进制去发起真实的端到端网络分配探测
    • 这种解耦设计意味着“静态配置存在”并不等同于“具备动态网络分配能力”;真实的沙箱网络分配只有在 Pod 被实际调度并触发 RunPodSandbox 时才会验证。

02 故障排查与恢复闭环 SOP

1. 现场止血与范围界定

# 1. 发现单机批量异常时,优先封锁节点,防止新任务继续落盘扩散影响
kubectl cordon <target-node>

# 2. 查看目标 Pod 事件及报错堆栈
kubectl describe pod <pod-name> -n <namespace>

# 3. 检查异常 Pod 的宿主机分布,判断是单机故障还是集群全局故障
kubectl get pods -A --field-selector spec.nodeName=<target-node>
  • 全局故障:集群多个节点均报 FailedCreatePodSandBox。排查重心:CNI 控制面组件(如 kube-system 下的 DaemonSet/Operator)、IPAM 全局地址池耗尽、APIServer 证书或集群鉴权凭证异常。
  • 单机故障:故障仅集中在特定节点。排查重心:宿主机 CRI 运行环境、本地 CNI 二进制/配置文件、节点 IPBlock 配额、本地系统资源限制。

2. 节点现场排查流程

单机 FailedCreatePodSandBox

├─ 1. 提取精准底层报错 (containerd / kubelet)
├─ 2. 检查 CNI 本机守护容器与路由建立状态
├─ 3. 检查 IPAM 资源池与虚拟网络接口
├─ 4. 核验 CNI 配置加载规则及二进制完整性与权限
└─ 5. 检查宿主机内核限制 (Inode / file-max / inotify)

步骤一:提取精准错误日志

Kubelet 事件多为顶层概括,深层堆栈需在目标宿主机调取 CRI 运行日志:

# 1. 优先查看 containerd 最近 10 分钟内包含 CNI 报错的上下文(重点关注 error 级别)
journalctl -u containerd --since "10 minutes ago" --no-pager | grep -C 5 -i "cni"

# 2. 若 containerd 无明确报错,查看 kubelet 针对该 Pod 的沙箱调用记录
journalctl -u kubelet --since "10 minutes ago" --no-pager | grep -i "FailedCreatePodSandBox"

步骤二:检查 CNI 节点守护进程

# 查看本机 CNI 容器状态
crictl ps -a | grep -E 'calico|flannel|cilium'

# 查看 CNI 容器日志
crictl logs <cni-container-id> --tail 100
  • 排查重点:容器是否存在频繁重启(CrashLoopBackOff)、OOMKilled、与集群控制面的通信超时。

步骤三:排查 IPAM(IP 分配状态)

# Calico 环境下在管理节点检查各节点 IPBlock 分配水线
calicoctl ipam show --show-blocks

# 检查节点虚拟网络设备状态
ip link show
  • 排查重点
    • 该节点的专属 IPBlock 是否已经无空闲 IP 可供借调;
    • 本地核心虚拟网卡(如 tunl0vxlan.calicoflannel.1)是否处于 DOWN 状态。

步骤四:检查 CNI 配置与插件文件

# 检查配置文件目录
ls -lah /etc/cni/net.d/

# 检查二进制目录文件属性
ls -lah /opt/cni/bin/
  • 排查重点
    • 配置文件选择机制:运行时通常按照配置目录的规则(多为字典序)筛选首个生效配置,但具体行为需结合实际 CRI 配置(如 containerd 的 conf_dircni_default_network 等)确认。排查时应重点检查目录中是否存在历史遗留的备份文件或干扰文件(如 .bak00-old.conf);
    • 二进制损坏与权限:检查核心文件大小(如 Calico 二进制通常在数十 MB,若显示仅几百字节则为下载截断或损坏),并确认具备可执行权限(-rwxr-xr-x)。

步骤五:排查宿主机内核与系统资源限制

# 检查磁盘与 Inode 水位
df -h
df -i

# 检查系统句柄与监听上限
sysctl fs.file-nr
sysctl fs.inotify.max_user_watches

3. 根因修复与恢复规范

[高危操作警示]

  • kubectl delete pod --force --grace-period=0:此命令会跳过容器优雅停机流程,强制从 etcd 抹除对象。仅在底层网络确认修复、且原 Pod 确实处于死锁无法自行退出时使用。对包含本地卷挂载的有状态应用(StatefulSet),严禁盲目强制删除,否则可能引发底层卷锁无法释放或脑裂。
  • calicoctl ipam release:手动回收 IP 必须明确确认该 IP 的宿主容器已在系统层面彻底销毁。若误回收正在使用的 Pod IP,将导致多容器网络地址冲突并引发大面积业务异常。
  • 直接替换 CNI 二进制文件:在生产环境手动替换 /opt/cni/bin/ 属于应急止血动作,操作前必须备份旧文件;事后必须核对该节点 CNI DaemonSet 的注入规范,防止 DaemonSet 重启后重新覆盖引发故障复现。
# 1. 针对二进制文件损坏的应急修复(以 Calico 为例)
cp /opt/cni/bin/calico /opt/cni/bin/calico.bak.$(date +%F)
scp /opt/cni/bin/calico root@<target-node>:/opt/cni/bin/calico
chmod 755 /opt/cni/bin/calico

# 2. 故障恢复与验证规范:
# 优先等待原 Pod 自动重试;若 Pod 处于长期指数退避中,可安全清理无状态实例以触发重新拉起
kubectl delete pod <pod-name> -n <namespace>

# 3. 实时跟踪新 Pod 的调度与网络分配
kubectl get pod <pod-name> -n <namespace> -o wide -w

# 4. 确认节点业务与网络链路完全恢复后,再解除调度隔离
kubectl uncordon <target-node>

03 真实生产报错日志案例库

案例 1:IPAM 地址池耗尽

  • containerd 抓取日志
    failed to run network programming: [failed to allocate for range 0: no IP addresses available in range set: 10.244.3.64-10.244.3.127]
  • 现场分析:节点绑定的 CIDR Block(如 /26)所有可用 IP 均已分发完毕,CNI 无法在本地池内申请新租约。
  • 处置流程
    1. 检查是否存在僵死容器未注销租约:calicoctl ipam check
    2. 若确认有残留未释放租约,在业务停止状态下执行安全释放:calicoctl ipam release --ip=<IP>
    3. 扩容该节点可借调的 Block 配额或调低节点的 maxPods 上限。

案例 2:CNI 二进制执行权限丢失

  • containerd 抓取日志
    failed to setup network for sandbox "...": fork/exec /opt/cni/bin/calico: permission denied
  • 现场分析:运维分发、备份同步或挂载参数异常,导致 /opt/cni/bin/calico 执行权限(x)丢失,或宿主机文件系统被以 noexec 参数挂载。
  • 处置流程
    1. 修复权限:chmod 755 /opt/cni/bin/calico
    2. 核查挂载参数:mount | grep /opt,若存在 noexec 需调整为 exec

案例 3:配置文件干扰导致解析异常

  • kubelet / containerd 抓取日志
    error parsing CNI configuration: plugin type "flannel" failed: open /run/flannel/subnet.env: no such file or directory
  • 现场分析:集群已全面切换为 Calico,但运维在 /etc/cni/net.d/ 下备份了旧文件 00-flannel.conf。CRI 在加载配置时优先匹配到了历史配置,导致新 Pod 仍被丢给缺失环境的 Flannel 插件解析。
  • 处置流程
    1. 清理或移除非生效的配置文件:mv /etc/cni/net.d/00-flannel.conf /tmp/
    2. 确保目录下仅留存唯一的有效主配置(如 10-calico.conflist)。

案例 4:CNI 本地守护进程崩溃或 Socket 无响应

  • containerd 抓取日志
    rpc error: code = Unavailable desc = connection error: desc = "transport: Error while dialing dial unix /var/run/calico/cni.sock: connect: connection refused"
  • 现场分析:Calico CNI 插件在执行分配时需要与本地运行的 cni.sock 通信,而宿主机上的 calico-node 容器已挂死或发生 OOM。
  • 处置流程
    1. 检查 CNI 容器状态:crictl ps -a | grep calico-node
    2. 提取崩溃日志并重启守护进程:crictl restart <calico-node-container-id>

04 生产排障速查表(FailedCreatePodSandBox)

遇到故障请按序单向排查:

顺序排查层级执行命令正常基线异常处置动作
01止血隔离kubectl cordon <node>节点进入 SchedulingDisabled优先防止新 Pod 继续调度至故障机
02深层日志journalctl -u containerd -n 100 --no-pager | grep -i cni无持续输出的 error 堆栈根据捕获的具体日志对号入座(见案例库)
03守护容器crictl ps -a | grep -E 'calico|flannel|cilium'状态维持 Running,无反复重启检查 OOMKilled,执行 crictl restart
04IP 资源calicoctl ipam show --show-blocks对应节点 IP 占用低于 100%释放孤儿租约 / 扩充 CIDR Block 借调
05配置目录ls /etc/cni/net.d/仅存在唯一的当前网络插件有效配置文件移除 .bak、历史废弃配置等干扰文件
06二进制ls -lah /opt/cni/bin/<plugin>权限为 755,文件体积通常数十 MB重新同步完整文件并加权:chmod +x
07系统限制df -i && sysctl fs.inotify.max_user_watchesInode 水位正常,句柄未耗尽清理磁盘残余文件,调整内核参数上限
08恢复验证修复根因后等待或重建 Pod,观察 IP 分配新拉起 Pod 成功获得 IP 并进入 Running确认网络与业务正常后,最后执行 kubectl uncordon

05 面试回答话术(STAR 法则)

面试官问:“你在运维过程中,遇到过 Pod 卡在 ContainerCreating 的情况吗?当时是怎么排查和定位的?”

  • 情境(Situation): “在一次生产发版过程中,新调度的业务 Pod 停滞在 ContainerCreating。检查集群节点,所有 Node 均显示正常的 Ready 状态。”
  • 任务(Task): “需要快速止血,并在节点健康上报具有局限性的前提下,精准排查底层网络沙箱创建失败的技术根因。”
  • 行动(Action)
    1. “我首先通过 kubectl cordon 隔离节点截断故障扩散,随后调取 Pod 事件捕获到核心特征码 FailedCreatePodSandBox。”
    2. “因为网络沙箱交互已下沉至 CRI,我直接登录节点提取了 containerd 的运行日志,快速定位到底层真实报错。”
    3. “按照标准链路核查了 CNI DaemonSet 容器健康度、IPAM 资源池占用、/etc/cni/net.d/ 生效配置项以及 /opt/cni/bin/ 二进制文件的完整度与执行权限。”
    4. “完成针对性修复后,验证网络沙箱已能正常分配,并有序恢复了业务 Pod,最终解除节点调度封锁。”
  • 结果(Result): “业务在几分钟内恢复。事后我们对故障复盘,完善了对节点 IPAM 水位与沙箱创建异常事件的 Prometheus 告警,并在 CNI 层面优化了节点配置的自愈校验机制。”

06 面试高频追问与应答预案

追问 1:既然 CNI 异常,为什么 Node 依然能维持 Ready 状态?

  • 回答要点
    • Kubelet 对 Node Ready 的判断基于心跳与资源压力条件(磁盘、内存、PID)。
    • 针对网络状态,主流 CRI 运行时通常仅基于网络配置目录的存在性进行基础健康上报。
    • 运行时与 Kubelet 均不会在常规心跳中主动调用 CNI 二进制发起端到端的沙箱网络预分配探测,因此无法提前感知底层二进制损坏或动态 IP 耗尽等运行时异常。

追问 2:如果定位到错误是 IP 耗尽,应该怎么处理?

  • 回答要点
    • 应急手段:使用 calicoctl ipam check 查找是否存在由于 Pod 异常退出但 IP 租约未释放的泄漏资源,通过 calicoctl ipam release 手动回收。
    • 架构优化:如果单节点 Pod 密度过大,需调整节点 IPPool 分配的 Block 大小(如从 /26 扩容),或在调度层通过 maxPods 参数限制单节点承载上限。

追问 3:在离线环境下,如何快速验证 CNI 插件是否能正常工作?

  • 回答要点
    • 使用官方提供的 cnitool 工具,手动模拟 CRI 调用 CNI 插件的 ADDDEL 行为,脱离 Kubernetes 控制平面直接验证底层网络配置是否畅通。
    • 执行命令示例
      # 1. 声明 CNI 环境变量
      export CNI_PATH=/opt/cni/bin
      export NETCONFPATH=/etc/cni/net.d

      # 2. 创建临时测试 Network Namespace
      ip netns add test-cni-ns

      # 3. 调用 CNI 执行 ADD 操作(以加载 calico 为例)
      cnitool add calico /var/run/netns/test-cni-ns

      # 4. 验证分配结果并清理测试环境
      ip netns exec test-cni-ns ip addr
      cnitool del calico /var/run/netns/test-cni-ns
      ip netns del test-cni-ns

🔒 本部分核心干货已锁定

包含:后续高危场景避坑、四大生产真实案例复盘与面试预案。扫码获取暗号解锁全文。