k8s底层网络原理

k8s底层网络原理

在 K8s 中,最小的调度单元叫做 Pod

从网络工程师的角度来看,你只需要把一个 Pod 理解为一个独立的 Network Namespace(网络命名空间)

  • 它有自己独立的 IP 地址(Pod IP)。
  • 它有自己的路由表和 ARP 表。
  • 它有自己的 lo 回环接口。

这就好比你在宿主机(Node)里开了一台极其轻量级的“虚拟机”。

既然 Pod 是一个隔离的区域,它里面有一个虚拟网卡(通常叫 eth0),但它实际上是在宿主机内部。

考考你的 Linux 网络知识:

如果我们想把这个隔离的 Namespace(Pod)里的网卡,和宿主机(Node)的网络协议栈连通,就像插一根虚拟网线一样,在 Linux 中通常会使用哪种成对出现的虚拟设备接口

You said不知道

Gemini said

没关系,这个概念确实比较底层,但在 K8s 网络中非常关键。

答案是 Veth Pair(Virtual Ethernet Pair,虚拟以太网对)。

你可以把它生动地想象成一根虚拟网线 🔌:

  • 成对出现:它就像虫洞一样,有两个端口。
  • 连接两端:我们将“网线”的一头扔进 Pod 的 Namespace 里,命名为 eth0(这就是为什么你在 Pod 里能看到网卡);另一头则留在宿主机的 Root Namespace 里,通常名字类似 veth1234
  • 直通:任何从 Pod eth0 发出的数据,都会立刻无条件地从宿主机的 veth1234 出来,反之亦然。

现在我们解决了“连通”的问题。假设宿主机上运行了 10 个 Pod,也就意味着有 10 根这样的 veth 网线的一端都在宿主机上。

为了让这 10 个 Pod 能够互相通信(或者通向外网),我们需要把这 10 根网线在宿主机上的那一头,都插到一个共同的设备上。

在物理网络中,把多台电脑连在一起的设备叫“交换机”。在 Linux 网络(以及 Docker/K8s 早期默认模式)中,这个充当二层虚拟交换机角色的设备叫什么?

Gemini said

明白了,那我们回到那个问题!我们要找的这个设备在 Linux 网络中扮演着二层交换机的角色。

集线器(Hub)是把数据“广播”给所有人,效率比较低。而我们需要一个更聪明的设备,它能根据 MAC 地址把数据准确地转发给目标 Pod。

在 Linux 中,这个虚拟设备起到了“连接”和“转发”的作用,就像一座一样,把宿主机上的多个 veth 接口(也就是 Pod 的网线)连接在一起。

它的名字里正好包含“桥”字。你能猜到这个 Linux 核心网络设备叫什么吗?

(提示:它是 Linux 网__ ?)

You said网桥

Gemini said

Bingo!完全正确。就是 Linux Bridge(网桥)。 🌉

在 Kubernetes(特别是像 Flannel 这类 CNI 插件)中,这个网桥(通常叫 cni0)就把宿主机上所有的 Pod 连接在了一起。

现在我们可以把同节点 Pod 通信的完整链路串起来了:

  1. 出发Pod A 通过 eth0 发出数据包。
  2. 穿梭:数据包穿过 veth pair 到达宿主机一端。
  3. 交换Linux Bridge (cni0) 收到数据,发现目标 MAC 地址就在这台交换机上(连接着 Pod B)。
  4. 到达:Bridge 直接把包转发给 veth B,最终进入 Pod B

这就像两台电脑插在同一个交换机上,直接就能聊,非常简单。


挑战升级:跨节点通信 (Cross-Node) 🚀

现在情况变了:

  • Pod A节点 1 (Node 1)
  • Pod B节点 2 (Node 2)

数据包到达 节点 1 的网桥 cni0 后,网桥傻眼了:“目标 IP (Pod B) 并不在我连接的任何一根网线上啊?”

这时候,网桥只能把这个烫手山芋扔给 节点 1 的宿主机操作系统

宿主机要想知道“去往 Pod B 的数据包,应该发给哪台机器(比如节点 2)”,它必须查阅一张地图。

在 Linux 网络中,这张决定“数据包下一跳去哪里”的地图叫什么表?

You said路由表

Gemini said

完全正确!就是 路由表 (Routing Table)。 🗺️

在 Kubernetes 的跨节点通信中,宿主机的路由表起到了至关重要的指路作用。

让我们来看一眼 Node 1 上的路由表大概长什么样(简化版):

目标网络 (Destination) 接口 (Device) 说明
10.244.0.0/24 cni0 本机 Pod,直接扔给网桥
10.244.1.0/24 flannel.1 去往 Node 2 的 Pod
192.168.1.0/24 eth0 宿主机所在的物理局域网

关键点来了:

当宿主机发现数据包的目标 IP(Pod B)匹配第二条规则时,它把数据包交给了 flannel.1 这个设备。

这是一个特殊的虚拟接口(Virtual Interface)。因为 Pod B 的 IP(比如 10.244.1.2)在物理网络中是不可路由的(它只存在于 K8s 的虚拟世界里),我们不能直接把它发到物理交换机上。

为了让这个“虚拟”的数据包能够通过真实的物理网络(eth0)传输到对面的 Node 2,我们需要把原始数据包包装在另一个数据包里面(就像寄信时把信纸装进信封一样)。

在网络技术中,这种“把一种协议的数据包藏在另一种协议的数据包里传输”的技术叫什么?📦

You saidvpn技术

Gemini said

让我们一起来探索 VPN(虚拟专用网络)技术。

简单来说,VPN 就像是在公用的互联网上建立了一条加密的专用隧道 🚇。它允许远程用户像在本地网络(例如公司内部网)一样安全地访问资源,同时确传输数据的私密性。

VPN tunnel diagram的图片

Shutterstock

探索

这是一个非常广泛的技术领域。你想从哪个方面开始深入了解?

  1. 核心原理 🔒 了解“隧道技术”具体是如何工作的,以及数据是如何被封装和加密的。
  2. 常见协议 📜 对比不同的通信规则,比如经典的 IPsec、灵活的 OpenVPN 或新兴高效的 WireGuard。
  3. 实际应用场景 🌍 探讨 VPN 在企业远程办公、个人隐私保护以及不同网络环境下的具体用途。

请选择一个你最感兴趣的方向,或者直接告诉我你想了解的具体问题!

You said我是回答的上面的问题

Gemini said

啊,原来如此!抱歉我刚才误会了,你的直觉非常敏锐。👍

没错,VPN 技术的核心正是这种“包中包”的机制,我们在网络专业术语中通常称之为隧道(Tunneling)*或者*封装(Encapsulation)

在 Kubernetes 的 Flannel(VXLAN 模式)中,正是利用了这种机制来“欺骗”物理网络:

  1. 打包(Encapsulation):Node 1 把原始的 Pod 数据包(想去往 Pod B)作为“货物”,装进了一个巨大的 UDP 数据包(外层信封)里。
  2. 传输:这个 UDP 包的源地址是 Node 1 的物理 IP,目的地址是 Node 2 的物理 IP。因为它是合法的 UDP 包,所以物理路由器会一路绿灯把它放行到 Node 2。

那么,当这个“伪装”成 UDP 的大包裹终于顺着物理网线到达 Node 2 后,Node 2 也就是接收端,需要立刻对这个包裹做什么动作,才能看到里面的“真面目”(原始 Pod 数据包)呢?

You said解封装

Gemini said

完全正确!这就叫 解封装 (Decapsulation)。 🔓

你刚才提到的“VPN”概念在这里闭环了:就像收到信件后拆开信封一样,Node 2 的内核接手了这个 UDP 数据包后,做了一系列像外科手术一样的操作:

  1. 识别:内核发现这个包是发给 VXLAN 端口(默认 8472)的,于是交给 flannel.1 设备处理。
  2. 拆包flannel.1 把外层的宿主机 IP 头、UDP 头统统“撕掉”。
  3. 还原:此时,原始的 Pod A -> Pod B 的数据包就“重见天日”了。

最后一步:送货上门 🚚

现在,Node 2 的操作系统手里拿着这个原始数据包,目标 IP 是 Pod B

路由表会告诉宿主机:“去往本机 Pod 网段的数据,请交给 cni0”。

于是数据包来到了 Node 2 的 cni0 网桥 上。

还记得我们在最开始讲到的网桥(Bridge)的工作原理吗?当 cni0 拿到这个包,看到目标 MAC 地址对应的是 Pod B,它会通过什么设备把数据包最终塞进 Pod B 的肚子里?

You said不知道

Gemini said

没关系,这一步确实有点绕。其实答案还是我们最开始提到的那个概念。

还记得那个像虫洞一样的“虚拟网线”吗?答案就是 Veth Pair 在宿主机那一端的接口。

我们可以这样完整地看最后这一跳:

  1. 查表:Node 2 的网桥 (cni0) 看到数据包是要给 Pod B 的。
  2. 转发:网桥找到连接 Pod B 的那根虚拟网线(比如 vethB),把数据包塞进去。
  3. 直达:因为 Veth Pair 是直通的,数据包瞬间就从 Pod B 内部的 eth0 冒出来了。

🎉 恭喜!你已经掌握了 K8s 网络最核心的“Overlay 网络”模型。

我们来复盘一下这个逻辑:我们在底层的物理网络(节点 IP、物理路由器)之上,通过软件(Flannel/VXLAN)构建了一层虚拟网络(Pod IP、虚拟网桥)。

这种架构被称为 Overlay Network(覆盖网络)

基于刚才我们推导的流程,你觉得这种 Overlay 架构最大的好处是什么? (提示:想一想,如果我们在机房里增加了一台新的物理服务器,或者物理网络的 IP 变了,Pod 里的 IP 地址需要跟着修改吗?)

You said不需要,还有就是我对上面说的几个名词有疑问,比如node是指一台物理机吗,宿主机是在物理机上面,然后在宿主机中又运行多个容器,多个容器称为pod吗,那pod中的不同容器不需要不同的ip地址吗,网桥是宿主机连接物理机的吗,pair是宿主机连接pod吗

Gemini said

这个问题问得非常棒!你现在的困惑主要在于虚拟化层级网络设备的位置没对上号。

既然你是学网络的,我们直接用一张拓扑图的概念来理清这些关系。

我会针对你的四个疑问逐一拆解,帮你把脑海里的图修一修:

1. Node 和 宿主机 (Host) 是什么关系?

疑问:Node 是物理机吗?宿主机在物理机上面吗?

纠正: 在 Kubernetes 的语境下,Node = 宿主机 = 一台服务器。 它们不是上下级关系,而是同一个东西的不同叫法。

  • Node (节点):这是 K8s 的逻辑概念。K8s 说:“我有 3 个节点”,意思就是它管理着 3 台服务器。
  • 宿主机 (Host):这是操作系统层面的概念。指的就是运行着 Linux 内核的那台机器。
  • 物理机/虚拟机:这是硬件层面的概念。Node 可以是一台真实的物理服务器(裸金属),也可以是阿里云上的一台虚拟机(VM)。

总结: 你就把 Node 当作那台运行 Linux 的电脑本身。


2. Pod 和 容器的关系?

疑问:宿主机中运行多个容器,多个容器称为 Pod 吗?

纠正: 不完全对。Pod 是一个“逻辑包装盒”,里面装着 1 个或多个容器。

你可以把 Pod 想象成一个豌豆荚(Pod 的原意就是豌豆荚):

  • Pod (豌豆荚):是 K8s 调度的最小单位。
  • Container (豌豆):是实际运行应用的程序(比如 Nginx 容器、Tomcat 容器)。

通常情况下,一个 Pod 里只放一个容器。但在某些特定场景(比如日志收集、Service Mesh)下,一个 Pod 里会塞进两个或多个容器(主容器 + 边车容器)。


3. Pod 里的不同容器需要不同的 IP 吗?(关键点!)

疑问:那 Pod 中的不同容器不需要不同的 IP 地址吗?

答案: 绝对不需要,也不能有。 这是 K8s 网络设计的核心原则!

  • 共享网络命名空间 (Network Namespace): 同一个 Pod 里的所有容器,是共用同一个 Network Namespace 的。
  • 共享 IP 和 端口: 这就好比你们宿舍(Pod)里住了 3 个人(容器),你们共用同一个门牌号(IP 地址)。
  • 怎么通信? Pod 内部的容器之间通信,直接用 localhost! 比如容器 A 监听 80 端口,容器 B 只要访问 localhost:80 就能找到 A。
  • 冲突问题: 因为共用 IP,所以同一个 Pod 里的不同容器,不能监听同一个端口(比如不能两个容器都开 80 端口),否则会端口冲突。

4. 网桥 (Bridge) 和 Veth Pair 到底连着谁?

疑问:网桥是宿主机连接物理机的吗?Pair 是宿主机连接 Pod 的吗?

我们来看一下这张逻辑拓扑图:

Plaintext

       [ 物理网络 / 交换机 ]
                |
+-------------- | -----------------------+
|  Node (宿主机 / 服务器)                 |
|                                        |
|      [ eth0 (物理网卡) ]                |
|           |  (IP: 192.168.1.100)       |
|           |                            |
|      [ cni0 (虚拟网桥/二层交换机) ] <------- 关键位置!
|           /           \                |
|          /             \               |
|   [ vethxxx ]       [ vethyyy ] <-------- Veth Pair 的一端 (在宿主机上)
|        |                 |             |
|   +----|----+       +----|----+        |
|   | [ eth0 ]|       | [ eth0 ]| <-------- Veth Pair 的另一端 (在 Pod 里)
|   |  Pod A  |       |  Pod B  |        |
|   +---------+       +---------+        |
+----------------------------------------+

纠正你的理解:

  1. Veth Pair (虚拟网线)你说对了!
    • 它确实是连接 宿主机 (Host)Pod 的。
    • 一头插在宿主机的网桥上,另一头插在 Pod 肚子里。
  2. 网桥 (cni0)你说错了。
    • 它不是连接物理机的。它是宿主机内部的一个虚拟二层交换机
    • 它的作用是把宿主机上所有的 Veth Pair(也就是所有的 Pod)汇聚在一起,让 Pod A 和 Pod B 能在宿主机内部直接二层通信。
    • 并不直接连接物理网线。数据包要出物理网卡 eth0,需要走宿主机的路由 (Routing) + NAT 转发,而不是通过网桥直接连。

现在的画面感清晰了吗?

简单说:

  1. Node 就是电脑。
  2. Pod 是电脑里的独立小房间。
  3. Veth Pair 是从房间拉出来的网线。
  4. 网桥 是走廊里的交换机,插着所有房间的网线。
  5. Pod 里的容器 是房间里住的几个人,他们共用这根网线和房间号 (IP)。

如果这个逻辑通了,我们要不要回到刚才被打断的地方:当数据包要从这台电脑(Node 1)飞到另一台电脑(Node 2)时,Flannel 是怎么操作的?

You said封装udp包

Gemini said

没错,这就是 Overlay 网络 的核心魔法:把二层/三层的“私有数据”,打包塞进一个标准的 UDP 包里,然后在物理网络上传输。

既然你是学网络的,我们直接看报文结构,这比任何比喻都清楚。

这就好比俄罗斯套娃,外层是“物理世界的信封”,内层是“虚拟世界的信件”。

1. 报文解剖 (Anatomy of a VXLAN Packet)

当 Pod A (IP: 10.244.1.2) 想发数据给 Pod B (IP: 10.244.2.2),经过 flannel.1 接口封装后,在这个数据包离开宿主机物理网卡的一瞬间,它的结构是这样的:

我们由内向外拆解:

  1. 最内层:原始载荷 (Original Payload)
    • 数据:比如是一个 HTTP 请求。
    • 内层 TCP/IP 头
      • 源 IP:10.244.1.2 (Pod A)
      • 目的 IP:10.244.2.2 (Pod B)
    • 注意:这部分在物理网络传输中是不可见的,路由器只把它们当做数据。
  2. 中间层:VXLAN 头 (VXLAN Header)
    • 这是一个 8 字节的头部。
    • VNI (VXLAN Network Identifier):比如 1。这相当于 VLAN ID,用来区分不同的虚拟网络(虽然 Flannel 通常只用一个)。
    • 它告诉接收端:“嘿,我是 VXLAN 数据包,请用特殊方式解包。”
  3. 最外层:物理传输头 (Outer Transport Header)
    • UDP 头
      • 源端口:随机端口(用于哈希负载均衡)。
      • 目的端口:8472 (这是 IANA 分配给 VXLAN 的标准端口,或者是 4789)。
    • 外层 IP 头
      • 源 IP:192.168.1.100 (Node A 的物理 IP)。
      • 目的 IP:192.168.1.200 (Node B 的物理 IP)。
    • 外层 MAC 头
      • 源 MAC:Node A 物理网卡的 MAC。
      • 目的 MAC:物理网关或下一跳路由器的 MAC。

2. 这里的关键角色:VTEP

你可能会问:是谁在做这个“打包”和“拆包”的动作?

在网络术语中,这个设备叫 VTEP (VXLAN Tunnel Endpoint)

在 Kubernetes (Flannel) 中:

  • 那个 flannel.1 虚拟接口,就是 VTEP
  • 它是一只脚踏在虚拟世界(连接 cni0 网桥),另一只脚踏在宿主机内核网络栈(连接 eth0)的守门人。

数据流向总结:

  1. 进 VTEP:原始数据包撞上路由规则,被扔给 flannel.1
  2. 封包flannel.1 查表(FDB表),知道“去往 10.244.2.0/24 网段,对应的物理 IP 是 192.168.1.200”,于是加上 UDP 头和外层 IP 头。
  3. 出物理网卡:封装好的包变成了一个普通的 UDP 包,通过 eth0 发出去。
  4. 物理网络传输:物理交换机/路由器只看外层 IP,把包路由到 Node B。
  5. 拆包:Node B 收到 UDP 8472 端口的包,内核把它剥皮,取出内层数据。

3. 网络工程师的痛点:MTU 问题 ⚠️

作为一个网络工程专业的学生,你一定能意识到这种“包中包”带来的一个严重隐患。

思考一下: 物理网卡的 MTU(最大传输单元)通常是 1500 字节。 如果你在 1500 字节的物理包里,硬塞进去一个 VXLAN 头(8字节)+ UDP 头(8字节)+ IP 头(20字节)= 50字节的额外开销 (Overhead)

那么,内层的 Pod 还能发送 1500 字节的大包吗?

如果 Pod 里的应用傻乎乎地发了一个 1500 字节的包,会发生什么?

  1. 要么被分片(Fragment),导致性能极其低下。
  2. 要么直接被丢弃(如果设置了 DF 位)。

这就是为什么 K8s 网络必须要调整 MTU: 通常 Flannel 会自动把 cni0 和 Pod 网卡 (eth0) 的 MTU 设置为 1450(或者更小),给外层头部留出空间。


到这里,Pod-to-Pod 的通信链路(同节点直连、跨节点隧道)你应该已经打通了。

接下来有两个方向,你想了解哪个?

  1. CNI 的另一种流派 (BGP):像 Calico 这种不封装、直接改物理路由表的方案是怎么做到的?(这是纯三层路由,性能更好)。
  2. Service (服务发现):Pod 的 IP 是会变的(重启就变),外面的用户怎么才能通过一个固定 IP 找到这些动态的 Pod?

You said1

Gemini said

很好,选得太棒了!作为网络工程专业的学生,Calico 绝对会让你感到倍感亲切。

之所以这么说,是因为 Flannel 的 VXLAN 模式是在玩弄数据包(封装/解封装),而 Calico 则是在玩弄路由表

Calico 的核心思想非常“硬核”:不要把 Pod 当作虚拟机,要把它们当作挂在路由器后面的终端设备。 而每一台宿主机(Node),其实就是一台 虚拟路由器 (vRouter)

既然是路由器,它们之间怎么知道对方有哪些 Pod 网段呢?答案就是你最熟悉的互联网核心协议——BGP (Border Gateway Protocol)


1. 架构剧变:网桥 (cni0) 消失了! 😲

这是 Calico 和 Flannel 最直观的区别。

在 Flannel 模式下,宿主机上有一个 cni0 网桥,像交换机一样把所有 Pod 连在一起。 但在 Calico 的默认模式下,网桥不见了

那 Pod 的 veth 网线插在哪里?

  • 答案:它孤零零地挂在宿主机的 Root Namespace 里。
  • 连接方式:宿主机通过 三层路由 直接找到这个接口。

这就好比你给路由器插了一根网线直连电脑,路由器不需要交换机,它只需要一条路由规则:“去往 IP 10.244.1.2 的包,请走接口 cali12345”。


2. 核心机制:BGP 广播 📢

想象一下,你的 K8s 集群里有 3 台节点(Node 1, Node 2, Node 3)。 每个节点上都运行着一个 calico-node 的 Pod。这个 Pod 里面实际上运行了一个标准的路由软件(通常是 BIRD)。

它们之间会建立 BGP Peering(对等连接),并不停地“窃窃私语”:

  • Node 1 喊话:“嘿!兄弟们听好了。凡是目的 IP 是 10.244.1.0/24 (我这边的 Pod 网段) 的包,下一跳 (Next Hop) 请发给我 (Node 1 的物理 IP)!”
  • Node 2 喊话:“收到!顺便说一句,去往 10.244.2.0/24 的,请发给我!”

于是,整个集群的路由表自动收敛。所有的节点都变成了一张巨大的、互通的路由表。


3. 数据包漫游指南 (No Encapsulation) 🚀

现在,Pod A (在 Node 1) 要发包给 Pod B (在 Node 2)。 注意看,这次没有 UDP 封装,没有额外的开销,全是纯粹的 IP 转发

  1. Pod A 发出:数据包源 IP 10.244.1.2,目的 IP 10.244.2.2
  2. 进入 Node 1:数据包通过 veth 对,直接进入 Node 1 的内核网络栈。
  3. Node 1 查路由表
    • 内核看到目的 IP 是 10.244.2.2
    • 查表发现:10.244.2.0/24 via 192.168.1.200 (这是 Node 2 的物理 IP)。
    • 关键点:这一条路由规则,正是刚才通过 BGP 学来的!
  4. 物理传输
    • Node 1 直接把原始的 IP 包发给 Node 2。
    • 注意:物理交换机必须能看懂这个 IP 包。如果中间隔了路由器,物理路由器也必须运行 BGP 或者配置静态路由(这就是为什么云厂商通常不支持这种模式,因为你改不了他们的路由器)。
  5. 到达 Node 2
    • Node 2 收到包,查自己的路由表。
    • 发现:10.244.2.2 dev calixxxxx scope link
    • 意思是:“哦,这个 IP 就在我身上挂着的 calixxxxx 接口那头。”
  6. 直达 Pod B:数据包直接塞进 Pod B。

4. 这种模式的优缺点 (vs Flannel VXLAN)

作为未来的网络专家,你能总结出这种 纯三层路由 (Pure Layer 3) 方案的优劣吗?

优势 (Pros):

  1. 性能无敌:没有封装/解封装的 CPU 损耗,几乎和物理网络一样快。
  2. 便于调试:你用 tcpdump 抓包,看到的就是真实的 IP,没有那层讨厌的 UDP 外壳。
  3. 更符合网络直觉:支持标准的网络策略(Network Policy),比如 ACL。

劣势 (Cons) / 限制:

  1. 物理网络依赖:它要求所有节点必须在同一个二层网络 (L2) 下(才能直接把包扔给对方的 MAC 地址)。
    • 思考:如果 Node 1 和 Node 2 跨了网段(中间有物理路由器),而物理路由器不认识 Pod 的 IP,包就会被丢弃。
    • 解决:你需要把物理路由器也拉进 BGP 圈子(Tor 交换机做 BGP Peer),这在传统企业数据中心很常见,但在公有云上很难做到。

5. 一个小测验 📝

在 Calico 模式下,如果我用 route -nNode 1 上查看路由表,我会看到类似这样的条目:

Bash

Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         192.168.1.1     0.0.0.0         UG    0      0        0 eth0
10.244.1.2      0.0.0.0         255.255.255.255 UH    0      0        0 cali123
10.244.2.0      192.168.1.200   255.255.255.0   UG    0      0        0 eth0

请仔细看第三行: 10.244.2.0 (目标 Pod 网段) -> 192.168.1.200 (Node 2 IP)。

问题来了: 如果 Node 1 和 Node 2 中间经过了一个不支持 BGP 的物理路由器,当 Node 1 把这个包发给物理路由器时,物理路由器的路由表里如果没有 10.244.2.0 的路由,会发生什么?

You said丢弃

Gemini said

回答完全正确。这就是著名的 路由黑洞 (Routing Blackhole) 问题。 ⚫️

物理路由器会看着这个莫名其妙的 IP (10.244.2.2) 说:“我不认识这个网段,我的默认路由是去公网的,内网也没有这个路由,丢弃!”

这也引出了 Calico 的一个妥协模式,专门用来解决这个问题:IPIP 模式 (IP-in-IP)。 当跨网段通信时,Calico 会自动把 IP 包再封装一层 IP 头(就像简易版的 VXLAN),利用宿主机的 IP 进行路由。所以 Calico 既可以是 Underlay(纯路由),也可以是 Overlay(隧道),非常灵活。


现在你已经打通了 K8s 网络最难懂的“任督二脉”:

  1. Pod 内部:Pause 容器、Network Namespace、Veth Pair。
  2. Pod 之间:网桥(Bridge)、VXLAN 隧道(Flannel)、BGP 路由(Calico)。

接下来我们要面对一个更现实的问题:Pod 是会死的。 💀

  • Pod A 的 IP 是 10.244.1.2
  • Pod A 挂了,K8s 重启了一个新的,IP 变成了 10.244.1.5
  • 既然 IP 随时会变,前端怎么能稳定地访问后端呢?

这就引入了 K8s 网络的第三大支柱 —— Service (服务)

K8s 的“虚拟 IP”魔法:Service

K8s 发明了一个虚构的 IP,叫 ClusterIP(比如 10.96.0.1)。 只要你往这个 IP 发包,它就会自动“负载均衡”到后面随即变动的 Pod IP 上。

但在 Linux 内核里,根本不存在一个叫“ClusterIP”的网卡或设备。 如果你在 Node 上 ping 10.96.0.1,可能是通的,但你用 ifconfigip addr 根本找不到它。

作为网络工程师,请你大胆猜测一下: 既然没有真实的网卡接收这个 IP,那 K8s 是用了 Linux 内核的什么功能,在数据包刚刚进入内核、还没来得及查路由表的时候,就把目标 IP (10.96.0.1) 偷偷改写成了真实的 Pod IP (10.244.1.5)?

(提示:它是 Linux 防火墙的基石,也是 NAT 的幕后推手。)

You saidiptable

Gemini said

宾果!完全正确。🎯

iptables 正是 Kubernetes Service 的幕后功臣(当然,现代 K8s 更多使用 IPVS,本质也是内核态转发,我们稍后细说)。

作为一个网络工程师,你一定配置过防火墙的 NAT 规则。Kubernetes 的 Service 其实就是一个自动化配置的 DNAT (Destination Network Address Translation) 规则集。

1. ClusterIP 的“骗局” 🎭

你看到的 Service IP(比如 10.96.0.1)在网络层面上是一个虚拟 IP (VIP),它根本没有挂载在任何物理或虚拟网卡上。

当一个 Pod 试图访问这个 VIP 时,Linux 内核的 Netfilter 框架(也就是 iptables 的内核态实现)会在路由决策之前(PREROUTING 链)拦截这个数据包:

  1. 拦截:内核发现目标 IP 是 10.96.0.1
  2. 匹配:它去查 iptables 的规则表(由 kube-proxy 维护)。
  3. 改写 (DNAT):规则说:“去往 10.96.0.1:80 的包,请以 33% 的概率改写目标 IP 为 10.244.1.5:8080 (Pod A),33% 改为 10.244.1.6:8080 (Pod B)...”
  4. 放行:改写后的数据包(目标变成了真实的 Pod IP)继续在内核中流转,查路由表,最终被转发到对应的 Pod。

所以,ClusterIP 只是 iptables 规则里的一行字符串而已。


2. 网络工程师的噩梦:iptables 的性能瓶颈 🐢

这里有一个非常硬核的知识点,作为 H3CIE 你肯定能秒懂。

iptables 本质上是一个链表 (Linked List)。 这意味着,如果你集群里有 10,000 个 Service,内核在处理每个数据包时,最坏情况下要从头到尾遍历这 10,000 条规则才能找到匹配的那一条。

时间复杂度是 O(n)。

当规模上来后,CPU 会被大量的软中断(SoftIRQ)吃光,网络延迟飙升。

解决方案:IPVS (IP Virtual Server) 🚀

Linux 内核其实有一个专门做负载均衡的神器,叫 IPVS(也是 LVS 的核心)。

  • 它使用 哈希表 (Hash Table) 存储规则。
  • 时间复杂度是 O(1)
  • 无论你有 10 个 Service 还是 100,000 个,查找速度几乎一样快。
  • 支持更复杂的负载均衡算法(如最小连接数 Least Conn,而 iptables 只支持随机/轮询)。

所以,现在的生产环境 K8s 集群,我们通常会开启 ipvs 模式。


3. 总结 K8s 网络三剑客 ⚔️

到现在为止,我们已经把 K8s 底层网络最核心的三个部分串起来了:

  1. Pod 网络
    • Veth Pair:连接 Pod 和宿主机。
    • Bridge/Routing:连接同节点的 Pod。
  2. 跨节点通信 (CNI)
    • Overlay (Flannel):UDP 封装 (VXLAN),对物理网络无感知,通用性强但有性能损耗。
    • Underlay (Calico/BGP):纯路由转发,性能极致但依赖物理网络环境。
  3. 服务发现 (Service)
    • iptables/IPVS:内核态 DNAT,把虚拟 VIP 映射到真实 Pod IP,实现负载均衡。

至此,K8s 的内网流量闭环已经完成。

最后一步挑战: 既然 Pod IP 和 ClusterIP 都是局域网/虚拟 IP,外面的用户(比如互联网上的浏览器)是怎么访问到 K8s 里面的服务的呢?

这涉及到 K8s 网络的出口大门。你想了解:

  1. NodePort:最原始暴力的方式(在宿主机开端口)。
  2. Ingress:七层应用网关(基于 Nginx/Traefik),也是最常用的方式。

You said1

Gemini said

既然你选择了 NodePort,作为 H3CIE,你只要把它理解为网络设备上的 端口映射 (Port Mapping) 或者 DNAT,就瞬间懂了。

1. 暴力美学:在宿主机上打洞 🕳️

NodePort 的原理非常简单粗暴:它强制在 Kubernetes 集群的每一台节点(Node)上,都监听同一个端口(默认范围 30000-32767)。

举个例子: 假设你的 Service 定义了 nodePort: 30080。 那么,无论你的集群有 3 台节点还是 300 台节点,每一台服务器的物理网卡都会监听 TCP 30080 端口。

外部用户只要访问 任意一台节点IP:30080,数据包就能进入 K8s 网络。


2. 流量路径:不只是简单的端口转发 twisted

这里有一个会让网络工程师感到“反直觉”的设计:全网格路由 (Mesh Routing)

假设:

  • Node A (IP: 1.1.1.1) 上没有运行任何这个 Service 的 Pod。
  • Node B (IP: 2.2.2.2) 上运行了 Pod-B

场景: 外部用户访问了 Node A 的 30080 端口 (1.1.1.1:30080)。

作为网络工程师,你可能会问: “Node A 上明明没有 Pod,连接应该会被 Refuse 或者 Drop 吧?”

K8s 的回答: “不,我会帮你转发过去!”

真实的数据包路径:

  1. 入站:用户包到达 Node A 的 30080 端口。
  2. 劫持:Node A 的 iptables / IPVS 规则捕获该包。
  3. 查表:规则发现:“哦,这是去往 Service X 的,虽然我本机没有 Pod,但我知道 Node B 有。”
  4. SNAT (源地址转换)关键一步! Node A 把数据包的源 IP 改成 Node A 自己的 cni0flannel.1 IP(为了让回包能原路返回)。
  5. DNAT (目的地址转换):把目的 IP 改成 Pod-B 的 IP。
  6. 转发:数据包通过 Overlay 网络(VXLAN/IPIP)飞向 Node B。
  7. 到达:Pod-B 收到请求,处理完后回包给 Node A,Node A 再回包给用户。

这就是为什么你访问集群中任何一台机器的 30080 端口,都能访问到服务,无论 Pod 在哪儿。


3. 网络工程师视角的缺点 (Cons) 📉

虽然 NodePort 简单,但在企业级生产环境中,我们网络团队通常极其讨厌这种方式。

基于你的专业背景,你应该能一眼看出它的硬伤

  1. 端口管理噩梦
    • 端口范围受限(30000-32767)。
    • 你需要维护一张巨大的 Excel 表格来记录哪个应用用了哪个端口,避免冲突。这简直是倒退回了手工管理 IP 的时代。
  2. 安全隐患
    • 你在防火墙上需要开放一堆高位端口。安全团队会拿着扫描报告天天找你麻烦。
  3. 客户端配置麻烦
    • 如果 Node A 挂了,客户端的 IP 填的是 Node A,那服务就断了。
    • 解决办法:你通常还得在这些 Node 前面再架设一台 硬件负载均衡器 (F5/LVS/Nginx),把流量轮询分发给后端所有 Node 的 30080 端口。

这就变成了:Client -> F5 (VIP) -> NodePort -> Service IP -> Pod IP。 是不是觉得链路太长、太折腾了?


总结与下一阶段

恭喜你!到目前为止,你已经把 Kubernetes 网络最核心的 “数据平面” (Data Plane) 全部打通了:

  1. 容器间:Localhost 通信。
  2. Pod 间:Overlay (VXLAN) 或 Underlay (BGP) 互通。
  3. Pod 到 Service:iptables/IPVS 做 DNAT 负载均衡。
  4. 外部到 Pod:NodePort 在宿主机开端口映射。

作为网络专业的毕业生,你现在的理解深度已经超过了绝大多数 K8s 初学者。

下一步建议: 既然你已经懂了底层原理,要不要最后了解一下 K8s 网络的“应用层网关” —— Ingress? 它是目前最主流的暴露服务方式,因为它工作在 七层 (HTTP/HTTPS),可以根据 域名 (Host) 和 路径 (Path) 来转发流量,完美解决了 NodePort 端口不够用的问题。

你想看 Ingress 是怎么基于 Nginx 复用同一个 80/443 端口的吗?

You said好的

Gemini said

作为网络工程专业的毕业生,你应该对 Nginx 或者 F5/A10 这类负载均衡设备非常熟悉。

Ingress 本质上就是 Kubernetes 里的“软负载均衡器” (Layer 7 Load Balancer)。

如果说 NodePort 是在防火墙上粗暴地打洞(四层转发),那 Ingress 就是一个智能的前台接待(七层代理)。

1. 核心原理:虚拟主机 (Virtual Hosting) 🌐

在 NodePort 模式下,你是通过端口号来区分服务的:

  • Service A -> Port 30001
  • Service B -> Port 30002

在 Ingress 模式下,所有的流量都只走 80 (HTTP)443 (HTTPS) 端口。那它怎么区分你是要找 Service A 还是 B 呢?

靠的是 HTTP 协议里的 Host 头 (域名) 和 URI Path (路径)。

这完全就是 Nginx 的 Server Block 配置逻辑:

  • www.example.com/api -> 转发给 Service A
  • www.example.com/web -> 转发给 Service B
  • blog.example.com -> 转发给 Service C

2. Ingress Controller:其实就是一个特殊的 Pod 🤖

初学者最容易晕的地方是:Kubernetes 自带 Ingress 吗? 答案是:不带。 K8s 只有 Ingress 的接口定义(规则),没有实现

你需要自己安装一个 Ingress Controller。最常用的就是 Nginx Ingress Controller

请注意,这个 Controller 本质上就是:

  1. 一个运行着 Nginx 的 Pod
  2. 它里面有一个 Go 语言写的监听程序
  3. 这个监听程序通过 K8s API 实时监控集群里的 Ingress 规则变化。

工作流程(动态配置): 当你写了一个 YAML 文件说:“把 abc.com 转发给 Service-A”。

  1. 监听:Ingress Controller 的 Go 程序检测到了这个新规则。
  2. 翻译:它把这个 K8s 规则瞬间翻译成 Nginx 的配置格式 (nginx.conf)。
    • server { listen 80; server_name abc.com; location / { proxy_pass ... } }
  3. 重载:它执行 nginx -s reload(或者通过 Lua 动态热加载),让配置生效。

这就实现了一个神奇的效果:你不需要 SSH 到服务器上去改 Nginx 配置文件,你只需要提交 YAML,Nginx 就自动变了。


3. 完整流量链路 (The Big Picture) 🗺️

让我们把之前学的所有知识串起来,看一个外网请求是如何到达 Pod 的:

  1. DNS 解析:用户访问 myapp.com,DNS 解析到了 K8s 集群的入口 IP(通常是负载均衡器的 VIP)。
  2. 入口网关:流量到达集群边缘,通过 NodePort (如 30080) 或者 Cloud LB,进入到 Ingress Controller Pod (也就是那个 Nginx Pod)。
  3. 七层路由 (Ingress)
    • Nginx 拆开 HTTP 包头,看到 Host: myapp.com
    • 匹配配置,决定把包转发给后端 Service (ClusterIP: 10.96.x.x)。
  4. 四层转发 (Service)
    • Linux 内核 (iptables/IPVS) 拦截这个请求。
    • 做 DNAT,把 Service IP 变成具体的 Pod IP (10.244.1.5)。
  5. 二层/三层传输 (CNI)
    • 通过 Flannel (VXLAN) 或 Calico (路由),跨主机把包送到目标 Pod 的容器里。

4. 网络工程师视角的 Ingress 优势

相比 NodePort,Ingress 简直是降维打击:

  1. 端口复用:整个集群只需要对外暴露 80/443 两个端口,就能支撑成千上万个服务。防火墙策略极其简单。
  2. TLS 卸载 (SSL Termination)
    • 你把 HTTPS 证书配置在 Ingress 这一层。
    • Ingress 解密后,通过明文 HTTP 转发给后端的 Pod。
    • 好处:后端开发人员不需要在代码里处理复杂的证书逻辑,也不需要关心证书过期,运维统一管理。
  3. 高级流量控制
    • 金丝雀发布 (Canary):可以让 5% 的流量去新版本,95% 走老版本。
    • 限流 (Rate Limiting):限制每秒请求数,防止攻击。
    • 黑白名单:基于 IP 段拦截流量。

这实际上就是把传统的应用交付控制器 (ADC) 软件化、容器化了。


总结:你的 K8s 网络技能树已点亮 🌲

回顾一下,作为一名网络工程专业的毕业生,你现在已经掌握了 K8s 网络的完整拼图:

层次 核心组件 你的理解 (网络术语)
L2/L3 (网络互通) CNI (Flannel/Calico) VLAN, VXLAN, BGP, 路由表, 网桥
L4 (服务发现) Service (ClusterIP) VIP, DNAT, iptables, 负载均衡
L4 (外部接入) NodePort 端口映射 (Port Mapping)
L7 (应用路由) Ingress 反向代理, 虚拟主机, URL 路由, SSL 卸载
posted @ 2026-02-12 21:47  骷髅漫天  阅读(77)  评论(0)    收藏  举报