TCP/IP 底层基础
应用层数据如何被逐层封装
核心规则:从上往下,每一层都把上层整体当作自己的「Payload(载荷)」,加上本层头部;到接收端反向逐层解封装。
发送方的封装顺序(由上层至下层):
应用层 → 传输层(TCP/UDP)→ 网络层(IP)→ 数据链路层(以太网帧)→ 物理层(比特流)
平铺结构:
|
|
其中:
- 应用 Payload:业务原始数据(HTTP 网页、MySQL 查询、JSON 等,纯业务字节,没有协议头)
- TCP/UDP 眼里,应用数据就是它的 payload
- IP 眼里,整个 TCP+应用数据都是 IP 的 payload
- 以太网帧里,整个 IP 报文都是帧的 payload
标准无隧道、普通以太网帧结构
|
|
- DMAC:目标 MAC
- SMAC:源 MAC
- EtherType:
0x0800= IPv4;0x86DD= IPv6;0x8100= 带 VLAN 标签 - 后面就是三层 IP 头、传输层、应用层原始数据
- FCS:Frame Check Sequence,帧校验序列,算法是CRC‑32(循环冗余校验),做差错校验,检查这个帧在传输过程中,字节有没有被干扰、比特发生翻转、传错、损坏
关于MAC 地址
MAC 地址是二层(以太网帧)专属,只有当 IP 包被装进以太网二层帧的时候,才会套上 DMAC/SMAC。换句话说:
- 单独拎出来的 IP 报文(只有 IP 头 + TCP + 应用数据),本身没有 MAC!
- MAC 是以太网链路专属,只有当这个 IP 包要跑在以太网上时,才会外面套一层以太网帧(加上 DMAC/SMAC)。
- 如果不是以太网(比如串口链路),甚至根本不用 MAC 地址。
二层转发 vs 三层转发
不管是二层转发(交换机)还是三层转发(路由器),数据包长的样子不变,都是上面这套嵌套结构。二者区别不在于包结构,而在于:设备读取哪一层头部、依据什么字段决策转发。
二层转发(普通交换机,只看数据链路层)
只解析以太网头部,只关心 DMAC(目标 MAC):
- 不拆 IP 头、不看 IP 地址
- 行为:MAC 地址表匹配,直接从对应端口转发,原始帧从头到尾几乎不改
- 唯一改动:FCS 校验重算,源目 MAC 一般不变
适合:同一个广播域、同一个网段内部转发。
结构如下:同网段 PC1 → 交换机 → PC2:
|
|
三层转发(路由器 / Linux 内核路由,看网络层 IP 头)
设备会解析到 IP 头部,根据目的 IP查路由表。
注意点:IP 头里的源/目的 IP,全程端到端不变;但是【二层以太网头部的 DMAC/SMAC,每一跳都会重写!】
举个跨网段最简例子:PC1(192.168.1.10) → 路由器 R → PC2(10.0.0.20)
- PC1 发给 R:
- 以太网:DMAC=路由器入接口MAC,SMAC=PC1 MAC
- IP:SIP=192.168.1.10,DIP=10.0.0.20
- 路由器路由查找后,重新封装二层帧,转发给 PC2:
- 以太网:DMAC=PC2 MAC,SMAC=路由器出接口 MAC ,MAC地址变了
- IP:SIP=192.168.1.10,DIP=10.0.0.20 ,IP 完全不变
即:三层转发是IP 端到端不变,二层 MAC 逐跳刷新,用来完成直连链路传递。
二层 vs 三层转发对比
| 项目 | 二层转发(交换机) | 三层转发(路由器) |
|---|---|---|
| 解析到哪一层 | 只解析二层以太网头 | 解析到三层 IP 头 |
| 转发依据 | 目标 MAC 地址 | 目标 IP 地址 |
| 源目 IP | 不变 | 不变 |
| 源目 MAC | 不变 | 每一跳重新改写 |
| 适用范围 | 同一广播域/同一网段 | 跨网段、跨网关 |
| 数据包封装结构 | 不变 | 不变 |
VLAN——传统二层隔离技术
核心目的
在同一台物理交换机上,切出多个独立虚拟局域网,互相二层隔离
比如一台 48 口交换机:
- 端口 1-10 → VLAN10(业务 A)
- 端口 11-20 → VLAN20(业务 B)
- VLAN10 和 VLAN20 默认二层不通,必须经过路由器/三层网关才能互通。
vlan报文结构–打Tag
原始普通以太网帧(无 VLAN):
|
|
经过 802.1Q 打 VLAN 标签之后,在源 MAC 和 Type 之间插入4 字节 VLAN Tag:
|
|
拆解 4 字节 Tag:
- TPID:固定
0x8100,告诉交换机"这是 802.1Q VLAN 帧" - TCI 里面包含12bit VLAN ID
12bit最大4096个VLAN(0-4095,可用 1-4094),这是 VLAN 最致命短板!大规模虚拟化/云场景直接不够用。
交换机Access 口 / Trunk 口
示例拓扑:
|
|
- Access 口:接终端(PC/服务器),进出帧不带 VLAN 标签,交换机内部打上/剥掉 Tag
- Trunk 口:交换机之间互联,带标签转发,可以同时透传多个 VLAN
流程:
- PC1 发出去的包没有标签,进入 SW 的 Access 口,交换机自动打上 VLAN10 标签
- Trunk 链路带着标签传给 SW2
- SW2 从 Access 口发往 PC2 前,把标签剥掉
- 终端本身感知不到 VLAN 存在
vlan短板
- 最多 4096 个,公有云/大规模虚拟化远远不够
- VLAN 是二层标签,只能在同一个连续二层广播域里跑;跨三层路由网络不能直接透传 VLAN 标签(VLAN 标签不能被路由器原生转发,一旦经过三层网关,802.1Q 标签就会被终结)。
BGP 协议
基本概念
AS(自治系统):一个独立管理、统一管控的大网络,有唯一 AS 号。比如电信是一个 AS、联通一个 AS、阿里云一个 AS。
BGP 邻居(Peer):两台路由器之间手动建立 TCP 连接(端口 179),然后互相交换路由,注意不是自动发现!
注意BGP 只管传递网段路由条目,不负责把数据包直接转发;转发还是靠路由器路由表。OSPF是自动找邻居,但BGP必须手动指定对端 IP 建邻居。BGP 全称Border Gateway Protocol 边界网关协议,现在主流是 BGP4。OSPF 选路径看带宽、延迟;BGP 主要靠各种路径属性做策略选路,叫“策略驱动型路由协议”,不是单纯算最短路径。
BGP 路由条目里带什么
BGP 传递的不是单纯"目标网段+下一跳",它会带一整串路径属性,最最重要就是:
AS-PATH(AS 路径):记录这条路由沿途经过了哪些 AS,是 BGP 的灵魂
AS-PATH 就是一串 AS 号列表,数据包去目标网段,需要依次穿过这些 AS,有两个巨大作用:
-
防环路:收到路由,如果 AS-PATH 里面已经包含自己的 AS 号,直接拒收,杜绝环路!(OSPF 靠拓扑防环,BGP 靠 AS-PATH 防环,这是根本性差异)
-
选路:优先挑 AS-PATH 更短的路径(只是规则之一,不是唯一)
其他常用属性:
- Next-Hop:下一跳 IP,数据包发到这个 IP
- Local-Preference(本地优先级):AS 内部选路用,数字越大越优先,只在本 AS 传递,不会传给邻居 AS
- MED(多出口区分):发给邻居 AS,告诉对方"走我这个出口更优",数字越小越好
- Origin:路由来源,简单理解就是这条路由怎么进 BGP 的
BGP流程:AS1 和 AS2 怎么把路由互相传通
示例拓扑
|
|
65001、65002 属于私有 AS 号,测试随便用;公网运营商用公有 AS 号。
目标:让 AS1 里面的机器能访问 10.2.0.0/24,AS2 里面能访问 10.1.0.0/24
-
R1、R2 先建立 BGP 邻居
BGP 底层先用 TCP 179 端口建连接,三次握手成功之后,才成为 BGP 邻居,然后交换路由,邻居建立成功不代表路由可以自动交换,要把内网网段注入 BGP。
-
R1 把 AS1 内网 10.1.0.0/24 宣告进 BGP,发给 R2
R1 构造一条 BGP 路由发给 R2,这条路由信息简化表示:
1 2 3目标网段:10.1.0.0/24 Next-Hop:192.168.1.1 AS-PATH:65001即:目标 10.1.0.0/24,下一跳 192.168.1.1,要经过 AS65001
R2 收到这条路由:
-
检查 AS-PATH:里面没有自己 AS 号 65002 则 合法,收下
-
放进 BGP 表,优选之后注入本机全局路由表
-
-
同理,R2 把 AS2 内网 10.2.0.0/24 发给 R1
R2 发给 R1 的 BGP 路由:
1 2 3目标网段:10.2.0.0/24 Next-Hop:192.168.1.2 AS-PATH:65002R1 校验没问题,收下,写入路由表。
到这里,AS1 边界 R1 知道怎么去 10.2.0.0/24,AS2 边界 R2 知道怎么去 10.1.0.0/24,两个 AS 的网段就打通了
-
数据包实际转发路径
-
10.1.0.10 发包,目标 10.2.0.20,包先到 R1
-
R1 查路由表:去 10.2.0.0/24 下一跳是 192.168.1.2 ,丢给 R2
-
R2 收到包,查路由,转发到 AS2 内网 10.2.0.20
-
回程包反向走一样逻辑
-
AS-PATH 防环
再加一个AS说明环路,扩展拓扑:AS1(65001) <-> AS2(65002) <-> AS3(65003)
-
AS1 把 10.1.0.0/24 发给 AS2,AS-PATH=[65001]
-
AS2 再转发给 AS3,转发时把自己 AS 号加到 AS-PATH 最前面 ,AS-PATH=[65002, 65001]
规则:路由跨 AS 传递时,下一跳 AS 会把自身 AS 号追加进 AS-PATH 头部
-
假设 AS3 又把这条路由发回给 AS1,此时 AS-PATH=[65003, 65002, 65001]
-
AS1 收到后一看:AS-PATH 里面存在 65001(自己),则直接丢弃,环路直接扼杀,这就是 BGP 防环机制。
BGP 选路极简顺序
当一条目标网段收到多条 BGP 路由,路由器按顺序挑最优:
- 优先 Local-Preference 越大越好(本 AS 内部优先,不传外部)
- 优先本地注入的路由 > 从邻居学来
- 优先 AS-PATH 长度更短
- Origin:i(IGP 注入) 优于 incomplete
- MED 越小越好(只对比同一个相邻 AS 传来的路由)
- EBGP 路由优先于 IBGP
- 再看下一跳 IGP 开销……后面还有一堆
IBGP——同一个 AS 内部的 BGP
概述
EBGP是不同 AS 之间的 BGP,IBGP是同一个 AS 内部,两台路由器之间跑的 BGP。IBGP最核心规则是:IBGP 学到的路由,默认不能再转发给其他 IBGP 邻居(IBGP 水平分割)。IBGP 传递路由时,不会追加 AS 号到 AS-PATH!(这是和 EBGP 最明显区别)
EBGP / IBGP 核心对比
| 项目 | EBGP(跨 AS) | IBGP(同 AS 内) |
|---|---|---|
| 邻居位置 | 不同自治系统 | 同一个自治系统 |
| AS-PATH | 传递路由时,把自己 AS 号加到 AS-PATH 最前面 | 不会修改、不会追加 AS-PATH |
| 水平分割规则 | EBGP 没有这个限制(EBGP 学到可以传给其他 EBGP) | IBGP 核心:从 IBGP 邻居收到的路由,不能再发给另一个 IBGP 邻居 |
| Next-Hop 默认 | 发给 EBGP 对端时,下一跳改成自己和对端直连 IP | 默认不修改 Next-Hop,原样传递 |
IBGP数据流
基础拓扑示例入下:
|
|
- AS65002: 外部AS,网段10.2.0.0/24,R2 192.168.1.2在此AS
- AS65001:R1/R3 IBGP内部统一
- R1:192.168.1.1,和外部 AS65002 的 R2 建立 EBGP,拿到外部网段 10.2.0.0/24,内网口10.0.0.1
- R3:内网口是10.0.0.3,R1 和 R3 在同一个 AS 内部,建立 IBGP 邻居
目标: R1 从 EBGP 学到 10.2.0.0/24,通过 IBGP 传给 R3,让 AS 内部 R3 也能知道怎么去 10.2.0.0/24
步骤 1:R1 通过 EBGP,从 R2 学到路由
R2 发给 R1(EBGP 路由):
|
|
R1 收下,放进 BGP 表
步骤 2:R1 把这条路由通过 IBGP 发给同 AS 的 R3
⚠️ IBGP 转发规则生效AS-PATH 原样不动,不追加 6500
R1 发给 R3 的 IBGP 路由条目:
|
|
那么问题来了 :
R3 收到路由:想去 10.2.0.0/24,下一跳是 192.168.1.2
R3 本地必须要有路由可达 192.168.1.2,否则这条 BGP 路由在 R3 上无效、不进路由表!
两种解决办法:
- 在 R1 配置
next-hop-self(最常用) R1 再发给 R3 的时候,自动把 Next-Hop 改成 R1 自己10.0.0.1,R3 内部自然可达。 - 把边界直连网段 192.168.1.0/24 宣告进 AS 内部 OSPF,让 R3 能到达 192.168.1.2
打开 next-hop-self 之后,R1 发给 R3 的 IBGP 路由变成:
|
|
R3 校验下一跳可达,路由生效
IBGP 水平分割
扩展拓扑,演示水平分割:
|
|
默认规则:R1(EBGP 学到路由),并通过IBGP 发给 R3,允许;R3(从 IBGP 邻居 R1 学到的路由)再通过 IBGP 转发给 R4,默认禁止。
为什么要有这个规则?IBGP内部AS号一样,不追加 AS-PATH,没法靠 AS-PATH 防环,EBGP 靠 AS-PATH 天然防环;IBGP 没有这个机制,所以强制加一条硬性枷锁:IBGP 收到的路由,不能再传给别的 IBGP 邻居,避免 AS 内部路由环路。
问题又来了:一个 AS 里面很多台路由器怎么办?
如果 AS 内部有 4 台路由器:R1、R3、R4、R5,如果严格遵守 IBGP 水平分割,最简单粗暴方案:全互联 IBGP,也就是两两之间全部建立 IBGP 邻居:R1-R3、R1-R4、R1-R5、R3-R4、R3-R5、R4-R5,两两建连宣告自己的网络,不存在环路,也能全部打通。
缺点显而易见:节点一多,邻居数量爆炸,N 台设备需要 N*(N-1)/2 个 IBGP 邻居,没法大规模部署。所以工程上衍生两种方案,专门解决 IBGP 全互联痛点:
- 路由反射器 RR(Route Reflector)
- BGP 联邦 Confederation(相对少用,大型网络)
简单说明联邦 Confederation,就是把一个大 AS,逻辑上切割成多个小子 AS,子 AS 之间跑 EBGP、外层对外还是同一个 AS 号,一般超大型骨干网才会用。
路由反射器 RR
角色划分:
-
RR 路由反射器(中心点)
-
Client 客户端路由器
规则:
-
RR 收到 Client 的 IBGP 路由,可以反射转发给其他 Client(打破原生 IBGP 水平分割限制)
-
Client 和 Client 之间不需要建立 IBGP 邻居,只需要和 RR 建邻居
即:RR 做转发枢纽,不用全网全互联。但是需要注意,RR 反射路由依然不会追加 AS-PATH,只是放宽转发限制,不是改变底层原理。
举个极简 RR 例子:
|
|
R1 把外部路由发给 RR,RR 反射给 R3/R4/R5, R3/R4/R5 只和 RR 建立 IBGP,互相不用建邻居,大幅减少邻居数量。
总结 RR:专门用来解决 IBGP 全互联爆炸问题,可控地打破 IBGP 水平分割。
IBGP 路由防环方式:
- 原生 IBGP 水平分割(默认):IBGP 学到的路由,禁止转发给其他 IBGP 邻居
- RR 反射场景额外防环规则:
- 路由反射时,会带上
Originator-ID(路由原始始发路由器)+Cluster-List(反射簇列表) - 如果收到路由,Originator-ID 等于自己,或者 Cluster-List 包含本簇 ID,直接丢弃,防止反射环路
- 路由反射时,会带上
IBGP 选路说明
IBGP 路由和 EBGP 路由放在一起比较优先级时:EBGP 路由天生优于 IBGP 路由(选路规则靠前)
举个例子:同一条网段 10.2.0.0/24,R1 同时收到:
-
一条 EBGP 路由(来自 AS2)
-
一条 IBGP 路由(来自同 AS 另一台路由器)
路由器会优选 EBGP 那条。
其他属性(Local-Pref、MED、AS-PATH 长度等)IBGP 完全沿用 BGP 通用选路逻辑。
Local-Preference 这个属性就是设计给 IBGP 场景用的:在本 AS 内部所有 IBGP 邻居之间传递,用来统一控制整个 AS 的出站选路,不会传给外部 EBGP 邻居。
IBGP 核心规则总结
-
IBGP = 同一个 AS 内部的 BGP 邻居,底层 TCP端口 179
-
IBGP 转发路由:不追加 AS 号,AS-PATH 保持原样
-
默认硬规则:IBGP 学到的路由,不能再转发给别的 IBGP 邻居(水平分割)
-
IBGP 传递路由默认不改 Next-Hop,极易下一跳不可达;工程常规配置 next-hop-self
-
IBGP 大规模部署解决方案:路由反射器 RR(主流)、BGP 联邦(小众)
-
IBGP 防环不靠 AS-PATH:原生靠水平分割;RR 场景靠 Originator-ID + Cluster-List
-
Local-Preference 作用域就是整个 AS 内部,随 IBGP 传递,用来控制本 AS 出口方向
VXLAN——跨三层的虚拟大二层
VLAN & VXLAN
VLAN:二层隔离,给同一个交换机里打标签,范围局限在本地二层域;标准 802.1Q VXLAN:Overlay 隧道(在原有底层网络Underlay之上,再虚拟一层网络),把二层帧封装在 UDP 包里,跨三层网络传输,解决 VLAN 数量不够、跨机房跨云二层迁移问题
VLAN 是传统物理二层标签;VXLAN 是"VLAN 的跨三层升级版隧道"
核心概念
-
VNI(VXLAN Network Identifier):对应 VLAN 的 VLAN ID,但是 24bit,2^24 = 16777216 个,一千六百多万
-
VTEP(VXLAN Tunnel End Point):VXLAN 隧道两端的封装/解封装节点,在 K8s Flannel 里,每个 Node 就是一个 VTEP
-
Underlay 网络:底层承载网络(就是 Node 宿主机之间可达的 IP 网络,三层)
-
Overlay 网络:覆盖网络,此处理解为VXLAN 虚拟出来的大二层,Pod 属于这个 Overlay
完整报文封装结构
顺序:原始二层帧 → VXLAN 头 → UDP 头 → 外层 IP 头 → 外层 MAC 头
|
|
按Flannel场景说明封装过程,PodA(node1)发包给 PodB(node2):
-
node1 Flannel VTEP 拿到 Pod 原始二层帧
-
套 VXLAN 头,打上 VNI
-
套 UDP 头 4789
-
套外层 IP:源 node1 宿主机 IP,目的 node2 宿主机 IP
-
底层物理网络按照外层 IP 路由,把包送到 node2
-
node2 的 VTEP 解封装,剥掉外层 UDP/IP/MAC,拿出内层原始二层帧,交给 PodB
关键点:
底层 Underlay 只需要node1 和 node2 宿主机三层 IP 可达就行,不需要大二层、不需要透传 VLAN。哪怕 node1 和 node2 跨网关、跨机房、跨云,只要 IP 能通,VXLAN 隧道就能跑。
对比 VLAN:VLAN 标签是插在二层帧中间;VXLAN 是把整个内层二层帧打包塞进 UDP 包里走三层
VXLAN 组播 / 单播(Flannel 两种模式)
VXLAN 本身要解决一个问题:ARP 广播(二层网络里查 MAC)
-
标准传统 VXLAN:依靠 Underlay 组播,同一个 VNI 的所有 VTEP 加入同一个组播组,ARP 广播泛洪给所有 VTEP。缺点是底层网络要支持组播,很多公有云不开放组播,所以 Flannel 默认不用这个
-
Flannel VXLAN(直接路由/单播模式):预先知道所有对端 VTEP IP,ARP 不泛洪,直接点对点单播封装
注意:VXLAN 本质依然是大二层,广播/未知单播会泛洪,规模很大之后广播风暴风险会上来
VXLAN 优缺点
优点
-
VNI 1600 万+,彻底摆脱 4096 VLAN 限制
-
跨三层 Underlay 构建虚拟大二层,云/虚拟化标配
-
公有云友好,只需要节点 IP 互通,不用底层改路由、不用 BGP
缺点
-
封装开销:额外 50 字节左右头部(VXLAN+UDP+外层 IP),小包场景 CPU 损耗明显
-
属于 Overlay 大二层,广播泛洪风险,不适合超大规模集群
-
原生只是连通,没有网络策略
VXLAN 有硬件和软件实现:
- 硬件 VXLAN:高端交换机/路由器(华为、H3C、Cisco 数据中心款)内置 VXLAN 处理芯片,VTEP 跑在硬件里,不需要服务器上装 agent,多用于传统大型虚拟化/云厂商底层。
- Linux 内核 VXLAN(K8s Flannel 这种)**:**内核原生支持 VXLAN 模块,但是必须由用户态程序(flanneld/cilium-agent)来控制配置。
Linux 内核自带 VXLAN 驱动(
vxlan.ko),封装解包是内核做,flanneld 不做报文封装,它只干控制面:-
监听 apiserver,拿到所有 Node、PodCIDR 信息
-
在本机自动创建 vxlan 网卡(比如
flannel.1) -
动态填充内核 FDB 转发数据库(把"对端 VTEP IP ↔ MAC"写到内核)
-
维护路由
-
IPIP——轻量三层隧道
IPIP(IP-in-IP)是一种最简单的三层隧道:把一个 IP 包,外面再套一层 IP 头。
它**不保留内层二层 MAC 头,不是大二层。**这是和 VXLAN 本质区别,VXLAN 要保留完整二层帧、虚拟大二层,IPIP 只封装三层 IP 包,直接砍掉内层二层。
IPIP 报文结构
|
|
IPIP 关键:只有外层 MAC,不存在内层 MAC,没有虚拟大二层。这就是为什么 IPIP 不需要跨节点 ARP,而 VXLAN 必须处理 ARP 代理。
IPIP vs VXLAN 差异
| 项目 | IPIP | VXLAN |
|---|---|---|
| 封装内容 | 只封装三层 IP 包 | 完整封装内层二层以太网帧 |
| 内层 MAC | 没有 | 有 |
| 传输协议 | IP 协议号 4(直接 IP over IP) | UDP 4789 |
| VNI/标识 | 无 | 24bit VNI |
| 模型 | 三层隧道 | 大二层 Overlay |
| 额外头部开销 | 约 20 字节 | 约 50 字节 |
| 跨节点 ARP | 不需要 | 需要(ARP 代理或组播泛洪) |
Calico——基于 BGP 的 CNI
Calico 是基于纯三层路由(BGP)的 K8s CNI,默认方案没有 Overlay 封装,Pod 之间互通靠路由表转发。
概念说明:
-
CNI:容器网络接口,K8s 用来给 Pod 分配网络、连通的插件标准。解耦了容器运行时(如 Kubernetes 或 Containerd)与底层网络实现,让任何网络插件都可以通过统一的 JSON 格式配置和管理网络,点此参考官方仓库
-
Calico 两大核心组件:Felix + BGP Client(bird)+ etcd
Calico 支持两种模式:
-
BGP 模式(默认经典模式,无封装,三层直路由)
-
IPIP/VXLAN Overlay 隧道模式(跨子网节点、云厂商禁止 BGP 时需要)
Calico 核心组件
Felix
Calico 的大脑代理,最核心组件 职责:
- 监听 apiserver/etcd:看新增 Pod、删除 Pod、NetworkPolicy 网络策略
- 在宿主机 Linux 上编程:配置路由表、iptables/nftables
- 通知本机 BIRD:要宣告哪些 Pod 网段路由出去
Felix 本身不跑 BGP,只管写内核路由+防火墙规则。
BIRD
就是一个轻量级 BGP 路由器实现(BIRD Internet Routing Daemon) Felix 告诉 BIRD:把本机 PodCIDR 10.244.1.0/24 宣告给集群内其他节点 BIRD,所有节点 BIRD 之间建立 IBGP 邻居,互相交换 PodCIDR 路由。所有 BIRD 属于同一个 AS,互相 IBGP,传递路由时不修改 AS-PATH
etcd
存储 Calico 全部状态,两种部署形态:
-
Calico 直连独立 etcd(老方案)
-
K8s APIServer 作为数据存储(现在默认,CalicoDatastore=kubernetes)
存的内容:节点信息、PodCIDR、BGP 配置、NetworkPolicy、IPPool等
calicoctl
命令行管理工具,用来手动改 BGP 配置、查看路由、BGP 邻居状态等,参考官网文档
数据流
示例 K8s 拓扑
|
|
核心思想: 每个 Node 上的 Felix+BIRD,互相建立 BGP 邻居,把本节点的 PodCIDR 当作一条 BGP 路由,发给其他所有节点,整个 K8s 集群可以看成一个自治系统 AS(比如 AS 64512),每个 K8s Node,等价于一台小型 BGP 路由器,所有 node 上的 bird 进程互相建立 IBGP 邻居。
前面提到的IBGP 水平分割、next-hop,在 Calico 里面全部存在
BGP 无 overlay 模式步骤
步骤 1:集群初始化,node1/node2 的 BIRD 建立 IBGP 邻居(TCP 179),同 AS
步骤 2:node1 把自己 PodCIDR 10.244.1.0/24 通过 BGP 宣告出去
node1-BIRD 发给 node2-BIRD 的 BGP 路由条目:
|
|
同理 node2-BIRD 发给 node1:
|
|
步骤 3:每个节点内核路由表自动写入这条路由
node1 宿主机路由表新增:
10.244.2.0/24 via 10.0.0.12
node2 宿主机路由表新增:
10.244.1.0/24 via 10.0.0.11
步骤 4:PodA 发包给 PodB
-
PodA(10.244.1.10)发包,目标 IP 10.244.2.20
-
包先出 Pod 的 veth 虚拟网卡,到达 node1 宿主机内核
-
node1 查内核路由表:去 10.244.2.0/24 的下一跳 10.0.0.12(node2 宿主机 IP)
-
直接走底层物理网络/交换机,裸 IP 包转发到 node2
-
node2 收到包,查本地路由,转给 PodB
全程没有封装,标准三层 IP 路由
对比 Flannel VXLAN:Flannel 会在宿主机之间包一层 UDP 隧道头,对上层透明,有额外开销。
Calico BGP 两种对等模型
-
Node-to-Node Mesh(全互联 IBGP,默认)
由于IBGP水平分割限制,集群里所有节点 BIRD 两两建立 IBGP 邻居,如3 节点集群:node1<->node2,node1<->node3,node2<->node3
缺点:节点数量较多后,BGP 邻居数量爆炸,BIRD 压力大,仅适合小规模集群
-
Route Reflector(RR 路由反射器)
不需要所有节点两两 IBGP,挑选 1~3 台节点做 Calico BGP RR,其他普通节点只和 RR 建立 IBGP 邻居,普通节点之间不建邻居。
|
|
-
Client 把本机 PodCIDR 发给 RR
-
RR 反射路由给所有其他 Client
完美解决 IBGP 全互联爆炸问题,生产 100+ 节点集群标配
上面裸 BGP 有个硬性前提:所有 K8s Node 宿主机之间,三层可达,网络设备允许 BGP、允许自定义路由。
很多公有云(阿里云/AWS)底层网络不允许你自定义私网 BGP、不允许随意注入路由,这种场景不能直接裸 BGP,Calico 开启 IPIP 隧道,IPIP 是简单 IP-in-IP 封装,原始 IP 包外面再套一层宿主机 IP 头,点对点隧道,此时路由逻辑不变,只是包在跨节点时加了封装,底层网络只需要宿主机互通即可。
Calico IPIP 模式说明
Calico IPIP 依然是「三层路由优先」架构,控制平面还是标准 BGP(BIRD IBGP 交换 PodCIDR);IPIP 只是一个可选的三层隧道封装,不保留内层二层 MAC 头,不是大二层
继续沿用之前拓扑:
- node1:10.0.0.11,PodCIDR 10.244.1.0/24
- node2:10.0.0.12,PodCIDR 10.244.2.0/24
- Calico IPIP 开启
Calico IPIP 完整数据流(PodA访问PodB)
-
PodA 发包:源 10.244.1.10,目标 10.244.2.20(纯 IP 包,不需要 ARP 跨节点!)
因为 Calico 是三层路由模型,跨节点不需要二层 ARP 广播,根本不需要知道远端 Pod MAC! 这和 Flannel VXLAN 完全两条路。
-
node1 内核路由表(由 BIRD+BGP 同步过来):
10.244.2.0/24 via 10.0.0.12,并且命中 IPIP 策略:去往这个网段需要封装 -
内核 ipip 模块封装:内层 PodIP 包外面套一层外层宿主机 IP 头
-
底层网络把封装包送到 node2
-
node2 内核解封装,剥离外层 IP,拿到原始 Pod 之间 IP 包,直接转发给 PodB
Calico NetworkPolicy
底层本质
Felix 监听 K8s NetworkPolicy 资源,翻译成宿主机上 iptables/nftables 规则,在节点上直接拦截 Pod 流量。
简单示例
声明策略:只允许 PodA 访问 PodB,其他全部拒绝,Felix 在 node2 生成 iptables 规则,源 IP 不是 10.244.1.10 直接丢弃。
常见问题
-
BGP Mesh 模式节点量大之后 BIRD 负载高 上 Calico BGP Route Reflector
-
底层网络不支持自定义路由(公有云默认限制) 开启 IPIP 隧道
-
节点 BGP 邻居起不来:防火墙是否封禁 TCP 179 端口
-
跨节点 Pod 不通,但宿主机互通:
大概率底层网络丢弃 Calico 注入的 PodCIDR 路由,需要切 IPIP
-
不要混淆:Calico 的 BGP 只负责宣告 Pod 网段,不管 Service,Calico 只管 Pod 到 Pod 底层连通。