本章目标
这一章你需要真正弄懂 4 件事:
- 为什么 Kubernetes insist(坚持)“每个 Pod 一个独立 IP”::cite[352]
- Pod 与 Pod、Pod 与 Service 的通信到底走了什么路径::cite[351]
- 容器网络命名空间、veth、网桥这些基础概念为什么会影响 Kubernetes 网络理解::cite[352]
- 跨节点通信为什么可行,本质上依赖的到底是什么::cite[343]
很多同学第一次学 Kubernetes 网络,会把它理解成“容器 + 端口映射”的升级版。但 Kubernetes 的设计思路并不是给容器做端口映射,而是先把 Pod 当成一个独立的网络端点,再用 Service、Ingress、Gateway 等对象把访问关系组织起来。官方文档对这个模型的核心表述非常直接:每个 Pod 拥有唯一的集群内 IP;同一 Pod 内的容器共享网络命名空间;集群里的 Pod 之间默认可以直接互通且无需 NAT。::cite[352]
1. 先建立正确心智模型
1.1 每个 Pod 都有独立 IP,真正意味着什么
Kubernetes 的网络模型不是“一个节点一个 IP,容器靠端口区分”,而是“一个 Pod 就像一台微型主机”。每个 Pod 在集群内都有唯一 IP,同一个 Pod 内的所有容器共享这个 IP,也共享同一个网络命名空间,因此它们可以直接通过 localhost 通信。::cite[352]
这带来 3 个非常重要的结果:
- 应用不需要再关心宿主机端口映射。
- 同一个 Pod 内的主容器与 sidecar 容器可以直接走
127.0.0.1。 - 从网络视角看,Pod 更像虚拟机或物理机上的一个进程组,而不是一堆各自独立的容器。::cite[354]
你可以把 Pod 理解成 Kubernetes 网络中的最小通信单元,而不是单个容器。容器只是运行在 Pod 这个“网络壳子”里面的多个进程。::cite[352]
1.2 同一 Pod 内,为什么容器能走 localhost
原因是:同一个 Pod 里的容器共享一个 network namespace。也就是说,它们看到的是同一块网卡、同一套路由表、同一个 IP 地址空间。因此一个容器监听 0.0.0.0:80,另一个容器直接访问 localhost:80 就能打通。::cite[352]
这也是 sidecar 模式能成立的基础。日志采集、服务网格代理、热更新代理等能力,本质上都在利用“同 Pod 共享网络栈”这个设计。::cite[354]
1.3 Pod 与 Pod 通信,为什么默认不需要 NAT
Kubernetes 官方教程明确指出:集群里的所有 Pod 可以彼此可见,而且默认不需要 NAT。也就是说,源 Pod 发出的目标地址就是目标 Pod 的 IP,中间不需要像 Docker 单机 bridge 那样做一层端口映射转换。::cite[354]
这就是 Kubernetes 网络模型常说的“flat network(扁平网络)”。所谓扁平,并不是说底层实现一定简单,而是从使用者视角看,Pod 到 Pod 像同一个大二层/大三层网络中的主机互访。::cite[343]
1.4 Pod 与 Service 通信,为什么要多一层虚拟 IP
Pod IP 会变,Service IP 尽量不变。Pod 是易失的,重建后 IP 可能变化;Service 提供一个稳定的访问入口,背后再绑定一组 Pod。kube-proxy(或被 Cilium 等替代的实现)会在节点上把访问 Service ClusterIP 的流量转发到后端 Pod。::cite[218]
所以你可以把 Service 看成“稳定入口 + 后端发现 + 负载分发”的组合,而不是一台真正存在的机器。::cite[216]
2. 通信路径拆解
2.1 Pod 与 Pod 的通信路径
最常见路径是这样的:
- 进程在源 Pod 中发起请求。
- 数据包从 Pod 内网卡发出。
- 通过 veth pair 进入节点网络栈。
- 节点根据路由把流量转发到目标 Pod 所在节点,或者直接转给本节点上的目标 Pod。
- 目标 Pod 收到包并返回响应。::cite[343]
如果两个 Pod 在同一个节点,路径通常更短,可能只在节点本地桥接或路由一次就到了。::cite[352]
如果两个 Pod 不在同一个节点,底层会依赖 CNI 提供的跨节点可达能力:要么通过封装(如 VXLAN),要么通过底层路由/BGP/云路由把 Pod 网段打通。上层应用看到的仍然只是“访问另一个 Pod IP”。::cite[343]
2.2 Pod 与 Service 的通信路径
Pod 访问 Service 时,一般不是直接连后端 Pod,而是先访问 Service 的 ClusterIP 或 DNS 名称:
- 应用请求
my-service.default.svc.cluster.local。 - CoreDNS 返回 Service 的 ClusterIP。
- 流量到达节点后,被 kube-proxy 维护的规则捕获。
- kube-proxy 再把流量转给某个后端 Pod。::cite[216]
这条路径里最重要的不是 DNS,而是“Service IP 到真实 Pod IP”的转发规则。DNS 只是帮你找到 ClusterIP,真正的流量分发发生在节点内核态或内核规则层。::cite[216]
2.3 外部访问为什么通常不能直接打 Pod IP
当 Pod IP 只在集群内部可路由时,外部系统并不知道这些 Pod 网段怎么走,所以通常不能直接访问 Pod IP。Calico 文档对这个现象解释得很清楚:如果 Pod IP 在集群外不可路由,那么外部访问只能通过 Kubernetes Service 或 Ingress 之类的入口对象完成。::cite[343]
这也是为什么我们会看到 ClusterIP、NodePort、LoadBalancer、Ingress、Gateway 这些不同层次的入口对象。它们都在解决“怎样把外部流量引到集群内部”这个问题,只是抽象层次不同。::cite[351]
3. 网络命名空间与虚拟网络基础
3.1 先记住 4 个关键词
- network namespace:隔离网卡、路由表、iptables 视图的“网络沙箱”。
- veth pair:成对出现的虚拟网卡,一头在 Pod 内,一头在节点侧。
- bridge / route:负责把 Pod 发出的包继续转发出去。
- CNI:负责给 Pod 接网、分配地址、接入整个集群网络。::cite[364]
3.2 一个 Pod 创建出来时,底层通常发生了什么
虽然不同 CNI 实现细节不同,但思路基本一致:
- kubelet/容器运行时创建 Pod sandbox。
- 调用 CNI 插件为 Pod 分配 IP。
- 为 Pod 创建网络命名空间。
- 创建一对 veth,Pod 内一端作为
eth0,另一端挂到节点网络。 - 配置默认路由和必要规则,让 Pod 可以出网、可以到其他 Pod。::cite[364]
所以,Pod 之所以“看起来像一台主机”,并不是 Kubernetes 在概念层面规定了就行,而是 CNI 在节点上真实做了这些网络接线动作。::cite[364]
3.3 为什么说 Pod 网络不是 Docker 端口映射模型
在 Docker 单机桥接网络里,常见思路是容器挂在一个 bridge 上,外部靠宿主机端口映射访问容器。Kubernetes 则把“Pod 具备独立地址、可直接被集群内其他 Pod 访问”作为基础前提。这样应用迁移、服务发现、sidecar 协作都会简单很多。::cite[354]
所以你在 Kubernetes 里要优先思考“这个 Pod 在网络里是谁”,而不是“这个容器映射到了宿主机哪个端口”。::cite[352]
4. 跨节点通信为什么可行
这是最容易让初学者卡住的问题。
4.1 表面现象
Pod A 在 Node 1,Pod B 在 Node 2,但 A 直接访问 B 的 Pod IP 居然能通。为什么?
4.2 本质原因
因为 CNI 和底层网络共同保证了“节点知道怎么到达对端 Pod 网段”。实现方式通常有两类:
- Overlay:把 Pod 数据包再封装一层,例如 VXLAN。底层网络只需要认识节点 IP,不需要认识 Pod IP。::cite[343]
- Underlay / Native Routing:直接把 Pod 网段路由到目标节点,可能借助云厂商路由、静态路由、BGP 等机制,让底层网络直接认识 Pod 网段。::cite[343]
Calico 文档明确说明:不使用 overlay 时,是否能跨节点通信,取决于 CNI、云路由集成或者 BGP 对 Pod 路由的传播;使用 overlay 时,底层网络不需要知道 Pod IP,只需要知道节点 IP。::cite[343]
Cilium 的 native routing 文档也强调:一旦启用原生路由模式,底层网络必须能够转发 PodCIDR;如果所有节点处于同一 L2 网络,可以直接下发节点路由;如果跨网段,就需要额外路由分发组件(例如 BGP)来传播 Pod 路由。::cite[476]
4.3 一句话总结
跨节点通信之所以可行,不是因为 Kubernetes 自带“魔法隧道”,而是因为 CNI 已经提前把“Pod 网段如何到对端节点”这件事打通了。应用只是在用 Pod IP 发包,真正把路修好的,是网络插件和底层路由。::cite[343]
5. 实操:观察 Pod、Service 与 localhost 通信
5.1 准备 YAML
apiVersion: v1
kind: Namespace
metadata:
name: unit4-network
---
apiVersion: v1
kind: Pod
metadata:
name: multi-container-demo
namespace: unit4-network
labels:
app: shared-netns
spec:
containers:
- name: web
image: nginx:1.27
ports:
- containerPort: 80
- name: toolbox
image: busybox:1.36
command:
- sh
- -c
- sleep 360000
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-demo
namespace: unit4-network
spec:
replicas: 2
selector:
matchLabels:
app: web-demo
template:
metadata:
labels:
app: web-demo
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app: web-demo
topologyKey: kubernetes.io/hostname
containers:
- name: nginx
image: nginx:1.27
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: web-svc
namespace: unit4-network
spec:
selector:
app: web-demo
ports:
- name: http
port: 80
targetPort: 80
---
apiVersion: v1
kind: Pod
metadata:
name: net-debug
namespace: unit4-network
spec:
containers:
- name: busybox
image: busybox:1.36
command:
- sh
- -c
- sleep 360000
5.2 执行步骤
kubectl apply -f network-model-demo.yaml
kubectl get pods -n unit4-network -o wide
kubectl get svc -n unit4-network
步骤 A:验证同 Pod 内容器通过 localhost 通信
kubectl exec -n unit4-network multi-container-demo -c toolbox -- wget -qO- http://127.0.0.1
如果能返回 nginx HTML,就说明同一个 Pod 内共享网络命名空间这件事你已经亲手验证了。
步骤 B:验证 Pod 直连 Pod IP
先查看两个 web-demo Pod 的 IP:
kubectl get pods -n unit4-network -l app=web-demo -o wide
然后从调试 Pod 直接访问其中一个 Pod IP:
kubectl exec -n unit4-network net-debug -- wget -qO- http://<某个PodIP>
步骤 C:验证 Pod 访问 Service
kubectl exec -n unit4-network net-debug -- wget -qO- http://web-svc
kubectl exec -n unit4-network net-debug -- nslookup web-svc
这里第一次请求走的是“Pod -> Service ClusterIP -> kube-proxy 规则 -> 后端 Pod”;第二个命令则用于观察 DNS 如何把服务名解析成 ClusterIP。::cite[216]
步骤 D:观察是否跨节点
kubectl get pods -n unit4-network -l app=web-demo -o wide
kubectl get nodes -o wide
如果两个副本被调度到了不同节点,你就能把这次实验继续理解为一次真实的跨节点 Pod 通信;如果你的集群只有一个节点,这一步就只能理解通信路径,无法观察跨节点现象。
6. 实操后的理解复盘
做完上面的实验后,请你用下面这 4 句话检查自己是否真的理解了:
- 同 Pod 内容器能走 localhost,因为它们共享同一个 network namespace。::cite[352]
- Pod 能直接访问 Pod IP,因为 Kubernetes 网络模型要求集群内 Pod 彼此可达。::cite[354]
- Pod 访问 Service 实际访问的是虚拟入口,再由节点规则转发到真实后端。::cite[216]
- 跨节点通信能成功,是因为 CNI 已经把 Pod 网段路由或封装打通了。::cite[343]
如果这 4 句话你都能自己展开讲清楚,那么 Kubernetes 网络模型这一章就算真正入门了。
7. 本章小结
这一章最核心的结论不是“记住几个术语”,而是建立一个稳定心智模型:Pod 是 Kubernetes 网络里的基本端点,Service 是稳定入口,CNI 是把网络模型落地的关键角色。::cite[352]
后面学习 CNI、Service 实现原理、Ingress、Gateway、NetworkPolicy 时,所有内容其实都建立在这一章的基础之上。如果这一章没吃透,后面的网络排障会非常痛苦;如果这一章吃透了,后面很多复杂现象都会变得顺理成章。::cite[351]
📝 版权声明:本文为原创技术博客,转载请注明出处。
如文章中存在错误或不准确之处,欢迎在评论区指正,感谢您的阅读与支持!