哎,说实话,刚接触 Kubernetes 的时候,我被它的网络模型绕得头晕眼花。那时候我总在想:为什么 Pod 死了重启 IP 会变?为什么 Service 那个魔法似的 VIP 到底是怎么转发的?为什么换了个 CNI 插件,集群就瘫痪了?
如果你也经历过这种“黑盒恐惧症”,那这篇文章就是写给你看的。我不打算给你念课本上的定义,咱们就像坐在咖啡店里聊天一样,把 K8s 网络从底朝天摸一遍。我会把那些复杂的术语掰开了、揉碎了,顺便配上实战代码和选型建议,保证你读完不仅能懂,还能回去给同事讲清楚。
一、 先别急着看代码,你得有个“空间感”
在深入技术细节之前,我想先让你建立一个直观的认知。K8s 的网络模型核心就一句话,这句话我把它刻在了脑子里:
每个 Pod 都拥有一个独立的、唯一的 IP 地址,所有 Pod 都能无需用 NAT 的方式与所有其他 Pod 通信。
听起来很美好,对吧?但这背后其实是一整套复杂的机制在支撑。为了理解它,我得先给你讲几个核心概念,这就好比你要装修房子,得先知道什么是承重墙、什么是水管电路。
1.1 为什么是 Pod IP,而不是 Container IP?
你可能会有疑问,K8s 里最小单元不是 Container 吗?为什么网络是按 Pod 算的?
这就得说到 Pause 容器 和 共享网络命名空间 了。
在一个 Pod 里,如果有多个容器(Sidecar 模式很常见),它们并不是各自独立拥有网络栈的。当你执行 kubectl describe pod <pod-name> 时,你会看到一个 Pause 容器(现在叫 infra)。这个容器非常轻量,它的作用就是提供一个基础的网络命名空间。
Pod 里的其他业务容器,通过挂载这个 Pause 容器的网络命名空间,共享同一个网络栈。
# 假设你有一个 pod 叫 my-app
kubectl get pod my-app -o yaml | grep -A 5 "pause"
# 你会发现它们共享同一个 network namespace
这意味着什么?意味着 Pod 内部的所有容器,看到的 IP 是一样的,端口也是共用的。如果你两个容器都想用 80 端口,那就得靠端口映射或者代理来解决冲突。但对于 Pod 来说,它对外只展示一个 IP,这就是 Pod IP 的由来。
1.2 Network Namespace:隔离的起点
网络 Namespace 是 Linux 内核提供的一种隔离技术。你可以把它想象成一个透明的盒子,每个 Pod 都住在一个这样的盒子里。
- 盒子内:你可以自由配置网卡、IP、路由表,想怎么折腾都行,不会影响别人。
- 盒子间:默认情况下,两个盒子是完全隔离的。盒子 A 里的东西,盒子 B 看不见,反之亦然。
K8s 就是利用 Linux 的 Network Namespace 来实现 Pod 网络隔离的。每一个 Pod 被创建时,K8s 会在宿主机上创建一个新的 Network Namespace,并分配一个虚拟网卡(veth pair)的一端连接到这个 Namespace,另一端留在宿主机的主网络空间里。
这就引出了下一个关键点:veth pair。
1.3 veth pair:连接两个世界的桥梁
veth(Virtual Ethernet)是一对虚拟网络设备。它们总是成对出现,就像两根管子连在一起。数据从一端进去,必然从另一端出来。
在 K8s 的网络拓扑里,veth pair 扮演了至关重要的角色:
- 一端:插入 Pod 的 Network Namespace,通常命名为
eth0或calixxx等。 - 另一端:留在宿主机的 Network Namespace 里,通常连接到虚拟交换机(如
cni0、br-netfilter或cali等)。
这样,Pod 里的流量就可以通过 veth pair 进出 Pod,到达宿主机的网络环境,再被 CNI 插件接管处理。
+---------------------------+ +---------------------------+
| Pod 1 | | Pod 2 |
| +---------------------+ | | +---------------------+ |
| | Container A | | | | Container B | |
| +---------------------+ | | +---------------------+ |
| | eth0 | | | eth0 |
| | | | | |
| +----+----+ | | +----+----+ |
| | veth pair |<-------->|<------>|<-------->| veth pair | |
| +---------+ | Host NS | | Host NS | +---------+ |
+----------------+ | | +-------------+
| |
+--+-------+--+
| CNI Bridge / Virtual Switch |
| (cni0 / calico-bgp / flannel) |
+----------------+----------------+
|
+----+----+
| Host NIC |
+---------+
看到了吗?这个架构图虽然简化了,但逻辑是清晰的。Pod 就像一个个孤岛,veth pair 是通往岸边的独木桥,而 CNI 插件负责在岸上修路架桥,让这些孤岛之间能够互联互通。
二、 CNI 插件:K8s 网络的“包工头”
讲完了基础概念,我们得聊聊真正干活的——CNI 插件。
CNI(Container Network Interface)是 Kubernetes 定义的一个标准接口。K8s 本身不负责实现具体的网络功能,它只负责调用 CNI 插件。这就好比 K8s 是个项目经理,它只需要告诉工人“我要一个网络”,至于工人用什么工具、怎么布线,那是工人的事(只要符合 CNI 规范)。
2.1 主流 CNI 插件大乱斗
目前市面上主流的 CNI 插件主要有几家:Calico、Flannel、Cilium、Weave Net 等。每个都有自己的脾气和适用场景。
Flannel:最简单的入门选手
Flannel 是 K8s 生态中最早流行的 CNI 之一,主打一个“简单”。它背后的理念是:只要 Pod 能通就行,别搞太复杂。
Flannel 主要提供两种模式:
- VXLAN:在 UDP 之上封装一层 VxLAN 头,通过隧道传输。适合多节点跨主机通信,性能一般,但配置简单。
- Host-gw:直接利用宿主机的路由表,通过网关方式转发。性能接近物理网络,但要求所有节点必须在同一二层网络(同一个 VLAN)。
适合场景:小集群、测试环境、对网络性能要求不高、网络架构简单的生产环境。
# 安装 Flannel 的简单示例
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
Calico:性能与策略的平衡大师
Calico 是另一款老牌选手,它的亮点在于高性能和网络策略(Network Policy)。
Calico 基于 BGP(边界网关协议)来传播路由信息。每个节点上的 Felix 组件会学习 Pod 的路由,并通过 BGP 协议广播给其他节点。这样,数据包不需要经过 NAT,也不需要复杂的隧道封装,而是像物理路由器一样直接转发,性能极高。
更重要的是,Calico 实现了强大的 Network Policy,可以让你精细控制 Pod 之间的访问权限。
适合场景:中大型集群、对网络性能有高要求、需要复杂网络策略的安全合规场景。
Cilium:新一代的云原生网络专家
Cilium 是近年来崛起的新星,它的独特之处在于基于 eBPF(extended Berkeley Packet Filter)技术。
传统 CNI 插件大多工作在 Linux 内核的网络栈,而 Cilium 利用 eBPF 程序直接在内核中处理网络包,绕过了传统的 iptables 和 conntrack 表,带来了极致的性能和灵活性。
此外,Cilium 还提供原生的 L7(应用层)流量管理,可以基于 HTTP 头部、Header 等精细控制流量,这是 iptables 做不到的。
适合场景:大规模集群、对性能极致追求、需要微服务细粒度流量治理、Service Mesh 集成场景。
Weave Net:多集群互联的爱好者
Weave Net 曾经以简单的跨主机通信和多集群互联著称,但近年来更新频率下降,社区活跃度不如 Calico 和 Cilium。如果你不是特别需要跨云、跨数据中心互联,目前不太推荐首选。
2.2 如何选型?我的实战建议
选 CNI 插件没有银弹,得看你的具体需求。我给你总结了一个决策树:
你是新手,或者集群很小(< 50 节点):
- 选 Flannel (VXLAN)。简单粗暴,装好就能跑,出了问题也容易排查。
你需要高并发、低延迟,且集群规模中等(50-200 节点):
- 选 Calico。性能和稳定性经过大量生产环境验证,网络策略功能完善,文档丰富,社区成熟。
你是大规模集群(> 200 节点),或者对性能有极致要求,或者想用 Service Mesh:
- 选 Cilium。eBPF 技术在大规模场景下优势明显,且与 Istio、Linkerd 等 Service Mesh 集成良好。
你的环境有特殊限制(如必须运行在某些老旧内核上):
- 检查一下各插件的内核要求。Cilium 需要较新的内核支持 eBPF,而 Calico 和 Flannel 对内核版本要求相对宽松。
我个人偏好的选择:在实际工作中,如果是我主导的项目,我通常会选 Calico。因为它稳定、成熟、文档多,遇到问题能在 Stack Overflow 上找到答案。但如果是我个人探索新技术,或者在大型云原生平台上,Cilium 是我的首选,它的未来潜力巨大。
三、 Service:K8s 的网络魔法
讲完了 Pod 之间的通信,我们得聊聊 K8s 最迷人的特性之一:Service。
Pod 是易失的,它们随时可能挂掉、重启,IP 地址会变。如果我们的微服务直接依赖 Pod IP,那维护成本简直噩梦。Service 的作用就是提供一个稳定的访问入口,让客户端不用关心后端 Pod 的变化。
3.1 Service 的四种类型
K8s 提供了四种主要的 Service 类型,每种都有其特定的用途:
ClusterIP:内部访问的默认选择
这是默认的 Service 类型。它会为 Service 分配一个集群内部的虚拟 IP(ClusterIP),这个 IP 只能在集群内部访问。
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: MyApp
ports:
- protocol: TCP
port: 80
targetPort: 8080
type: ClusterIP
工作原理:当你访问 my-service:80 时,流量会被转发到后端某个 Pod 的 8080 端口。这个转发过程是由 kube-proxy 控制的。
NodePort:暴露到集群外部
如果你希望从集群外部直接访问 Service,NodePort 是最直接的方式。它会在每个节点上打开一个端口,外部用户可以通过 <NodeIP>:<NodePort> 访问。
spec:
type: NodePort
ports:
- port: 80
targetPort: 8080
nodePort: 30080 # 可选,不指定则自动分配
缺点:端口管理麻烦,且依赖节点 IP,不够灵活。
LoadBalancer:云厂商的救星
在公有云环境中(如 AWS、GCP、阿里云),LoadBalancer 类型会自动调用云厂商的 API,创建一个外部负载均衡器,并将流量转发到集群内的 NodePort 或直接到 Pod。
spec:
type: LoadBalancer
优点:开箱即用,无需手动管理负载均衡器。 缺点:依赖特定云厂商,迁移成本高。
ExternalName:DNS 别名
这是一种特殊类型的 Service,它不代理流量,而是通过 CNAME 记录将 Service 映射到外部域名。
spec:
type: ExternalName
externalName: my.database.example.com
当你访问 my-service 时,DNS 会解析到 my.database.example.com。适合用于访问集群外的数据库或服务。
3.2 kube-proxy:Service 背后的幕后黑手
Service 的核心实现依赖于 kube-proxy。每个节点上都运行着一个 kube-proxy 进程,它的职责是监听 API Server,发现 Service 和 Endpoint 的变化,并更新本地的网络规则,实现流量转发。
kube-proxy 主要有三种工作模式:
iptables 模式
这是较早期的模式,也是默认模式之一。kube-proxy 会在每个节点上配置 iptables 规则,将流入 ClusterIP 的流量随机转发到后端的某个 Pod IP。
# 你可以看到 kube-proxy 生成的 iptables 规则
iptables -t nat -L -n -v | grep my-service
优点:配置简单,性能尚可。 缺点:规则数量随 Service 和 Endpoint 数量线性增长,大规模集群下 iptables 规则过多会导致性能下降,甚至内核 panic。
ipvs 模式
ipvs(IP Virtual Server)是 Linux 内核提供的一种负载均衡技术,基于 Netfilter,但比 iptables 更高效。
# 启用 ipvs 模式需要在 kube-proxy 配置中指定
kube-proxy --proxy-mode=ipvs
优点:
- 高性能:ipvs 使用哈希表查找规则,时间复杂度为 O(1),而 iptables 是 O(n)。
- 支持更多负载均衡算法:如轮询、最少连接、源地址哈希等。
- 规则数量少:一个 Service 对应一条 ipvs 规则,而不是 iptables 的多条规则。
缺点:需要加载 ip_vs 内核模块,且配置稍复杂。
eBPF 模式(Cilium)
Cilium 使用 eBPF 来替代 iptables 和 ipvs,实现了更高效的流量转发和网络策略。
优点:
- 极致性能:直接在内核中处理,绕过传统网络栈。
- 丰富功能:支持 L7 流量管理、可观测性等。
3.3 Endpoint 与 Endpointslice
Service 只是提供了一个虚拟 IP,真正承载流量的是后端的 Pod。那么,Service 怎么知道该把流量转发给哪些 Pod 呢?这就引入了 Endpoint 和 Endpointslice 的概念。
- Endpoint:一个 Endpoint 对象记录了 Service 后端所有可用的 Pod IP 和端口。当 Pod 创建或销毁时,Controller Manager 会更新 Endpoint。
- Endpointslice:为了解决 Endpoint 对象大小限制(K8s 对象有 1MB 大小限制),K8s 1.20+ 引入了 Endpointslice,将一个 Service 的后端 Pod 分成多个 slice,每个 slice 是一个独立的小对象。
# 查看 Service 的 Endpoint
kubectl get endpoints my-service
# 输出示例:
# NAME ENDPOINTS AGE
# my-service 10.244.1.5:8080,10.244.2.3:8080 10m
关键点:Service 的流量转发是动态的。如果某个 Pod 挂了,Endpoint 会自动剔除,流量就不会再转发到那里。这就是 Service 的高可用性保障。
四、 集群通信:跨节点、跨 Pod 的秘密
讲了 Pod IP 和 Service,我们得聊聊更宏观的——集群通信。如何让节点 A 上的 Pod 和节点 B 上的 Pod 通信?这涉及到 CNI 插件的具体实现。
4.1 Calico 的路由模式
Calico 默认使用 BGP 路由模式。每个节点上的 BIRD 进程(BGP 实现)会将 Pod 网段的路由信息广播给其他节点。
Pod 1 (10.244.1.2) --veth--> Node 1 (eth0) --BGP--> Node 2 (eth0) --veth--> Pod 2 (10.244.2.3)
数据包从 Pod 1 发出,经过 veth pair 到达 Node 1 的主网卡,然后通过物理网络路由到 Node 2,最后再经过 veth pair 到达 Pod 2。整个过程不需要 NAT,路由表直接指向 Pod 网段。
优点:
- 高性能,无封装开销。
- 支持大规模的 Pod 网络。
缺点:
- 需要网络基础设施支持 BGP(或者使用 Route Reflector)。
- 路由表较大时,可能对节点内存有一定压力。
4.2 Flannel 的 VXLAN 模式
Flannel 的 VXLAN 模式通过在物理网络上叠加一层虚拟网络,将 Pod 流量封装在 UDP 包里传输。
Pod 1 (10.244.1.2) --veth--> Node 1 (eth0) --VXLAN 封装--> UDP 包 --> 物理网络 --> Node 2 (eth0) --VXLAN 解封装--> veth --> Pod 2 (10.244.2.3)
优点:
- 不依赖物理网络支持 BGP,任何网络都能跑。
- 支持跨机房、跨数据中心通信。
缺点:
- 性能损耗,因为有封装和解封装的开销。
- MTU 需要调整,否则可能导致分片。
4.3 Cilium 的 eBPF 模式
Cilium 使用 eBPF 程序直接在内核中处理网络包,无需 veth pair,也无需隧道。
Pod 1 (10.244.1.2) --veth--> Node 1 (eth0) --eBPF 程序--> 路由到 Node 2 --eBPF 程序--> veth --> Pod 2 (10.244.2.3)
**
