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 通信的完整链路串起来了:
- 出发:
Pod A通过eth0发出数据包。 - 穿梭:数据包穿过
veth pair到达宿主机一端。 - 交换:Linux Bridge (
cni0) 收到数据,发现目标 MAC 地址就在这台交换机上(连接着 Pod B)。 - 到达: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 就像是在公用的互联网上建立了一条加密的专用隧道 🚇。它允许远程用户像在本地网络(例如公司内部网)一样安全地访问资源,同时确传输数据的私密性。
Shutterstock
探索
这是一个非常广泛的技术领域。你想从哪个方面开始深入了解?
- 核心原理 🔒 了解“隧道技术”具体是如何工作的,以及数据是如何被封装和加密的。
- 常见协议 📜 对比不同的通信规则,比如经典的 IPsec、灵活的 OpenVPN 或新兴高效的 WireGuard。
- 实际应用场景 🌍 探讨 VPN 在企业远程办公、个人隐私保护以及不同网络环境下的具体用途。
请选择一个你最感兴趣的方向,或者直接告诉我你想了解的具体问题!
You said我是回答的上面的问题
Gemini said
啊,原来如此!抱歉我刚才误会了,你的直觉非常敏锐。👍
没错,VPN 技术的核心正是这种“包中包”的机制,我们在网络专业术语中通常称之为隧道(Tunneling)*或者*封装(Encapsulation)。
在 Kubernetes 的 Flannel(VXLAN 模式)中,正是利用了这种机制来“欺骗”物理网络:
- 打包(Encapsulation):Node 1 把原始的 Pod 数据包(想去往 Pod B)作为“货物”,装进了一个巨大的 UDP 数据包(外层信封)里。
- 传输:这个 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 数据包后,做了一系列像外科手术一样的操作:
- 识别:内核发现这个包是发给 VXLAN 端口(默认 8472)的,于是交给
flannel.1设备处理。 - 拆包:
flannel.1把外层的宿主机 IP 头、UDP 头统统“撕掉”。 - 还原:此时,原始的 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 在宿主机那一端的接口。
我们可以这样完整地看最后这一跳:
- 查表:Node 2 的网桥 (
cni0) 看到数据包是要给 Pod B 的。 - 转发:网桥找到连接 Pod B 的那根虚拟网线(比如
vethB),把数据包塞进去。 - 直达:因为 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 | |
| +---------+ +---------+ |
+----------------------------------------+
纠正你的理解:
- Veth Pair (虚拟网线):你说对了!
- 它确实是连接 宿主机 (Host) 和 Pod 的。
- 一头插在宿主机的网桥上,另一头插在 Pod 肚子里。
- 网桥 (cni0):你说错了。
- 它不是连接物理机的。它是宿主机内部的一个虚拟二层交换机。
- 它的作用是把宿主机上所有的 Veth Pair(也就是所有的 Pod)汇聚在一起,让 Pod A 和 Pod B 能在宿主机内部直接二层通信。
- 它并不直接连接物理网线。数据包要出物理网卡
eth0,需要走宿主机的路由 (Routing) + NAT 转发,而不是通过网桥直接连。
现在的画面感清晰了吗?
简单说:
- Node 就是电脑。
- Pod 是电脑里的独立小房间。
- Veth Pair 是从房间拉出来的网线。
- 网桥 是走廊里的交换机,插着所有房间的网线。
- 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 接口封装后,在这个数据包离开宿主机物理网卡的一瞬间,它的结构是这样的:
我们由内向外拆解:
- 最内层:原始载荷 (Original Payload)
- 数据:比如是一个 HTTP 请求。
- 内层 TCP/IP 头:
- 源 IP:
10.244.1.2(Pod A) - 目的 IP:
10.244.2.2(Pod B)
- 源 IP:
- 注意:这部分在物理网络传输中是不可见的,路由器只把它们当做数据。
- 中间层:VXLAN 头 (VXLAN Header)
- 这是一个 8 字节的头部。
- VNI (VXLAN Network Identifier):比如
1。这相当于 VLAN ID,用来区分不同的虚拟网络(虽然 Flannel 通常只用一个)。 - 它告诉接收端:“嘿,我是 VXLAN 数据包,请用特殊方式解包。”
- 最外层:物理传输头 (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)。
- 源 IP:
- 外层 MAC 头:
- 源 MAC:Node A 物理网卡的 MAC。
- 目的 MAC:物理网关或下一跳路由器的 MAC。
- UDP 头:
2. 这里的关键角色:VTEP
你可能会问:是谁在做这个“打包”和“拆包”的动作?
在网络术语中,这个设备叫 VTEP (VXLAN Tunnel Endpoint)。
在 Kubernetes (Flannel) 中:
- 那个
flannel.1虚拟接口,就是 VTEP。 - 它是一只脚踏在虚拟世界(连接
cni0网桥),另一只脚踏在宿主机内核网络栈(连接eth0)的守门人。
数据流向总结:
- 进 VTEP:原始数据包撞上路由规则,被扔给
flannel.1。 - 封包:
flannel.1查表(FDB表),知道“去往 10.244.2.0/24 网段,对应的物理 IP 是 192.168.1.200”,于是加上 UDP 头和外层 IP 头。 - 出物理网卡:封装好的包变成了一个普通的 UDP 包,通过
eth0发出去。 - 物理网络传输:物理交换机/路由器只看外层 IP,把包路由到 Node B。
- 拆包:Node B 收到 UDP 8472 端口的包,内核把它剥皮,取出内层数据。
3. 网络工程师的痛点:MTU 问题 ⚠️
作为一个网络工程专业的学生,你一定能意识到这种“包中包”带来的一个严重隐患。
思考一下: 物理网卡的 MTU(最大传输单元)通常是 1500 字节。 如果你在 1500 字节的物理包里,硬塞进去一个 VXLAN 头(8字节)+ UDP 头(8字节)+ IP 头(20字节)= 50字节的额外开销 (Overhead)。
那么,内层的 Pod 还能发送 1500 字节的大包吗?
如果 Pod 里的应用傻乎乎地发了一个 1500 字节的包,会发生什么?
- 要么被分片(Fragment),导致性能极其低下。
- 要么直接被丢弃(如果设置了 DF 位)。
这就是为什么 K8s 网络必须要调整 MTU: 通常 Flannel 会自动把 cni0 和 Pod 网卡 (eth0) 的 MTU 设置为 1450(或者更小),给外层头部留出空间。
到这里,Pod-to-Pod 的通信链路(同节点直连、跨节点隧道)你应该已经打通了。
接下来有两个方向,你想了解哪个?
- CNI 的另一种流派 (BGP):像 Calico 这种不封装、直接改物理路由表的方案是怎么做到的?(这是纯三层路由,性能更好)。
- 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 转发。
- Pod A 发出:数据包源 IP
10.244.1.2,目的 IP10.244.2.2。 - 进入 Node 1:数据包通过
veth对,直接进入 Node 1 的内核网络栈。 - Node 1 查路由表:
- 内核看到目的 IP 是
10.244.2.2。 - 查表发现:
10.244.2.0/24 via 192.168.1.200(这是 Node 2 的物理 IP)。 - 关键点:这一条路由规则,正是刚才通过 BGP 学来的!
- 内核看到目的 IP 是
- 物理传输:
- Node 1 直接把原始的 IP 包发给 Node 2。
- 注意:物理交换机必须能看懂这个 IP 包。如果中间隔了路由器,物理路由器也必须运行 BGP 或者配置静态路由(这就是为什么云厂商通常不支持这种模式,因为你改不了他们的路由器)。
- 到达 Node 2:
- Node 2 收到包,查自己的路由表。
- 发现:
10.244.2.2 dev calixxxxx scope link。 - 意思是:“哦,这个 IP 就在我身上挂着的
calixxxxx接口那头。”
- 直达 Pod B:数据包直接塞进 Pod B。
4. 这种模式的优缺点 (vs Flannel VXLAN)
作为未来的网络专家,你能总结出这种 纯三层路由 (Pure Layer 3) 方案的优劣吗?
优势 (Pros):
- 性能无敌:没有封装/解封装的 CPU 损耗,几乎和物理网络一样快。
- 便于调试:你用
tcpdump抓包,看到的就是真实的 IP,没有那层讨厌的 UDP 外壳。 - 更符合网络直觉:支持标准的网络策略(Network Policy),比如 ACL。
劣势 (Cons) / 限制:
- 物理网络依赖:它要求所有节点必须在同一个二层网络 (L2) 下(才能直接把包扔给对方的 MAC 地址)。
- 思考:如果 Node 1 和 Node 2 跨了网段(中间有物理路由器),而物理路由器不认识 Pod 的 IP,包就会被丢弃。
- 解决:你需要把物理路由器也拉进 BGP 圈子(Tor 交换机做 BGP Peer),这在传统企业数据中心很常见,但在公有云上很难做到。
5. 一个小测验 📝
在 Calico 模式下,如果我用 route -n 在 Node 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 网络最难懂的“任督二脉”:
- Pod 内部:Pause 容器、Network Namespace、Veth Pair。
- 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,可能是通的,但你用 ifconfig 或 ip 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 链)拦截这个数据包:
- 拦截:内核发现目标 IP 是
10.96.0.1。 - 匹配:它去查
iptables的规则表(由kube-proxy维护)。 - 改写 (DNAT):规则说:“去往
10.96.0.1:80的包,请以 33% 的概率改写目标 IP 为10.244.1.5:8080(Pod A),33% 改为10.244.1.6:8080(Pod B)...” - 放行:改写后的数据包(目标变成了真实的 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 底层网络最核心的三个部分串起来了:
- Pod 网络:
- Veth Pair:连接 Pod 和宿主机。
- Bridge/Routing:连接同节点的 Pod。
- 跨节点通信 (CNI):
- Overlay (Flannel):UDP 封装 (VXLAN),对物理网络无感知,通用性强但有性能损耗。
- Underlay (Calico/BGP):纯路由转发,性能极致但依赖物理网络环境。
- 服务发现 (Service):
- iptables/IPVS:内核态 DNAT,把虚拟 VIP 映射到真实 Pod IP,实现负载均衡。
至此,K8s 的内网流量闭环已经完成。
最后一步挑战: 既然 Pod IP 和 ClusterIP 都是局域网/虚拟 IP,外面的用户(比如互联网上的浏览器)是怎么访问到 K8s 里面的服务的呢?
这涉及到 K8s 网络的出口大门。你想了解:
- NodePort:最原始暴力的方式(在宿主机开端口)。
- 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 的回答: “不,我会帮你转发过去!”
真实的数据包路径:
- 入站:用户包到达 Node A 的 30080 端口。
- 劫持:Node A 的
iptables/IPVS规则捕获该包。 - 查表:规则发现:“哦,这是去往 Service X 的,虽然我本机没有 Pod,但我知道 Node B 有。”
- SNAT (源地址转换):关键一步! Node A 把数据包的源 IP 改成 Node A 自己的
cni0或flannel.1IP(为了让回包能原路返回)。 - DNAT (目的地址转换):把目的 IP 改成 Pod-B 的 IP。
- 转发:数据包通过 Overlay 网络(VXLAN/IPIP)飞向 Node B。
- 到达:Pod-B 收到请求,处理完后回包给 Node A,Node A 再回包给用户。
这就是为什么你访问集群中任何一台机器的 30080 端口,都能访问到服务,无论 Pod 在哪儿。
3. 网络工程师视角的缺点 (Cons) 📉
虽然 NodePort 简单,但在企业级生产环境中,我们网络团队通常极其讨厌这种方式。
基于你的专业背景,你应该能一眼看出它的硬伤:
- 端口管理噩梦:
- 端口范围受限(30000-32767)。
- 你需要维护一张巨大的 Excel 表格来记录哪个应用用了哪个端口,避免冲突。这简直是倒退回了手工管理 IP 的时代。
- 安全隐患:
- 你在防火墙上需要开放一堆高位端口。安全团队会拿着扫描报告天天找你麻烦。
- 客户端配置麻烦:
- 如果 Node A 挂了,客户端的 IP 填的是 Node A,那服务就断了。
- 解决办法:你通常还得在这些 Node 前面再架设一台 硬件负载均衡器 (F5/LVS/Nginx),把流量轮询分发给后端所有 Node 的 30080 端口。
这就变成了:Client -> F5 (VIP) -> NodePort -> Service IP -> Pod IP。 是不是觉得链路太长、太折腾了?
总结与下一阶段
恭喜你!到目前为止,你已经把 Kubernetes 网络最核心的 “数据平面” (Data Plane) 全部打通了:
- 容器间:Localhost 通信。
- Pod 间:Overlay (VXLAN) 或 Underlay (BGP) 互通。
- Pod 到 Service:iptables/IPVS 做 DNAT 负载均衡。
- 外部到 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 Awww.example.com/web-> 转发给 Service Bblog.example.com-> 转发给 Service C
2. Ingress Controller:其实就是一个特殊的 Pod 🤖
初学者最容易晕的地方是:Kubernetes 自带 Ingress 吗? 答案是:不带。 K8s 只有 Ingress 的接口定义(规则),没有实现。
你需要自己安装一个 Ingress Controller。最常用的就是 Nginx Ingress Controller。
请注意,这个 Controller 本质上就是:
- 一个运行着 Nginx 的 Pod。
- 它里面有一个 Go 语言写的监听程序。
- 这个监听程序通过 K8s API 实时监控集群里的 Ingress 规则变化。
工作流程(动态配置): 当你写了一个 YAML 文件说:“把 abc.com 转发给 Service-A”。
- 监听:Ingress Controller 的 Go 程序检测到了这个新规则。
- 翻译:它把这个 K8s 规则瞬间翻译成 Nginx 的配置格式 (
nginx.conf)。server { listen 80; server_name abc.com; location / { proxy_pass ... } }
- 重载:它执行
nginx -s reload(或者通过 Lua 动态热加载),让配置生效。
这就实现了一个神奇的效果:你不需要 SSH 到服务器上去改 Nginx 配置文件,你只需要提交 YAML,Nginx 就自动变了。
3. 完整流量链路 (The Big Picture) 🗺️
让我们把之前学的所有知识串起来,看一个外网请求是如何到达 Pod 的:
- DNS 解析:用户访问
myapp.com,DNS 解析到了 K8s 集群的入口 IP(通常是负载均衡器的 VIP)。 - 入口网关:流量到达集群边缘,通过 NodePort (如 30080) 或者 Cloud LB,进入到 Ingress Controller Pod (也就是那个 Nginx Pod)。
- 七层路由 (Ingress):
- Nginx 拆开 HTTP 包头,看到
Host: myapp.com。 - 匹配配置,决定把包转发给后端 Service (
ClusterIP: 10.96.x.x)。
- Nginx 拆开 HTTP 包头,看到
- 四层转发 (Service):
- Linux 内核 (
iptables/IPVS) 拦截这个请求。 - 做 DNAT,把 Service IP 变成具体的 Pod IP (
10.244.1.5)。
- Linux 内核 (
- 二层/三层传输 (CNI):
- 通过 Flannel (VXLAN) 或 Calico (路由),跨主机把包送到目标 Pod 的容器里。
4. 网络工程师视角的 Ingress 优势
相比 NodePort,Ingress 简直是降维打击:
- 端口复用:整个集群只需要对外暴露 80/443 两个端口,就能支撑成千上万个服务。防火墙策略极其简单。
- TLS 卸载 (SSL Termination):
- 你把 HTTPS 证书配置在 Ingress 这一层。
- Ingress 解密后,通过明文 HTTP 转发给后端的 Pod。
- 好处:后端开发人员不需要在代码里处理复杂的证书逻辑,也不需要关心证书过期,运维统一管理。
- 高级流量控制:
- 金丝雀发布 (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 卸载 |

浙公网安备 33010602011771号