在 Kubernetes 环境中,节点(宿主机)开启防火墙后导致容器内部无法访问外部/内网 IP,这是一个非常典型的问题。
为什么会发生这种情况?
Kubernetes 的网络插件(从 ip a 输出中可以看到 cali* 和 tunl0,说明使用的是 Calico )和容器运行时(Docker/Containerd)严重依赖 Linux 的 iptables 或 ipvs 来进行流量转发(Forwarding)、路由和源地址转换(SNAT/Masquerade)。
当手动开启系统的防火墙(如 firewalld 或 ufw)时,防火墙会清空或覆盖 Kubernetes 和 Calico 原本精心配置的 iptables 规则,并且默认的防火墙策略通常会拒绝转发(FORWARD)流量。导致容器发出的数据包无法被正确 NAT(伪装)并转发到宿主机的物理网卡(enp125s0f0)。
这里为提供两种解决方案:
方案一:关闭宿主机防火墙(K8s 官方及业界强烈推荐)
在 Kubernetes 集群中,通常不建议 在 Node 节点上直接运行 firewalld 或 ufw。网络隔离和安全控制应该交由 Kubernetes 的 NetworkPolicy 和云提供商的安全组(或物理防火墙)来实现。
如果环境允许,最彻底且无副作用的方法是关闭并禁用它:
如果是 CentOS/RHEL (firewalld):
bash
systemctl stop firewalld
systemctl disable firewalld
如果是 Ubuntu/Debian (ufw):
bash
ufw disable
关键的一步:重建 K8s 网络规则
关闭防火墙后,必须让 CNI 重新写入 iptables 规则:
- 重启容器运行时(根据环境选择):
bash
systemctl restart containerd
# 或者
systemctl restart docker
- 重启 Calico 和 Kube-proxy 从而重建规则:
bash
kubectl delete pod -n kube-system -l k8s-app=calico-node
kubectl delete pod -n kube-system -l k8s-app=kube-proxy
方案二:必须开启防火墙的解决办法(配置放行规则)
如果因为公司合规或安全要求,宿主机必须开启防火墙,需要手动对防火墙进行配置,放行 Kubernetes 的网络流量并开启 IP 伪装。
使用的是 firewalld(CentOS/RHEL 等主流环境):
1. 开启 IP 伪装 (Masquerade)
容器访问内网 10.110.151.44 必须经过 SNAT(源地址转换),否则目标服务器无法将回包路由回容器的内网 IP(如 10.244.x.x)。
bash
firewall-cmd --permanent --add-masquerade
2. 将 Calico 和容器网卡加入信任区
从 ip a 可以看到有很多 caliXXX 虚拟网卡以及 tunl0 隧道网卡,必须放行这些网卡的流量:
bash
# 信任 Calico 的虚拟网卡
firewall-cmd --permanent --zone=trusted --add-interface=cali+
# 信任 IPIP 隧道网卡
firewall-cmd --permanent --zone=trusted --add-interface=tunl0
# 如果有 docker0 或 cni0 也一并加入
firewall-cmd --permanent --zone=trusted --add-interface=docker0
3. 允许网卡之间的转发 (FORWARD)
防火墙可能默认 drop 了 forward 链的包:
bash
firewall-cmd --permanent --direct --add-rule ipv4 filter FORWARD 1 -j ACCEPT
4. 重载防火墙并重启网络组件
让防火墙配置生效:
bash
firewall-cmd --reload
防火墙重启后,K8s 之前的 iptables 规则又会被冲刷掉,因此必须重新触发 Calico 写入规则:
bash
# 重启 Calico 节点组件
kubectl delete pod -n kube-system -l k8s-app=calico-node
验证修复结果
完成上述任意一种方案后,进入一个容器内部测试连通性:
bash
kubectl exec -it <Pod名称> -n <对应Namespace> -- sh
# 在容器内 ping 目标内网 IP
ping 10.110.151.44