1. 问题现象与背景分析
那天下午,我正在测试一个Kubernetes集群的新功能。为了快速回滚测试环境,我随手点下了VMware的"恢复快照"按钮。几秒钟后,虚拟机回到了之前的干净状态,但当我尝试kubectl get nodes时,却看到了令人窒息的报错:
Unable to connect to the server: dial tcp 192.168.1.100:6443: connect: no route to host更诡异的是,ifconfig显示网卡还在,IP地址也正常分配,但就是无法访问任何Kubernetes服务。这个看似简单的快照恢复操作,竟然让整个Kubernetes网络"人间蒸发"了。
2. 初步排查与关键发现
2.1 基础网络检查
首先确认基础网络连通性:
ping 8.8.8.8 # 成功 ping gateway # 成功 curl https://www.baidu.com # 成功这说明底层网络栈是正常的,问题出在Kubernetes自身的网络组件。
2.2 核心组件状态检查
查看kubelet日志发现关键线索:
journalctl -u kubelet -n 100 --no-pager输出中反复出现:
networkPlugin cni failed to set up pod "kube-controller-manager-node1_kube-system" network: failed to set bridge addr: "cni0" already has an IP address different from 10.244.0.1/242.3 CNI网络残留证据
执行ip link show发现异常:
ip link show cni0输出显示:
4: cni0: <BROADCAST,MULTICAST> mtu 1500 qdisc noqueue state DOWN mode DEFAULT group default qlen 1000 link/ether 00:11:22:33:44:55 brd ff:ff:ff:ff:ff:ff这个接口处于DOWN状态,且MTU值异常。
3. 问题根源深度解析
3.1 VMware快照的隐藏陷阱
VMware的快照恢复机制存在一个鲜为人知的特点:它只恢复磁盘状态,而虚拟机的运行时状态(包括网络配置)会被保留。这就导致:
- 磁盘上的Kubernetes配置回滚到了旧版本
- 内存中的网络命名空间、CNI配置保持现状
- 新旧网络配置产生冲突
3.2 Kubernetes网络组件冲突细节
具体到CNI插件(以flannel为例):
- 快照恢复后,/etc/cni/net.d/中的配置回滚
- 但宿主机的网络命名空间保留了之前的cni0网桥
- kubelet尝试用旧配置初始化新网桥时发生冲突
3.3 关键时间线还原
通过分析系统日志,还原出完整的事件链:
快照恢复前:
- cni0网桥IP:10.244.1.1/24
- flannel配置子网:10.244.1.0/24
快照恢复后:
- flannel配置回滚到:10.244.0.0/24
- 但旧cni0网桥(10.244.1.1)仍存在
4. 完整解决方案与操作步骤
4.1 紧急恢复方案
# 1. 清理残留网络设备 sudo ip link delete cni0 sudo ip link delete flannel.1 # 2. 重启关键服务 sudo systemctl restart flanneld sudo systemctl restart kubelet # 3. 验证网络恢复 kubectl get pods -n kube-system4.2 永久解决方案
- 创建快照前标准操作流程:
# 优雅关闭Kubernetes kubectl drain <node-name> --ignore-daemonsets sudo systemctl stop kubelet sudo systemctl stop docker # 清理网络命名空间 sudo ip link delete cni0 sudo ip link delete flannel.1- 使用自动化脚本管理快照:
#!/bin/bash # snapshot_helper.sh NODE_NAME=$(hostname) kubectl drain $NODE_NAME --ignore-daemonsets systemctl stop kubelet docker ip link delete cni0 2>/dev/null ip link delete flannel.1 2>/dev/null vmrun snapshot /path/to/vm "Snapshot_$(date +%Y%m%d)"5. 深度防护措施
5.1 监控预警配置
在Prometheus中添加以下告警规则:
groups: - name: k8s-network rules: - alert: CNIBridgeDown expr: count(ip_link_up{interface="cni0"} == 0) > 0 for: 5m labels: severity: critical annotations: summary: "CNI bridge is down on {{ $labels.instance }}"5.2 架构级优化建议
- 考虑使用Multus CNI实现多网络平面冗余
- 在关键节点部署NetworkPolicy备份控制器
- 实现定期网络配置备份:
# 备份网络配置 crontab -e 0 3 * * * /usr/bin/nsenter --net=/var/run/netns/cni-$(cat /var/run/flannel/subnet.env | grep NETWORK | cut -d= -f2 | sed 's/\//-/') ip addr show > /backup/network_$(date +\%Y\%m\%d).log6. 同类问题扩展排查
6.1 其他CNI插件处理方案
| CNI类型 | 清理命令 | 注意事项 |
|---|---|---|
| Calico | calicoctl node diags | 需要先收集诊断信息 |
| Weave | weave reset | 会丢失所有网络配置 |
| Cilium | cilium cleanup | 需要重启cilium-agent |
6.2 VMware高级配置建议
在.vmx文件中添加以下参数可增强稳定性:
ethernet0.noRestoreState = "TRUE" vmci0.noRestoreState = "TRUE"7. 故障复盘与经验总结
这次事故教会我几个关键经验:
- 快照不是万能药:对复杂系统要理解快照的局限性
- 网络状态具有特殊性:网络命名空间不随快照恢复而重置
- 操作顺序很重要:关闭服务→清理网络→创建快照的标准流程
实际测试发现,在VMware ESXi 7.0环境下,以下操作序列最可靠:
- kubectl drain
- systemctl stop kubelet docker
- ip -all netns delete
- 等待30秒
- 创建快照
关键提示:永远不要在Kubernetes节点运行时直接创建快照,这相当于在飞机飞行时更换发动机