Skip to content

RKE2 ingress-nginx-controller 一直 ContainerCreating 排查与修复

环境信息

  • 节点 IP192.168.122.78
  • OS:Rocky Linux 9.8 (Blue Onyx)
  • Kubernetes:RKE2 v1.35.6+rke2r1
  • CNI:RKE2 Canal(Calico + Flannel + host-local IPAM)
  • 故障 Podkube-system/rke2-ingress-nginx-controller-vcrfj

故障现象

bash
kubectl -n kube-system get pods
rke2-ingress-nginx-controller-vcrfj   0/1     ContainerCreating   0          23h

Pod 持续处于 ContainerCreating 状态超过 23 小时,无重启次数。

排查过程

1. 查看 Pod 事件

bash
kubectl -n kube-system describe pod rke2-ingress-nginx-controller-vcrfj

初始关键事件:

Warning  FailedCreatePodSandBox  100s (x6507 over 23h)  kubelet
  Failed to create pod sandbox: rpc error: code = Unknown desc = failed to setup network for sandbox "...":
  plugin type="calico" failed (add): failed to allocate for range 0:
  no IP addresses available in range set: 10.12.0.1-10.12.0.254

表面看是 Pod CIDR(10.12.0.0/24)IP 地址耗尽。

2. 检查节点和 Pod 数量

bash
kubectl get nodes -o wide
kubectl get pods -A -o wide --field-selector spec.nodeName=192.168.122.78

节点上实际运行的 Pod 只有约 13 个,远未达到 /24 的 254 个 IP 上限。说明存在 IP 泄漏

3. 检查 host-local IPAM 状态

Canal 使用 host-local IPAM,状态保存在节点本地目录:

bash
ls /var/lib/cni/networks/k8s-pod-network/ | wc -l

发现该目录下存在 254 个 IP 文件10.12.0.1 ~ 10.12.0.254),几乎所有 IP 都被标记为已分配。

文件内容示例:

bash
cat /var/lib/cni/networks/k8s-pod-network/10.12.0.2
581354c8853349a71656b52ac1383e6059e553c6f4a03b0613e997f3cc2d04ad
eth0

第一行为容器 ID,第二行为接口名。

4. 确认泄漏原因

对比容器运行时中实际存在的容器:

bash
crictl ps -aq

发现 IPAM 文件中记录的容器 ID 绝大多数已不存在。说明这些 IP 对应的旧 Sandbox 已销毁,但 CNI DEL 未正确回收 IP。

5. 定位根因

清理泄漏 IP 后,新 Pod 立即又开始大量创建 IP 文件并再次泄漏。查看新 Pod 的事件:

bash
kubectl -n kube-system describe pod rke2-ingress-nginx-controller-9fcc9

发现真实错误:

Warning  FailedCreatePodSandBox  ...  kubelet
  Failed to create pod sandbox: rpc error: code = Unknown desc = failed to setup network for sandbox "...":
  plugin type="portmap" failed (add): failed to open iptables:
  exec: "iptables": executable file not found in $PATH

根因:Rocky Linux 9 默认没有安装 iptables 命令,而 CNI 的 portmap 插件需要 iptables 来配置端口映射。每次 kubelet 尝试创建 sandbox:

  1. Calico CNI ADD 成功 → 分配 IP,写入 host-local IPAM 状态文件
  2. portmap CNI ADD 失败 → 因为找不到 iptables
  3. sandbox 创建失败 → CNI DEL 未正确调用 → IP 未被回收
  4. kubelet 不断重试 → IP 逐个泄漏 → 最终耗尽整个 10.12.0.0/24

修复步骤

步骤 1:安装 iptables

在故障节点上执行:

bash
dnf install -y iptables

Rocky Linux 9 会安装 iptables-nft,提供兼容的 iptables 命令(底层使用 nf_tables)。

验证:

bash
which iptables
iptables --version

输出示例:

/usr/sbin/iptables
iptables v1.8.10 (nf_tables)

步骤 2:清理泄漏的 host-local IPAM 记录

创建并执行清理脚本:

bash
cat <<'EOF' > /tmp/cleanup_ipam.sh
#!/bin/bash
set -e

IPAM_DIR="/var/lib/cni/networks/k8s-pod-network"
CONTAINER_FILE="/tmp/all_containers.txt"

crictl ps -aq > "$CONTAINER_FILE" 2>/dev/null

if [ ! -d "$IPAM_DIR" ]; then
  echo "IPAM dir not found"
  exit 1
fi

declare -a to_delete=()
for f in "$IPAM_DIR"/*; do
  basename="$(basename "$f")"
  # 保留特殊文件
  if [[ "$basename" == "last_reserved_ip.0" ]] || [[ "$basename" == "lock" ]]; then
    continue
  fi
  # 读取容器 ID(第一行)
  cid="$(head -n1 "$f" 2>/dev/null | tr -d '[:space:]')"
  if [ -z "$cid" ]; then
    echo "Stale (empty): $basename"
    to_delete+=("$f")
    continue
  fi
  # 若容器已不存在,则删除该 IP 文件
  if ! grep -qx "$cid" "$CONTAINER_FILE"; then
    echo "Stale (container gone): $basename -> $cid"
    to_delete+=("$f")
  fi
done

echo "Files to delete: ${#to_delete[@]}"
for f in "${to_delete[@]}"; do
  rm -f "$f"
done

echo "Remaining IP files: $(ls "$IPAM_DIR" | grep -v '^last_reserved_ip.0$' | grep -v '^lock$' | wc -l)"
EOF

chmod +x /tmp/cleanup_ipam.sh
/tmp/cleanup_ipam.sh

本次共清理约 350+ 个泄漏的 IP 文件

步骤 3:删除旧 Pod,让 DaemonSet 重建

bash
kubectl delete pod -n kube-system rke2-ingress-nginx-controller-vcrfj --force --grace-period=0

由于 rke2-ingress-nginx-controller 是 DaemonSet,会自动创建新 Pod。

验证结果

bash
kubectl -n kube-system get pods | grep ingress-nginx
helm-install-rke2-ingress-nginx-fhppn                   0/1     Completed   0          23h
rke2-ingress-nginx-controller-9fcc9                     1/1     Running     0          2m7s
bash
kubectl -n kube-system get pods rke2-ingress-nginx-controller-9fcc9 -o wide
NAME                                  READY   STATUS    RESTARTS   AGE    IP            NODE             NOMINATED NODE   READY GATES
rke2-ingress-nginx-controller-9fcc9   1/1     Running   0          2m7s   10.12.0.115   192.168.122.78   <none>           <none>

Pod 已成功获取 IP 并进入 Running / Ready 状态。

经验总结

现象本质处理要点
no IP addresses available in range sethost-local IPAM 地址池耗尽检查 /var/lib/cni/networks/<network-name>/ 下是否存在大量 stale IP 文件
IP 文件数量远大于实际运行 Pod 数CNI IP 泄漏对比文件中容器 ID 与 crictl ps -aq,删除不存在的记录
portmap 插件报 iptables: executable file not found节点缺少 iptables 命令Rocky Linux 9 需手动安装 iptables-nft
清理后 IP 迅速再次耗尽根因未除,sandbox 持续失败并泄漏 IP先解决底层错误(如安装 iptables),再清理 IPAM

预防措施

  1. 节点初始化时安装常用网络工具

    bash
    dnf install -y iptables nftables
  2. 部署前检查 CNI 依赖: RKE2/Canal 节点应确保 iptablesiptables-nft 可用。

  3. 监控 host-local IPAM 泄漏: 可定期比较以下两者数量:

    bash
    ls /var/lib/cni/networks/k8s-pod-network/ | wc -l
    crictl ps -aq | wc -l
  4. 避免强制删除 Pod 作为常规修复手段: 强制删除会导致 CNI DEL 可能不被调用,加剧 IP 泄漏。仅在必要时使用。

相关路径与命令速查

bash
# CNI 配置
/etc/cni/net.d/10-calico.conflist

# host-local IPAM 状态目录
/var/lib/cni/networks/k8s-pod-network/

# CNI 执行结果记录
/var/lib/cni/results/

# 查看节点 Pod CIDR
kubectl get node <node> -o jsonpath='{.spec.podCIDR}'

# 列出所有容器(含已退出)
crictl ps -aq

# 查看 Pod 事件
kubectl describe pod -n <ns> <pod>

# 查看 RKE2 日志
journalctl -u rke2-server -f

文档生成时间:2026-07-13