嘿,朋友。如果你刚接触 Kubernetes (K8s),看到那一堆 Pod、Service、Ingress 还有各种 CNI 插件,感觉脑袋快要炸了,那太正常了。我就经历过那种对着 kubectl get pods 却 ping 不通另一个 Pod 的绝望时刻。
别担心,今天我们不背教科书式的定义,而是像拆炸弹一样,把 K8s 的网络层层剥开。我会用大白话,配合真实的场景和代码片段,带你理清从最底层的容器网络,到中间的 CNI 桥梁,再到顶层的 Service 流量分发,最后聊聊怎么选型和排查那些让人头秃的网络故障。
准备好了吗?我们要开始这场网络探险了。
1. 核心概念:打破“容器孤岛”的幻想
首先,我们要纠正一个巨大的误区:Kubernetes 里的 Pod 并不是简单的 Docker 容器。
在早期的 Docker 时代,每个容器都有自己独立的网络命名空间,IP 也是由宿主机的桥接网络分配的,不同宿主机上的容器想通信?难如登天。
但 K8s 的设计哲学非常强硬且优雅:Pod 是网络的最小单位,同一个 Pod 内的所有容器共享同一个网络命名空间(Network Namespace),并且拥有相同的 IP 地址。
这意味着什么?意味着你在 Pod 内部,可以用 localhost 访问同一个 Pod 里 Sidecar 模式部署的 Envoy 代理,就像访问本机进程一样简单。
为什么需要 CNI?
既然 Pod 共享网络,那跨节点的 Pod 怎么通信?这就引入了 CNI (Container Network Interface)。
你可以把 CNI 想象成 K8s 网络的“插件系统”。K8s 本身不关心底层是用 Flannel、Calico 还是 Cilium,它只负责调用 CNI 提供的接口:“嘿,给这个新创建的 Pod 分配一个 IP,并配置好路由。”
没有 CNI,K8s 就是一具没有神经系统的僵尸,Pod 之间无法对话。
2. 深入底层:Pod 之间的通信路径
让我们把目光聚焦在一个具体的场景:
- Node A 上运行着
Pod-App(IP: 10.244.1.5) - Node B 上运行着
Pod-DB(IP: 10.244.2.8)
当 Pod-App 想要访问 Pod-DB 时,数据包是怎么旅行的?这里有一个经典的“Veth Pair + Bridge”模型,我们以最常见的 Flannel 为例(虽然 Calico/Cilium 逻辑类似,但 Flannel 最容易理解)。
第一步:出站 (From Pod-App)
- Pod 内部:应用发起 TCP 连接,目标 IP 是
10.244.2.8。 - Pod 网络栈:内核检查路由表。发现
10.244.2.8不在本地子网,于是数据包被发送到默认网关。 - veth pair:K8s 为每个 Pod 创建了一对虚拟网卡,一端在 Pod 里(如
eth0),另一端在宿主机网络命名空间里(如veth123)。数据包穿过这对“管子”,到达宿主机的网络空间。 - CNI 网桥/隧道:
- 如果是 Flannel (UDP/VXLAN):宿主机上的
flannel.1虚拟网卡会接管流量。它会将原始以太网帧封装进 VXLAN 包,源 IP 是 Node A 的物理 IP,目的 IP 是 Node B 的物理 IP。 - 如果是 Calico (BGP):宿主机通过 BGP 协议将
10.244.2.0/24的路由广播给其他节点。数据包直接从cali123接口发出,通过物理交换机转发(三层路由)。
- 如果是 Flannel (UDP/VXLAN):宿主机上的
第二步:穿越网络
- 数据包通过物理网络(云厂商的 VPC 或数据中心交换机)从 Node A 传送到 Node B。
- 注意:在这个过程中,Pod 的原始 IP (
10.244.2.8) 是不可见的,可见的是 Node 的物理 IP。这就是为什么我们需要 Overlay 网络或者 BGP 路由来隐藏复杂性。
第三步:入站 (To Pod-DB)
- Node B 接收:Node B 收到 VXLAN 包(Flannel 场景)或直接收到 IP 包(Calico 场景)。
- 解封装/路由查找:
- Flannel 解封装 VXLAN,还原出原始的以太网帧。
- 内核检查本地路由,发现
10.244.2.8属于本地的 Pod 网络。
- veth pair:数据包被转发到对应的 veth 接口(如
veth456)。 - 进入 Pod:数据包穿过 veth pair,进入
Pod-DB的网络命名空间,最终交给eth0,送达应用程序。
关键点:在整个过程中,Pod 之间的通信看起来就像是它们在同一个局域网内,尽管它们可能相隔千里。这就是 CNI 的魔力。
3. Service:K8s 的灵魂抽象
好了,Pod 能互相通信了。但是,等等!Pod 是会死的。如果 Pod-DB 挂了,重启后 IP 变了怎么办?应用还要硬编码 IP 吗?这简直是运维灾难。
这时候,Service 登场了。
Service 是一个抽象层,它定义了一组逻辑上的 Pod 集合,并提供了一个固定的入口点(VIP - Virtual IP)。
Service 的工作原理:kube-proxy 与 iptables/IPVS
当你创建一个 Service 时,K8s 会分配一个 ClusterIP(集群内部 IP)。那么,流量是如何从 ClusterIP 转发到后端真实的 PodIP 的呢?
这就取决于你使用的 kube-proxy 模式:
模式一:iptables (传统模式)
这是默认且广泛使用的模式。kube-proxy 监听 API Server,一旦发现 Service 或 Endpoint 变化,就动态更新宿主机的 iptables 规则。
# 伪代码展示 iptables 规则逻辑
-A KUBE-SERVICES -d 10.96.0.10/32 -p tcp -m comment --comment "default/my-service:http" -j KUBE-MARK-MASQ
-A KUBE-SERVICES -d 10.96.0.10/32 -p tcp -m comment --comment "default/my-service:http" -m tcp --dport 80 -j KUBE-SVC-XYZ...
-A KUBE-SVC-XYZ... -m statistic --mode random --probability 0.33333 -j KUBE-SEP-1
-A KUBE-SVC-XYZ... -m statistic --mode random --probability 0.50000 -j KUBE-SEP-2
-A KUBE-SVC-XYZ... -j KUBE-SEP-3
# 最后跳转到具体的 Pod IP DNAT
-A KUBE-SEP-1 -s 10.244.1.5/32 -j RETURN
-A KUBE-SEP-1 -p tcp -j DNAT --to-destination 10.244.1.5:8080
- 优点:实现简单,兼容性好。
- 缺点:规则数量随 Service 和 Pod 数量线性增长。当集群规模达到数千个 Service 时,iptables 规则的刷新会导致明显的延迟和 CPU 抖动。
模式二:IPVS (高性能模式)
如果你使用较新的 K8s 版本,推荐开启 IPVS。它利用 Linux 内核的 IPVS 模块,性能接近 LVS (Linux Virtual Server)。
# IPVS 查看命令
ipvsadm -Ln
# 输出示例:
# VIP 10.96.0.10:80 tcp
# -> 10.244.1.5:8080 Route 1 0 0
# -> 10.244.2.8:8080 Route 1 0 0
- 优点:支持负载均衡算法(RR, LC, DH, SH 等),规则管理更高效,切换延迟极低。
- 注意:需要在内核中加载
ip_vs模块,并在 kube-proxy 配置中指定mode: ipvs。
模式三:eBPF (Cilium 等现代方案)
这是未来的趋势。Cilium 等项目直接使用 eBPF 程序在内核态处理流量转发,完全绕过了 iptables/IPVS 的用户态到内核态拷贝开销。
- 优点:极致的性能,原生支持网络策略(NetworkPolicy)的内核加速。
- 缺点:学习曲线陡峭,依赖较新的 Linux 内核。
4. 外部世界如何进入集群?Ingress vs LoadBalancer
Service 解决了内部通信,但外部用户怎么访问我们的 Web 应用?
LoadBalancer Service
最简单粗暴的方式:
apiVersion: v1
kind: Service
metadata:
name: my-web-lb
spec:
type: LoadBalancer
ports:
- port: 80
targetPort: 8080
selector:
app: web
当你创建这个 Service 时,云厂商(如 AWS ELB, Azure LB, 阿里云 SLB)会自动创建一个外部负载均衡器,并将流量转发到你的 NodePort 或 ClusterIP。
- 缺点:每个 LoadBalancer 通常消耗一个公网 IP 或昂贵的云资源配额。如果你有 100 个微服务,你就需要 100 个 LB,钱包会痛。
Ingress:智能的反向代理
为了解决上述问题,我们引入 Ingress。Ingress 不是 Service,而是一个资源对象,它描述了外部访问集群的规则(域名、路径、SSL 证书等)。
你需要一个 Ingress Controller 来实际执行这些规则。常见的有 Nginx Ingress Controller 和 Traefik。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-web-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: www.example.com
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: api-service
port:
number: 80
- path: /
pathType: Prefix
backend:
service:
name: web-frontend
port:
number: 80
流程解析:
- 用户访问
www.example.com/api。 - DNS 解析指向 Ingress Controller 的外部 IP。
- Ingress Controller (如 Nginx) 读取 Ingress 资源。
- Nginx 根据 Host 和 Path 匹配规则,将请求反向代理到
api-service的 ClusterIP。 - Service 再将流量分发到具体的 Pod。
专家提示:Ingress 是七层(HTTP/HTTPS)负载均衡。如果你需要做四层(TCP/UDP)负载均衡(比如数据库端口映射),请使用 LoadBalancer Service 或专门的四层 Ingress Controller。
5. CNI 插件大乱斗:选型指南
现在,到了最让人纠结的部分:选哪个 CNI?
市面上主流的 CNI 插件主要有:Flannel, Calico, Cilium, Weave Net。
1. Flannel:极简主义者的最爱
- 特点:简单、轻量。默认使用 VXLAN 覆盖网络。
- 适用场景:小型集群、测试环境、对性能要求不高、只需要基本连通性。
- 优点:部署极其简单,几乎零配置。
- 缺点:性能开销较大(VXLAN 封装解封装),不支持复杂的网络策略(NetworkPolicy)。
2. Calico:企业级的稳健选择
- 特点:基于 BGP 路由,支持三层网络。可以选择纯二层(VXLAN)或纯三层(BGP)模式。
- 适用场景:中大型生产环境、需要精细网络策略、多租户隔离。
- 优点:性能优异(特别是三层模式),功能丰富(支持 NetworkPolicy, GlobalNetworkPolicy),社区活跃。
- 缺点:配置相对复杂,BGP 模式需要网络设备支持(或配置静态路由)。
3. Cilium:云原生时代的明星
- 特点:基于 eBPF,提供网络、安全、可观测性一体化解决方案。
- 适用场景:大规模集群、对性能和安全有极高要求、使用 Service Mesh (Istio) 的场景。
- 优点:极高的性能,原生支持 L7 网络策略,内置 Hubble 可观测性。
- 缺点:对 Linux 内核版本有要求(通常需要 4.9+,推荐 5.4+),调试难度较高。
4. Weave Net:易于上手的多云方案
- 特点:自动加密,跨云支持好。
- 适用场景:混合云、多云环境,需要快速加密通信。
- 优点:开箱即用,自动加密。
- 缺点:性能不如 Calico 和 Cilium,社区活跃度下降。
选型决策树
- 你是初学者或小团队? -> 选 Flannel。
- 你需要强大的网络策略和稳定的生产环境? -> 选 Calico。
- 你追求极致性能,或者正在构建 Service Mesh? -> 选 Cilium。
- 你在混合云环境,且担心明文传输? -> 考虑 Weave Net 或 Calico 加密模式。
6. 实战排错:当网络不通时,我该怎么办?
即使是最完美的架构,也会出问题。当 curl: (7) Failed to connect 出现时,不要慌。按照以下“五步法”进行排查。
场景模拟
假设:
Pod-A(IP: 10.244.1.10) 无法访问Service-B(IP: 10.96.0.50, Port: 80)。
第一步:检查基础连通性 (Ping)
在 Pod-A 中执行:
ping 10.96.0.50
- 如果 Ping 不通:说明 Layer 3 (网络层) 有问题。可能是 CNI 故障、路由缺失、或者防火墙拦截。
- 如果 Ping 通但 curl 不通:说明网络层正常,问题可能在 Layer 4⁄7 (传输层/应用层),如端口错误、Service 后端无 Ready Pod、或 iptables/IPVS 规则异常。
第二步:检查 Endpoint 状态
在控制平面节点执行:
kubectl get endpoints <service-name> -n <namespace>
- 如果 Endpoints 为空:说明 Service 没有关联任何健康的 Pod。检查 Pod 的 Label 是否匹配,以及 Pod 的 Readiness Probe 是否失败。
- 如果 Endpoints 有 IP:继续下一步。
第三步:检查 kube-proxy 和 CNI 组件
查看 kube-proxy 日志:
kubectl logs -n kube-system <kube-proxy-pod-name>寻找是否有报错信息,如无法更新 iptables 规则。
检查 CNI 插件日志:
# 以 calico-node 为例 kubectl logs -n kube-system <calico-node-pod-name>
第四步:深入节点层面 (Node Level Debugging)
这是最关键的一步。登录到 Pod-A 所在的节点 (Node-A)。
检查路由表:
ip route show table all确保存在指向 CNI 网桥或隧道的路由。例如,Flannel 应该有
10.244.0.0/16 dev flannel.1。检查 iptables 规则 (如果使用 iptables 模式):
iptables -t nat -L KUBE-SERVICES -n -v查看是否有针对
10.96.0.50的规则。如果没有,说明 kube-proxy 没有同步状态。检查 IPVS 规则 (如果使用 IPVS 模式):
ipvsadm -Ln | grep 10.96.0.50确认后端 Pod 的 IP 是否在列表中。
抓包分析 (Tcpdump): 在
Node-A上,监听 CNI 接口(如flannel.1或cali123):tcpdump -i flannel.1 host 10.96.0.50 and port 80 -nn- 如果看到 SYN 包发出,但没有 ACK 回来:流量到达了 CNI 接口,但没被正确转发,或者被目标节点丢弃。
- 如果看不到 SYN 包:流量在更早的阶段就被拦截了(如默认网关错误、veth pair 问题)。
第五步:常见陷阱与解决方案
MTU 问题:
- 现象:小包 Ping 通,大包传输丢包。
- 原因:Overlay 网络增加了封装头部,如果物理网络 MTU 较小(如 1500),而 CNI 未调整 MTU,会导致分片丢失。
- 解决:检查物理网卡 MTU,并在 CNI 配置中设置合适的 MTU(如 Flannel 建议设为 1450 或更低)。
Dual Stack (IPv4/IPv6) 混淆:
- 现象:Pod 获取了 IPv6 地址,但 Service 是 IPv4。
- 原因:K8s 双栈配置不当。
- 解决:明确指定 Service 的 IPFamily,或确保 CNI 插件正确支持双栈。
NetworkPolicy 误杀:
- 现象:默认允许所有流量,突然某个 Pod 访问不了其他 Pod。
- 原因:有人应用了 NetworkPolicy,默认拒绝所有入站/出站。
- 解决:检查相关 Namespace 下的 NetworkPolicy 资源,确保允许必要的流量。
结语
Kubernetes 的网络模型确实复杂,但它的设计初衷是为了让分布式系统的通信变得标准化和自动化。从 Pod 的共享网络,到 CNI 的灵活插拔,再到 Service 的智能负载均衡,每一步都凝聚了工程师们的智慧。
作为开发者,你不需要成为网络专家,但你必须理解这些抽象背后的逻辑。这样,当问题出现时,你才能像侦探一样,抽丝剥茧,找到那个隐藏的 Bug。
希望这篇文章能帮你建立起对 K8s 网络的整体认知。下次再遇到网络不通的问题,记得先深呼吸,然后按照上面的步骤一步步排查。祝你调试愉快!
