跳转到内容

Calico(etcd datastore)IPAM 残留导致 Pod 创建失败:定位与释放

结论:KubeVirt VM 的 virt-launcher Pod 反复报 networkPlugin cni failed ... Address already assigned in block 时,根因通常是 Calico IPAM 数据残留:Pod 已删除但 etcd 里 handle/assignment 没清干净,新 Pod 拿不到同一个 IP。标准修法是 calicoctl ipam release --ip=X。但本案例集群是 Calico v3.12.0 + etcd datastore + master-1 麒麟 ARM64 + 无外网,calicoctl 完全不可用(老版本无独立二进制、node 镜像不带 ctl、ARM64 无镜像、内网 registry 没有),最终改走 amd64 计算节点 + amd64 calico/ctl:v3.12.0 镜像ipam show 定位满 block,ipam release 释放残留。诊断路径在本案例全部走通,释放执行结果未在会话内闭环验证。

适用范围与环境

  • 集群:Calico v3.12.0,etcd datastore(etcdv3)——注意不是 K8s API datastore,无 ipamblock CRD,数据都在 etcd
  • etcd:原 3 master(172.16.11.11/12/13)已缩为单 master(172.16.11.11),etcd 证书在宿主机 /etc/venus/pki/ca.crt/etc/venus/etcd/etcd-client.crt/etc/venus/etcd/etcd-client.key
  • master-1:麒麟 V10 ARM64(影响 calicoctl 可用性);computing 节点均为 amd64
  • 故障 Pod:virt-launcher-computing-7-windows-1-*(walter 命名空间,KubeVirt Windows VM)

症状

text
failed to create pod sandbox ... networkPlugin cni failed to set up pod
"virt-launcher-computing-7-windows-1-...": k8s-pod-network: calico failed (add):
error getting IP from IPAM: Address already assigned in block

同一 VM 的 Pod 反复创建失败,但集群里其他 VM(computing-2/3 的 windows-2/3/8 等)正常 Running。

排障路径(全在 master-1 或计算节点执行,均为只读)

1. 确认 Calico 存储类型与 etcd 连接

bash
kubectl -n kube-system get ds calico-node -o yaml | grep -iE "ETCD_ENDPOINTS|ETCD_CA|ETCD_CERT|ETCD_KEY|DATASTORE_TYPE"
# DATASTORE_TYPE=etcdv3  → 数据在 etcd,不在 K8s API
# ETCD_ENDPOINTS 若仍指向已删的 172.16.11.12/13,属 3→1 缩容残留,需要改但不一定是本故障根因
kubectl -n kube-system get cm calico-config -o yaml   # 看 cni_network_config / etcd_endpoints

2. 确认 etcd 当前真实成员(只读)

bash
ETCDCTL_API=3 etcdctl --endpoints=https://172.16.11.11:2379 \
  --cacert=/etc/venus/pki/ca.crt \
  --cert=/etc/venus/etcd/etcd-client.crt \
  --key=/etc/venus/etcd/etcd-client.key member list

3. 查 IPAM handle:先看是否已释放干净

bash
ETCDCTL_API=3 etcdctl --endpoints=https://172.16.11.11:2379 \
  --cacert=/etc/venus/pki/ca.crt --cert=/etc/venus/etcd/etcd-client.crt --key=/etc/venus/etcd/etcd-client.key \
  get /calico/ipam/v2/handle/ --prefix --keys-only 2>&1 | head -60

handle 列表里若已没有 windows-1 残留 → handle 释放干净了,问题在 block 分配表(assignment)

4. 查 assignment:找 Address already assigned 的真正来源

bash
ETCDCTL_API=3 etcdctl --endpoints=https://172.16.11.11:2379 \
  --cacert=/etc/venus/pki/ca.crt --cert=/etc/venus/etcd/etcd-client.crt --key=/etc/venus/etcd/etcd-client.key \
  get /calico/ipam/v2/ --prefix 2>&1 | grep -A 20 -i "computing-7-windows-1"

命中几十条 virt-launcher-computing-7-windows-1-* 的 IPAM 残留记录(handle 释放了但 block 槽位没清)——这就是新 Pod 拿不到 IP 的直接来源。

5. 确认残留 Pod 确实已不存在(写操作前必查)

bash
kubectl get pod -n walter -o wide | grep virt-launcher
# 现存 Pod 列表 vs etcd 残留 handle 名单做比对,残留的都不在当前列表里 → 可安全清理

calicoctl 不可用的现实(v3.12 + ARM64 + 无外网)

  • calicoctl ipam release --ip=X 是官方标准释放命令,底层做的事 = 删残留 handle + 把 block 里对应 IP 槽位改回未分配
  • 但本集群三条路全断:
    • v3.12.0 没有独立 calicoctl 二进制分发(ctl 只作为镜像提供),且 没有 ARM64 变体
    • calico-node 镜像里不带 calicoctl 命令which calicoctl 为空)
    • 无外网,无法 docker pull quay.io/calico/ctl:v3.12.0;内网 registry 也没有
  • 结论:没有 ctl 也能安全释放,关键是先备份 + 只动故障 VM 的残留,其他 VM 的 IP 一个不碰

走通路径:amd64 计算节点 + amd64 ctl 镜像

amd64 镜像在 ARM64 的 master-1 上跑不了,但 calicoctl 只是连 etcd 做操作,跑在哪台都一样——选一台有 etcd 证书的 amd64 计算节点执行。

1. 备份整个 Calico IPAM 数据(写操作前必做)

bash
BK=/tmp/calico-ipam-$(date +%Y%m%d-%H%M%S).json
ETCDCTL_API=3 etcdctl --endpoints=https://172.16.11.11:2379 \
  --cacert=/etc/venus/pki/ca.crt --cert=/etc/venus/etcd/etcd-client.crt --key=/etc/venus/etcd/etcd-client.key \
  get /calico/ipam/v2/ --prefix > "$BK" && echo "备份OK: $BK ($(wc -c < "$BK") 字节)"

2. 拉取/导入 amd64 ctl 镜像并挂证书运行

bash
# 从内网 registry 拉(或离线 docker save/load)
docker pull <registry>/calico/ctl:v3.12.0

docker run --rm --network=host --entrypoint calicoctl \
  -v /etc/venus/pki/ca.crt:/secrets/ca.crt \
  -v /etc/venus/etcd/etcd-client.crt:/secrets/etcd-client.crt \
  -v /etc/venus/etcd/etcd-client.key:/secrets/etcd-client.key \
  -e DATASTORE_TYPE=etcdv3 \
  -e ETCD_ENDPOINTS=https://172.16.11.11:2379 \
  -e ETCD_CA_CERT_FILE=/secrets/ca.crt \
  -e ETCD_CERT_FILE=/secrets/etcd-client.crt \
  -e ETCD_KEY_FILE=/secrets/etcd-client.key \
  calico/ctl:v3.12.0 ipam show --show-blocks

3. 定位满 block

ipam show --show-blocks 输出中,多个 block 达到 64/64 100% 满(本案例 10.233.105.0/26、.128/26 等),说明这些 /26 block 的 IP 被历史残留耗尽。

注意:v3.12.0 的 calicoctl ipam 子命令只有 showrelease,没有 checkcheck 是后加的能力)。用 ipam release 即可。

4. 释放残留 IP

从备份 JSON 或 etcdctl get ... assignment 输出里读出残留 handle 占用的具体 IP(handle JSON 值里带 block/分配的 IP),逐个释放:

bash
docker run --rm --network=host --entrypoint calicoctl \
  -v /etc/venus/pki/ca.crt:/secrets/ca.crt \
  -v /etc/venus/etcd/etcd-client.crt:/secrets/etcd-client.crt \
  -v /etc/venus/etcd/etcd-client.key:/secrets/etcd-client.key \
  -e DATASTORE_TYPE=etcdv3 \
  -e ETCD_ENDPOINTS=https://172.16.11.11:2379 \
  -e ETCD_CA_CERT_FILE=/secrets/ca.crt \
  -e ETCD_CERT_FILE=/secrets/etcd-client.crt \
  -e ETCD_KEY_FILE=/secrets/etcd-client.key \
  calico/ctl:v3.12.0 ipam release --ip=<残留IP>

释放后重新创建故障 VM 的 virt-launcher Pod,观察是否能拿到 IP。

风险与边界

  • 只删故障 VM(windows-1)相关的残留,不碰其他 handle/assignment——删除是写操作,删错会影响正常 VM 的 IP
  • 无 ctl 时若走 etcdctl del 手动清理,必须先备份 /calico/ipam/v2/ 全量(本案例给出的路径即备份命令)
  • ETCD_ENDPOINTS 仍指向已删除的 etcd 成员(12/13)属 3 master → 1 master 缩容遗留,需要单独更新,但不是本故障根因
  • IPAM 数据键结构(etcd datastore):/calico/ipam/v2/handle/<host>/<handle_id> 记录谁占用;/calico/ipam/v2/assignment/<cidr> 记录 block 槽位分配

验证方法与实际结果

  • 已验证(本案例会话内):handle 残留排查命令、assignment 定位命令、备份命令、amd64 ctl 镜像运行 ipam show 定位满 block(多个 /26 block 100%)
  • 用户提供(执行中):v3.12.0 ctl 镜像里 ipam 只有 show/release,无 check
  • 未知ipam release 释放后的最终结果(会话在释放执行阶段结束,未在会话内确认 VM 恢复);建议按上述命令释放后验证

参考

基于 MIT 许可发布