返回首页

4.1 Kubernetes 网络模型

本章目标

这一章你需要真正弄懂 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 的通信路径

最常见路径是这样的:

  1. 进程在源 Pod 中发起请求。
  2. 数据包从 Pod 内网卡发出。
  3. 通过 veth pair 进入节点网络栈。
  4. 节点根据路由把流量转发到目标 Pod 所在节点,或者直接转给本节点上的目标 Pod。
  5. 目标 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 名称:

  1. 应用请求 my-service.default.svc.cluster.local
  2. CoreDNS 返回 Service 的 ClusterIP。
  3. 流量到达节点后,被 kube-proxy 维护的规则捕获。
  4. 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]

这也是为什么我们会看到 ClusterIPNodePortLoadBalancerIngressGateway 这些不同层次的入口对象。它们都在解决“怎样把外部流量引到集群内部”这个问题,只是抽象层次不同。::cite[351]

3. 网络命名空间与虚拟网络基础

3.1 先记住 4 个关键词

  • network namespace:隔离网卡、路由表、iptables 视图的“网络沙箱”。
  • veth pair:成对出现的虚拟网卡,一头在 Pod 内,一头在节点侧。
  • bridge / route:负责把 Pod 发出的包继续转发出去。
  • CNI:负责给 Pod 接网、分配地址、接入整个集群网络。::cite[364]

3.2 一个 Pod 创建出来时,底层通常发生了什么

虽然不同 CNI 实现细节不同,但思路基本一致:

  1. kubelet/容器运行时创建 Pod sandbox。
  2. 调用 CNI 插件为 Pod 分配 IP。
  3. 为 Pod 创建网络命名空间。
  4. 创建一对 veth,Pod 内一端作为 eth0,另一端挂到节点网络。
  5. 配置默认路由和必要规则,让 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 句话检查自己是否真的理解了:

  1. 同 Pod 内容器能走 localhost,因为它们共享同一个 network namespace。::cite[352]
  2. Pod 能直接访问 Pod IP,因为 Kubernetes 网络模型要求集群内 Pod 彼此可达。::cite[354]
  3. Pod 访问 Service 实际访问的是虚拟入口,再由节点规则转发到真实后端。::cite[216]
  4. 跨节点通信能成功,是因为 CNI 已经把 Pod 网段路由或封装打通了。::cite[343]

如果这 4 句话你都能自己展开讲清楚,那么 Kubernetes 网络模型这一章就算真正入门了。

7. 本章小结

这一章最核心的结论不是“记住几个术语”,而是建立一个稳定心智模型:Pod 是 Kubernetes 网络里的基本端点,Service 是稳定入口,CNI 是把网络模型落地的关键角色。::cite[352]

后面学习 CNI、Service 实现原理、Ingress、Gateway、NetworkPolicy 时,所有内容其实都建立在这一章的基础之上。如果这一章没吃透,后面的网络排障会非常痛苦;如果这一章吃透了,后面很多复杂现象都会变得顺理成章。::cite[351]


📝 版权声明:本文为原创技术博客,转载请注明出处。

如文章中存在错误或不准确之处,欢迎在评论区指正,感谢您的阅读与支持!

上一篇

2.5 Namespace 与资源隔离:多环境与多团队基础

下一篇

7.4 Kubernetes Events 与告警