主题
RKE2 ingress-nginx-controller 一直 ContainerCreating 排查与修复
环境信息
- 节点 IP:
192.168.122.78 - OS:Rocky Linux 9.8 (Blue Onyx)
- Kubernetes:RKE2 v1.35.6+rke2r1
- CNI:RKE2 Canal(Calico + Flannel + host-local IPAM)
- 故障 Pod:
kube-system/rke2-ingress-nginx-controller-vcrfj
故障现象
bash
kubectl -n kube-system get podsrke2-ingress-nginx-controller-vcrfj 0/1 ContainerCreating 0 23hPod 持续处于 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.2581354c8853349a71656b52ac1383e6059e553c6f4a03b0613e997f3cc2d04ad
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:
- Calico CNI ADD 成功 → 分配 IP,写入 host-local IPAM 状态文件
portmapCNI ADD 失败 → 因为找不到iptables- sandbox 创建失败 → CNI DEL 未正确调用 → IP 未被回收
- kubelet 不断重试 → IP 逐个泄漏 → 最终耗尽整个
10.12.0.0/24
修复步骤
步骤 1:安装 iptables
在故障节点上执行:
bash
dnf install -y iptablesRocky 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-nginxhelm-install-rke2-ingress-nginx-fhppn 0/1 Completed 0 23h
rke2-ingress-nginx-controller-9fcc9 1/1 Running 0 2m7sbash
kubectl -n kube-system get pods rke2-ingress-nginx-controller-9fcc9 -o wideNAME 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 set | host-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 |
预防措施
节点初始化时安装常用网络工具:
bashdnf install -y iptables nftables部署前检查 CNI 依赖: RKE2/Canal 节点应确保
iptables或iptables-nft可用。监控 host-local IPAM 泄漏: 可定期比较以下两者数量:
bashls /var/lib/cni/networks/k8s-pod-network/ | wc -l crictl ps -aq | wc -l避免强制删除 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