Kubernetes 容器网络学习笔记
Kubernetes 容器网络学习笔记
本文档是基于容器网络课程整理的系统性学习笔记,面向运维工程师/SRE,帮助深入理解 Kubernetes 容器网络。
📚 目录
第一部分:网络基础
- 第1章 TCP/IP 协议栈
- 第2章 Linux 网络命名空间
- 第3章 Veth Pair 虚拟网络设备
- 第4章 Linux Bridge 网桥
- 第5章 路由与 ARP
- 第6章 iptables 防火墙
- 第7章 IPVS 负载均衡
第二部分:Kubernetes 网络核心
- 第8章 Docker 网络模型
- 第9章 Kubernetes 网络模型
- 第10章 CNI 容器网络接口
- 第11章 Pod 网络
- 第12章 Service 网络
- 第13章 DNS 与服务发现
- 第14章 Ingress 与 Gateway API
第三部分:Cilium CNI 深度实践
- 第15章 Cilium 概述与架构
- 第16章 Cilium 安装与部署
- 第17章 Cilium 网络策略
- 第18章 Cilium Observability
- 第19章 Cilium Service Mesh
- 第20章 Cilium Cluster Mesh
- 第21章 Cilium BGP
- 第22章 Cilium Egress Gateway
- 第23章 Cilium Bandwidth Manager
- 第24章 Cilium Host Firewall
- 第25章 Cilium 透明加密
- 第26章 Cilium NAT46/NAT64
- 第27章 Cilium Local Redirect Policy
- 第28章 Cilium Health Checking
- 第29章 Cilium 性能调优
- 第30章 Cilium 故障排除
- 第31章 Cilium 升级与运维
- 第32章 Cilium 生产最佳实践
- 第33章 Cilium eBPF Internals
- 第34章 Cilium Datapath
- 第35章 Cilium Identity 与安全
- 第36章 Cilium IPAM
- 第37章 Cilium LB IPAM
- 第38章 Cilium Envoy
- 第39章 Cilium Tetragon
- 第40章 Cilium Hubble 深度解析
- 第41章 Cilium 与 Istio 集成
- 第42章 Cilium 未来展望
第四部分:Flannel CNI 实践
- 第43章 Flannel 概述与架构
- 第44章 Flannel VXLAN 模式
- 第45章 Flannel VXLAN DirectRouting
- 第46章 Flannel Host-GW 模式
- 第47章 Flannel UDP 模式
- 第48章 Flannel Alloc 模式
- 第49章 Flannel 多网卡与 Public IP
- 第50章 Flannel IPsec 模式
- 第51章 Flannel WireGuard 模式
第五部分:高性能 CNI(Multus/SR-IOV/DPDK)
- 第52章 Multus 多网卡方案
- 第53章 Multus IPVLAN L2 模式
- 第54章 Multus IPVLAN L3 模式
- 第55章 Multus IPVLAN SBR 模式
- 第56章 IPVLAN-SBR 深度解析
- 第57章 MACVLAN-SBR 实践
- 第58章 Multus-with-SRIOV-Kernel
- 第59章 Multus-with-SRIOV-DPDK-VPP
- 第60章 K8s-CNI-IPAM 机制详解
第一部分:网络基础
第1章 TCP/IP 协议栈
🎯 学习目标
- 理解 OSI 七层模型与 TCP/IP 四层模型的区别与联系
- 掌握网络分层架构的设计思想
- 理解 TCP 和 UDP 协议的核心差异
- 学会使用 tcpdump 和 Wireshark 进行网络抓包分析
1.1 OSI 七层模型与 TCP/IP 四层模型
背景
在计算机网络发展早期,不同厂商的网络设备和协议互不兼容,导致设备之间无法互联互通。为了解决这个问题,国际标准化组织(ISO)提出了 OSI(Open Systems Interconnection)参考模型,将网络通信划分为七个层次,每一层负责特定的功能。
[!NOTE]
OSI 模型是一个理论参考模型,而实际生产环境中更多使用的是简化后的 TCP/IP 四层模型。
原理
分层架构的核心思想:术业有专攻
网络分层的本质是解耦,使得:
- 每一层专注于自身的协议实现
- 不同厂商的设备只要遵循同一标准,就可以互联互通
- 便于问题定位和排查
图解说明:
上图展示了 OSI 七层模型与 TCP/IP 四层模型的对应关系:
- OSI 的应用层、表示层、会话层统一为 TCP/IP 的应用层
- OSI 的传输层对应 TCP/IP 的传输层
- OSI 的网络层对应 TCP/IP 的网络层
- OSI 的数据链路层、物理层合并为 TCP/IP 的网络接口层
各层功能与核心协议
| 层级 | OSI 模型 | TCP/IP 模型 | 核心功能 | 代表协议/设备 |
|---|---|---|---|---|
| 7 | 应用层 | 应用层 | 为用户提供网络服务 | HTTP、FTP、DNS、DHCP |
| 6 | 表示层 | 应用层 | 数据编解码、压缩、加密 | SSL/TLS、JPEG |
| 5 | 会话层 | 应用层 | 建立、管理、终止会话 | RPC、NetBIOS |
| 4 | 传输层 | 传输层 | 端到端的可靠传输 | TCP、UDP、SCTP |
| 3 | 网络层 | 网络层 | 路由选择、逻辑寻址 | IP、ICMP、路由器 |
| 2 | 数据链路层 | 网络接口层 | 物理寻址、帧封装 | MAC、交换机、ARP |
| 1 | 物理层 | 网络接口层 | 比特流传输 | 光纤、双绞线、无线 |
要点解读:
对于容器网络学习,我们需要重点关注以下三层:
- 传输层(L4):TCP/UDP 端口,这是应用开发最常接触的层
- 网络层(L3):IP 地址、路由表,跨节点通信主要依赖此层
- 数据链路层(L2):MAC 地址、交换机,同网段通信的基础
关键点
[!IMPORTANT]
运维排障思路:当服务不可达时,应该逐层排查:
- 物理层:网线是否连接正常?
- 数据链路层:MAC 地址是否正确?ARP 表是否正确?
- 网络层:IP 是否可达?路由表是否正确?
- 传输层:端口是否监听?防火墙是否放行?
- 应用层:服务是否正常启动?
1.2 数据封装与解封装流程
背景
当应用程序需要发送数据时,数据会从上层向下层逐级传递,每经过一层都会添加该层的头部信息(Header),这个过程称为封装(Encapsulation)。接收端则进行相反的操作,称为解封装(Decapsulation)。
原理
图解说明:
数据在发送端逐层封装,每层添加自己的协议头:
- 应用层:生成原始数据
- 传输层:添加 TCP/UDP 头(包含源端口、目的端口)
- 网络层:添加 IP 头(包含源 IP、目的 IP、TTL)
- 数据链路层:添加 MAC 头和尾(包含源 MAC、目的 MAC)
- 物理层:转换为 0/1 比特流进行传输
数据包结构示意
+------------------+----------+--------+----------+-----------------+
| 以太网帧头 | IP 头 | TCP 头 | 数据 | 以太网帧尾 |
| 14 字节 | 20 字节 | 20 字节| N 字节 | 4 字节 |
+------------------+----------+--------+----------+-----------------+
|←-- 二层 MAC --→|←-三层IP-→|←-四层-→| |
代码说明:
以上结构展示了一个完整的以太网帧:
- 以太网帧头(14 字节):包含目的 MAC(6B)+ 源 MAC(6B)+ 类型(2B)
- IP 头(20 字节):包含版本、TTL、源 IP、目的 IP 等
- TCP 头(20 字节):包含源端口、目的端口、序列号、标志位等
- 以太网帧尾(4 字节):FCS 帧校验序列
1.3 TCP 协议详解
背景
TCP(Transmission Control Protocol,传输控制协议)是一种面向连接的、可靠的传输层协议。它通过三次握手建立连接、四次挥手断开连接,并提供流量控制、拥塞控制等机制来保证数据可靠传输。
原理
1.3.1 套接字(Socket)概念
套接字 = IP 地址 + 端口号
例如:192.168.1.100:8080 表示一个套接字
[!NOTE]
TCP 端口和 UDP 端口是独立的。同一个端口号(如 53)可以同时被 TCP 和 UDP 监听,因为它们属于不同的协议。
1.3.2 TCP 三次握手
图解说明:
三次握手的过程:
- 第一次握手:客户端发送 SYN 包(同步序列号),进入 SYN_SENT 状态
- 第二次握手:服务端收到 SYN 后,回复 SYN+ACK 包,进入 SYN_RCVD 状态
- 第三次握手:客户端收到后回复 ACK,双方进入 ESTABLISHED 状态,连接建立
1.3.3 TCP 标志位
| 标志位 | 名称 | 作用 |
|---|---|---|
| SYN | Synchronize | 发起连接,同步序列号 |
| ACK | Acknowledge | 确认收到数据 |
| FIN | Finish | 请求关闭连接 |
| RST | Reset | 重置连接(异常终止) |
| PSH | Push | 立即推送数据给应用层 |
| URG | Urgent | 紧急数据 |
1.3.4 MSS 与分片机制
MSS(Maximum Segment Size):最大报文段长度,指 TCP 数据部分的最大长度。
MTU = 1500 字节(以太网默认)
MSS = MTU - IP头(20) - TCP头(20) = 1460 字节
要点解读:
[!IMPORTANT]
为什么分片在 TCP 层做而不是 IP 层?
- TCP 有序列号,可以精确知道哪个片丢失,只需重传丢失的片
- IP 没有重传机制,如果一个分片丢失,需要重传整个原始数据
- 在 TCP 层分片可以减少重传开销
1.3.5 TCP 卸载技术
当数据量较大时,TCP 分片等操作会消耗大量 CPU 资源。现代网卡支持将这些操作卸载(Offload)到硬件,减轻 CPU 负担:
| 技术 | 全称 | 作用 |
|---|---|---|
| TSO | TCP Segmentation Offload | 将 TCP 分片卸载到网卡 |
| GSO | Generic Segmentation Offload | 通用分片卸载 |
| GRO | Generic Receive Offload | 接收端合并小包 |
| LRO | Large Receive Offload | 大包接收卸载 |
代码说明:
查看网卡 offload 状态:
# 查看网卡 offload 配置
ethtool -k eth0
# 输出示例:
# tcp-segmentation-offload: on
# generic-segmentation-offload: on
# generic-receive-offload: on
# large-receive-offload: off
1.4 UDP 协议特点
背景
UDP(User Datagram Protocol,用户数据报协议)是一种无连接的、不可靠的传输层协议。它没有握手过程,发送数据时不保证对方能收到。
TCP 与 UDP 对比
| 特性 | TCP | UDP |
|---|---|---|
| 连接性 | 面向连接 | 无连接 |
| 可靠性 | 可靠(有确认、重传) | 不可靠 |
| 有序性 | 保证顺序 | 不保证 |
| 速度 | 较慢(握手开销) | 较快 |
| 头部大小 | 20 字节 | 8 字节 |
| 应用场景 | HTTP、数据库、文件传输 | DNS、视频流、游戏 |
图解说明:
- TCP:需要三次握手建立连接后才能传输数据
- UDP:直接发送数据,无需建立连接,简单高效但不可靠
1.5 使用 nc 工具进行网络测试
背景
nc(netcat)是一个功能强大的网络工具,可以用于 TCP/UDP 端口监听、网络调试、数据传输等场景。
常用命令
# ============ TCP 监听与连接 ============
# 服务端:监听 TCP 8088 端口(-l 表示 listen,-k 表示保持监听)
nc -lk 192.168.1.100 8088
# 客户端:连接 TCP 8088 端口
nc 192.168.1.100 8088
# ============ UDP 监听与连接 ============
# 服务端:监听 UDP 8088 端口(-u 表示使用 UDP)
nc -lu 192.168.1.100 8088
# 客户端:连接 UDP 8088 端口
nc -u 192.168.1.100 8088
代码说明:
-l:listen 模式,作为服务端监听端口-k:keep-open,连接断开后保持监听-u:使用 UDP 协议(默认为 TCP)
1.6 使用 tcpdump 和 Wireshark 进行抓包分析
背景
网络抓包是排查网络问题的核心技能。tcpdump 是 Linux 下的命令行抓包工具,Wireshark 是图形化的抓包分析工具。
tcpdump 常用命令
# 基础抓包:在 eth0 接口抓包并保存到文件
tcpdump -i eth0 -w capture.pcap
# 抓取指定端口的 TCP 包
tcpdump -i eth0 port 8088
# 抓取指定 IP 的包
tcpdump -i eth0 host 192.168.1.100
# 详细显示包内容(-nn 不解析主机名和端口名)
tcpdump -i eth0 -nn -vv port 8088
代码说明:
| 选项 | 说明 |
|---|---|
-i |
指定网络接口 |
-w |
写入文件(.pcap 格式) |
-nn |
不解析主机名和端口名 |
-vv |
详细输出 |
Wireshark 过滤技巧
| 过滤条件 | 说明 |
|---|---|
tcp.port == 8088 |
过滤 TCP 8088 端口 |
tcp.stream eq 0 |
过滤第一个 TCP 流 |
tcp.flags.syn == 1 |
过滤 SYN 包 |
tcp.len == 0 |
过滤无数据的 TCP 包(握手包) |
ip.addr == 192.168.1.100 |
过滤指定 IP |
1.7 实战:TCP 与 UDP 抓包分析
实验目的
通过 nc 工具模拟 TCP 和 UDP 通信,使用 tcpdump 抓包,用 Wireshark 分析数据包结构。
实验步骤
步骤一:TCP 抓包实验
# 终端 1:启动 tcpdump 抓包
tcpdump -i eth0 -w tcp_capture.pcap port 8088
# 终端 2:启动 TCP 服务端
nc -lk 192.168.1.100 8088
# 终端 3:启动 TCP 客户端并发送数据
nc 192.168.1.100 8088
# 输入:hello CNI
步骤二:用 Wireshark 分析 TCP 包
使用 Wireshark 打开 tcp_capture.pcap,可以看到:
- 三次握手:前三个包(SYN → SYN+ACK → ACK)
- 数据传输:带有 PSH 标志的包,包含 "hello CNI" 数据
- 四次挥手:断开连接的 FIN 包
步骤三:UDP 抓包实验
# 终端 1:启动 tcpdump 抓包
tcpdump -i eth0 -w udp_capture.pcap udp port 8088
# 终端 2:启动 UDP 服务端
nc -lu 192.168.1.100 8088
# 终端 3:启动 UDP 客户端并发送数据
nc -u 192.168.1.100 8088
# 输入:hello UDP
步骤四:对比分析
| 对比项 | TCP | UDP |
|---|---|---|
| 握手过程 | 有三次握手 | 无握手 |
| 数据发送 | 有确认机制 | 直接发送 |
| 包数量 | 多(握手 + 数据 + 确认) | 少(仅数据包) |
📝 章节小结
本章介绍了网络通信的基础知识:
- OSI 七层模型与 TCP/IP 四层模型:理解网络分层架构的设计思想,掌握各层的职责
- 数据封装与解封装:理解数据在网络中传输时的打包和拆包过程
- TCP 协议:掌握三次握手、四次挥手、标志位、MSS 分片等核心概念
- UDP 协议:理解无连接、不可靠传输的特点和应用场景
- 网络抓包:学会使用 tcpdump 和 Wireshark 进行网络问题分析
[!TIP]
学习建议:
- 动手实践 nc 和 tcpdump 命令
- 使用 Wireshark 分析真实的网络包
- 理解每一层的头部信息,这对后续学习容器网络非常重要
第2章 IP 地址与 MAC 地址精讲
🎯 学习目标
- 掌握 IP 地址的点分十进制表示与二进制转换
- 理解有类地址(A/B/C/D/E)与无类地址(CIDR)的区别
- 熟练掌握 VLSM(可变长子网掩码)的计算方法
- 理解 MAC 地址的结构与 OUI 厂商标识
- 掌握二层交换与三层路由的核心区别
- 理解路由转发过程中 IP 和 MAC 地址的变化规律
2.1 IP 地址基础
背景
IP 地址是网络层的核心标识,用于在互联网中唯一标识一台设备的逻辑位置。与 MAC 地址不同,IP 地址是逻辑地址,可以根据网络规划进行分配和更改。
[!NOTE]
IP 地址是逻辑地址,它不与设备绑定。例如,你的手机今天在家获得192.168.1.100,明天同事来你家用他的手机也可能获得这个地址。
原理
2.1.1 点分十进制与二进制转换
IPv4 地址是一个 32 位的二进制数,为了便于人类阅读,将其分为 4 组,每组 8 位(1 字节),转换为十进制后用点分隔,称为点分十进制。
图解说明:
上图展示了 IP 地址 192.168.2.72 的二进制与十进制转换:
- 每 8 位二进制对应一个十进制数
- 每个十进制数的范围是 0-255
二进制权重计算表:
| 位置 | 第7位 | 第6位 | 第5位 | 第4位 | 第3位 | 第2位 | 第1位 | 第0位 |
|---|---|---|---|---|---|---|---|---|
| 权重 | 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 |
| 示例 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
| 计算 | 128 | 64 | 0 | 0 | 0 | 0 | 0 | 0 |
结果:128 + 64 = 192
2.1.2 IP 地址分类(有类地址)
早期的 IP 地址按照有类方式划分为 A、B、C、D、E 五类:
| 类别 | 首位模式 | 网络位 | 主机位 | 地址范围 | 用途 |
|---|---|---|---|---|---|
| A 类 | 0xxxxxxx | 8 位 | 24 位 | 1.0.0.0 ~ 127.255.255.255 | 大型网络 |
| B 类 | 10xxxxxx | 16 位 | 16 位 | 128.0.0.0 ~ 191.255.255.255 | 中型网络 |
| C 类 | 110xxxxx | 24 位 | 8 位 | 192.0.0.0 ~ 223.255.255.255 | 小型网络 |
| D 类 | 1110xxxx | - | - | 224.0.0.0 ~ 239.255.255.255 | 组播地址 |
| E 类 | 1111xxxx | - | - | 240.0.0.0 ~ 255.255.255.255 | 保留/实验 |
要点解读:
[!NOTE]
有类地址划分已经过时,现代网络普遍使用无类域间路由(CIDR)。但理解有类地址有助于理解网络发展历史和一些遗留配置。
2.1.3 公有地址与私有地址
| 类别 | 私有地址范围 | 可用主机数 |
|---|---|---|
| A 类 | 10.0.0.0 ~ 10.255.255.255 | 约 1677 万 |
| B 类 | 172.16.0.0 ~ 172.31.255.255 | 约 104 万 |
| C 类 | 192.168.0.0 ~ 192.168.255.255 | 约 6.5 万 |
要点解读:
- 私有地址只能在内网使用,不能直接访问互联网
- 家庭路由器通常使用
192.168.0.x或192.168.1.x - 大型企业通常使用
10.x.x.x段,因为地址空间更大
2.2 子网划分与 VLSM
背景
在实际网络规划中,很少严格按照 A/B/C 类来划分网络。VLSM(Variable Length Subnet Mask,可变长子网掩码)允许我们根据实际需求灵活划分子网,这就是无类域间路由(CIDR)的核心思想。
原理
2.2.1 网络位与主机位
一个 IP 地址由两部分组成:
- 网络位:标识所属网络(固定不变的部分)
- 主机位:标识网络内的具体主机(可变的部分)
图解说明:
/24表示前 24 位是网络位(固定),后 8 位是主机位(可变)- 子网掩码
255.255.255.0的二进制表示中,1表示网络位,0表示主机位
2.2.2 VLSM 计算实例
问题:如何将一个 /24 网络 192.168.1.0/24 划分为两个子网?
解答:
将掩码从 /24 变为 /25,即多借用 1 位作为网络位:
| 子网 | 网络地址 | 地址范围 | 广播地址 | 可用主机数 |
|---|---|---|---|---|
| 子网1 | 192.168.1.0/25 | 192.168.1.1 ~ 192.168.1.126 | 192.168.1.127 | 126 |
| 子网2 | 192.168.1.128/25 | 192.168.1.129 ~ 192.168.1.254 | 192.168.1.255 | 126 |
计算过程:
/24 掩码:11111111.11111111.11111111.00000000
/25 掩码:11111111.11111111.11111111.10000000
↑
多借 1 位
第 25 位 = 0 → 子网1:主机位范围 0~127(0000000 ~ 1111111)
第 25 位 = 1 → 子网2:主机位范围 128~255(10000000 ~ 11111111)
2.2.3 常用掩码速查表
| CIDR | 子网掩码 | 可用主机数 | 常用场景 |
|---|---|---|---|
| /30 | 255.255.255.252 | 2 | 点对点链路(路由器互联) |
| /29 | 255.255.255.248 | 6 | 小型服务器组 |
| /28 | 255.255.255.240 | 14 | 小型 VLAN |
| /27 | 255.255.255.224 | 30 | 小型办公室 |
| /26 | 255.255.255.192 | 62 | 中型 VLAN |
| /25 | 255.255.255.128 | 126 | 大型 VLAN |
| /24 | 255.255.255.0 | 254 | 标准子网 |
| /16 | 255.255.0.0 | 65534 | 大型网络 |
关键点
[!IMPORTANT]
CNI 中的 /32 掩码在 Kubernetes CNI 中,Pod IP 经常使用
/32掩码。这意味着:
- 所有 32 位都是网络位,没有主机位
- 每个 Pod IP 都是一个独立的"网络"
- 同节点的两个 Pod 之间也需要通过路由通信,而非二层交换
这是理解 Calico 等 CNI 路由模式的关键!
2.3 MAC 地址详解
背景
MAC(Media Access Control)地址是数据链路层的物理地址,用于在同一局域网内唯一标识一个网络设备。与 IP 地址不同,MAC 地址通常与网卡硬件绑定,也称为硬件地址或物理地址。
原理
2.3.1 MAC 地址结构
MAC 地址是一个 48 位(6 字节) 的地址,通常用冒号分十六进制表示:
AA:BB:CC:DD:EE:FF
└──┬──┘ └──┬──┘
OUI 设备标识
(厂商) (序列号)
| 部分 | 位数 | 说明 |
|---|---|---|
| OUI | 前 24 位 | Organizationally Unique Identifier,厂商标识 |
| 设备标识 | 后 24 位 | 由厂商分配的设备序列号 |
常见厂商 OUI:
| OUI 前缀 | 厂商 |
|---|---|
00:50:56 |
VMware |
FA:16:3E |
OpenStack |
52:54:00 |
QEMU/KVM |
00:0C:29 |
VMware |
00:1A:A0 |
Dell |
2.3.2 特殊 MAC 地址
| 类型 | 地址 | 说明 |
|---|---|---|
| 广播 MAC | FF:FF:FF:FF:FF:FF |
发送给同一广播域内所有设备 |
| 组播 MAC | 01:xx:xx:xx:xx:xx |
第 8 位为 1,发送给一组设备 |
2.3.3 MAC 地址表与学习机制
交换机通过MAC 地址表来转发数据帧:
图解说明:
交换机的学习过程:
- 收到数据帧后,记录源 MAC 地址与入端口的对应关系
- 查找目的 MAC 地址对应的端口,从该端口转发
- 如果找不到目的 MAC,则泛洪到所有端口(除入端口外)
- 表项有老化时间,长时间无流量会被删除
2.4 二层交换与三层路由
背景
网络通信的核心问题是:数据包应该发给谁?如何到达目的地?
根据通信双方是否在同一网段,分为:
- 同网段通信:走二层交换(基于 MAC 地址)
- 跨网段通信:走三层路由(基于 IP 地址)
原理
图解说明:
| 场景 | 判断依据 | 转发方式 | 地址变化 |
|---|---|---|---|
| 同网段 | 目的 IP 与源 IP 在同一子网 | 二层交换 | MAC 不变 |
| 跨网段 | 目的 IP 与源 IP 不在同一子网 | 三层路由 | MAC 每跳改变 |
判断是否同网段的方法
已知:
- 主机 A:
192.168.1.2/24 - 主机 B:
192.168.1.130/24 - 主机 C:
192.168.1.130/25
分析:
| 比较 | A 的网段 | B 的网段 | 是否同网段 |
|---|---|---|---|
| A 和 B(/24) | 192.168.1.0 | 192.168.1.0 | ✅ 是 |
| A 和 C(不同掩码) | - | - | 需按各自掩码计算 |
如果 A 使用 /25 掩码:
- A (192.168.1.2/25):网段 192.168.1.0,范围 0~127
- C (192.168.1.130/25):网段 192.168.1.128,范围 128~255
- 结论:A 和 C 不在同一网段!
2.5 ARP 协议与 IP-MAC 映射
背景
当主机知道目的 IP 但不知道目的 MAC 时,需要通过 ARP(Address Resolution Protocol) 协议来获取 MAC 地址。
原理
图解说明:
- 主机 A 发送 ARP 请求广播,询问
192.168.1.20的 MAC 地址 - 主机 B 收到请求后,单播回复自己的 MAC 地址
- 主机 A 将 IP-MAC 映射缓存到 ARP 表,后续通信直接使用
查看 ARP 缓存:
# Linux 查看 ARP 表
ip neigh show
# 或使用传统命令
arp -a
2.6 路由转发过程中的 IP/MAC 变化规律
背景
这是理解网络转发的最核心知识点。很多从业多年的工程师也不一定能说清楚:在路由转发过程中,哪些地址在变化,哪些不变?
原理
核心结论(非 NAT 场景):
| 地址类型 | 是否变化 | 说明 |
|---|---|---|
| 源 IP | 不变 | 始终是发送方的 IP |
| 目的 IP | 不变 | 始终是接收方的 IP |
| 源 MAC | 每跳改变 | 变为上一跳设备的出接口 MAC |
| 目的 MAC | 每跳改变 | 变为下一跳设备的入接口 MAC |
图解说明:
当 Server 1 ping Server 2 时:
| 阶段 | 源 IP | 目的 IP | 源 MAC | 目的 MAC |
|---|---|---|---|---|
| S1 → R (eth1) | 10.1.5.10 | 10.1.8.10 | AA:11 | BB:11 |
| R (eth2) → S2 | 10.1.5.10 | 10.1.8.10 | BB:22 | CC:11 |
要点解读:
[!IMPORTANT]
关键理解:
- IP 地址决定最终目的地,在整个路由过程中始终不变(NAT 除外)
- MAC 地址决定下一跳,每经过一个路由器都会改变
- 发送数据时,目的 MAC 是网关的 MAC,不是最终目标的 MAC
- 这就是为什么同网段走交换(MAC 直达),跨网段走路由(MAC 逐跳变化)
实验验证
步骤一:创建测试环境(使用 Containerlab)
name: routing-lab
topology:
nodes:
router:
kind: linux
image: vyos/vyos:1.2.8
server1:
kind: linux
server2:
kind: linux
links:
- endpoints: ["router:eth1", "server1:net0"]
- endpoints: ["router:eth2", "server2:net0"]
步骤二:在 Server 2 上抓包
# 在 Server 2 的 net0 接口抓包
tcpdump -i net0 -e -nn icmp
# 输出示例:
# 12:00:00.001 BB:22 > CC:11, 10.1.5.10 > 10.1.8.10: ICMP echo request
# 12:00:00.002 CC:11 > BB:22, 10.1.8.10 > 10.1.5.10: ICMP echo reply
步骤三:分析结果
- 源 MAC(BB:22):是路由器 eth2 的 MAC,不是 Server 1 的 MAC
- 源 IP(10.1.5.10):仍然是 Server 1 的 IP,没有变化
TTL 值的变化
TTL(Time To Live):数据包的生存时间,每经过一个路由器减 1。
| 场景 | TTL 变化 | 说明 |
|---|---|---|
| 二层交换 | 不变 | 不经过路由器 |
| 三层路由 | 减 1 | 每跳减 1 |
作用:防止数据包在网络中无限循环。当 TTL 减为 0 时,路由器丢弃该数据包。
# 通过 TTL 判断是否经过路由
ping 10.1.8.10
# 同网段:TTL = 64(Linux 默认)
# 跨一跳:TTL = 63
# 跨两跳:TTL = 62
2.7 使用 Containerlab 进行网络实验
背景
Containerlab 是一个强大的容器化网络实验工具,可以快速搭建包含路由器、交换机、主机的复杂网络拓扑,非常适合学习和验证网络知识。
实验:二层交换 vs 三层路由
二层交换拓扑:
name: layer2-lab
topology:
nodes:
bridge:
kind: bridge
server1:
kind: linux
exec:
- ip addr add 10.1.5.10/24 dev net0
server2:
kind: linux
exec:
- ip addr add 10.1.5.11/24 dev net0
links:
- endpoints: ["bridge:eth1", "server1:net0"]
- endpoints: ["bridge:eth2", "server2:net0"]
验证:
# 在 server1 ping server2
ping 10.1.5.11
# TTL = 64,说明没有经过路由器,走的二层交换
三层路由拓扑:
name: layer3-lab
topology:
nodes:
router:
kind: linux
image: vyos/vyos:1.2.8
server1:
kind: linux
exec:
- ip addr add 10.1.5.10/24 dev net0
- ip route add default via 10.1.5.1
server2:
kind: linux
exec:
- ip addr add 10.1.8.10/24 dev net0
- ip route add default via 10.1.8.1
links:
- endpoints: ["router:eth1", "server1:net0"]
- endpoints: ["router:eth2", "server2:net0"]
验证:
# 在 server1 ping server2
ping 10.1.8.10
# TTL = 63,说明经过了一跳路由器
📝 章节小结
本章深入讲解了 IP 地址和 MAC 地址的核心知识:
- IP 地址基础:点分十进制、二进制转换、有类地址分类
- VLSM 子网划分:掌握网络位/主机位计算、快速确定地址范围
- MAC 地址结构:48 位硬件地址、OUI 厂商标识
- 二层交换 vs 三层路由:
- 同网段走交换,MAC 不变
- 跨网段走路由,MAC 每跳改变
- 路由转发中的地址变化:
- IP 不变(NAT 除外)
- MAC 每跳改变
[!TIP]
学习建议:
- 熟练掌握 VLSM 计算,这是网络工程师的基本功
- 使用 Containerlab 动手搭建实验环境
- 通过抓包验证 IP/MAC 地址的变化规律
- 理解
/32掩码在 CNI 中的应用场景
[!CAUTION]
常见误区:
❌ 认为目的 MAC 就是目标主机的 MAC
✅ 目的 MAC 是下一跳设备的 MAC(可能是网关)
❌ 认为 IP 地址在路由过程中会变化
✅ 在非 NAT 场景下,IP 地址始终不变
第3章 VETH 虚拟网络设备
🎯 学习目标
- 理解 VETH Pair 的概念与工作原理
- 掌握 Linux 网络命名空间(Network Namespace)的基本操作
- 学会手工创建和配置 VETH Pair
- 理解 Linux Bridge 与 VETH 的结合使用
- 掌握 VETH 在容器网络(CNI)中的核心应用
3.1 VETH Pair 概念与原理
背景
在容器网络中,每个容器(Pod)都运行在独立的网络命名空间(Network Namespace)中,与宿主机的网络隔离。那么,容器如何与外界通信呢?
答案是:VETH Pair(虚拟以太网设备对)。
原理
VETH Pair 是 Linux 内核提供的一种虚拟网络设备,它总是成对出现,就像一根虚拟的网线,一端插在容器内,一端插在宿主机上。
图解说明:
- VETH Pair 由两个虚拟网卡组成,它们总是成对创建
- 从一端发送的数据包,会立即出现在另一端
- 通过 VETH Pair,可以实现跨网络命名空间的通信
核心特性
| 特性 | 说明 |
|---|---|
| 成对出现 | 一个 VETH 设备必定有一个 Peer 设备 |
| 双向通信 | 从一端发送的包会从另一端收到 |
| 跨命名空间 | 两端可以分别位于不同的网络命名空间 |
| 即时传输 | 数据包在内核中直接传递,无需经过物理网络 |
[!NOTE]
形象理解:VETH Pair 就像一根虚拟网线,把容器内部和宿主机连接起来。从网线一端发送的数据,必然从另一端出来。
3.2 VETH 在容器网络中的应用
背景
在 Kubernetes 中,每个 Pod 都有自己独立的网络命名空间。Pod 内的应用要与外界通信,必须先将流量从 Pod 的网络命名空间"引出"到宿主机的 Root Namespace,然后才能进行后续的路由或转发。
原理
图解说明:
- Pod 网络命名空间:Pod 内的 eth0 是 VETH Pair 的一端
- 宿主机 Root Namespace:VETH Pair 的另一端在宿主机上
- 流量转发:
- 南北向流量(Pod ↔ 外网):从 VETH 到宿主机,经过 SNAT 后发出
- 东西向流量(Pod ↔ Pod):从 VETH 到宿主机,根据路由转发到目标节点
CNI 实现模式
不同的 CNI 插件使用 VETH 的方式略有不同:
| CNI | VETH 使用方式 | 说明 |
|---|---|---|
| Flannel | VETH + Bridge | Pod 的 VETH 插入到 cni0 网桥 |
| Calico | VETH + Routing | Pod 的 VETH 直接路由,使用 /32 掩码 |
| Cilium | VETH + eBPF | 使用 eBPF 加速,可绕过部分内核协议栈 |
3.3 手工创建 VETH Pair
背景
理解 VETH Pair 的最好方式是亲手创建一个。通过手工操作,可以深入理解容器网络的底层实现。
实现步骤
步骤一:创建网络命名空间
# 创建两个网络命名空间
ip netns add ns1
ip netns add ns2
# 查看已创建的命名空间
ip netns list
代码说明:
ip netns add:创建网络命名空间ip netns list:列出所有网络命名空间
步骤二:创建 VETH Pair
# 创建 VETH Pair:veth01 和 veth10
ip link add veth01 type veth peer name veth10
# 查看创建的设备
ip link show type veth
代码说明:
ip link add ... type veth peer name ...:创建一对 VETH 设备- 此时两个设备都在 Root Namespace 中
步骤三:分配到不同命名空间
# 将 veth01 移动到 ns1
ip link set veth01 netns ns1
# 将 veth10 移动到 ns2
ip link set veth10 netns ns2
步骤四:配置 IP 地址并启用
# 在 ns1 中配置 IP 并启用接口
ip netns exec ns1 ip addr add 10.1.5.10/24 dev veth01
ip netns exec ns1 ip link set veth01 up
ip netns exec ns1 ip link set lo up
# 在 ns2 中配置 IP 并启用接口
ip netns exec ns2 ip addr add 10.1.5.11/24 dev veth10
ip netns exec ns2 ip link set veth10 up
ip netns exec ns2 ip link set lo up
步骤五:测试连通性
# 从 ns1 ping ns2
ip netns exec ns1 ping 10.1.5.11
# 应该能够 ping 通,输出类似:
# PING 10.1.5.11 (10.1.5.11) 56(84) bytes of data.
# 64 bytes from 10.1.5.11: icmp_seq=1 ttl=64 time=0.050 ms
步骤六:查看 VETH Pair 关系
# 在 ns1 中查看 veth01 的 peer index
ip netns exec ns1 ethtool -S veth01
# 输出示例:
# NIC statistics:
# peer_ifindex: 12 # peer 的接口索引
# 在 ns2 中查看 veth10 的接口索引
ip netns exec ns2 ip link show veth10
# 应该显示接口索引为 12
清理环境
# 删除命名空间(会自动删除其中的 VETH 设备)
ip netns del ns1
ip netns del ns2
3.4 Linux Bridge 与 VETH
背景
在 Flannel 等 CNI 插件中,同一节点上的多个 Pod 需要互相通信。如果每对 Pod 之间都创建一个 VETH Pair,网络结构会非常复杂。
解决方案:引入 Linux Bridge(网桥),让所有 Pod 的 VETH 都"插"在同一个网桥上。
原理
图解说明:
- 每个 Pod 的 VETH 一端在 Pod 内(eth0),另一端插在宿主机的 cni0 网桥上
- 同节点的 Pod 之间通信:通过 cni0 网桥的二层交换完成
- 跨节点的 Pod 通信:从网桥经路由转发到物理网卡
手工实现 Bridge + VETH
# 1. 创建两个命名空间
ip netns add ns1
ip netns add ns2
# 2. 创建 Linux Bridge
ip link add br0 type bridge
ip link set br0 up
# 3. 创建两对 VETH
ip link add int0 type veth peer name br-int0
ip link add int1 type veth peer name br-int1
# 4. 将 VETH 一端移入命名空间
ip link set int0 netns ns1
ip link set int1 netns ns2
# 5. 将 VETH 另一端插入 Bridge
ip link set br-int0 master br0
ip link set br-int1 master br0
ip link set br-int0 up
ip link set br-int1 up
# 6. 配置命名空间内的 IP
ip netns exec ns1 ip addr add 10.1.5.10/24 dev int0
ip netns exec ns1 ip link set int0 up
ip netns exec ns1 ip link set lo up
ip netns exec ns2 ip addr add 10.1.5.11/24 dev int1
ip netns exec ns2 ip link set int1 up
ip netns exec ns2 ip link set lo up
# 7. 测试连通性
ip netns exec ns1 ping 10.1.5.11
代码说明:
ip link set ... master br0:将接口插入网桥,相当于把网线插入交换机- 插入网桥后,该接口会显示
master br0标识
查看 Bridge 状态
# 查看所有网桥
brctl show
# 输出示例:
# bridge name bridge id STP enabled interfaces
# br0 8000.aabbccdd1122 no br-int0
# br-int1
# 查看网桥的 MAC 地址表
brctl showmacs br0
3.5 VETH 在不同 CNI 中的应用
背景
不同的 CNI 插件虽然都使用 VETH,但实现方式和网络模型有明显区别。
Flannel 的 Bridge 模式
要点解读:
| 特性 | Flannel Bridge 模式 |
|---|---|
| Pod 掩码 | /24(同节点 Pod 同网段) |
| 同节点通信 | 通过 cni0 网桥二层交换 |
| 跨节点通信 | 通过 flannel.1 VxLAN 封装 |
| 优点 | 配置简单,同节点通信高效 |
| 缺点 | 二层广播域可能导致广播风暴 |
Calico 的路由模式
要点解读:
| 特性 | Calico 路由模式 |
|---|---|
| Pod 掩码 | /32(每个 Pod 独立网络) |
| 同节点通信 | 通过路由表三层转发 |
| 跨节点通信 | BGP 协议或 IPIP/VxLAN 封装 |
| 优点 | 无广播风暴,支持网络策略 |
| 缺点 | 需要维护路由表,配置较复杂 |
[!IMPORTANT]
Calico 使用 /32 掩码的关键意义:
- 使用 /32 掩码意味着没有任何 IP 与该 Pod 同网段
- 因此同节点的 Pod 之间也必须走路由,而非二层交换
- 这使得 Calico 可以在路由层面实施网络策略
3.6 使用 Containerlab 模拟 VETH 网络
简单 VETH 拓扑
name: veth-demo
topology:
nodes:
server1:
kind: linux
exec:
- ip addr add 10.1.5.10/24 dev net0
server2:
kind: linux
exec:
- ip addr add 10.1.5.11/24 dev net0
links:
- endpoints: ["server1:net0", "server2:net0"]
代码说明:
links定义的连接会自动创建 VETH Pair- server1 的 net0 和 server2 的 net0 通过 VETH 直连
Bridge + VETH 拓扑
name: bridge-veth-demo
topology:
nodes:
bridge:
kind: bridge
server1:
kind: linux
exec:
- ip addr add 10.1.5.10/24 dev net0
server2:
kind: linux
exec:
- ip addr add 10.1.5.11/24 dev net0
links:
- endpoints: ["bridge:eth1", "server1:net0"]
- endpoints: ["bridge:eth2", "server2:net0"]
代码说明:
kind: bridge创建一个 Linux Bridge- server1 和 server2 都通过 VETH 连接到这个 Bridge
- 可以使用
brctl show查看 Bridge 上的接口
部署与验证
# 部署拓扑
clab deploy -t veth-demo.yaml
# 进入容器测试
docker exec -it clab-veth-demo-server1 ping 10.1.5.11
# 查看 VETH Pair 关系
docker exec -it clab-veth-demo-server1 ethtool -S net0
# 清理环境
clab destroy -t veth-demo.yaml
3.7 常见问题与排障
问题1:ping 自己不通
现象:在命名空间内 ping 自己的 IP 地址不通
原因:loopback 接口(lo)未启用
解决:
ip netns exec ns1 ip link set lo up
[!TIP]
创建网络命名空间后,记得启用 lo 接口。虽然 ping 自己通常不经过 lo,但某些协议栈处理需要 lo 处于 UP 状态。
问题2:如何判断两个接口是否为 VETH Pair
方法:使用 ethtool -S 查看 peer_ifindex
# 查看 veth0 的 peer 接口索引
ethtool -S veth0 | grep peer_ifindex
# 对比另一端的接口索引
ip link show | grep "12:" # 假设 peer_ifindex 是 12
问题3:如何查看接口属于哪个 Bridge
方法:查看接口的 master 属性
ip link show veth0
# 输出中如果包含 master br0,说明该接口插在 br0 网桥上
# 例如:5: veth0@eth0: <BROADCAST,MULTICAST,UP> ... master br0 state UP
📝 章节小结
本章深入讲解了 VETH 虚拟网络设备:
- VETH Pair 概念:成对出现的虚拟网卡,用于跨网络命名空间通信
- CNI 中的应用:所有主流 CNI 都使用 VETH 将 Pod 流量引入宿主机
- 手工创建 VETH:掌握
ip link add ... type veth peer name ...命令 - Linux Bridge:多个 VETH 可以插入同一个 Bridge,实现二层交换
- CNI 实现差异:
- Flannel:VETH + Bridge,同节点走二层
- Calico:VETH + Routing(/32 掩码),同节点也走三层
[!TIP]
学习建议:
- 动手执行手工创建 VETH 的全部步骤
- 使用 Containerlab 快速搭建各种 VETH 拓扑
- 对比 Flannel 和 Calico 的 VETH 使用方式
- 理解 "VETH 是容器网络的基石" 这一核心概念
第4章 Host-Gateway 网络模式
🎯 学习目标
- 理解 Host-Gateway(主机网关)模式的核心概念
- 掌握静态路由在容器网络中的应用
- 学会使用 Containerlab 模拟 Host-GW 网络
- 掌握手工配置 Host-GW 模式的完整步骤
- 理解 Host-GW 与 Overlay 网络的优劣对比
4.1 Host-Gateway 概念与原理
背景
在容器网络中,Pod 需要与其他节点上的 Pod 通信。实现跨节点通信有两种主要方式:
- Overlay 网络:将容器网络封装在底层网络之上(如 VxLAN、IPIP)
- Host-Gateway:利用主机的路由功能直接转发容器流量
Host-Gateway(主机网关)是最简单、性能最高的容器网络模式之一。
原理
Host-Gateway 的核心思想:把宿主机当作 Pod 的网关。
图解说明:
- Pod1 发送数据包到 Pod2(目的地址 10.244.2.10)
- Pod1 的默认网关是 cni0 网桥(10.244.1.1)
- 节点1 查询路由表:去往 10.244.2.0/24 的下一跳是 192.168.2.73
- 数据包发送到节点2,节点2 将其转发给 Pod2
关键理解:
[!NOTE]
Host-Gateway = 把主机当网关就像你家的无线路由器是你手机的网关一样,Pod 把宿主机当作自己的网关。所有出站流量都先发给宿主机,由宿主机负责路由转发。
4.2 Host-GW 与 Overlay 网络对比
原理对比
图解说明:
| 特性 | Host-Gateway | Overlay (VxLAN/IPIP) |
|---|---|---|
| 性能 | ⭐⭐⭐⭐⭐ 最高 | ⭐⭐⭐ 有封装开销 |
| MTU | 无损耗(1500) | 有损耗(1450左右) |
| 配置复杂度 | 简单 | 中等 |
| 对底层网络要求 | 节点必须二层可达 | 节点只需三层可达 |
| Pod IP 暴露 | 暴露在物理网络 | 封装隐藏 |
| 跨子网 | ❌ 不支持 | ✅ 支持 |
适用场景
| 场景 | 推荐模式 |
|---|---|
| 节点在同一二层网络 | Host-Gateway(性能最优) |
| 节点跨子网/跨机房 | Overlay(必须封装) |
| 对性能敏感的业务 | Host-Gateway |
| 多云/混合云环境 | Overlay |
[!IMPORTANT]
Host-GW 的限制:节点之间必须二层可达(在同一个广播域)。如果节点跨子网,数据包无法直接路由,必须使用 Overlay 封装。
4.3 使用 Containerlab 实现 Host-GW
背景
Containerlab 可以快速搭建包含路由器和主机的网络拓扑,非常适合理解 Host-GW 的工作原理。
网络拓扑设计
图解说明:
- Server1 的网关是 GW0(10.1.5.1)
- Server2 的网关是 GW1(10.1.8.1)
- GW0 和 GW1 通过 172.12.1.x 互联
- GW0 需要知道如何到达 10.1.8.0/24,GW1 需要知道如何到达 10.1.5.0/24
Containerlab 配置文件
name: host-gw-lab
topology:
nodes:
# 路由器 GW0
gw0:
kind: linux
image: vyos/vyos:1.2.8
startup-config: gw0.cfg
# 路由器 GW1
gw1:
kind: linux
image: vyos/vyos:1.2.8
startup-config: gw1.cfg
# 服务器 Server1
server1:
kind: linux
exec:
- ip addr add 10.1.5.10/24 dev net0
- ip route add default via 10.1.5.1
# 服务器 Server2
server2:
kind: linux
exec:
- ip addr add 10.1.8.10/24 dev net0
- ip route add default via 10.1.8.1
links:
# Server1 连接 GW0
- endpoints: ["gw0:eth1", "server1:net0"]
# Server2 连接 GW1
- endpoints: ["gw1:eth1", "server2:net0"]
# GW0 和 GW1 互联
- endpoints: ["gw0:eth2", "gw1:eth2"]
GW0 路由配置 (gw0.cfg)
# 接口配置
set interfaces ethernet eth1 address 10.1.5.1/24
set interfaces ethernet eth2 address 172.12.1.10/24
# 静态路由:去往 10.1.8.0/24 的下一跳是 GW1
set protocols static route 10.1.8.0/24 next-hop 172.12.1.11
GW1 路由配置 (gw1.cfg)
# 接口配置
set interfaces ethernet eth1 address 10.1.8.1/24
set interfaces ethernet eth2 address 172.12.1.11/24
# 静态路由:去往 10.1.5.0/24 的下一跳是 GW0
set protocols static route 10.1.5.0/24 next-hop 172.12.1.10
验证连通性
# 部署拓扑
clab deploy -t host-gw-lab.yaml
# 从 Server1 ping Server2
docker exec -it clab-host-gw-lab-server1 ping 10.1.8.10
# 观察 TTL 变化
# TTL = 62(经过两个路由器,从 64 减 2)
# 清理
clab destroy -t host-gw-lab.yaml
4.4 手工实现 Host-GW 模式
背景
理解 Host-GW 最好的方式是手工实现一个完整的配置,模拟 Flannel 的 Host-GW 模式。
实验拓扑
实现步骤(节点1: 192.168.2.71)
步骤一:创建网络基础设施
# 创建网络命名空间(模拟 Pod)
ip netns add ns1
# 创建 Linux Bridge(模拟 cni0 网桥)
ip link add br0 type bridge
ip link set br0 up
# 创建 VETH Pair
ip link add int0 type veth peer name br-int0
步骤二:配置命名空间内的网络
# 将 VETH 一端移入命名空间
ip link set int0 netns ns1
# 配置命名空间内的接口
ip netns exec ns1 ip addr add 10.1.5.10/24 dev int0
ip netns exec ns1 ip link set int0 up
ip netns exec ns1 ip link set lo up
# 配置默认路由(指向网关)
ip netns exec ns1 ip route add default via 10.1.5.1
步骤三:配置宿主机网桥
# 将 VETH 另一端插入网桥
ip link set br-int0 master br0
ip link set br-int0 up
# 给网桥配置 IP(作为 Pod 的网关)
ip addr add 10.1.5.1/24 dev br0
步骤四:添加跨节点路由
# 添加静态路由:去往 10.1.8.0/24 的下一跳是节点2
ip route add 10.1.8.0/24 via 192.168.2.73 dev ens160
实现步骤(节点2: 192.168.2.73)
# 创建网络命名空间
ip netns add ns2
# 创建网桥
ip link add br0 type bridge
ip link set br0 up
# 创建 VETH Pair 并配置
ip link add int0 type veth peer name br-int0
ip link set int0 netns ns2
# 配置命名空间
ip netns exec ns2 ip addr add 10.1.8.10/24 dev int0
ip netns exec ns2 ip link set int0 up
ip netns exec ns2 ip link set lo up
ip netns exec ns2 ip route add default via 10.1.8.1
# 配置网桥
ip link set br-int0 master br0
ip link set br-int0 up
ip addr add 10.1.8.1/24 dev br0
# 添加跨节点路由
ip route add 10.1.5.0/24 via 192.168.2.71 dev ens160
验证测试
# 从节点1的 ns1 ping 节点2的 ns2
ip netns exec ns1 ping 10.1.8.10
# 应该能够 ping 通,TTL = 62(经过两跳)
查看路由表
# 节点1 路由表
ip route show
# 输出类似:
# 10.1.5.0/24 dev br0 proto kernel scope link src 10.1.5.1
# 10.1.8.0/24 via 192.168.2.73 dev ens160 # Host-GW 关键路由
代码说明:
10.1.8.0/24 via 192.168.2.73:这就是 Host-GW 的核心路由- 去往 Pod 网段(10.1.8.0/24)的流量,下一跳是对端节点(192.168.2.73)
- 这与 Flannel Host-GW 模式自动创建的路由完全一致
4.5 Host-GW 在 Flannel 中的实现
背景
Flannel 是 Kubernetes 中最常用的 CNI 插件之一。它支持多种后端模式,其中 Host-GW 是性能最高的模式。
Flannel Host-GW 架构
图解说明:
- flanneld 进程监听 etcd 中的网络配置
- 当新节点加入或 Pod 网段变化时,flanneld 自动更新路由表
- 每个节点的路由表包含所有其他节点的 Pod 网段路由
Flannel Host-GW 配置
{
"Network": "10.244.0.0/16",
"Backend": {
"Type": "host-gw"
}
}
查看 Flannel 创建的路由
# 在 Kubernetes 节点上查看路由表
ip route show | grep 10.244
# 输出示例(假设有三个节点):
# 10.244.0.0/24 via 192.168.1.10 dev eth0 # 节点1 的 Pod 网段
# 10.244.1.0/24 dev cni0 proto kernel scope link src 10.244.1.1 # 本节点
# 10.244.2.0/24 via 192.168.1.12 dev eth0 # 节点3 的 Pod 网段
要点解读:
[!IMPORTANT]
Flannel Host-GW 的路由特征:
- 本节点的 Pod 网段:
dev cni0(直连)- 其他节点的 Pod 网段:
via <节点IP>(通过节点路由)- flanneld 自动维护这些路由,无需手工配置
4.6 Next Hop 的核心作用
背景
在 Host-GW 模式中,Next Hop(下一跳)是最关键的概念。理解 Next Hop 对于排障和网络设计至关重要。
原理
图解说明:
- Pod1 → Node1:Pod1 把所有出站流量发给默认网关
- Node1 查路由表:发现去往 10.244.2.0/24 的下一跳是 192.168.2.73
- Node1 → Node2:数据包发送到 Node2
- Node2 → Pod2:Node2 发现目的地址在本地 cni0,直接投递
关键点
| 概念 | 说明 |
|---|---|
| 默认网关 | Pod 的"下一跳",通常是 cni0 网桥 |
| 静态路由 | 节点路由表中的跨节点路由 |
| Next Hop | 静态路由指定的下一跳地址 |
| 出接口 | 发送数据包的网卡接口 |
# 完整的路由条目格式
ip route add 10.244.2.0/24 via 192.168.2.73 dev eth0
# ↑目的网段 ↑下一跳地址 ↑出接口
[!CAUTION]
Next Hop 必须可达:如果 Next Hop 地址不可达(如跨子网),Host-GW 模式将无法工作。此时必须使用 Overlay 模式。
4.7 常见问题与排障
问题1:跨节点 Pod 通信失败
检查步骤:
# 1. 检查路由表是否正确
ip route show | grep 10.244
# 2. 检查下一跳是否可达
ping <下一跳IP>
# 3. 检查是否开启 IP 转发
cat /proc/sys/net/ipv4/ip_forward
# 如果是 0,需要开启:
echo 1 > /proc/sys/net/ipv4/ip_forward
问题2:Host-GW 模式下节点跨子网
现象:节点在不同子网,Host-GW 无法工作
原因:Next Hop 不可达,ARP 无法解析
解决方案:改用 Overlay 模式(VxLAN 或 IPIP)
{
"Network": "10.244.0.0/16",
"Backend": {
"Type": "vxlan"
}
}
问题3:路由条目丢失
现象:flanneld 重启后路由丢失
检查:
# 检查 flanneld 是否正常运行
kubectl get pods -n kube-flannel
# 查看 flanneld 日志
kubectl logs -n kube-flannel <flannel-pod-name>
📝 章节小结
本章深入讲解了 Host-Gateway 网络模式:
- 核心概念:把宿主机当作 Pod 的网关,利用路由转发容器流量
- 工作原理:Pod → 默认网关 → 查路由表 → 下一跳 → 目标节点
- 优势:性能最高、配置简单、无封装开销
- 限制:节点必须二层可达(同一广播域)
- 实现方式:
- Flannel Host-GW 模式
- Calico BGP 模式(类似原理)
- 手工静态路由配置
[!TIP]
学习建议:
- 使用 Containerlab 搭建 Host-GW 实验环境
- 手工配置一次完整的 Host-GW 网络
- 在真实 Kubernetes 集群中查看 Flannel 路由表
- 理解 "Next Hop 必须可达" 这一核心约束
[!IMPORTANT]
选择 Host-GW 还是 Overlay?
- 同一二层网络 → 优先选择 Host-GW(性能更好)
- 跨子网/跨机房 → 必须使用 Overlay(VxLAN/IPIP)
- 混合场景 → 可以使用 Calico 的 ipip: CrossSubnet 模式
第5章 CNI 网络模型
🎯 学习目标
- 理解 CNI 网络的四大分类:Overlay、Underlay、Routing、高性能
- 掌握 Pod 数据包在宿主机上的转发路径
- 理解网卡复用技术(IPVLAN、MACVLAN)的原理与应用
- 了解高性能网络(SR-IOV、DPDK、VPP)的核心概念
- 掌握 Cilium eBPF 的优化原理
5.1 CNI 网络模型概述
背景
在 Kubernetes 中,CNI(Container Network Interface)负责为 Pod 配置网络。不同的 CNI 插件采用不同的网络模型,各有优劣。
四大网络模型
图解说明:
| 类型 | 核心原理 | 代表性 CNI/技术 |
|---|---|---|
| Overlay | 封装隧道,Pod IP 隐藏 | Flannel VxLAN、Calico IPIP |
| Underlay | 网卡复用,Pod 与宿主机同平面 | IPVLAN、MACVLAN |
| Routing | 路由转发,Pod IP 暴露 | Host-GW、Calico BGP |
| 高性能 | 绕过内核协议栈 | SR-IOV、DPDK、Cilium eBPF |
5.2 Pod 数据包转发路径
背景
理解 Pod 数据包在宿主机上的转发路径,是掌握 CNI 网络模型的关键。
原理
图解说明:
- Pod 内部协议栈:应用发送数据包,经过 Pod 自己的 TCP/IP 协议栈
- VETH Pair 传输:数据包通过 VETH Pair 传到宿主机
- 宿主机协议栈:再次经过宿主机的协议栈(iptables、路由查询等)
- 物理网卡发出:最终从物理网卡发送到外部网络
[!IMPORTANT]
关键理解:Pod 发送一个数据包,需要经过两次协议栈处理:
- Pod 自己的协议栈(一次)
- 宿主机的协议栈(一次)
这是容器网络相比传统网络有性能损耗的主要原因。
5.3 Overlay 叠加网络
背景
Overlay 网络通过隧道封装技术,将 Pod 网络"叠加"在物理网络之上,使 Pod IP 对外部网络不可见。
原理
图解说明:
| 特性 | 说明 |
|---|---|
| 封装方式 | VxLAN、IPIP、GRE、Geneve |
| 外层报头 | 宿主机 IP(物理网络可路由) |
| 内层报头 | Pod IP(物理网络不可见) |
| 优点 | 跨子网、跨机房通信 |
| 缺点 | 有封装开销、MTU 损耗 |
常见 Overlay 协议对比
| 协议 | 封装开销 | 特点 | 使用场景 |
|---|---|---|---|
| VxLAN | 50 字节 | 基于 UDP,成熟稳定 | Flannel、Calico |
| IPIP | 20 字节 | 封装最小,性能较好 | Calico |
| GRE | 24+ 字节 | 支持多协议 | 较少使用 |
| Geneve | 可变 | 可扩展,未来趋势 | OVN |
[!NOTE]
MTU 问题:Overlay 封装会增加报头大小,需要调整 Pod 网络的 MTU。
- VxLAN:MTU = 1450(1500 - 50)
- IPIP:MTU = 1480(1500 - 20)
5.4 Underlay 底层网络
背景
Underlay 网络通过网卡复用技术,让 Pod 网络与宿主机网络处于同一平面,减少协议栈处理次数。
原理
图解说明:
- 物理网卡通过 IPVLAN/MACVLAN 技术创建多个子接口
- 每个子接口分配给一个 Pod
- Pod 的 IP 地址与宿主机在同一网段
- 绕过了 Pod 内部的协议栈处理
IPVLAN vs MACVLAN
| 特性 | IPVLAN | MACVLAN |
|---|---|---|
| MAC 地址 | 子接口与父接口 MAC 相同 | 子接口有独立 MAC |
| IP 地址 | 子接口有独立 IP | 子接口有独立 IP |
| 二层通信 | 不支持(需要三层) | 支持 |
| 适用场景 | IaaS 虚拟化环境 | 裸机 K8s 环境 |
| 内核版本 | 4.2+ 稳定 | 3.x 即可 |
适用场景
| 场景 | 推荐技术 |
|---|---|
| OpenStack + K8s 环境 | IPVLAN |
| 裸机 K8s 环境 | MACVLAN |
| 高吞吐量需求 | IPVLAN/MACVLAN(多网卡) |
| 传统 Service 场景 | 不推荐(使用 Overlay/Routing) |
[!TIP]
多网卡架构:在实际生产中,Pod 通常有多张网卡:
- 默认 CNI 网卡:用于 Service 发现、K8s 网络
- IPVLAN/MACVLAN 网卡:用于高速数据通信
5.5 Routing 路由网络
背景
Routing 网络模式通过路由协议直接转发 Pod 流量,Pod IP 完全暴露在物理网络中。
原理
图解说明:
| 模式 | 路由来源 | 适用场景 |
|---|---|---|
| Host-GW | 静态路由(flanneld 维护) | 同二层网络 |
| BGP | 动态路由(BGP 协议) | 跨子网、大规模集群 |
Host-GW vs BGP
| 特性 | Host-GW | BGP |
|---|---|---|
| 路由协议 | 静态路由 | BGP 动态路由 |
| 跨子网 | ❌ 不支持 | ✅ 支持 |
| 外部集成 | 不需要 | 需要与 ToR 交换机对接 |
| 复杂度 | 简单 | 较复杂 |
| 代表 CNI | Flannel Host-GW | Calico BGP |
[!IMPORTANT]
BGP 的核心价值:
- 可以将 Pod IP 发布到物理网络
- 外部设备可以直接访问 Pod IP(无需 NodePort/LoadBalancer)
- 支持 Service ClusterIP 的外部发布(Calico 特性)
5.6 高性能网络
背景
对于延迟敏感、高吞吐量的应用(如电信、视频流、金融交易),传统的内核协议栈处理已成为瓶颈。高性能网络技术通过绕过内核协议栈来实现极致性能。
技术分类
SR-IOV(网卡直通)
要点解读:
| 概念 | 说明 |
|---|---|
| PF | Physical Function,物理网卡 |
| VF | Virtual Function,虚拟子网卡 |
| 直通 | VF 直接分配给 Pod,绕过宿主机协议栈 |
| 硬件要求 | 需要支持 SR-IOV 的网卡(Intel、Mellanox) |
DPDK + VPP(用户态协议栈)
要点解读:
| 技术 | 说明 |
|---|---|
| DPDK | Data Plane Development Kit,Intel 开源的高速数据包处理框架 |
| VPP | Vector Packet Processing,Cisco 开源的用户态协议栈 |
| PMD | Poll Mode Driver,轮询模式驱动,避免中断开销 |
| 性能 | 可达千万级 PPS,远超内核协议栈 |
Cilium eBPF 优化
要点解读:
| 函数 | 作用 | 适用场景 |
|---|---|---|
| bpf_redirect_peer | 同节点 Pod 间数据包重定向 | Pod ↔ Pod(同节点) |
| bpf_redirect_neigh | 跨节点数据包直接发往物理网卡 | Pod → 外部网络 |
[!TIP]
Cilium eBPF Host Routing 的效果:
- 同节点 Pod 通信:绕过宿主机协议栈和 iptables
- 抓包现象:在 veth 上只能看到单向流量(因为数据包被 redirect 了)
5.7 CNI 模式对比与选型
综合对比
| 特性 | Overlay | Underlay | Routing | 高性能 |
|---|---|---|---|---|
| 性能 | ⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| 跨子网 | ✅ | ❌ | 部分支持 | 取决于具体技术 |
| 配置复杂度 | 简单 | 中等 | 中等 | 复杂 |
| Pod IP 可见性 | 隐藏 | 暴露 | 暴露 | 暴露 |
| 硬件要求 | 无 | 无 | 无 | SR-IOV 需要特定网卡 |
选型建议
| 场景 | 推荐 CNI |
|---|---|
| 小型集群、测试环境 | Flannel Host-GW |
| 需要 NetworkPolicy | Calico 或 Cilium |
| 跨子网、大规模集群 | Calico BGP |
| 追求极致性能 | Cilium eBPF Host Routing |
| 电信/视频流/高频交易 | SR-IOV + DPDK |
📝 章节小结
本章系统讲解了 CNI 网络模型:
-
四大网络类型:
- Overlay:封装隧道(VxLAN、IPIP),跨子网通信
- Underlay:网卡复用(IPVLAN、MACVLAN),同平面通信
- Routing:路由转发(Host-GW、BGP),Pod IP 暴露
- 高性能:绕过内核(SR-IOV、DPDK、eBPF)
-
数据包路径:Pod 发包需经过两次协议栈处理(Pod + 宿主机)
-
关键技术:
- VxLAN/IPIP 的封装原理
- IPVLAN/MACVLAN 的网卡复用
- SR-IOV 的网卡直通
- Cilium eBPF 的 bpf_redirect 优化
-
选型关键点:
- 跨子网 → Overlay 或 BGP
- 同二层 → Host-GW(最简单高效)
- 需要 NetworkPolicy → Calico 或 Cilium
- 极致性能 → SR-IOV + DPDK 或 Cilium eBPF
[!TIP]
学习建议:
- 理解"两次协议栈处理"是容器网络性能损耗的根源
- 根据业务场景选择合适的网络模型
- 关注 eBPF 技术的发展趋势
- 高性能场景需要了解 SR-IOV、DPDK 的基本原理
第6章 CNI 工作原理
🎯 学习目标
- 理解 CNI 的插件分类:Main、IPAM、Meta
- 掌握 CNI 的两大核心目录结构
- 理解 CNI 配置文件的格式与字段含义
- 掌握 IPAM(IP 地址管理)的工作机制
- 了解多网卡方案(Multus、Network Attachment Definition)
6.1 CNI 概述
背景
CNI(Container Network Interface)是 Kubernetes 网络的标准接口规范。它定义了容器运行时如何调用网络插件来配置 Pod 的网络。
原理
图解说明:
- kubelet 调用容器运行时创建 Pod
- 容器运行时调用 CNI 插件配置网络
- CNI 插件读取
/etc/cni/net.d/下的配置文件 - CNI 插件执行
/opt/cni/bin/下的二进制程序 - 完成 Pod 网络配置(创建 VETH、分配 IP、配置路由等)
[!NOTE]
CNI 的本质:CNI 只是一个接口规范,定义了输入输出格式。具体网络实现由各 CNI 插件负责(如 Flannel、Calico、Cilium)。
6.2 CNI 插件分类
原理
CNI 插件分为三大类:
图解说明:
| 类型 | 职责 | 代表插件 |
|---|---|---|
| Main | 创建网络接口、配置网络连通性 | bridge、veth、ipvlan、macvlan、sriov |
| IPAM | 分配和回收 IP 地址 | host-local、dhcp、static、whereabouts |
| Meta | 辅助功能(端口映射、带宽限制等) | portmap、bandwidth、tuning、sbr |
Main 插件详解
| 插件 | 功能 | 使用场景 |
|---|---|---|
| bridge | 创建 Linux Bridge,VETH 插入 Bridge | Flannel、默认方案 |
| ptp | 点对点 VETH Pair | Calico |
| ipvlan | IPVLAN 子接口 | 高性能虚拟化环境 |
| macvlan | MACVLAN 子接口 | 裸机高性能 |
| sriov | SR-IOV VF 直通 | 电信/金融高性能 |
IPAM 插件详解
| 插件 | 作用域 | 特点 |
|---|---|---|
| host-local | 单节点 | 最常用,每个节点独立管理 IP 池 |
| dhcp | 跨节点 | 使用 DHCP 服务器分配 IP |
| static | 固定 IP | 手工指定 IP,不自动分配 |
| whereabouts | 集群级 | 跨节点 IP 管理,多网卡常用 |
[!IMPORTANT]
IPAM 插件的选择:
- 普通场景:host-local(Flannel、Calico 默认)
- 多网卡场景:whereabouts(集群级 IP 管理)
- 固定 IP 需求:static
6.3 CNI 核心目录结构
背景
理解 CNI 的目录结构,是排障和自定义网络配置的基础。
两大核心目录
/etc/cni/net.d/ - 配置文件目录
# 查看 CNI 配置文件
ls -la /etc/cni/net.d/
# 典型输出
# 10-flannel.conflist # Flannel 配置
# 10-calico.conflist # Calico 配置
# 10-cilium.conflist # Cilium 配置
配置文件命名规则:
- 按数字前缀排序,数字小的优先加载
.conf格式:单个插件配置.conflist格式:多个插件链式调用
/opt/cni/bin/ - 二进制程序目录
# 查看 CNI 二进制程序
ls -la /opt/cni/bin/
# 典型输出
# bandwidth # 带宽限制插件
# bridge # Linux Bridge 插件
# dhcp # DHCP IPAM 插件
# flannel # Flannel 专用插件
# host-local # host-local IPAM 插件
# ipvlan # IPVLAN 插件
# macvlan # MACVLAN 插件
# portmap # 端口映射插件
# ptp # 点对点 VETH 插件
# sbr # Source Based Routing 插件
# sriov # SR-IOV 插件
# tuning # 网卡参数调优插件
# vlan # VLAN 插件
代码说明:
- 这些二进制文件由 CNI 插件项目提供
- 容器运行时会调用这些二进制来配置网络
- 安装不同 CNI 时会添加对应的二进制(如 Calico 的
calico、calico-ipam)
6.4 CNI 配置文件详解
背景
CNI 配置文件定义了网络如何配置。理解配置文件结构,有助于自定义网络和排障。
配置文件示例(Calico)
{
"name": "k8s-pod-network",
"cniVersion": "0.3.1",
"plugins": [
{
"type": "calico",
"datastore_type": "kubernetes",
"mtu": 0,
"nodename_file_optional": false,
"log_level": "Info",
"log_file_path": "/var/log/calico/cni/cni.log",
"ipam": {
"type": "calico-ipam",
"assign_ipv4": "true",
"assign_ipv6": "false"
},
"container_settings": {
"allow_ip_forwarding": false
},
"policy": {
"type": "k8s"
},
"kubernetes": {
"k8s_api_root": "https://10.96.0.1:443",
"kubeconfig": "/etc/cni/net.d/calico-kubeconfig"
}
},
{
"type": "portmap",
"snat": true,
"capabilities": {
"portMappings": true
}
},
{
"type": "bandwidth",
"capabilities": {
"bandwidth": true
}
}
]
}
配置字段解析:
| 字段 | 说明 |
|---|---|
| name | 网络名称 |
| cniVersion | CNI 规范版本 |
| plugins | 插件列表(链式调用) |
| type | 插件类型(对应 /opt/cni/bin/ 下的二进制) |
| ipam | IP 地址管理配置 |
| datastore_type | 数据存储类型(kubernetes/etcdv3) |
| policy | NetworkPolicy 实现方式 |
链式调用示意图
图解说明:
- CNI 插件按
plugins数组顺序依次执行 - 每个插件完成特定功能后,传递给下一个插件
- 类似 Linux 管道的链式处理
6.5 IPAM 工作机制
背景
IPAM(IP Address Management)负责 Pod 的 IP 地址分配与回收。不同的 IPAM 插件有不同的管理范围和特点。
原理
图解说明:
| IPAM | 管理范围 | IP 池分配 | 适用场景 |
|---|---|---|---|
| host-local | 单节点 | 每个节点独立 IP 池 | 默认 CNI(Flannel/Calico) |
| whereabouts | 集群级 | 统一 IP 池,跨节点分配 | 多网卡、需要 IP 跨节点迁移 |
host-local 存储位置
# host-local 的 IP 分配记录存储位置
ls /var/lib/cni/networks/<network-name>/
# 每个文件名是已分配的 IP 地址
# 文件内容是 Pod 的 Container ID
whereabouts 存储方式
# whereabouts 使用 Kubernetes CRD 存储 IP 分配信息
kubectl get ippools.whereabouts.cni.cncf.io -A
kubectl get overlappingrangeipreservations.whereabouts.cni.cncf.io -A
6.6 多网卡方案(Multus)
背景
在高性能场景中,Pod 可能需要多张网卡:默认网卡用于 Service 通信,额外网卡用于高速数据传输。
原理
图解说明:
- Multus CNI 是一个 CNI "元插件",可以调用其他 CNI 为 Pod 配置多张网卡
- 默认网卡(eth0):由集群默认 CNI(如 Calico)配置
- 额外网卡(net1, net2...):通过 Network Attachment Definition 配置
Network Attachment Definition (NAD)
apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
name: macvlan-net
namespace: default
spec:
config: '{
"cniVersion": "0.3.1",
"type": "macvlan",
"master": "eth0",
"mode": "bridge",
"ipam": {
"type": "whereabouts",
"range": "192.168.1.0/24",
"gateway": "192.168.1.1"
}
}'
配置说明:
| 字段 | 说明 |
|---|---|
| type | 网卡类型(macvlan/ipvlan/sriov) |
| master | 父接口(物理网卡) |
| mode | MACVLAN 模式(bridge/vepa/private) |
| ipam.type | IP 管理方式(whereabouts 用于多网卡) |
| ipam.range | IP 地址范围 |
Pod 使用多网卡
apiVersion: v1
kind: Pod
metadata:
name: multi-nic-pod
annotations:
k8s.v1.cni.cncf.io/networks: macvlan-net
spec:
containers:
- name: app
image: nginx
代码说明:
- 通过
k8s.v1.cni.cncf.io/networks注解指定额外网卡 - 可以指定多个,用逗号分隔:
macvlan-net, sriov-net
6.7 Source Based Routing (SBR)
背景
当 Pod 有多张网卡时,需要确保从某张网卡进来的流量,响应也从同一张网卡出去。这需要基于源地址的路由(SBR)。
原理
配置示例:
# 添加路由规则:从 192.168.1.0/24 出来的流量,查 table 100
ip rule add from 192.168.1.0/24 table 100
# 在 table 100 中添加默认路由
ip route add default via 192.168.1.1 dev net1 table 100
要点解读:
- 基于源地址路由 优先级高于基于目的地址的路由
- 多网卡场景必须配置 SBR,否则响应流量可能走错网卡
- CNI 的
sbr插件可以自动配置
6.8 Pod 网络配置流程
完整流程
流程说明:
- kubelet 调用容器运行时创建 Pod
- 容器运行时读取
/etc/cni/net.d/下的配置文件(按名称排序,优先加载) - 按配置文件中的
plugins列表顺序:- 调用 Main 插件:创建网络接口
- 调用 IPAM 插件:分配 IP 地址
- 调用 Meta 插件:附加功能
- 所有插件执行完成,Pod 网络就绪
6.9 常见问题与排障
问题1:Pod 网络配置失败
检查步骤:
# 1. 检查 CNI 配置文件是否存在
ls -la /etc/cni/net.d/
# 2. 检查 CNI 二进制是否存在
ls -la /opt/cni/bin/
# 3. 查看 kubelet 日志
journalctl -u kubelet | grep -i cni
# 4. 查看容器运行时日志
crictl logs <container-id>
问题2:IP 地址分配失败
# 检查 host-local IPAM 的 IP 分配记录
ls /var/lib/cni/networks/
# 如果 IP 池耗尽,可以清理无效记录
# (谨慎操作,仅在确认 IP 未被使用时)
问题3:多网卡 IP 冲突
# 检查 whereabouts 的 IP 分配
kubectl get ippools.whereabouts.cni.cncf.io -A -o wide
# 查看 IP 预留情况
kubectl get overlappingrangeipreservations -A
📝 章节小结
本章深入讲解了 CNI 工作原理:
-
CNI 插件分类:
- Main 插件:创建网络接口(bridge、veth、ipvlan、macvlan、sriov)
- IPAM 插件:分配 IP 地址(host-local、dhcp、static、whereabouts)
- Meta 插件:辅助功能(portmap、bandwidth、sbr)
-
两大核心目录:
/etc/cni/net.d/:配置文件,定义"怎么配置网络"/opt/cni/bin/:二进制程序,"实际执行配置"
-
配置文件结构:链式调用多个插件
-
IPAM 机制:
- host-local:节点级,每节点独立 IP 池
- whereabouts:集群级,跨节点 IP 管理
-
多网卡方案:
- Multus CNI 编排多网卡
- Network Attachment Definition 定义额外网卡
- SBR 保证多网卡响应路径正确
[!TIP]
学习建议:
- 在实际集群中查看
/etc/cni/net.d/和/opt/cni/bin/目录- 理解 CNI 配置文件中的 plugins 链式调用
- 尝试使用 Multus 配置多网卡 Pod
- 出现网络问题时,首先检查 CNI 配置文件和二进制
[!IMPORTANT]
CNI 排障核心思路:
- 配置文件存在吗?(
/etc/cni/net.d/)- 二进制存在吗?(
/opt/cni/bin/)- 配置文件内容正确吗?(type、ipam 等字段)
- IP 池是否耗尽?(检查 IPAM 存储)
第二部分:Cilium-CNI
第7章 eBPF 介绍与网络应用
🎯 学习目标
- 理解 eBPF 技术的背景与核心概念
- 掌握 eBPF 程序的加载流程
- 理解 eBPF 在网络中的 Hook 点(XDP、TC)
- 掌握数据包收发流程与 eBPF 介入点
- 了解 eBPF Map 的作用
7.1 eBPF 技术背景
背景
eBPF(extended Berkeley Packet Filter)是 Linux 内核中的一项革命性技术。它允许在不修改内核源码的情况下,动态向内核注入特定逻辑。
从 cBPF 到 eBPF
图解说明:
| 特性 | cBPF | eBPF |
|---|---|---|
| 诞生时间 | 1992 年(伯克利大学) | 2014 年(Linux 3.18) |
| 功能范围 | 仅包过滤 | 网络、追踪、安全、性能等 |
| 寄存器 | 2 个 32 位 | 10 个 64 位 |
| 指令集 | 简单 | 丰富(跳转、调用等) |
eBPF 的核心价值
[!IMPORTANT]
eBPF = 内核可编程化
- 传统方式:修改内核源码 → 重新编译 → 重启
- eBPF 方式:编写 eBPF 程序 → 动态加载 → 无需重启
这就像给内核"打补丁",但不需要重新编译内核。
7.2 eBPF 程序加载流程
原理
流程说明:
| 步骤 | 说明 |
|---|---|
| 编写源码 | 使用 C、Python、Go 等高级语言编写 |
| 编译 | 通过 LLVM/Clang 编译为 eBPF 字节码 |
| 验证 Verify | 内核验证器检查:无死循环、无越界访问、执行时间有限 |
| JIT 编译 | 将字节码编译为本机机器指令,提高执行效率 |
| 注入 Hook | 将程序注入到内核的特定位置(Hook 点) |
| 运行 | 当事件(如数据包到达)触发时执行 |
[!NOTE]
Verify 验证器的作用:确保 eBPF 程序不会导致内核崩溃。检查项包括:
- 无无限循环
- 无越界内存访问
- 执行指令数有上限
- 仅能调用白名单内的内核函数
7.3 eBPF 网络 Hook 点
背景
eBPF 可以在网络数据包处理路径的多个位置注入程序,实现流量过滤、转发、修改等功能。
核心 Hook 点
图解说明:
| Hook 点 | 位置 | 特点 | 适用场景 |
|---|---|---|---|
| XDP | 网卡驱动层 | 最早、最快 | DDoS 防护、简单丢弃/放行 |
| TC | sk_buff 分配后 | 功能丰富 | 流量整形、复杂策略 |
| Netfilter | 协议栈内部 | 传统方式 | iptables 规则 |
| Socket | 应用层接口 | 最靠近应用 | Service Mesh |
XDP vs TC 对比
| 特性 | XDP | TC |
|---|---|---|
| 位置 | 网卡驱动(最前) | sk_buff 分配后 |
| 速度 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
| 功能复杂度 | 简单(无完整协议信息) | 复杂(可访问完整 sk_buff) |
| Cilium 使用 | 部分功能(DDoS) | 主要使用 |
| 可实现功能 | DROP/PASS/TX/REDIRECT | SNAT/DNAT/策略/重定向 |
[!IMPORTANT]
Cilium 主要使用 TC Hook:
- XDP 过于靠前,无法获取完整的协议信息
- TC 可以实现地址转换(SNAT/DNAT)、策略路由等复杂功能
- TC 在 Cilium 中替代了 iptables 的功能
7.4 数据包收发流程
背景
理解数据包在 Linux 内核中的收发流程,是理解 eBPF 网络优化的基础。
收包流程(RX)
流程说明:
- 网卡接收:数据包到达物理网卡
- DMA 拷贝:网卡通过 DMA 将数据拷贝到内存(Ring Buffer)
- 硬中断:通知 CPU 有数据到达
- 软中断:ksoftirqd 处理数据包
- 分配 sk_buff:为数据包分配内核数据结构
- XDP/TC Hook:eBPF 程序介入点
- 协议栈处理:Netfilter → IP → TCP/UDP
- 应用读取:数据到达 Socket 缓冲区,应用程序读取
发包流程(TX)
[!TIP]
eBPF 优化的关键:在数据包进入完整协议栈之前,在 XDP 或 TC 层面就完成处理,避免不必要的协议栈开销。
7.5 TC ingress/egress 方向
背景
理解 TC 的 ingress 和 egress 方向,对于理解 Cilium 的流量处理至关重要。
原理
图解说明:
| 方向 | 定义 | 数据流向 |
|---|---|---|
| ingress | 入方向 | 网卡 → 协议栈 |
| egress | 出方向 | 协议栈 → 网卡 |
在 VETH 上的 ingress/egress
Cilium 的处理方式:
- 在 VETH 宿主机端的 TC ingress 挂载 eBPF 程序
- 当数据包从 Pod 发出时,到达 VETH 宿主机端的 ingress 方向
- eBPF 程序可以直接重定向(redirect),绕过宿主机协议栈
7.6 eBPF Map
背景
eBPF 程序运行在内核空间,如何与用户空间的程序通信?答案是 eBPF Map。
原理
图解说明:
| 特性 | 说明 |
|---|---|
| 数据结构 | Key-Value 存储(类似 Python 字典) |
| 作用 | 内核空间 ↔ 用户空间通信桥梁 |
| 类型 | Hash、Array、LRU、Ring Buffer 等 |
| 持久化 | 可通过文件系统(/sys/fs/bpf/)持久化 |
在 Cilium 中的应用
# 查看 Cilium 的 eBPF Map
cilium bpf ct list global # 连接跟踪表
cilium bpf lb list # 负载均衡表
cilium bpf policy get --all # 策略表
cilium bpf endpoint list # Endpoint 表
代码说明:
- Cilium Agent 将 Service、Endpoint、Policy 等信息写入 eBPF Map
- 内核中的 eBPF 程序读取 Map 进行决策
- 无需经过用户空间,实现高性能数据平面
7.7 抓包与 eBPF Hook 点
背景
使用 tcpdump 抓包时,可能会遇到"抓不到包"的情况。理解 eBPF Hook 点与 tcpdump 的位置关系,有助于排障。
原理
图解说明:
| 情况 | 能否抓到包 | 原因 |
|---|---|---|
| 入方向,TC 未 redirect | ✅ 能 | tcpdump 在 TC 之前 |
| 入方向,TC redirect | ✅ 能 | 数据包先经过 tcpdump |
| 出方向,TC redirect | ❌ 不能 | 数据包被 redirect,不经过 tcpdump |
[!WARNING]
Cilium eBPF Host Routing 抓包现象:
- 在 VETH 上只能看到单向流量
- 因为响应流量被
bpf_redirect_peer直接重定向了- 排障时需要在 Pod 内部抓包,或使用 Cilium 的
cilium monitor命令
7.8 eBPF 在 Cilium 中的应用
Cilium 的 eBPF 程序分布
Cilium 使用 eBPF 实现的功能:
| 功能 | 传统方式 | Cilium eBPF 方式 |
|---|---|---|
| Service 负载均衡 | kube-proxy + iptables | eBPF Map + TC |
| NetworkPolicy | iptables 规则 | eBPF Map + TC |
| SNAT/Masquerade | iptables MASQUERADE | eBPF TC |
| 连接跟踪 | nf_conntrack | eBPF CT Map |
| Host Routing | 协议栈路由 | bpf_redirect_peer |
📝 章节小结
本章介绍了 eBPF 技术及其在网络中的应用:
-
eBPF 概念:
- 从 cBPF 演进而来的内核扩展技术
- 无需重编译内核,动态加载程序
- "给内核打补丁"的能力
-
程序加载流程:
- 编写 → 编译 → Verify → JIT → 注入 Hook → 运行
-
网络 Hook 点:
- XDP:网卡驱动层,最快但功能简单
- TC:Traffic Control 层,Cilium 主要使用
- ingress/egress 方向理解
-
eBPF Map:
- 内核与用户空间通信的桥梁
- Key-Value 存储结构
-
在 Cilium 中的应用:
- 替代 iptables 实现 Service、NetworkPolicy
- 使用 bpf_redirect 实现 Host Routing
[!TIP]
学习建议:
- 记住 XDP 和 TC 的位置关系
- 理解 ingress/egress 方向的定义
- 了解 tcpdump 与 eBPF Hook 的位置关系
- 使用
cilium bpf命令查看 eBPF Map
[!IMPORTANT]
核心记忆点:
- XDP 最快但功能简单,TC 功能丰富是 Cilium 主力
- 数据包从网卡 → XDP → tcpdump → TC → 协议栈
- Cilium 用 eBPF 替代 iptables,实现高性能数据平面
第8章 Cilium 及安装模式介绍
🎯 学习目标
- 了解 Cilium 的核心架构与组件
- 掌握 Cilium 的三种安装模式及其区别
- 理解 native routing 与 host routing 的概念差异
- 学会使用 Helm 安装和配置 Cilium
- 掌握 Cilium 常用命令
8.1 Cilium 概述
背景
Cilium 是一个基于 eBPF 的高性能 CNI 插件,专为 Kubernetes 设计。它不仅提供网络连通性,还集成了安全策略、负载均衡、可观测性等功能。
核心架构
组件说明:
| 组件 | 位置 | 职责 |
|---|---|---|
| cilium-operator | Deployment | 集群级资源管理(IPAM、BGP 等) |
| cilium-agent | DaemonSet | 节点级网络配置、eBPF 程序管理 |
| cilium-envoy | 嵌入 Agent | L7 策略、可观测性 |
| hubble | 可选组件 | 网络流量可视化 |
Cilium 核心功能
8.2 三种安装模式
背景
Cilium 提供三种安装模式,对应不同的功能层次和内核要求。理解这三种模式是使用 Cilium 的基础。
模式概览
模式对比:
| 特性 | with kube-proxy | kube-proxy replacement | eBPF Host Routing |
|---|---|---|---|
| Service 实现 | iptables | eBPF | eBPF |
| Host 路由 | 内核协议栈 | 内核协议栈 | eBPF redirect |
| 性能 | 一般 | 较好 | 最优 |
| 内核要求 | 4.9+ | 4.19+ | 5.10+ |
| 兼容性 | 最好 | 较好 | 部分功能不支持 |
[!IMPORTANT]
概念澄清:
- native routing = direct routing = Host-GW → 路由转发方式
- host routing → eBPF 绕过宿主机协议栈的能力
这两个概念完全不同,不要混淆!
8.3 模式1: with kube-proxy
背景
这是最基础的模式,Cilium 只负责 CNI 网络连通性,Service 依然由 kube-proxy + iptables 实现。
原理
图解说明:
- Cilium 在 TC 层实现 Pod 网络
- Service 访问仍经过 iptables(kube-proxy 维护)
- 适合内核版本较低或需要最大兼容性的场景
Helm 安装参数
helm install cilium cilium/cilium --version 1.13.0 \
--namespace kube-system \
--set tunnel=disabled \
--set autoDirectNodeRoutes=true \
--set ipv4NativeRoutingCIDR="10.244.0.0/16"
参数说明:
| 参数 | 值 | 说明 |
|---|---|---|
tunnel |
disabled | 禁用 VxLAN 隧道,使用 direct routing |
autoDirectNodeRoutes |
true | 自动配置节点间路由 |
ipv4NativeRoutingCIDR |
Pod CIDR | 不做 SNAT 的地址范围 |
8.4 模式2: kube-proxy replacement
背景
此模式下 Cilium 完全替代 kube-proxy,使用 eBPF 实现 Service 负载均衡,不再需要 iptables。
原理
图解说明:
- Service ClusterIP、NodePort 由 eBPF 直接处理
- 不再需要 kube-proxy 和 iptables 规则
- 性能提升明显(减少 iptables 规则匹配)
Helm 安装参数
helm install cilium cilium/cilium --version 1.13.0 \
--namespace kube-system \
--set kubeProxyReplacement=strict \
--set k8sServiceHost=${API_SERVER_IP} \
--set k8sServicePort=6443 \
--set tunnel=disabled \
--set autoDirectNodeRoutes=true \
--set ipv4NativeRoutingCIDR="10.244.0.0/16"
关键参数:
| 参数 | 值 | 说明 |
|---|---|---|
kubeProxyReplacement |
strict | 严格模式,完全替代 kube-proxy |
k8sServiceHost |
API Server IP | 必须提供,因为没有 kube-proxy |
k8sServicePort |
6443 | API Server 端口 |
[!NOTE]
kubeProxyReplacement 取值:
disabled:不替代 kube-proxypartial:部分替代strict:严格替代,完全使用 eBPF
8.5 模式3: eBPF Host Routing
背景
这是性能最优的模式。除了替代 kube-proxy,还使用 eBPF 绕过宿主机协议栈的路由处理。
原理
图解说明:
- 使用
bpf_redirect_peer和bpf_redirect_neigh函数 - 绕过宿主机的协议栈处理
- 同节点 Pod 通信只经过一次协议栈(Pod 内)
Host Routing 的核心价值
Helm 安装参数
helm install cilium cilium/cilium --version 1.13.0 \
--namespace kube-system \
--set kubeProxyReplacement=strict \
--set k8sServiceHost=${API_SERVER_IP} \
--set k8sServicePort=6443 \
--set tunnel=disabled \
--set autoDirectNodeRoutes=true \
--set ipv4NativeRoutingCIDR="10.244.0.0/16" \
--set bpf.masquerade=true
新增关键参数:
| 参数 | 值 | 说明 |
|---|---|---|
bpf.masquerade |
true | 使用 eBPF 实现 SNAT(而非 iptables) |
[!WARNING]
eBPF Host Routing 的限制:
- 不支持 IPsec 加密
- 内核要求 5.10+
- 部分高级功能可能受限
8.6 安装参数详解
Helm Values 查找方法
# 1. 添加 Cilium Helm 仓库
helm repo add cilium https://helm.cilium.io/
# 2. 下载 chart 查看 values.yaml
helm pull cilium/cilium --version 1.13.0 --untar
cat cilium/values.yaml
核心配置分类:
| 配置类别 | 典型参数 |
|---|---|
| 网络模式 | tunnel, routingMode, autoDirectNodeRoutes |
| kube-proxy | kubeProxyReplacement, k8sServiceHost |
| eBPF 功能 | bpf.masquerade, hostRouting |
| IPAM | ipam.mode, ipam.operator.clusterPoolIPv4PodCIDR |
| 调试 | debug.enabled, debug.verbose |
常用安装参数对照表
| 需求 | 参数设置 |
|---|---|
| 使用 Direct Routing | tunnel=disabled |
| 使用 VxLAN | tunnel=vxlan(默认) |
| 替代 kube-proxy | kubeProxyReplacement=strict |
| 启用 eBPF masquerade | bpf.masquerade=true |
| 启用 BGP | bgpControlPlane.enabled=true |
| 启用 Hubble | hubble.enabled=true |
8.7 Cilium 常用命令
cilium status
# 查看 Cilium 状态
cilium status
# 查看详细状态
cilium status --verbose
输出解读:
| 字段 | 说明 |
|---|---|
KubeProxyReplacement |
strict/partial/disabled |
Host Routing |
Legacy(传统)或 BPF(eBPF) |
Masquerading |
IPTables 或 BPF |
Tunnel Mode |
Disabled/VXLAN/Geneve |
cilium bpf 命令
# 查看 eBPF Service 表(替代 iptables -L)
cilium bpf lb list
# 查看连接跟踪表
cilium bpf ct list global
# 查看 Endpoint 信息
cilium bpf endpoint list
# 查看策略表
cilium bpf policy get --all
cilium monitor
# 实时监控数据包流向
cilium monitor
# 详细模式
cilium monitor -v
# 查看 datapath 事件
cilium monitor --type trace
8.8 VxLAN vs Direct Routing
背景
Cilium 支持两种基础网络模式:VxLAN(隧道)和 Direct Routing(路由)。
对比
| 特性 | VxLAN | Direct Routing |
|---|---|---|
| 跨子网 | ✅ 支持 | ❌ 需同二层 |
| 性能 | 有封装开销 | 更高 |
| MTU | 需调整(-50) | 无需调整 |
| 配置 | 默认 | tunnel=disabled |
📝 章节小结
本章介绍了 Cilium 的架构与安装模式:
-
Cilium 架构:
- cilium-operator:集群级控制平面
- cilium-agent:节点级数据平面
- eBPF 程序:实际的数据包处理
-
三种安装模式:
模式 Service 路由 性能 with kube-proxy iptables 协议栈 一般 kube-proxy replacement eBPF 协议栈 较好 eBPF Host Routing eBPF eBPF redirect 最优 -
关键概念澄清:
- native/direct routing = Host-GW 路由方式
- host routing = eBPF 绕过协议栈
- 两者是完全不同的概念!
-
Helm 安装:
kubeProxyReplacement=strict:替代 kube-proxybpf.masquerade=true:启用 eBPF Host Routing
[!TIP]
学习建议:
- 准备三套安装脚本作为基础模板
- 使用
cilium status --verbose验证安装结果- 通过
cilium bpf lb list验证 kube-proxy replacement- 参考官方 values.yaml 了解所有配置项
[!IMPORTANT]
模式选择指南:
- 内核 < 4.19 → with kube-proxy
- 内核 ≥ 4.19,追求稳定 → kube-proxy replacement
- 内核 ≥ 5.10,追求极致性能 → eBPF Host Routing
第9章 Native-Routing-with-kubeProxy 模式
🎯 学习目标
- 理解 Native Routing 与 Host-GW 的等价关系
- 掌握同节点 Pod 通信的数据包流程
- 掌握跨节点 Pod 通信的数据包流程
- 理解 32 位掩码的作用与 L3 路由转发
- 理解 Cilium eBPF ARP 劫持机制
9.1 Native Routing 概述
背景
Native Routing 是 Cilium 中与 VxLAN 隧道模式相对的另一种网络模式。它不使用封装,而是依赖节点间的路由进行 Pod 网络通信。
核心概念
[!IMPORTANT]
概念等价关系:Native Routing = Direct Routing = Host-GW
三者描述的是同一种网络模式,只是不同场景下的叫法不同。
启用 Native Routing
# 关键参数
--set tunnel=disabled # 禁用隧道
--set autoDirectNodeRoutes=true # 自动配置节点路由
--set ipv4NativeRoutingCIDR="10.244.0.0/16" # 原生路由网段
9.2 Pod 的 32 位掩码
背景
在 Cilium 中,Pod 的 IP 地址使用 32 位掩码(/32),这与传统 CNI 有所不同。
原理
# Pod 内查看 IP
$ ip addr show eth0
inet 10.0.2.253/32 scope global eth0
32 位掩码意味着什么?
| 掩码 | 含义 | 结果 |
|---|---|---|
| /24 | 同网段有 254 个主机 | 同网段走二层 |
| /32 | "网段"只有自己 | 与任何地址都不同网段 |
[!NOTE]
32 位掩码的设计目的:
- 强制所有流量走 L3 路由(而非 L2 交换)
- 流量必须经过网关,便于 eBPF 程序介入处理
- 这是 Cilium 能够劫持和重定向流量的基础
9.3 同节点 Pod 通信
背景
理解同节点 Pod 间的通信流程,是理解 Cilium 数据平面的第一步。
场景描述
Pod A (10.0.2.253) → Pod B (10.0.2.36)
两个 Pod 位于同一节点
网络拓扑
路由表分析
# Pod A 内部路由表
$ ip route
default via 10.0.2.131 dev eth0 # 默认路由,网关是 cilium_host
10.0.2.131 dev eth0 scope link # 网关地址的直连路由
关键点解读:
| 路由条目 | 说明 |
|---|---|
default via 10.0.2.131 |
所有流量发往 cilium_host |
10.0.2.131 dev eth0 |
告诉系统如何到达网关 |
数据包流程
ARP 劫持机制
传统行为 vs Cilium 行为:
| 步骤 | 传统 CNI | Cilium |
|---|---|---|
| ARP 请求 | 请求网关 MAC | 请求网关 MAC |
| ARP 响应 | 返回网关 MAC | 返回 lxc 网卡 MAC |
| 数据包 dst-MAC | 填写网关 MAC | 填写 lxc 网卡 MAC |
# 验证 ARP 响应
$ arping -I eth0 10.0.2.131
# 返回的 MAC 是 lxc 网卡的 MAC,而非 cilium_host 的 MAC
[!WARNING]
Cilium 的 ARP 劫持:Pod 认为它在和网关通信,但实际上 eBPF 程序劫持了 ARP 响应,返回的是 veth pair 对端(lxc 网卡)的 MAC 地址。这使得数据包直接发往 lxc 网卡,而非经过完整的协议栈路由。
关键发现
虽然路由表显示出接口是 cilium_host,但抓包发现:
# 在 cilium_host 上抓包
$ tcpdump -pne -i cilium_host icmp
# 没有任何包!
# 在 lxc-B 上抓包
$ tcpdump -pne -i lxc-yyyy icmp
# 能看到 ICMP 请求和响应!
[!IMPORTANT]
同节点通信的精髓:
- 路由表说走 cilium_host,但实际 eBPF 将包 redirect 到 lxc 网卡
- 数据包直接从 lxc-A → lxc-B,不经过协议栈路由
- 这就是 Cilium 的 "跳跃式转发" 特性
9.4 跨节点 Pod 通信
背景
跨节点通信需要经过物理网络(或底层网络),流程比同节点通信多了节点间路由的步骤。
场景描述
Pod A (10.0.2.253, Node 1) → Pod C (10.0.1.173, Node 2)
两个 Pod 位于不同节点
网络拓扑
宿主机路由表
# Node 1 路由表
$ ip route
default via 172.18.0.1 dev eth0
10.0.1.0/24 via 172.18.0.4 dev eth0 # 去 10.0.1.x 走 Node 2
10.0.2.0/24 via 10.0.2.131 dev cilium_host # 本节点 Pod 网段
10.0.3.0/24 via 172.18.0.3 dev eth0 # 去 10.0.3.x 走 Node 3
路由表解读:
| 目的网段 | 下一跳 | 说明 |
|---|---|---|
| 10.0.1.0/24 | 172.18.0.4 | Pod C 在 Node 2,走 Node 2 |
| 10.0.2.0/24 | cilium_host | 本节点 Pod,走内部网关 |
| 默认 | 172.18.0.1 | 其他流量走默认网关 |
数据包流程
MAC 地址变化
抓包验证(在 Node1 eth0 上):
$ tcpdump -pne -i eth0 icmp
# src-MAC: 12:00:00:02 (Node1 eth0)
# dst-MAC: 12:00:00:04 (Node2 eth0)
# src-IP: 10.0.2.253 (不变)
# dst-IP: 10.0.1.173 (不变)
[!TIP]
跨节点通信的核心规律:
- IP 地址不变:src-IP 和 dst-IP 全程保持
- MAC 地址逐跳变化:每经过一个路由点,MAC 都会更新
- 这就是 L3 路由转发 的本质
9.5 Cilium 与传统 CNI 的差异
架构对比
关键差异
| 特性 | 传统 CNI | Cilium |
|---|---|---|
| 路由执行者 | Linux 协议栈 | eBPF 程序 |
| ARP 响应 | 真实网关 MAC | lxc 网卡 MAC(劫持) |
| 同节点转发 | 经过 Bridge | eBPF redirect |
| 抓包位置 | 网卡上可抓 | 部分位置抓不到 |
| 转发风格 | 一跳一跳 | "跳跃式"转发 |
📝 章节小结
本章介绍了 Cilium Native-Routing 模式下的数据包转发流程:
-
Native Routing 概念:
- 等价于 Host-GW 模式
- 使用
tunnel=disabled启用 - 依赖节点间路由(非隧道封装)
-
32 位掩码的作用:
- 强制所有流量走 L3 路由
- 便于 eBPF 程序介入处理
-
同节点 Pod 通信:
- eBPF 劫持 ARP,返回 lxc 网卡 MAC
- 数据包通过 lxc 网卡直接 redirect,不走协议栈路由
- 路由表显示走 cilium_host,实际走 lxc 网卡
-
跨节点 Pod 通信:
- 遵循传统 L3 路由规则
- IP 不变,MAC 逐跳变化
- 依赖宿主机路由表(autoDirectNodeRoutes)
-
与传统 CNI 的差异:
- ARP 劫持机制
- eBPF redirect 替代协议栈路由
- "跳跃式"转发
[!TIP]
学习建议:
- 使用
tcpdump在不同接口抓包,验证数据流向- 使用
arping验证 ARP 响应的 MAC 地址- 对比路由表和实际抓包结果,理解 eBPF redirect
[!IMPORTANT]
Native Routing with kube-proxy 模式核心特点:
- Pod 间流量:eBPF redirect(bypass 协议栈)
- Service 流量:iptables(kube-proxy 维护)
- 这是一个"混合"模式
第10章 Native-Routing-with-eBPF-HostRouting 模式
🎯 学习目标
- 掌握 eBPF Host Routing 的核心函数
- 理解
bpf_redirect_peer和bpf_redirect_neigh的作用 - 掌握同节点/跨节点 Pod 通信的数据流
- 理解 TC Hook 的位置与方向
- 了解 Socket LB 机制
10.1 eBPF Host Routing 概述
背景
eBPF Host Routing 是 Cilium 的最高级模式,它在 kube-proxy replacement 基础上,进一步使用 eBPF 绕过宿主机协议栈的路由处理,实现最高性能。
核心优势
[!IMPORTANT]
核心优势:跳过宿主机协议栈处理,减少:
- 上下文切换
- 数据包拷贝
- 中断处理
- iptables 规则匹配
10.2 核心 eBPF 函数
两个关键函数
函数对比:
| 函数 | 作用 | 跳转目标 |
|---|---|---|
bpf_redirect_peer |
同节点 Pod 间 redirect | 直接到目的 Pod 的 eth0 |
bpf_redirect_neigh |
跨节点通信 redirect | 从 lxc 网卡到物理网卡 |
内核版本影响
| 内核版本 | 可用函数 | 跳转能力 |
|---|---|---|
| < 5.10 | bpf_redirect |
只能跳到 lxc 网卡 |
| ≥ 5.10 | bpf_redirect_peer |
直接跳到 Pod 的 eth0 |
[!NOTE]
为什么需要 5.10+ 内核:
bpf_redirect_peer在 Linux 5.10 引入- 它允许直接跳转到 veth pair 的对端(即 Pod 的 eth0)
- 这使得数据包完全绕过宿主机协议栈
10.3 同节点 Pod 通信
场景描述
Pod A (10.0.2.253) → Pod B (10.0.2.141)
两个 Pod 位于同一节点,使用 eBPF Host Routing
数据包流程
抓包验证
# 在 lxc-A(Pod A 对应的网卡)抓包
$ tcpdump -pne -i lxc-xxxx icmp
# 结果:只能看到 ICMP Request,没有 Reply!
# 原因:Reply 通过 bpf_redirect_peer 直接送到 Pod A 的 eth0
# 在 Pod A 内部抓包
$ tcpdump -pne -i eth0 icmp
# 结果:ICMP Request 和 Reply 都有!
[!WARNING]
抓包"诡异"现象:
- lxc 网卡上只能看到"去的包"(Request)
- "回的包"(Reply)直接 redirect 到 Pod 的 eth0
- 这是 eBPF Host Routing 的正常行为!
10.4 跨节点 Pod 通信
场景描述
Pod A (10.0.2.253, Node 1) → Pod C (10.0.1.193, Node 2)
两个 Pod 位于不同节点
数据包流程
抓包验证
# 在 Node1 的 lxc-A 抓包
$ tcpdump -pne -i lxc-xxxx icmp
# 只有 ICMP Request,没有 Reply
# 在 Node1 的 eth0 抓包
$ tcpdump -pne -i eth0 icmp
# Request 和 Reply 都有!
# 说明:
# - 发出的包经过 lxc 网卡
# - 返回的包直接 redirect 到 Pod 的 eth0,不经过 lxc
10.5 TC Hook 位置与方向
背景
理解 TC Hook 的位置和方向,是理解 Cilium 数据平面的关键。
四个关键 Hook
Hook 说明:
| Hook 名称 | 位置 | 方向 | 作用 |
|---|---|---|---|
from_container |
lxc 网卡 | ingress | 处理 Pod 发出的包 |
to_container |
调用函数 | - | 发往 Pod 的包 |
to_netdev |
物理网卡 | egress | 发往外部的包 |
from_netdev |
物理网卡 | ingress | 收到外部的包 |
方向判断口诀
Ingress(进入):数据包进入某个网卡
Egress(发出):数据包从某个网卡发出
从 Pod 出来 → lxc ingress → from_container
发往外部网络 → eth0 egress → to_netdev
从外部收包 → eth0 ingress → from_netdev
10.6 Socket LB 机制
背景
在 eBPF Host Routing 模式下,Service 访问也通过 eBPF 实现(而非 iptables)。
原理
与 iptables 的对比
| 特性 | iptables (kube-proxy) | Socket LB (eBPF) |
|---|---|---|
| DNAT 位置 | 宿主机协议栈 | Pod 的 Socket 层 |
| 第一跳目的 IP | 仍是 ClusterIP | 已是 Pod IP |
| SNAT | 需要(externalTrafficPolicy: Cluster) | 可不需要 |
| 抓包看到的 IP | ClusterIP → Pod IP 转换 | 直接是 Pod IP |
抓包验证
# 访问 Service
$ curl 172.18.0.2:32000
# 在 Pod 内抓包
$ tcpdump -pne -i eth0 port 80
# 结果:目的 IP 直接是 Pod IP,不是 NodePort IP!
[!TIP]
Socket LB 的意义:
- 在 Pod 发包前就完成 Service → Pod 的解析
- 数据包从发出的第一跳就是 Pod IP
- 不需要经过宿主机做 DNAT
10.7 eBPF Host Routing 的限制
不支持的场景
| 功能 | 是否支持 | 原因 |
|---|---|---|
| IPsec 加密 | ❌ | 需要经过协议栈处理 |
| WireGuard 加密 | ❌ | 需要经过协议栈处理 |
| 某些 NetworkPolicy | 部分 | 需要协议栈介入 |
[!CAUTION]
如果需要加密:使用 IPsec 或 WireGuard 时,不能启用 eBPF Host Routing。
流量需要进入协议栈进行加解密处理。
10.8 验证 eBPF Host Routing 状态
# 查看 Cilium 状态
$ cilium status --verbose
# 关键字段
KubeProxyReplacement: Strict
Host Routing: BPF # ← 这里显示 BPF 表示已启用
Masquerading: BPF
| Host Routing 值 | 含义 |
|---|---|
Legacy |
传统模式,使用协议栈路由 |
BPF |
eBPF 模式,绕过协议栈 |
📝 章节小结
本章介绍了 Cilium 最高级模式 —— eBPF Host Routing:
-
核心函数:
bpf_redirect_peer:同节点 Pod 间直接跳转bpf_redirect_neigh:跨节点通信,lxc → 物理网卡
-
同节点通信:
- lxc 网卡只能看到"去的包"
- "回的包"直接 redirect 到 Pod 的 eth0
-
跨节点通信:
- 发出的包:lxc → bpf_redirect_neigh → 物理网卡
- 收到的包:物理网卡 → bpf_redirect_peer → Pod eth0
-
TC Hook:
from_container:lxc ingress,处理 Pod 发出的包from_netdev:物理网卡 ingress,处理收到的包to_netdev:物理网卡 egress,处理发出的包
-
Socket LB:
- Service 解析在 Pod 的 Socket 层完成
- 第一跳目的 IP 就是 Pod IP
-
限制:
- 不支持 IPsec/WireGuard 加密
- 需要内核 ≥ 5.10
[!TIP]
学习要点:
- 理解两个 eBPF 函数的作用
- 在不同接口抓包,验证 redirect 行为
- 用
cilium status确认 Host Routing: BPF
[!IMPORTANT]
模式对比总结:
模式 Service Pod 路由 性能 with kube-proxy iptables eBPF redirect 一般 kube-proxy replacement eBPF eBPF redirect 较好 eBPF Host Routing Socket LB bpf_redirect_peer/neigh 最优
第11章 Cilium-VxLAN 模式
🎯 学习目标
- 理解 VxLAN 诞生的背景与解决的问题
- 掌握 VxLAN 报文结构
- 理解 VTEP、VNI 等核心概念
- 掌握 Linux 下创建 VxLAN 设备的方法
- 理解 Overlay vs Underlay 网络
11.1 VxLAN 诞生背景
背景
VxLAN(Virtual eXtensible Local Area Network)是由 VMware、Cisco 等厂商提出的一种 Overlay 网络技术。
解决的问题
核心场景:
| 场景 | 问题 | VxLAN 方案 |
|---|---|---|
| 虚机迁移 | 跨机房后 IP/MAC 变化 | 封装后传输,IP/MAC 保持 |
| 多租户隔离 | VLAN ID 只有 4094 个 | VNI 支持 1600 万+ |
| 跨三层通信 | 二层网络无法跨越路由器 | Overlay 隧道穿越 |
大二层概念
[!NOTE]
大二层的本质:将中间复杂的三层路由网络抽象成一个"大交换机"。
无论底层网络多复杂,对上层应用来说就像在同一个二层局域网内。
11.2 VxLAN 报文结构
封装原理
报文各层说明
| 层级 | 内容 | 说明 |
|---|---|---|
| 外层 MAC | VTEP 的 MAC 地址 | 源/目的都是 VTEP 设备 |
| 外层 IP | VTEP 的 IP 地址 | 如节点的物理网卡 IP |
| UDP | 端口 8472 | Cilium/Flannel 使用 8472 |
| VxLAN Header | 8 字节,含 VNI | 标识隧道/租户 |
| 内层包 | 原始完整以太网帧 | Pod 间通信的真实数据 |
+----------------+----------------+---------------+
| 外层 MAC | 外层 IP | UDP 8472 |
+----------------+----------------+---------------+
| VxLAN Header (VNI) | 原始数据包 |
+----------------------+-------------------------+
[!IMPORTANT]
VxLAN 的核心思想:把原始数据包作为外层 UDP 的 Payload。
相当于把一个信封(原始包)塞进另一个信封(VxLAN 包)发送。
11.3 核心概念
VTEP
VTEP(VxLAN Tunnel End Point)是 VxLAN 隧道的端点设备。
VTEP 的职责:
| 方向 | 操作 | 说明 |
|---|---|---|
| 发送 | 封装 | 添加外层 MAC/IP/UDP/VxLAN Header |
| 接收 | 解封装 | 剥离外层,露出原始包 |
VNI
VNI(VxLAN Network Identifier)是 VxLAN 的网络标识。
| 对比 | VLAN ID | VNI |
|---|---|---|
| 位数 | 12 位 | 24 位 |
| 可用数量 | 4094 个 | 约 1600 万个 |
| 用途 | 传统交换机隔离 | 大规模多租户隔离 |
[!TIP]
在 Cilium 中:VNI 的使用比较灵活,不像传统网络那样要求两端 VNI 严格匹配。
Cilium 可能会动态分配 VNI,这是虚拟化网络与传统网络的区别之一。
11.4 Overlay vs Underlay
概念对比
| 特性 | Overlay | Underlay |
|---|---|---|
| 定义 | 构建在现有网络之上的逻辑网络 | 承载 Overlay 的物理网络 |
| 代表 | VxLAN、GRE、GENEVE | 数据中心交换机、路由器 |
| 优势 | 灵活、跨三层、多租户 | 性能高、延迟低 |
| 劣势 | 封装开销、MTU 减少 | 配置复杂、VLAN 数量有限 |
11.5 Linux 创建 VxLAN 设备
命令格式
ip link add <name> type vxlan \
id <VNI> \
dev <物理网卡> \
local <本端 VTEP IP> \
remote <对端 VTEP IP> \
dstport <端口>
示例
# 在 Node1 创建 VxLAN 设备
ip link add vxlan0 type vxlan \
id 100 \
dev eth0 \
local 192.168.1.10 \
remote 192.168.1.20 \
dstport 8472
# 启用设备
ip link set vxlan0 up
# 配置 IP
ip addr add 10.0.0.1/24 dev vxlan0
在对端 Node2 创建对称配置:
ip link add vxlan0 type vxlan \
id 100 \
dev eth0 \
local 192.168.1.20 \
remote 192.168.1.10 \
dstport 8472
ip link set vxlan0 up
ip addr add 10.0.0.2/24 dev vxlan0
验证
# 查看 VxLAN 设备
ip -d link show vxlan0
# 测试连通性
ping 10.0.0.2
# 抓包验证
tcpdump -pne -i eth0 udp port 8472
11.6 Cilium 的 VxLAN 实现
启用方式
# 默认就是 VxLAN 模式,或显式指定
helm install cilium cilium/cilium \
--set tunnel=vxlan # 或 geneve
Cilium 创建的设备
# 查看 Cilium 创建的 VxLAN 设备
$ ip -d link show cilium_vxlan
# 输出示例
cilium_vxlan: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 ...
vxlan id 1 ... dstport 8472 ...
与 Native Routing 的对比
| 特性 | VxLAN 模式 | Native Routing 模式 |
|---|---|---|
| 跨节点通信 | VxLAN 封装 | 路由转发 |
| 底层网络要求 | 仅需 IP 可达 | 需要配置路由 |
| MTU | 减少(封装开销) | 保持原始 |
| 性能 | 略低(封装/解封装) | 较高 |
| 配置复杂度 | 低(默认即可) | 较高(需路由配置) |
📝 章节小结
本章介绍了 VxLAN 技术及其在 Cilium 中的应用:
-
VxLAN 背景:
- 解决数据中心虚机迁移问题
- 支持大规模多租户(VNI 1600 万+)
- 将三层网络抽象为"大二层"
-
报文结构:
- 外层 MAC + IP + UDP(8472) + VxLAN Header
- 内层是原始完整以太网帧
- 原始包成为外层的 Payload
-
核心概念:
- VTEP:隧道端点,负责封装/解封装
- VNI:网络标识,24 位,约 1600 万个
-
Overlay vs Underlay:
- Overlay 运行在 Underlay 之上
- VxLAN 是典型的 Overlay 技术
-
Linux 创建 VxLAN:
- 使用
ip link add type vxlan命令 - 需要指定 VNI、local、remote、dstport
- 使用
-
Cilium 实现:
- 默认使用 VxLAN 模式
- 创建
cilium_vxlan设备
[!TIP]
选择 VxLAN 的场景:
- 节点跨三层网络(不在同一二层)
- 底层网络配置受限
- 快速部署,不想配置路由
- 多租户隔离需求
[!IMPORTANT]
VxLAN vs Native Routing 选择:
- 同二层网络 → Native Routing(性能更好)
- 跨三层网络 / 配置简单 → VxLAN
第12章 Containerlab 实现通用 VxLAN 环境
🎯 学习目标
- 掌握 Containerlab 搭建 VxLAN 实验环境
- 深入理解 VxLAN 数据包转发的三步走
- 理解 VxLAN 封装过程中 MAC 地址的变化
- 掌握 VxLAN 配置的关键参数
- 能够独立分析 VxLAN 抓包结果
12.1 实验拓扑
背景
Containerlab 是一个用于创建网络实验环境的工具,非常适合用来学习和验证网络概念。
拓扑图
拓扑说明:
| 设备 | 接口 | IP 地址 | 说明 |
|---|---|---|---|
| Server0 | eth0 | 10.1.5.10 | 源端 Pod |
| GW0 | eth1 | 10.1.5.1 | Server0 的网关 |
| GW0 | eth2 | 172.12.1.10 | 物理互联接口 |
| GW0 | vxlan0 | 1.1.1.1 | VxLAN 隧道端点 |
| GW1 | vxlan0 | 1.1.1.2 | VxLAN 隧道端点 |
| GW1 | eth2 | 172.12.1.11 | 物理互联接口 |
| GW1 | eth1 | 10.1.8.1 | Server1 的网关 |
| Server1 | eth0 | 10.1.8.10 | 目的端 Pod |
12.2 关键配置
VxLAN 接口配置
# 在 GW0 上配置
# 1. 创建 vxlan0 接口
ip link add vxlan0 type vxlan \
id 10 \
remote 172.12.1.11 \
dstport 8472
# 2. 配置 IP 地址
ip addr add 1.1.1.1/24 dev vxlan0
# 3. 启用接口
ip link set vxlan0 up
# 4. 添加路由
ip route add 10.1.8.0/24 via 1.1.1.2 dev vxlan0
配置参数说明:
| 参数 | 值 | 说明 |
|---|---|---|
id |
10 | VNI ID,两端必须一致 |
remote |
172.12.1.11 | 对端 VTEP 的物理 IP |
local |
(默认) | 本端物理 IP,默认使用同网段接口 |
dstport |
8472 | VxLAN 目的端口 |
为什么需要两套地址?
[!IMPORTANT]
为什么需要物理地址:VxLAN 接口是虚拟接口,没有物理网线连接。
数据包实际上需要通过物理接口(172.12.1.x)发送。
虚拟接口"借助"物理接口完成数据传输。
12.3 数据包转发三步走
核心概念
VxLAN 数据包转发可以分解为三个步骤:
三步详解
| 步骤 | 发生位置 | 关键操作 | 路由条目 |
|---|---|---|---|
| 第一步 | Server0 | 默认路由引导到网关 | default via 10.1.5.1 |
| 第二步 | GW0 | 路由引入 VxLAN 接口 | 10.1.8.0/24 via 1.1.1.2 dev vxlan0 |
| 第三步 | vxlan0 | VxLAN 封装 + 发送 | 查询 remote/local 配置 |
[!TIP]
通用套路:这三步是所有 Overlay 网络的通用模式:
- 引导数据包到 VTEP 设备
- 通过路由让数据包"经过" VxLAN 接口
- VxLAN 接口进行封装并发送
无论是 VxLAN、IPIP、GRE,都是这个套路!
12.4 抓包分析
抓包命令
# 在 GW0 的 eth2 接口抓包
tcpdump -pne -i eth2 udp port 8472 -w vxlan.pcap
抓包结果分析
外层 MAC: aa:ce:ab:xx:xx:xx -> 9a:83:2a:xx:xx:xx
外层 IP: 172.12.1.10 -> 172.12.1.11
UDP: 随机端口 -> 8472
VxLAN: VNI = 10
内层 MAC: 76:29:63:xx:xx:xx -> 16:26:36:xx:xx:xx
内层 IP: 10.1.5.10 -> 10.1.8.10
MAC 地址来源
[!WARNING]
内层 MAC 不是 Server 的:内层源 MAC 是 vxlan0 接口的 MAC 地址,而不是 Server0 的 MAC。
这是因为数据包"经过"了 vxlan0 接口,MAC 地址会被替换。
12.5 为什么下一跳是 1.1.1.2?
问题
为什么路由是 10.1.8.0/24 via 1.1.1.2 而不是 via 172.12.1.11?
答案
[!CAUTION]
关键点:
- 下一跳必须指向 VxLAN 接口的 IP(1.1.1.2)
- 只有经过 VxLAN 接口,才会触发封装
- 如果直接指向物理 IP(172.12.1.11),包不会被封装,无法到达目的地
12.6 VxLAN 接口的 IP 地址作用
问题
vxlan0 接口的 IP 地址(1.1.1.1/1.1.1.2)在通信过程中真的用到了吗?
分析
| 观察点 | 结果 |
|---|---|
| 外层 IP | 172.12.1.10 → 172.12.1.11(物理接口 IP) |
| 内层 IP | 10.1.5.10 → 10.1.8.10(Server IP) |
| vxlan0 IP | 没有出现在数据包中 |
那为什么还需要配置?
- 路由匹配:下一跳 1.1.1.2 需要有对应的接口
- ARP 学习:需要知道 1.1.1.2 对应的 MAC 地址
- 接口可达性:用于判断 VxLAN 隧道是否可用
[!NOTE]
vxlan0 的 IP 地址主要用于路由决策和 ARP 学习,
而不是直接用于数据包封装。
12.7 实践练习
练习 1:使用 ip 命令搭建 VxLAN
在两台 Linux 机器上搭建 VxLAN 隧道:
# 机器 A (192.168.1.10)
ip link add vxlan0 type vxlan id 100 remote 192.168.1.20 dstport 8472
ip addr add 10.0.0.1/24 dev vxlan0
ip link set vxlan0 up
# 机器 B (192.168.1.20)
ip link add vxlan0 type vxlan id 100 remote 192.168.1.10 dstport 8472
ip addr add 10.0.0.2/24 dev vxlan0
ip link set vxlan0 up
# 测试
ping 10.0.0.2
练习 2:观察 ARP 过程
# 清除 ARP 缓存
ip neigh del 10.0.0.2 dev vxlan0
# 抓包观察 ARP
tcpdump -pne -i eth0 udp port 8472
# 触发通信
ping 10.0.0.2
📝 章节小结
本章通过 Containerlab 实验环境,深入理解了 VxLAN 的工作原理:
-
实验拓扑:
- Server → GW (VTEP) → 物理网络 → GW (VTEP) → Server
- 需要两套地址:虚拟地址 + 物理地址
-
VxLAN 配置:
- VNI ID 两端一致
- remote 指向对端物理 IP
- dstport 通常为 8472
-
三步转发:
- 第一步:引导到网关(默认路由)
- 第二步:路由引入 VxLAN 接口
- 第三步:VxLAN 封装并发送
-
MAC 地址变化:
- 内层 MAC:vxlan0 接口的 MAC
- 外层 MAC:物理接口的 MAC
-
下一跳设置:
- 必须指向 VxLAN 接口的 IP
- 不能直接指向物理 IP
[!IMPORTANT]
Overlay 网络通用模式:无论是 VxLAN、IPIP 还是 GRE,都遵循相同的三步走:
- 引导数据包到隧道设备
- 路由让包"经过"隧道接口
- 隧道接口进行封装
掌握这个模式,所有 Overlay 网络都能理解!
第13章 Cilium-VxLAN-DataPath
🎯 学习目标
- 理解 Cilium VxLAN 与传统 VxLAN 的差异
- 掌握 Cilium eBPF Map 存储隧道信息的方式
- 理解 to_overlay / from_overlay eBPF Hook
- 掌握 Cilium Identity 概念
- 学会使用 cilium monitor 调试数据路径
13.1 Cilium VxLAN 概述
背景
Cilium 的 VxLAN 模式是其默认的 Overlay 方案,但实现方式与传统 VxLAN 有显著差异。
与传统 VxLAN 的对比
| 特性 | 传统 VxLAN | Cilium VxLAN |
|---|---|---|
| 接口 IP | 有(用于 ARP 学习) | 无 |
| 流量引入方式 | 路由(via VxLAN 接口) | eBPF redirect |
| VNI 来源 | 静态配置 | Cilium Identity ID |
| 隧道信息存储 | 内核配置 | eBPF Map |
| MAC 地址 | 替换为 vxlan0 的 MAC | 保留原始 Pod 的 MAC |
[!IMPORTANT]
Cilium VxLAN 的核心差异:
- 不依赖路由,使用 eBPF redirect 引入流量
- 不需要在 vxlan 接口配置 IP 地址
- VNI ID 使用 Cilium Identity,而非静态配置
13.2 cilium_vxlan 设备
查看设备
# 查看 VxLAN 类型的设备
$ ip -d link show type vxlan
cilium_vxlan: <BROADCAST,MULTICAST,UP,LOWER_UP> ...
vxlan id 1 ... dstport 8472 nolearning ...
奇怪之处
# 传统 VxLAN 会显示 remote、local 等信息
# Cilium 的 cilium_vxlan 几乎没有这些信息!
$ ip -d link show cilium_vxlan
# 没有 remote
# 没有 local
# 只有基本的 vxlan 配置
[!NOTE]
为什么没有隧道信息?因为 Cilium 的隧道信息存储在 eBPF Map 中,而不是内核的 VxLAN 模块配置。
13.3 eBPF Map 存储隧道信息
查看隧道信息
# 查看 Cilium 的隧道 Map
$ cilium bpf tunnel list
TUNNEL VALUE
10.0.2.0/24 172.18.0.3
10.0.0.0/24 172.18.0.2
输出解释:
| 字段 | 含义 |
|---|---|
TUNNEL |
目标 Pod CIDR |
VALUE |
对端 VTEP 的 IP(节点 IP) |
隧道 Map 结构
[!TIP]
eBPF Map 的优势:
- 查询速度快(O(1) 复杂度)
- 可动态更新,无需重启
- 与 eBPF 程序紧密集成
13.4 Cilium Identity
概念
Cilium 为每个 Pod 分配一个 Identity ID,用于:
- 安全策略
- VxLAN 封装的 VNI
# 查看 Pod 的 Identity
$ cilium endpoint list
ENDPOINT IDENTITY LABELS STATUS
238 68863 k8s:app=web ready
145 68863 k8s:app=web ready
Identity vs Endpoint
| 概念 | 粒度 | 用途 |
|---|---|---|
| Identity | 一类 Pod(相同标签) | 安全策略、VNI |
| Endpoint | 单个 Pod | 具体路由、状态 |
[!IMPORTANT]
VNI = Identity IDVxLAN 封装时,VNI 字段使用的是目标 Pod 的 Identity ID。
这使得同一类 Pod 共享相同的 VNI,便于策略管理。
13.5 数据路径分析
从 Pod 到 VxLAN 封装
关键点
- 不走路由:流量通过
bpf_redirect直接到 cilium_vxlan - 保留原始 MAC:因为没有经过路由替换 MAC
- 两次协议栈处理:
- 第一次:Pod 内部协议栈
- 第二次:VxLAN 封装后,作为普通 UDP 包处理
13.6 eBPF Hook 说明
to_overlay
to_overlay 职责:
- 接收从 lxc 网卡 redirect 过来的包
- 调用内核 VxLAN 模块进行封装
- 但不替换 MAC 地址(与传统方式不同)
from_overlay
from_overlay 职责:
- 处理接收到的 VxLAN 包
- 解封装
- 直接 redirect 到目标 Pod(跳过 lxc 网卡)
13.7 抓包分析
VNI 的变化
# 抓包观察 VNI
tcpdump -pne -i eth0 udp port 8472 -w cilium_vxlan.pcap
抓包结果:
# 去程(Request)
VNI: 68863
# 回程(Reply)
VNI: 18705
[!WARNING]
VNI 去回不一致:
- 去程 VNI = 目标 Pod 的 Identity
- 回程 VNI = 源 Pod 的 Identity
- 这与传统 VxLAN "两端 VNI 必须一致" 的规则不同!
MAC 地址分析
# 内层 MAC
源 MAC: Pod A 的 eth0 MAC(保留原始)
目的 MAC: 目标 Pod 的 MAC
# 外层 MAC
源 MAC: eth0 的 MAC
目的 MAC: 对端 eth0 的 MAC
13.8 使用 cilium monitor 调试
命令
# 进入 Cilium Agent Pod
kubectl exec -it -n kube-system cilium-xxxx -- bash
# 监控所有流量
cilium monitor -v
# 只监控特定 Pod
cilium monitor --related-to <endpoint-id>
输出分析
# 第一次协议栈处理(Pod 内部)
-> endpoint 238 flow 0x1234 identity 68863
ICMP 10.0.1.112 -> 10.0.2.237
# VxLAN 封装后
<- host flow 0x1234
UDP 172.18.0.2:random -> 172.18.0.3:8472
# 收到回复
-> endpoint 238 flow 0x1234
ICMP 10.0.2.237 -> 10.0.1.112 (reply)
关键信息:
identity:Pod 的 Identity IDendpoint:Pod 的 Endpoint ID- 两次处理:先是 ICMP,后是 UDP(封装后)
13.9 Cilium VxLAN vs 传统 VxLAN 总结
| 方面 | 传统 VxLAN | Cilium VxLAN |
|---|---|---|
| 流量引入 | 路由(下一跳是 VxLAN 接口 IP) | eBPF redirect |
| VxLAN 接口 IP | 必须配置 | 不需要 |
| VNI | 静态配置,两端一致 | 动态(Identity),去回可不同 |
| 隧道信息存储 | 内核 VxLAN 模块 | eBPF Map |
| 内层 MAC | vxlan0 接口的 MAC | 原始 Pod 的 MAC |
| 抓包 | 在 vxlan0 能看到完整流量 | 蹦蹦跳跳,需多点抓包 |
📝 章节小结
本章深入分析了 Cilium VxLAN 的数据路径:
-
与传统 VxLAN 的差异:
- 不依赖路由,使用 eBPF redirect
- cilium_vxlan 接口没有 IP 地址
- VNI 使用 Identity ID
-
eBPF Map 存储隧道信息:
cilium bpf tunnel list查看- Pod CIDR → 对端节点 IP
-
Cilium Identity:
- 一类 Pod 共享一个 Identity
- 用于安全策略和 VNI
-
数据路径:
- lxc → bpf_redirect → cilium_vxlan
- to_overlay Hook 封装
- 两次协议栈处理
-
调试方法:
cilium monitor监控流量- 多点抓包分析
[!TIP]
学习建议:
- 先掌握传统 VxLAN 的"朴实"玩法
- 再理解 Cilium 的 eBPF 增强方式
- 使用 cilium monitor + 抓包 验证理解
[!IMPORTANT]
Cilium VxLAN 数据路径特点:
- 流量"蹦蹦跳跳",不是线性经过每个设备
- 回程包可能跳过某些设备(如 lxc 网卡)
- 需要多点抓包 + cilium monitor 才能完整理解
第14章 IPSec 手工实现
🎯 学习目标
- 理解 IPSec 的基本概念和作用
- 掌握 IPSec 的两种模式:隧道模式和传输模式
- 理解 SA(安全联盟)和 SPI(安全参数索引)
- 学会使用 ip xfrm 命令手工配置 IPSec
- 掌握 Wireshark 解密 ESP 包的方法
14.1 IPSec 基础
背景
IPSec(Internet Protocol Security)是一个在 IP 层提供安全性的协议族,主要用于:
- 加密数据报文
- 保护数据机密性
- 防止中间人攻击
IPSec 的特性
| 特性 | 说明 |
|---|---|
| 机密性 | 数据加密,抓包看不到明文内容 |
| 完整性 | 校验数据是否被篡改 |
| 抗重放 | 防止攻击者重复发送旧数据包 |
| 来源认证 | 验证数据确实来自声称的发送者 |
[!NOTE]
与 TLS 的区别:
- TLS 工作在传输层(如 HTTPS)
- IPSec 工作在网络层(IP 层)
- IPSec 对上层应用透明
14.2 IPSec 模式
两种模式对比
| 模式 | 适用场景 | IP 头数量 | 典型应用 |
|---|---|---|---|
| 传输模式 | 点对点直接通信 | 1 个 | 两台主机直接加密 |
| 隧道模式 | 网关间通信 | 2 个(内外) | CNI Pod 间通信、VPN |
[!IMPORTANT]
Cilium 使用隧道模式:因为 Pod 间通信需要经过节点(网关),所以 Cilium IPSec 使用隧道模式。
数据包结构:[新 IP][ESP][原始 IP][Payload]
14.3 ESP 封装结构
ESP 头部
+-------------------+
| IP Header | ← 新的外层 IP(隧道模式)
+-------------------+
| ESP Header | ← SPI + Sequence Number
+-------------------+
| Original IP | ← 原始 IP 头(加密)
+-------------------+
| Payload | ← 原始数据(加密)
+-------------------+
| ESP Trailer | ← Padding + Next Header
+-------------------+
| ESP Auth | ← 认证数据(可选)
+-------------------+
ESP(Encapsulating Security Payload):封装安全载荷
| 字段 | 说明 |
|---|---|
| SPI | Security Parameter Index,安全参数索引 |
| Sequence Number | 序列号,用于抗重放 |
| Payload | 加密后的原始数据 |
| Auth Data | 认证数据(可选) |
14.4 核心概念
SA(Security Association)
安全联盟:定义了 IPSec 通信的参数
# 查看 SA
$ ip xfrm state
src 192.168.2.71 dst 192.168.2.73
proto esp spi 0x00000978 ...
enc cbc(aes) 0x2668a9a6...
SA 包含的信息:
| 参数 | 说明 |
|---|---|
src/dst |
源和目的 IP |
proto |
协议(ESP) |
spi |
安全参数索引 |
mode |
模式(tunnel/transport) |
enc |
加密算法和密钥 |
SPI(Security Parameter Index)
安全参数索引:32 位数值,用于标识 SA
# 抓包中查看 SPI
# ESP Header 中包含 SPI 字段
SPI: 0x00000978
[!TIP]
解密 ESP 包需要:
- SPI(标识使用哪个 SA)
- 加密算法
- 密钥(Key)
14.5 ip xfrm 命令
命令框架
# xfrm = transform(转换)
# 用于配置 IPSec
# 查看 State(安全联盟)
ip xfrm state
# 查看 Policy(安全策略)
ip xfrm policy
# 简写
ip x s # state
ip x p # policy
# 清空配置
ip xfrm state flush
ip xfrm policy flush
State vs Policy
| 对象 | 作用 | 包含内容 |
|---|---|---|
| State | 定义如何加密 | SPI、算法、密钥 |
| Policy | 定义哪些流量需要加密 | 源/目的、方向 |
14.6 手工配置 IPSec
实验拓扑
步骤 1:创建网络命名空间
# 节点 1 (192.168.2.71)
ip netns add ns1
ip link add veth type veth peer name ceth0
ip link set ceth0 netns ns1
# 配置 IP
ip netns exec ns1 ip addr add 1.1.1.2/24 dev ceth0
ip netns exec ns1 ip link set ceth0 up
ip netns exec ns1 ip link set lo up
# 添加路由
ip netns exec ns1 ip route add default via 1.1.1.1 dev ceth0
步骤 2:添加路由
# 节点 1:去往 1.1.2.0/24 走 192.168.2.73
ip route add 1.1.2.0/24 via 192.168.2.73 src 192.168.2.71
# 节点 2:去往 1.1.1.0/24 走 192.168.2.71
ip route add 1.1.1.0/24 via 192.168.2.71 src 192.168.2.73
步骤 3:配置 IPSec State
# 节点 1:添加 State
# 方向:71 -> 73(出)
ip xfrm state add \
src 192.168.2.71 dst 192.168.2.73 \
proto esp spi 0x00000978 \
mode tunnel \
enc "cbc(aes)" 0x2668a9a6b3c4d5e6f7a8b9c0d1e2f3a4
# 方向:73 -> 71(入)
ip xfrm state add \
src 192.168.2.73 dst 192.168.2.71 \
proto esp spi 0x00000978 \
mode tunnel \
enc "cbc(aes)" 0x2668a9a6b3c4d5e6f7a8b9c0d1e2f3a4
步骤 4:配置 IPSec Policy
# 出方向 Policy
ip xfrm policy add \
src 1.1.1.0/24 dst 1.1.2.0/24 \
dir out \
tmpl src 192.168.2.71 dst 192.168.2.73 \
proto esp mode tunnel
# 入方向 Policy
ip xfrm policy add \
src 1.1.2.0/24 dst 1.1.1.0/24 \
dir in \
tmpl src 192.168.2.73 dst 192.168.2.71 \
proto esp mode tunnel
# 转发 Policy
ip xfrm policy add \
src 1.1.2.0/24 dst 1.1.1.0/24 \
dir fwd \
tmpl src 192.168.2.73 dst 192.168.2.71 \
proto esp mode tunnel
14.7 验证配置
查看 State
$ ip xfrm state
src 192.168.2.71 dst 192.168.2.73
proto esp spi 0x00000978 reqid 16385 mode tunnel
replay-window 0
enc cbc(aes) 0x2668a9a6b3c4d5e6f7a8b9c0d1e2f3a4
src 192.168.2.73 dst 192.168.2.71
proto esp spi 0x00000978 reqid 16385 mode tunnel
replay-window 0
enc cbc(aes) 0x2668a9a6b3c4d5e6f7a8b9c0d1e2f3a4
查看 Policy
$ ip xfrm policy
src 1.1.1.0/24 dst 1.1.2.0/24
dir out priority 0
tmpl src 192.168.2.71 dst 192.168.2.73
proto esp mode tunnel
抓包验证
# 抓 ESP 包
tcpdump -pne -i eth0 esp
# 测试连通性
ip netns exec ns1 ping 1.1.2.2
14.8 Wireshark 解密 ESP
配置步骤
- 打开 Preferences:Edit → Preferences
- 找到 ESP 协议:Protocols → ESP
- 添加 SA:ESP SAs → Edit
填写信息
| 字段 | 值 |
|---|---|
| Protocol | IPv4 |
| Src IP | * 或具体 IP |
| Dest IP | * 或具体 IP |
| SPI | 从抓包中复制(如 0x00000978) |
| Encryption | AES-CBC |
| Encryption Key | 密钥(16 进制) |
解密结果
# 解密前
ESP (Encapsulating Security Payload)
SPI: 0x00000978
[Encrypted Data]
# 解密后
ESP (Encapsulating Security Payload)
SPI: 0x00000978
Inner IP: 1.1.1.2 -> 1.1.2.2
ICMP: Echo Request
14.9 Cilium IPSec 的特殊之处
与通用 IPSec 的差异
# 查看 Cilium 的 IPSec State
$ ip xfrm state
src 10.0.0.165 dst 10.0.0.187
proto esp spi 0x00000003 ...
mark 0x3cb6 ... # ← Cilium 特有
| 特点 | 通用 IPSec | Cilium IPSec |
|---|---|---|
| mark 字段 | 无 | 有(用于流量匹配) |
| 外层 IP | 节点 IP(如 172.18.0.x) | Pod CIDR IP(如 10.0.0.x) |
| 密钥管理 | 手工或 IKE | Cilium 自动管理 |
[!WARNING]
Cilium IPSec 的外层 IP 不是节点 IP:与 VxLAN 不同,Cilium IPSec 的外层 IP 使用 Pod CIDR 的 IP。
这需要通过源地址路由(SBR)来实现。
14.10 实践练习
练习 1:手工配置 IPSec
按照本章步骤,在两个 Linux 节点间配置 IPSec 隧道:
- 创建网络命名空间
- 添加路由
- 配置 State 和 Policy
- 验证 ping 连通性
- 抓包查看 ESP 封装
练习 2:使用 Wireshark 解密
- 抓取 ESP 包
- 配置 ESP SA 解密
- 查看解密后的原始内容
📝 章节小结
本章介绍了 IPSec 的基础知识和手工配置方法:
-
IPSec 基础:
- 机密性、完整性、抗重放、来源认证
- 工作在 IP 层
-
两种模式:
- 传输模式:点对点
- 隧道模式:网关间(Cilium 使用)
-
核心概念:
- SA:安全联盟,定义加密参数
- SPI:安全参数索引,标识 SA
-
ip xfrm 命令:
ip xfrm state:查看/配置 SAip xfrm policy:查看/配置策略
-
手工配置步骤:
- 创建网络环境
- 添加路由
- 配置 State(加密参数)
- 配置 Policy(流量匹配)
[!TIP]
学习建议:
- 先在空白环境练习手工配置
- 使用 Wireshark 解密验证理解
- 再对比 Cilium IPSec 的特殊实现
[!IMPORTANT]
IPSec 配置要点:
- State 需要双向配置(出和入)
- Policy 需要 out/in/fwd 三个方向
- 两端的 SPI、算法、密钥必须匹配
第15章 Cilium-IPSec-DataPath
🎯 学习目标
- 理解 Cilium IPSec 的部署和配置方式
- 掌握 mark 字段在 IPSec 中的作用
- 理解 SBR(源地址路由)的工作原理
- 分析 Cilium IPSec 的数据路径
- 理解外层 IP 为何使用 cilium_host 地址
15.1 Cilium IPSec 部署
背景
Cilium 支持 IPSec 加密模式,可以对 Pod 间的流量进行加密。部署时需要:
- 生成加密密钥
- 配置 Cilium 启用 IPSec
部署配置
# 创建加密密钥 Secret
apiVersion: v1
kind: Secret
metadata:
name: cilium-ipsec-keys
namespace: kube-system
type: Opaque
stringData:
keys: "3 rfc4106(gcm(aes)) 2668a9a6b3c4d5e6f7a8b9c0d1e2f3a4 128"
密钥格式说明:
| 字段 | 说明 |
|---|---|
3 |
SPI(安全参数索引) |
rfc4106(gcm(aes)) |
加密算法 |
2668a9a6... |
密钥(128 位) |
128 |
密钥长度 |
Helm 安装选项
helm install cilium cilium/cilium \
--set encryption.enabled=true \
--set encryption.type=ipsec
[!WARNING]
注意:Cilium IPSec 模式通常需要使用传统 kube-proxy,不支持完全替换 kube-proxy 的模式。
这是因为 IPSec 使用 xfrm 框架,与完全 eBPF 替换存在兼容性问题。
15.2 Cilium IPSec 的 mark 字段
与通用 IPSec 的差异
# 通用 IPSec(无 mark)
$ ip xfrm state
src 192.168.2.71 dst 192.168.2.73
proto esp spi 0x00000978 ...
enc cbc(aes) 0x2668a9a6...
# Cilium IPSec(有 mark)
$ ip xfrm state
src 10.0.0.159 dst 10.0.0.213
proto esp spi 0x00000003 ...
mark 0xd00/0xf00 output mark 0xe00/0xf00
enc cbc(aes) 0x2668a9a6...
mark 字段的作用:
| 字段 | 说明 |
|---|---|
mark 0xd00/0xf00 |
入向流量匹配标记 |
output mark 0xe00/0xf00 |
出向流量标记 |
[!IMPORTANT]
mark 的核心作用:mark 用于将流量与特定的 IPSec SA(安全联盟)关联。
Cilium 通过 mark 实现流量分类和路由选择。
15.3 SBR(源地址路由)机制
原理
SBR(Source Based Routing,源地址路由)是 Cilium IPSec 的关键机制。
| 路由方式 | 匹配依据 | 典型应用 |
|---|---|---|
| DBR (Destination Based) | 目的 IP | 默认路由 |
| SBR (Source Based) | 源 IP | IPSec、多网卡、策略路由 |
为什么需要 SBR
在 Cilium IPSec 中:
- IPSec 端点使用
cilium_host的 IP(如 10.0.0.159) - 而非节点的物理网卡 IP(如 172.18.0.2)
- 需要将流量引导到
cilium_host进行 IPSec 处理
15.4 ip rule 配置分析
查看 ip rule
$ ip rule list
0: from all lookup local
100: from all fwmark 0xd00/0xf00 lookup 200
101: from all fwmark 0xe00/0xf00 lookup 200
32766: from all lookup main
32767: from all lookup default
规则解读:
| 优先级 | 规则 | 说明 |
|---|---|---|
| 0 | lookup local | 本地路由 |
| 100 | fwmark 0xd00 → table 200 | IPSec 入向流量 |
| 101 | fwmark 0xe00 → table 200 | IPSec 出向流量 |
| 32766 | lookup main | 主路由表 |
查看路由表 200
$ ip route show table 200
10.0.0.0/24 dev cilium_host scope link
10.0.1.0/24 via 10.0.0.1 dev cilium_host
10.0.2.0/24 via 10.0.0.1 dev cilium_host
[!TIP]
关键理解:被标记为
0xd00或0xe00的流量会进入路由表 200,
然后通过cilium_host设备进行转发和 IPSec 处理。
15.5 数据路径分析
发送流程
流程说明:
| 步骤 | 位置 | 操作 |
|---|---|---|
| 1 | Pod | 发送原始数据包 |
| 2 | lxc 网卡 | eBPF 处理,打标记 |
| 3 | SBR 路由 | 根据标记匹配 table 200 |
| 4 | cilium_host | 进入 xfrm 框架 |
| 5 | xfrm | IPSec 加密封装 |
| 6 | eth0 | 通过物理网卡发出 |
外层 IP 的来源
# 抓包查看
$ tcpdump -pne -i eth0 esp
# 外层 IP
src: 10.0.0.159 (cilium_host)
dst: 10.0.0.213 (对端 cilium_host)
# 内层 IP
src: 10.0.0.233 (Pod)
dst: 10.0.0.192 (目标 Pod)
[!IMPORTANT]
为什么外层 IP 是 cilium_host?因为通过 SBR,流量被引导到
cilium_host设备。
xfrm 使用cilium_host的 IP 作为封装的源地址。
这与ip xfrm state中配置的 src/dst 一致。
15.6 mark 与 xfrm state 的关联
关联过程
查看关联
# ip xfrm state 中的 mark
$ ip xfrm state
src 10.0.0.159 dst 10.0.0.213
mark 0xd00/0xf00 output mark 0xe00/0xf00
...
# ip rule 中的 fwmark
$ ip rule list
100: from all fwmark 0xd00/0xf00 lookup 200
101: from all fwmark 0xe00/0xf00 lookup 200
# table 200 中的路由
$ ip route show table 200
10.0.2.0/24 dev cilium_host
15.7 抓包分析
抓包位置
在节点上使用 tcpdump -i any esp 可能抓到多个包:
| 接口 | 抓到的包 | 说明 |
|---|---|---|
cilium_host |
ESP 包 | IPSec 处理点 |
eth0 |
ESP 包 | 物理网卡出口 |
lxc_xxx |
原始包 | 未加密的 Pod 包 |
Wireshark 解密
- 获取密钥:
$ ip xfrm state
src 10.0.0.159 dst 10.0.0.213
proto esp spi 0x00000003
enc cbc(aes) 0x2c...
- 配置 Wireshark:
| 字段 | 值 |
|---|---|
| SPI | 0x00000003 |
| Algorithm | AES-CBC [RFC3602] |
| Key | 从 ip xfrm state 复制 |
15.8 与 Flannel/Calico IPSec 的对比
密钥管理差异
| 特性 | Cilium | Flannel/Calico |
|---|---|---|
| SPI | 固定(如 0x3) | 可能不同 |
| Key 方向 | 来回可能相同 | 来回可能不同 |
| mark 字段 | 有 | 无 |
| 外层 IP | cilium_host IP | 节点 IP |
[!NOTE]
Flannel IPSec 的差异:Flannel 的来去方向可能使用不同的 Key。
在 Wireshark 解密时需要配置两条 SA。
15.9 密钥更新
动态更新
Cilium 支持密钥轮换:
# 更新 Secret
kubectl patch secret cilium-ipsec-keys -n kube-system \
-p '{"stringData":{"keys":"4 rfc4106(gcm(aes)) new_key_here 128"}}'
# Cilium 会自动检测并更新
[!TIP]
安全建议:
- 定期轮换密钥(如每小时)
- 即使密钥泄露,更新后攻击者无法解密新流量
- Cilium 自动处理密钥分发
15.10 实践练习
练习 1:分析 SBR 配置
# 1. 查看 ip rule
ip rule list
# 2. 查看特定路由表
ip route show table 200
# 3. 分析 mark 与 table 的关系
练习 2:抓包分析
# 1. 在 cilium_host 抓包
tcpdump -pne -i cilium_host esp
# 2. 在 eth0 抓包
tcpdump -pne -i eth0 esp
# 3. 对比两个接口的包
📝 章节小结
本章介绍了 Cilium IPSec 的数据路径:
-
部署方式:
- 创建 Secret 存储密钥
- Helm 启用 IPSec
-
mark 字段:
- Cilium 特有
- 用于流量分类和 SA 匹配
-
SBR 机制:
- Source Based Routing
- 将流量引导到 cilium_host
-
数据路径:
- Pod → lxc → SBR → cilium_host → xfrm → eth0
- 外层 IP 使用 cilium_host 地址
-
关联关系:
- mark ↔ ip rule ↔ table 200 ↔ cilium_host ↔ xfrm state
[!TIP]
理解要点:Cilium IPSec 的核心是通过 mark + SBR 将流量引导到
cilium_host,
然后由 xfrm 框架完成 IPSec 加密。
[!IMPORTANT]
调试方法:
ip rule list- 查看规则ip route show table 200- 查看 SBR 路由ip xfrm state- 查看 SA 信息tcpdump -i eth0 esp- 抓包验证
第16章 WireGuard 手工实现
🎯 学习目标
- 理解 WireGuard 的基本概念和特点
- 掌握 WireGuard 与 IPSec/OpenVPN 的区别
- 学会手工配置 WireGuard 隧道
- 理解 WireGuard 的数据包结构
- 了解 WireGuard 在 VPN 场景的应用
16.1 WireGuard 简介
背景
WireGuard 是一种现代、高效的 VPN 协议,于 Linux 5.6 内核正式合入。
核心特点
| 特点 | 说明 |
|---|---|
| 简洁 | 内核代码仅约 4000 行(OpenVPN 约 10 万行) |
| 高性能 | 完全内核态实现,无用户态拷贝 |
| 现代加密 | 使用 Curve25519、ChaCha20 等 |
| 易配置 | 像配置 SSH 一样简单 |
[!NOTE]
Linus Torvalds 评价:"与 IPSec 和 OpenVPN 相比,WireGuard 更像一件艺术品"
(Can I just once again state my love for it and hope it gets merged soon? Compared to the horrors that are OpenVPN and IPSec, it is a work of art.)
16.2 与其他 VPN 技术对比
技术对比
| 特性 | WireGuard | IPSec | OpenVPN |
|---|---|---|---|
| 代码量 | ~4000 行 | ~10万行 | ~10万行 |
| 运行位置 | 内核态 | 半内核半用户态 | 用户态 |
| 配置复杂度 | 简单 | 复杂 | 中等 |
| 协议 | UDP | ESP | UDP/TCP |
| 密钥管理 | 公钥/私钥 | IKE/手工 | 证书/密钥 |
| 内核版本 | ≥5.6 | 原生支持 | 无需 |
性能对比
[!IMPORTANT]
WireGuard 的优势:
- 完全内核态 → 无上下文切换
- 代码简洁 → bug 少、审计容易
- 现代加密 → 更强安全性
16.3 内核支持检查
检查内核版本
# 查看内核版本
$ uname -r
5.15.0-generic
# WireGuard 需要 >= 5.6
# 如果版本低于 5.6,需要先升级内核
安装 WireGuard 工具
# Ubuntu/Debian
apt install wireguard-tools
# CentOS/RHEL
yum install wireguard-tools
验证安装
# 查看 wg 命令
$ which wg
/usr/bin/wg
# 检查模块
$ lsmod | grep wireguard
wireguard 81920 0
16.4 手工配置 WireGuard
实验拓扑
步骤 1:生成密钥
# 节点 1:生成私钥
wg genkey > /etc/wireguard/private
# 从私钥提取公钥
cat /etc/wireguard/private | wg pubkey
# 保护私钥权限
chmod 600 /etc/wireguard/private
步骤 2:创建 WireGuard 接口
# 创建 wg0 接口
ip link add wg0 type wireguard
# 配置 IP 地址
ip addr add 20.0.0.1/24 dev wg0
# 应用私钥
wg set wg0 private-key /etc/wireguard/private
# 设置监听端口
wg set wg0 listen-port 51820
# 启动接口
ip link set wg0 up
步骤 3:添加 Peer
# 在节点 1 添加 peer(节点 2)
# PEER_PUBLIC_KEY = 节点 2 的公钥
wg set wg0 peer <PEER_PUBLIC_KEY> \
allowed-ips 20.0.0.2/32 \
endpoint 192.168.2.73:51820
# 同样在节点 2 添加 peer(节点 1)
16.5 查看配置
wg 命令
$ wg
interface: wg0
public key: aWE4xxx...
private key: (hidden)
listening port: 51820
peer: bXY5xxx...
endpoint: 192.168.2.73:51820
allowed ips: 20.0.0.2/32
latest handshake: 4 seconds ago
transfer: 1.2 KiB received, 984 B sent
关键字段:
| 字段 | 说明 |
|---|---|
public key |
本端公钥 |
listening port |
监听端口 |
peer |
对端公钥 |
endpoint |
对端地址:端口 |
allowed ips |
允许的 IP 范围 |
latest handshake |
最近握手时间 |
wg show 子命令
# 显示公钥
wg show wg0 public-key
# 显示私钥
wg show wg0 private-key
# 显示监听端口
wg show wg0 listen-port
# 显示所有 peer
wg show wg0 peers
16.6 验证连通性
ping 测试
# 在节点 1 ping 节点 2
$ ping 20.0.0.2 -c 3
PING 20.0.0.2 (20.0.0.2) 56(84) bytes of data.
64 bytes from 20.0.0.2: icmp_seq=1 ttl=64 time=0.432 ms
查看握手状态
$ wg
# 成功握手会显示:
peer: bXY5xxx...
latest handshake: 4 seconds ago
# 未握手显示:
peer: bXY5xxx...
(no handshake yet)
16.7 数据包结构
WireGuard 封装格式
+-------------------+
| IP Header | ← 外层 IP(节点 IP)
+-------------------+
| UDP Header | ← 端口 51820
+-------------------+
| WireGuard | ← WireGuard 协议头
+-------------------+
| Encrypted | ← 加密的原始数据包
| Payload |
+-------------------+
[!TIP]
与 VxLAN 类似:
- VxLAN: IP + UDP + VNI + 原始帧
- WireGuard: IP + UDP + WG Header + 加密数据
两者都使用 UDP 封装。
链路类型
# 抓包查看
$ tcpdump -pne -i wg0
listening on wg0, link-type RAW (Raw IP)...
RAW IP:
- 没有以太网头(无 MAC 地址)
- 类似于 tun 设备
- 只有三层 IP 信息
16.8 抓包分析
在 wg0 接口抓包
# wg0 上抓到的是明文(已解密)
$ tcpdump -pne -i wg0 icmp
# 注意:没有 MAC 地址,只有 IP
20.0.0.1 > 20.0.0.2: ICMP echo request
20.0.0.2 > 20.0.0.1: ICMP echo reply
在物理接口抓包
# eth0 上抓到的是加密数据
$ tcpdump -pne -i eth0 udp port 51820
# 只能看到 WireGuard 协议,内容加密
192.168.2.71.51820 > 192.168.2.73.51820: UDP, length 128
Wireshark 过滤
# 过滤 WireGuard 协议
wireguard
[!WARNING]
WireGuard 解密困难:与 IPSec 不同,WireGuard 的解密需要从内核内存提取密钥,
目前没有简单的方法在 Wireshark 中解密 WireGuard 流量。
16.9 路由与流量引导
路由规则
# 当添加 peer 时,系统自动添加路由
$ ip route show
20.0.0.0/24 dev wg0 proto kernel scope link src 20.0.0.1
流量引导原理:
- 目的 IP 为 20.0.0.2
- 匹配路由表,出接口为 wg0
- wg0 查找 peer 配置
- 使用 peer 的 endpoint 封装发送
[!IMPORTANT]
与 IPSec 的区别:
- IPSec 使用 SBR(源地址路由)+ mark
- WireGuard 使用 DBR(目的地址路由)
- WireGuard 配置更简单
16.10 UDP 封装的优势
为什么使用 UDP
原因:
- 原始数据如果是 TCP,已有可靠性保证
- UDP 封装 + TCP 上层 = 单层可靠传输
- TCP 封装 + TCP 上层 = 双层可靠传输(性能灾难)
[!NOTE]
WireGuard 官方声明:"我们不会支持 TCP 封装"
因为 TCP-over-TCP 会导致严重的性能问题。
16.11 VPN 应用场景
典型场景:远程访问家庭网络
配置示例
服务端(家庭):
[Interface]
Address = 10.0.0.1/24
PrivateKey = <SERVER_PRIVATE_KEY>
ListenPort = 51820
# 允许转发到内网
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT
PostUp = iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
[Peer]
PublicKey = <CLIENT_PUBLIC_KEY>
AllowedIPs = 10.0.0.2/32
客户端(远程):
[Interface]
Address = 10.0.0.2/24
PrivateKey = <CLIENT_PRIVATE_KEY>
[Peer]
PublicKey = <SERVER_PUBLIC_KEY>
Endpoint = <公网IP>:51820
AllowedIPs = 10.0.0.0/24, 192.168.1.0/24
16.12 实践练习
练习 1:手工配置 WireGuard
按照本章步骤,在两个 Linux 节点间配置 WireGuard 隧道:
- 生成密钥对
- 创建 wg0 接口
- 添加 peer
- 测试连通性
练习 2:抓包对比
# 1. 在 wg0 抓包(明文)
tcpdump -pne -i wg0 icmp
# 2. 在 eth0 抓包(密文)
tcpdump -pne -i eth0 udp port 51820
# 3. 对比两个接口的数据
📝 章节小结
本章介绍了 WireGuard 的基础知识和手工配置方法:
-
WireGuard 特点:
- 简洁(~4000 行代码)
- 高性能(内核态)
- 现代加密
-
与其他 VPN 对比:
- 比 IPSec 配置简单
- 比 OpenVPN 性能更好
-
手工配置步骤:
- 生成密钥 → 创建接口 → 添加 peer
-
数据包结构:
- IP + UDP + WireGuard Header + 加密数据
- 类似 VxLAN 封装方式
-
路由机制:
- 使用 DBR(目的地址路由)
- 比 IPSec 的 SBR 更简单
[!TIP]
学习建议:
- 先手工配置两节点隧道
- 对比 wg0 和 eth0 的抓包数据
- 理解 UDP 封装的优势
[!IMPORTANT]
WireGuard 核心命令:# 生成密钥 wg genkey > private cat private | wg pubkey > public # 创建接口 ip link add wg0 type wireguard # 查看配置 wg show wg0
第17章 Cilium-WireGuard-DataPath
🎯 学习目标
- 理解 Cilium WireGuard 的部署方式
- 掌握 SBR(源地址路由)在 WireGuard 中的应用
- 理解 fwmark 标记机制
- 分析 Cilium WireGuard 的数据路径
- 对比手工配置与 Cilium 的差异
17.1 Cilium WireGuard 部署
部署配置
# 创建 WireGuard 密钥 Secret
apiVersion: v1
kind: Secret
metadata:
name: cilium-wireguard-keys
namespace: kube-system
type: Opaque
stringData:
# 私钥(base64 编码)
key: "..."
Helm 安装
helm install cilium cilium/cilium \
--set encryption.enabled=true \
--set encryption.type=wireguard \
--set routingMode=native \
--set kubeProxyReplacement=false
[!NOTE]
注意:Cilium WireGuard 使用 Native Routing 模式(Direct Routing)。
17.2 Cilium WireGuard 接口
查看接口
$ ip link show type wireguard
10: cilium_wg0: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1420 ...
link/none
接口特点:
| 属性 | 值 |
|---|---|
| 接口名 | cilium_wg0 |
| 类型 | wireguard |
| MTU | 1420 |
| 标志 | POINTOPOINT, NOARP |
查看 Peer
$ wg show cilium_wg0
interface: cilium_wg0
public key: aWE4xxx...
listening port: 51820
peer: bXY5xxx...
endpoint: 10.0.0.2:51820
allowed ips: 10.0.0.2/32
peer: cZA6xxx...
endpoint: 10.0.0.3:51820
allowed ips: 10.0.0.3/32
[!TIP]
Peer 数量:3 节点集群,每个节点有 2 个 peer(其他 2 个节点)。
allowed ips 使用 /32 掩码,对应每个 Pod IP。
17.3 SBR vs DBR
手工配置 vs Cilium
| 对比项 | 手工配置 | Cilium |
|---|---|---|
| 路由方式 | DBR(目的地址) | SBR(源地址) |
| 路由表 | main | table 201 |
| 流量引导 | ip route | ip rule + fwmark |
为什么 Cilium 使用 SBR
[!IMPORTANT]
SBR 的优势:
- 不污染默认路由表
- 可以精确控制哪些流量需要加密
- 与 eBPF 配合更灵活
17.4 fwmark 标记机制
标记过程
查看 ip rule
$ ip rule list
0: from all lookup local
100: from all fwmark 0x1/0xf0 lookup 201
32766: from all lookup main
32767: from all lookup default
规则解读:
| 优先级 | 条件 | 动作 |
|---|---|---|
| 100 | fwmark = 0x1 (ENCRYPT) | 查 table 201 |
查看 table 201
$ ip route show table 201
default dev cilium_wg0 scope link
[!TIP]
核心理解:被 eBPF 标记为
MARK_MAGIC_ENCRYPT的流量,
会匹配 ip rule,进入 table 201,
然后通过cilium_wg0发出。
17.5 数据路径分析
发送流程
步骤说明:
| 步骤 | 位置 | 操作 |
|---|---|---|
| 1 | Pod | 发送原始数据包 |
| 2 | lxc | eBPF 处理,打 ENCRYPT 标记 |
| 3 | ip rule | fwmark 匹配 table 201 |
| 4 | table 201 | 默认路由到 cilium_wg0 |
| 5 | cilium_wg0 | WireGuard 加密封装 |
| 6 | eth0 | 通过物理网卡发出 |
接收流程
[!NOTE]
接收判断:内核通过 UDP 端口 51820 判断是否为 WireGuard 流量。
监听在内核态(显示为-),不是用户态进程。
17.6 抓包分析
抓包位置
# 在 eth0 抓包(加密数据)
tcpdump -pne -i eth0 udp port 51820 -w cilium_wg.pcap
# 在 cilium_wg0 抓包(解密后的明文)
tcpdump -pne -i cilium_wg0
Wireshark 分析
# 过滤 WireGuard 协议
wireguard
抓包结果:
| 接口 | 内容 |
|---|---|
eth0 |
UDP 51820 + 加密数据 |
cilium_wg0 |
Raw IP(无 MAC) |
[!WARNING]
解密困难:WireGuard 的密钥需要从内核内存提取,
目前无法像 IPSec 那样在 Wireshark 中直接解密。
17.7 与手工配置的差异
主要区别
| 对比项 | 手工配置 | Cilium |
|---|---|---|
| 接口名 | wg0 | cilium_wg0 |
| 路由方式 | DBR(ip route) | SBR(ip rule + table 201) |
| 流量标记 | 无 | fwmark |
| Peer 管理 | 手工添加 | 自动管理 |
| allowed-ips | 手工指定 | 自动添加 Pod IP |
[!IMPORTANT]
为什么 Cilium 不用 DBR:手工配置时,目的地址路由很简单:
20.0.0.0/24 dev wg0但 Cilium 环境中:
- Pod IP 不在同一网段
- 需要精确控制哪些流量加密
- SBR + fwmark 更灵活
17.8 性能对比
WireGuard vs IPSec
| 场景 | WireGuard | IPSec |
|---|---|---|
| MTU 1500 | 更高吞吐量 | 较低 |
| MTU 9000 | 更高吞吐量 | 较低 |
| CPU 占用 | 较低 | 较高 |
[!NOTE]
Benchmark 说明:Cilium 官方测试显示 WireGuard 性能优于 IPSec。
但实际性能取决于:网络环境、MTU、负载类型等。
17.9 实践练习
练习 1:查看 Cilium WireGuard 配置
# 1. 查看 WireGuard 接口
ip link show type wireguard
# 2. 查看 Peer
wg show cilium_wg0
# 3. 查看 ip rule
ip rule list
# 4. 查看 table 201
ip route show table 201
练习 2:验证数据路径
# 1. 在节点间 ping Pod
kubectl exec -it pod1 -- ping <pod2-ip>
# 2. 抓包验证
tcpdump -pne -i eth0 udp port 51820
📝 章节小结
本章介绍了 Cilium WireGuard 的数据路径:
-
部署方式:
encryption.type=wireguard- 自动创建
cilium_wg0接口
-
SBR 机制:
- eBPF 打 fwmark 标记
- ip rule 匹配 table 201
- 路由到 cilium_wg0
-
与手工配置的区别:
- 手工:DBR(目的地址路由)
- Cilium:SBR(源地址路由)
-
数据路径:
- 发送:Pod → eBPF(mark) → SBR → cilium_wg0 → eth0
- 接收:eth0 → 51820端口 → cilium_wg0 → Pod
-
性能:
- WireGuard 通常优于 IPSec
[!TIP]
核心理解:Cilium 通过 fwmark + SBR 将流量引导到
cilium_wg0,
然后由 WireGuard 完成加密封装。
[!IMPORTANT]
调试命令:# 查看规则 ip rule list # 查看 SBR 路由 ip route show table 201 # 查看 WireGuard 状态 wg show cilium_wg0 # 抓包验证 tcpdump -i eth0 udp port 51820
第十八章 Cilium-SocketLB
本章介绍 Cilium 独有的 Socket-based Load Balancing(Socket LB)特性,这是一种基于 eBPF 在 Socket 层面实现服务负载均衡的技术,能够显著优化集群内部的流量路径。
18.1 背景与问题
18.1.1 传统 Service 访问模式的问题
在传统的 Kubernetes 网络实现中(如 Flannel、Calico 等),当 Pod 访问 Service 时,数据包需要经历以下过程:
问题分析:
| 问题 | 描述 |
|---|---|
| 多层处理 | 数据包需先到 Root Namespace,再经 iptables/IPVS 处理 |
| 规则匹配 | iptables 链式匹配,规则多时性能下降 |
| 额外跳转 | 跨节点时还需要 SNAT,增加处理开销 |
| 延迟增加 | 整个处理流程增加了访问延迟 |
18.1.2 理想的访问模式
用户期望的访问模式应该更加直接:
[!NOTE]
理想状态:Pod 发出的数据包直接以后端 Pod 的 IP 作为目的地址,无需经过中间的 NAT 转换。
18.2 Socket LB 原理
18.2.1 核心思想
Socket LB 的核心思想是:在 Socket 层面提前完成负载均衡决策,在数据包离开 Pod 之前就将 Service IP 替换为真实的后端 Pod IP。
18.2.2 工作机制
| 阶段 | 传统模式 | Socket LB 模式 |
|---|---|---|
| 发包位置 | Pod 内发包,dst = Service IP | Pod 内发包,dst = Backend Pod IP |
| NAT 位置 | Root Namespace (iptables) | Pod Namespace (eBPF) |
| 后续路由 | 需要 iptables 规则匹配 | 简单的跨节点/同节点路由 |
| 返回处理 | 可能需要 SNAT 反向转换 | 直接返回,无需额外处理 |
[!IMPORTANT]
关键优势:DNAT 提前在 Pod 命名空间内完成,后续流量变成简单的 Pod-to-Pod 通信,无需经过 iptables 规则链。
18.2.3 eBPF 实现位置
Socket LB 通过 eBPF 程序挂载在 cgroup 的 socket 操作钩子上实现:
18.3 传统模式抓包分析
18.3.1 环境准备(以 Flannel 为例)
# 创建测试服务
kubectl apply -f demo-deployment.yaml
# 查看 Service
kubectl get svc demo-svc
# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
# demo-svc NodePort 172.18.0.2 <none> 80:32000/TCP 1m
# 查看 iptables 规则链
iptables -t nat -L | grep 32000
# 可以看到 KUBE-SVC-xxx 链
18.3.2 抓包验证
# 进入 Pod 内抓包
kubectl exec -it frontend-pod -- tcpdump -i eth0 -n
# 在另一个终端,从 Pod 内访问 Service
kubectl exec -it frontend-pod -- curl 172.18.0.2:32000
抓包结果分析:
# TCP 三次握手
10.244.2.2.42962 > 172.18.0.2.32000: Flags [S] # SYN: dst=Service IP
172.18.0.2.32000 > 10.244.2.2.42962: Flags [S.] # SYN-ACK
10.244.2.2.42962 > 172.18.0.2.32000: Flags [.] # ACK
[!NOTE]
观察要点:
- 目的 IP 是 Service IP(172.18.0.2),而非后端 Pod IP
- 数据包需要到达 Root Namespace 后,由 iptables 进行 DNAT 转换
- 这是传统的、符合直觉的实现方式
18.4 Socket LB 模式抓包分析
18.4.1 启用 Socket LB
通过 Helm 部署 Cilium 并启用 Socket LB:
helm install cilium cilium/cilium --namespace kube-system \
--set kubeProxyReplacement=strict \
--set socketLB.enabled=true
18.4.2 验证配置
# 查看 Cilium 配置
cilium config view | grep -i socket
# socket-lb: enabled
# 或通过 cilium status
cilium status --verbose | grep -i socket
# KubeProxyReplacement Info: Socket LB: enabled
18.4.3 抓包验证
# 进入 Pod 内抓包
kubectl exec -it frontend-pod -- tcpdump -i eth0 -n
# 从 Pod 内访问 Service
kubectl exec -it frontend-pod -- curl 172.18.0.2:32000
抓包结果分析:
# TCP 三次握手
10.0.0.221.58504 > 10.0.2.22.80: Flags [S] # SYN: dst=Backend Pod IP !!!
10.0.2.22.80 > 10.0.0.221.58504: Flags [S.] # SYN-ACK
10.0.0.221.58504 > 10.0.2.22.80: Flags [.] # ACK
[!IMPORTANT]
关键发现:
- 虽然应用访问的是 Service IP(172.18.0.2:32000)
- 但抓包看到的目的 IP 直接是 Backend Pod IP(10.0.2.22:80)
- 这说明 Socket LB 在数据包发出前就完成了地址替换
18.5 对比总结
18.5.1 数据路径对比
18.5.2 核心差异表
| 对比项 | 传统模式 | Socket LB 模式 |
|---|---|---|
| DNAT 位置 | Root Namespace | Pod Namespace |
| DNAT 时机 | 数据包到达宿主机后 | 数据包离开 Pod 前 |
| 实现技术 | iptables / IPVS | eBPF (cgroup hooks) |
| 抓包看到的目的 IP | Service IP | Backend Pod IP |
| 后续处理 | 需要规则匹配 | 简单路由 |
| 性能开销 | 较高(规则链匹配) | 较低(Map 查询) |
18.5.3 性能优势
| 优势 | 说明 |
|---|---|
| 减少跳数 | 不需要经过 Root NS 的 iptables 处理 |
| 规避规则膨胀 | Service 多时,iptables 规则链很长 |
| 路径简化 | 变成简单的 Pod-to-Pod 通信 |
| 性能提升 | 减少 CPU 开销和延迟 |
18.6 配置选项
18.6.1 Helm 配置参数
# values.yaml 关键配置
kubeProxyReplacement: strict # 或 partial
socketLB:
enabled: true
hostNamespaceOnly: false # 是否仅对 host namespace 生效
18.6.2 模式说明
| 模式 | kubeProxyReplacement | 说明 |
|---|---|---|
| strict | strict | 完全替换 kube-proxy,启用所有 eBPF 功能 |
| partial | partial | 部分替换,与 kube-proxy 共存 |
18.6.3 验证命令
# 查看 Socket LB 状态
cilium status --verbose
# 查看详细配置
cilium config view
# 查看 BPF 程序
bpftool prog list | grep cgroup
18.7 历史演进
| 版本阶段 | 功能名称 | 说明 |
|---|---|---|
| 早期版本 | Host Reachable Services | 最初的功能名称 |
| 当前版本 | Socket LB | 统一命名为 Socket-based Load Balancing |
[!TIP]
如果在旧版本 Cilium 文档中看到 "Host Reachable Services",它与 Socket LB 描述的是同一功能。
18.8 适用场景
| 场景 | Socket LB 作用 |
|---|---|
| Pod → ClusterIP | ✅ 直接替换为后端 Pod IP |
| Pod → NodePort(集群内) | ✅ 同样可以优化 |
| 外部 → NodePort | ❌ 需要其他机制(如 DSR) |
18.9 章节小结
[!IMPORTANT]
核心要点总结:
什么是 Socket LB:在 Socket 层面通过 eBPF 实现的负载均衡,提前完成 Service IP 到 Backend Pod IP 的转换
与传统模式区别:
- 传统:Pod → Root NS → iptables DNAT → Backend
- Socket LB:Pod (eBPF DNAT) → Root NS → 直接路由 → Backend
抓包验证:
- 传统模式抓包看到 dst = Service IP
- Socket LB 模式抓包看到 dst = Backend Pod IP
配置方法:
kubeProxyReplacement=strict或单独设置socketLB.enabled=true核心优势:减少 iptables 规则匹配,降低延迟,提升大规模集群的 Service 访问性能
第十九章 Cilium-DSR 模式
本章介绍 Cilium 的 DSR(Direct Server Return)模式,这是一种优化南北向流量的技术,通过让后端 Pod 直接响应客户端,减少返回路径的跳数,降低入口节点的负载。
19.1 背景与问题
19.1.1 传统 SNAT 模式的数据路径
在传统的 NodePort/LoadBalancer 访问模式中,外部客户端访问集群内服务时,数据包需要经过 SNAT 处理:
SNAT 模式的问题:
| 问题 | 描述 |
|---|---|
| 入口节点瓶颈 | 所有进出流量都经过入口节点,容易成为瓶颈 |
| 多一跳 | 返回响应需要先回到入口节点,再转发给客户端 |
| 源 IP 丢失 | Pod 看到的源 IP 是入口节点 IP,而非真实客户端 IP |
| 负载不均 | 入口节点承担额外的转发负担 |
19.1.2 DSR 的核心思想
DSR(Direct Server Return)的核心思想是:让后端 Pod 直接将响应发送给客户端,跳过入口节点。
[!IMPORTANT]
DSR 的关键:后端 Pod 在发送响应时,必须使用客户端原始访问的目的 IP(入口节点 IP)作为源 IP,否则客户端会丢弃这个"意外"的响应包。
19.2 DSR vs SNAT 对比
19.2.1 数据路径对比
19.2.2 核心差异表
| 对比项 | SNAT 模式 | DSR 模式 |
|---|---|---|
| 返回路径 | Client ← Node A ← Pod | Client ← Pod (直接) |
| 跳数 | 请求 2 跳 + 响应 2 跳 = 4 跳 | 请求 2 跳 + 响应 1 跳 = 3 跳 |
| 入口节点负载 | 高(处理双向流量) | 低(只处理入向流量) |
| 源 IP 可见性 | Pod 看到 Node A IP | Pod 看到真实 Client IP |
| 响应包源 IP | Node A IP | Node A IP (伪装) |
19.3 核心挑战:IP 传递问题
19.3.1 问题描述
DSR 模式的核心挑战是:如何将客户端原始访问的目的 IP(入口节点 IP)传递给后端 Pod?
19.3.2 IP 响应的要求
客户端发送请求后,期望收到来自原始目的 IP 的响应:
| 场景 | 客户端行为 |
|---|---|
| 响应源 IP = 原始目的 IP | ✅ 接受响应,通信正常 |
| 响应源 IP ≠ 原始目的 IP | ❌ 丢弃响应,认为是无效包 |
[!NOTE]
类比理解:就像你 ping 1.1.1.1,但收到 1.1.1.2 的 reply,这个 reply 会被丢弃,因为"你问的是 A,但 B 来回答了"。
19.4 Cilium DSR 实现机制
19.4.1 IP Options 方案
Cilium 原生使用 IP Options 字段 来传递原始目的 IP 信息:
19.4.2 IP Options 字段解析
IP Header Options 字段内容(十六进制):
+------+------+------+------+------+------+------+------+
| 0x9a | ... | 0xAC | 0x12 | 0x00 | 0x04 | 0x7D | 0x00 |
+------+------+------+------+------+------+------+------+
↓ ↓ ↓ ↓ ↓ ↓
172 18 0 4 32000端口高位
解析结果:原始目的 IP = 172.18.0.4,端口 = 32000
| 十六进制 | 十进制 | 含义 |
|---|---|---|
| 0xAC | 172 | IP 第一段 |
| 0x12 | 18 | IP 第二段 |
| 0x00 | 0 | IP 第三段 |
| 0x04 | 4 | IP 第四段 |
| 0x7D00 | 32000 | 端口号 |
19.4.3 抓包验证
第一跳抓包(Client → Node A):
# 正常的 SYN 包,没有 Options 字段
172.18.0.1.38645 > 172.18.0.4.32000: Flags [S]
IP Header: 无 Options
第二跳抓包(Node A → Pod):
# 携带 Options 字段的 SYN 包
172.18.0.1.38645 > 10.0.2.148.80: Flags [S]
IP Header Options: unknown (0x9a) [包含原始目的 IP]
19.5 TCP 三次握手详解
19.5.1 握手过程分析
在 DSR 模式下,三次握手的包分布在不同路径:
| 包名 | 路径 | Node A 可见 | Node B/Pod 可见 |
|---|---|---|---|
| SYN | Client → A → Pod | ✅ | ✅ |
| SYN-ACK | Pod → Client | ❌ (跳过) | ✅ |
| ACK | Client → A → Pod | ✅ | ✅ |
[!TIP]
抓包技巧:在 Node A 上抓包只能看到 SYN 和 ACK,看不到 SYN-ACK;要看 SYN-ACK 需要在 Pod 所在节点抓包。
19.5.2 TCP 三次握手简单理解
- 第一次握手 (SYN):证明自己的存在
- 第二次握手 (SYN-ACK):证明对方的存在 + 确认收到
- 第三次握手 (ACK):确认彼此都存在
19.6 配置方法
19.6.1 Helm 部署
helm install cilium cilium/cilium --namespace kube-system \
--set kubeProxyReplacement=strict \
--set loadBalancer.mode=dsr
19.6.2 验证配置
# 查看 Cilium 状态
cilium status --verbose | grep -i "load"
# LoadBalancer Mode: DSR
# 或通过 config view
cilium config view | grep -i loadbalancer
# loadbalancer-mode: dsr
19.7 进阶方案:IPIP 隧道
19.7.1 IP Options 的局限性
| 问题 | 描述 |
|---|---|
| TCP Fast Open 不兼容 | 使用 Options 字段会影响 TCP Fast Open |
| 交换机处理慢 | 交换机需要解析 Options,增加处理开销 |
| Slow Path | 触发非快速路径处理 |
19.7.2 IPIP 隧道方案
字节跳动工程师提出了使用 IPIP 隧道 来传递原始 IP 信息的方案:
19.7.3 IPIP vs IP Options 对比
| 对比项 | IP Options | IPIP 隧道 |
|---|---|---|
| 交换机可见 | ✅ 可见,需解析 | ❌ 不可见,快速转发 |
| 处理路径 | Slow Path | Fast Path |
| 封装开销 | 低 | 略高(多一层 IP) |
| 实现状态 | Cilium 原生支持 | 需要二次开发 |
[!NOTE]
IPIP 隧道方案将 Options 信息"隐藏"在内层 IP 中,外层 IP 是标准包,交换机可以快速转发。
19.8 应用场景
| 场景 | 推荐模式 |
|---|---|
| 外部 → ClusterIP/NodePort | DSR |
| 外部 → LoadBalancer | DSR |
| Pod → Service(集群内) | Socket LB |
| 需要源 IP 保留 | DSR (externalTrafficPolicy=Local) |
19.9 与 LVS DSR 的关系
Cilium DSR 的思想源自 Linux LVS 的 DR 模式:
| LVS 术语 | Cilium 对应 |
|---|---|
| Director | 入口节点 (Node A) |
| Real Server | 后端 Pod |
| VIP | Service IP / NodePort |
| DIP | Pod IP |
[!TIP]
学习 LVS 的 DR/NAT/TUN 模式可以帮助理解 Cilium DSR 的设计思想。
19.10 章节小结
[!IMPORTANT]
核心要点总结:
什么是 DSR:Direct Server Return,后端 Pod 直接响应客户端,跳过入口节点
解决的问题:
- 减少返回路径跳数(4 跳 → 3 跳)
- 分散入口节点负载
- 保留真实客户端源 IP
核心挑战:如何将原始目的 IP 传递给后端 Pod
Cilium 实现:通过 IP Options 字段携带
orig_dst信息抓包验证:SYN-ACK 从 Pod 直接发往 Client,Node A 看不到
配置方法:
loadBalancer.mode=dsr与 Socket LB 的区别:
- Socket LB:优化东西向(集群内)流量
- DSR:优化南北向(外部访问)流量
第二十章 Cilium 双栈模式
本章介绍 Cilium 的 IPv4/IPv6 双栈(Dual Stack)支持,这是一种让 Pod 和 Service 同时拥有 IPv4 和 IPv6 地址的网络配置方式,是从 IPv4 向 IPv6 过渡的重要技术。
20.1 背景与概念
20.1.1 为什么需要双栈
随着 IPv4 地址资源枯竭和 IPv6 的推广,越来越多的企业开始进行 IPv6 改造:
| 方案 | 描述 |
|---|---|
| 纯 IPv4 | 传统方案,地址资源紧张 |
| 假双栈 | 两个网卡分别承载 IPv4/IPv6 |
| 真双栈 | 同一网卡同时拥有 IPv4 和 IPv6 地址 |
| 纯 IPv6 | 未来目标,彻底解决地址问题 |
20.1.2 双栈的定义
真正的双栈(Dual Stack):一个网络接口上同时配置 IPv4 和 IPv6 地址,两种协议栈独立运行。
[!NOTE]
假双栈 vs 真双栈:
- 假双栈:两个接口分别提供 IPv4 和 IPv6 服务,客户端根据协议访问不同接口
- 真双栈:一个接口同时提供两种协议,客户端可以使用任意协议访问
20.2 Cilium 双栈配置
20.2.1 Helm 部署参数
helm install cilium cilium/cilium --namespace kube-system \
--set ipv6.enabled=true \
--set ipam.mode=kubernetes
| 参数 | 说明 |
|---|---|
ipv6.enabled=true |
启用 IPv6 支持 |
ipv4.enabled=true |
默认启用,无需显式设置 |
kubeProxyReplacement |
双栈暂不支持 strict 模式 |
[!WARNING]
注意事项:Cilium 双栈模式目前不支持kubeProxyReplacement=strict,需要保留 kube-proxy。
20.2.2 Kind 集群配置
# kind-config.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
networking:
ipFamily: dual # 关键配置:启用双栈
podSubnet: "10.244.0.0/16,fd00:10:244::/56"
serviceSubnet: "10.96.0.0/12,fd00:10:96::/112"
20.3 Pod 双栈验证
20.3.1 查看 Pod IP
# 简单查看(只显示一个 IP)
kubectl get pod -o wide
# 详细查看(显示所有 IP)
kubectl get pod -o yaml | grep -A 10 "podIPs"
输出示例:
podIPs:
- ip: "10.244.1.57" # IPv4 地址
- ip: "fd00::982a:..." # IPv6 地址
20.3.2 验证网卡配置
# 进入 Pod 查看 IP 地址
kubectl exec -it <pod-name> -- ip addr show eth0
输出示例:
2: eth0@if15: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500
inet 10.244.1.57/32 scope global eth0
inet6 fd00::982a:.../128 scope global
inet6 fe80::xxxx/64 scope link
[!NOTE]
三种地址:
- IPv4 地址:Pod 的 IPv4 通信地址
- IPv6 Global 地址:Pod 的 IPv6 全局通信地址
- IPv6 Link-local 地址:以
fe80::开头,仅用于本地链路通信
20.4 Service 双栈支持
20.4.1 Service 配置
apiVersion: v1
kind: Service
metadata:
name: demo-svc
spec:
ipFamilyPolicy: PreferDualStack # 优先双栈
ipFamilies:
- IPv4
- IPv6
selector:
app: demo
ports:
- port: 80
| 字段 | 值 | 说明 |
|---|---|---|
ipFamilyPolicy |
PreferDualStack |
优先使用双栈 |
ipFamilyPolicy |
RequireDualStack |
强制要求双栈 |
ipFamilies |
[IPv4, IPv6] |
指定支持的 IP 类型 |
20.4.2 验证 Service IP
kubectl get svc demo-svc -o yaml | grep -A 5 "clusterIPs"
输出示例:
clusterIPs:
- "10.96.44.152" # IPv4 ClusterIP
- "fd00:10:96::xxx" # IPv6 ClusterIP
20.5 DNS 解析
20.5.1 A 记录与 AAAA 记录
20.5.2 解析示例
# 解析 IPv4 地址(A 记录)
nslookup -type=A demo-svc.default.svc.cluster.local
# 返回:10.96.44.152
# 解析 IPv6 地址(AAAA 记录)
nslookup -type=AAAA demo-svc.default.svc.cluster.local
# 返回:fd00:10:96::xxx
| 记录类型 | 返回内容 | 用途 |
|---|---|---|
| A | IPv4 地址 | IPv4 客户端访问 |
| AAAA | IPv6 地址 | IPv6 客户端访问 |
20.6 双栈路由
20.6.1 IPv4 路由表
# Pod 内查看 IPv4 路由
ip route
default via 10.244.1.5 dev eth0
10.244.1.5 dev eth0 scope link
20.6.2 IPv6 路由表
# Pod 内查看 IPv6 路由
ip -6 route
default via fe80::xxxx dev eth0
fd00::/64 dev eth0
20.7 IPv6 邻居发现
20.7.1 与 ARP 的区别
| 特性 | IPv4 ARP | IPv6 NDP |
|---|---|---|
| 协议名称 | ARP | NDP (Neighbor Discovery Protocol) |
| 请求消息 | ARP Request | NS (Neighbor Solicitation) |
| 响应消息 | ARP Reply | NA (Neighbor Advertisement) |
| 复杂度 | 简单 | 复杂(有状态机) |
| 查看命令 | arp -n |
ip -6 neigh |
20.7.2 IPv6 地址类型
| 地址类型 | 前缀 | 用途 |
|---|---|---|
| Global | 2000::/3 或 fd00::/8 |
全局通信 |
| Link-local | fe80::/10 |
本地链路通信 |
| Multicast | ff00::/8 |
组播通信 |
[!NOTE]
Link-local 地址:每个启用 IPv6 的网卡都会自动生成一个fe80::开头的链路本地地址,用于邻居发现等本地通信。
20.7.3 邻居发现状态机
IPv6 邻居发现有多种状态:
| 状态 | 说明 |
|---|---|
| INCOMPLETE | 正在解析,等待 NA 响应 |
| REACHABLE | 可达,最近确认过 |
| STALE | 过期,需要重新验证 |
| DELAY | 延迟探测 |
| PROBE | 主动探测中 |
20.8 跨节点通信验证
20.8.1 IPv6 Ping 测试
# Pod 内 ping IPv6 地址
ping6 fd00::9d86:...
20.8.2 抓包分析
# 在节点上抓包
tcpdump -i lxc-xxx icmp6
抓包输出:
# NS/NA 消息(邻居发现)
fe80::xxxx > ff02::1:ffxx:xxxx: ICMP6, neighbor solicitation
fe80::xxxx > fe80::yyyy: ICMP6, neighbor advertisement
# Echo Request/Reply
fd00::982a > fd00::9d86: ICMP6, echo request
fd00::9d86 > fd00::982a: ICMP6, echo reply
20.9 MAC 地址学习
20.9.1 eBPF 的作用
Cilium 使用 eBPF 来处理 IPv6 邻居发现:
[!TIP]
Cilium 的 eBPF 程序会劫持邻居发现请求,返回对应的 lxc 网卡 MAC 地址,类似于 Calico 中的169.254.1.1代理 ARP 机制。
20.9.2 验证 MAC 地址
# 抓包查看目的 MAC
tcpdump -i lxc-xxx -e icmp6
# 输出示例
# src MAC: 7c:f9:26:... (Pod 网卡)
# dst MAC: b6:44:1b:... (lxc 网卡)
20.10 配置总结
20.10.1 完整配置流程
20.10.2 关键配置对照表
| 层级 | 配置项 | 值 |
|---|---|---|
| Cluster | ipFamily |
dual |
| Cilium | ipv6.enabled |
true |
| Service | ipFamilyPolicy |
PreferDualStack |
| Service | ipFamilies |
[IPv4, IPv6] |
20.11 章节小结
[!IMPORTANT]
核心要点总结:
什么是双栈:同一网卡同时拥有 IPv4 和 IPv6 地址
配置要点:
- Cilium:
ipv6.enabled=true- Service:
ipFamilyPolicy: PreferDualStack验证方法:
- Pod:
ip addr show eth0看到两个地址- Service:
kubectl get svc -o yaml看到两个 ClusterIPDNS 解析:
- A 记录 → IPv4 地址
- AAAA 记录 → IPv6 地址
IPv6 特性:
- 使用 NDP 替代 ARP
- 自动生成 Link-local 地址 (
fe80::)- 邻居状态机比 ARP 复杂
当前限制:双栈不支持
kubeProxyReplacement=strict
第二十一章 Cilium-LB-IPAM
本章介绍 Cilium 的 LB IPAM(LoadBalancer IP Address Management)功能,这是一个专门为 LoadBalancer 类型 Service 分配 IP 地址的工具,常与 BGP Control Plane 结合使用实现外部访问。
21.1 背景与问题
21.1.1 LoadBalancer Service 的困境
在裸金属(Bare Metal)Kubernetes 集群中,创建 LoadBalancer 类型的 Service 时,常遇到以下问题:
| 环境 | LoadBalancer 支持 |
|---|---|
| 云环境(AWS/GCP/Azure) | 云厂商自动分配公网 IP |
| 裸金属集群 | 默认无 IP 分配,状态为 Pending |
[!NOTE]
很多初学者在部署 Ingress Controller 时遇到EXTERNAL-IP一直显示<pending>的问题,原因就是没有 LoadBalancer IP 分配器。
21.1.2 常见解决方案
| 方案 | 特点 |
|---|---|
| MetalLB | 独立项目,支持 L2 和 BGP 模式 |
| Cilium LB IPAM | Cilium 内置,需配合 BGP 使用 |
| kube-vip | 轻量级,支持 VIP 和 LB |
21.2 核心概念
21.2.1 LB IPAM 的职责
LB IPAM 只负责分配 IP 地址,不负责流量转发!
[!IMPORTANT]
关键理解:LB IPAM 分配的 IP 地址默认不可路由,需要配合 BGP Control Plane 将地址宣告出去,才能实现外部访问。
21.2.2 与 BGP 的关系
21.3 Cilium vs Calico 设计对比
21.3.1 宣告的 IP 类型
| 对比项 | Cilium | Calico |
|---|---|---|
| 宣告的 IP | LoadBalancer IP | ClusterIP |
| 设计契合度 | 符合 K8s 设计 | 略有争议 |
| ClusterIP 暴露 | ❌ 保持内部 | ✅ 可外部访问 |
[!TIP]
Kubernetes 设计中 ClusterIP 是集群内部地址,Cilium 选择宣告 LB IP 更符合这一理念。
21.4 配置与使用
21.4.1 CRD 资源
Cilium 安装后会自动创建 CiliumLoadBalancerIPPool CRD:
# 查看 CRD
kubectl api-resources | grep cilium | grep pool
# 输出
ciliumloadbalancerippools ippools,lbippool cilium.io/v2alpha1 false CiliumLoadBalancerIPPool
21.4.2 创建 IP Pool
apiVersion: cilium.io/v2alpha1
kind: CiliumLoadBalancerIPPool
metadata:
name: blue-pool
spec:
blocks:
- cidr: "20.0.10.0/24" # IP 地址池范围
serviceSelector:
matchLabels:
color: blue # 匹配的 Service 标签
---
apiVersion: cilium.io/v2alpha1
kind: CiliumLoadBalancerIPPool
metadata:
name: red-pool
spec:
blocks:
- cidr: "30.0.10.0/24"
serviceSelector:
matchLabels:
color: red
21.4.3 IP Pool 结构说明
| 字段 | 说明 |
|---|---|
blocks |
IP 地址段列表,支持多个 CIDR |
serviceSelector |
Service 标签选择器 |
matchLabels |
精确匹配标签 |
matchExpressions |
表达式匹配(可选) |
21.5 Service 配置
21.5.1 创建使用 IP Pool 的 Service
apiVersion: v1
kind: Service
metadata:
name: demo-svc
labels:
color: red # 匹配 red-pool
spec:
type: LoadBalancer # 必须是 LoadBalancer 类型
selector:
app: demo
ports:
- port: 80
21.5.2 验证 IP 分配
# 查看 Service
kubectl get svc demo-svc
# 输出示例
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)
demo-svc LoadBalancer 10.96.1.100 30.0.10.208 80:31234/TCP
21.6 工作原理
21.6.1 标签匹配机制
21.6.2 多 Pool 匹配规则
| 场景 | 行为 |
|---|---|
| Service 标签匹配单个 Pool | 从该 Pool 分配 IP |
| Service 标签匹配多个 Pool | 从第一个匹配的 Pool 分配 |
| Service 无匹配标签 | IP 不分配,状态 Pending |
21.7 与 MetalLB 对比
21.7.1 功能对比
| 特性 | Cilium LB IPAM | MetalLB |
|---|---|---|
| IP 分配 | ✅ | ✅ |
| L2 模式 | ❌ | ✅ |
| BGP 模式 | ✅(需 BGP CP) | ✅ |
| 独立使用 | ❌(需配合 BGP) | ✅ |
| CNI 集成 | ✅ 原生 | ❌ 独立部署 |
21.7.2 选择建议
21.8 重要限制
[!WARNING]
LB IPAM 单独使用时的限制:
- IP 不可路由:分配的 IP 只是写入 Service,外部无法访问
- 需要 BGP:必须配合 BGP Control Plane 宣告路由
- 版本要求:Cilium 1.13+ 支持 BGP 宣告 LB IP
21.8.1 单独使用 LB IPAM 的效果
# Service 获得 IP,但无法访问
kubectl get svc
NAME TYPE EXTERNAL-IP ...
demo-svc LoadBalancer 30.0.10.208 ...
# 从集群外部访问
curl 30.0.10.208
# 超时 - IP 不可路由
21.9 完整工作流程
21.9.1 LB IPAM + BGP Control Plane
21.9.2 ECMP 负载均衡
配合 BGP 可实现 ECMP(等价多路径)负载均衡:
# 路由表示例
30.0.10.208 via 10.1.5.10 # Node 1
30.0.10.208 via 10.1.5.11 # Node 2
# = 等价路由,流量分担
21.10 章节小结
[!IMPORTANT]
核心要点总结:
什么是 LB IPAM:Cilium 内置的 LoadBalancer IP 地址分配器
核心职责:只负责分配 IP,不负责路由和负载均衡
配置方式:
- 创建
CiliumLoadBalancerIPPool定义 IP 池- Service 通过 labels 匹配 Pool
重要限制:
- 单独使用时 IP 不可路由
- 需要配合 BGP Control Plane 宣告路由
与 Calico 对比:
- Cilium:宣告 LB IP(符合 K8s 设计)
- Calico:宣告 ClusterIP(有争议)
版本要求:Cilium 1.13+ 才支持 BGP 宣告 LB IP
第二十二章 Cilium 带宽管理
本章介绍 Cilium 的带宽管理(Bandwidth Manager)功能,这是一种通过 EDT(Earliest Departure Time)时间戳机制在物理网卡上实现 Pod 流量限速的技术。
22.1 背景与问题
22.1.1 传统限速思路的局限
最直觉的限速方式是在 Pod 的网卡上做限制:
为什么不能在 veth 网卡上限速?
| 问题 | 说明 |
|---|---|
| Buffer Bloat | veth 队列缓冲区容易被填满 |
| TCP TSQ 处理差 | 影响 TCP 小队列优化 |
| 驱动限制 | veth pair 驱动不支持高级队列调度 |
[!WARNING]
Docker/Kubernetes 的 veth pair 虚拟网卡在带宽管理上存在天然限制,不适合做精确限速。
22.1.2 Cilium 的解决方案
Cilium 选择在物理网卡上做限速,通过 EDT 时间戳告诉物理网卡何时发送数据包:
22.2 核心原理
22.2.1 EDT (Earliest Departure Time)
EDT 是一个内核功能,数据包在发送时会携带一个时间戳,告诉网卡设备这个包最早什么时候可以发出:
[!NOTE]
EDT 时间戳是 Linux 内核 5.x 版本引入的功能,Cilium 利用这一特性实现精确的带宽控制。
22.2.2 MQ + FQ 队列协作
物理网卡使用两种队列机制配合实现限速:
| 组件 | 全称 | 作用 |
|---|---|---|
| MQ | Multi-Queue | 多队列,将流量分散到多个 CPU/队列 |
| FQ | Fair Queue | 公平队列,基于时间轮按 EDT 调度发包 |
22.2.3 工作流程
22.3 配置方法
22.3.1 启用带宽管理
安装 Cilium 时需要开启 bandwidthManager 功能:
helm install cilium cilium/cilium --namespace kube-system \
--set bandwidthManager.enabled=true \
--set bandwidthManager.bbr=true \
--set bpf.masquerade=true \
--set kubeProxyReplacement=true
| 参数 | 说明 |
|---|---|
bandwidthManager.enabled=true |
启用带宽管理功能 |
bandwidthManager.bbr=true |
启用 BBR 拥塞控制算法 |
22.3.2 验证功能启用
# 查看 Cilium 状态
cilium status
# 输出中确认
BandwidthManager: EDT with BPF [BBR]
22.3.3 Pod 注解配置
通过注解(Annotation)为 Pod 设置带宽限制:
apiVersion: v1
kind: Pod
metadata:
name: bandwidth-limited-pod
annotations:
kubernetes.io/egress-bandwidth: "50M" # 限制出站带宽 50Mbps
# kubernetes.io/ingress-bandwidth: "100M" # 入站带宽(可选)
spec:
containers:
- name: app
image: nginx
| 注解 | 说明 |
|---|---|
kubernetes.io/egress-bandwidth |
限制出站(发送)带宽 |
kubernetes.io/ingress-bandwidth |
限制入站(接收)带宽 |
[!TIP]
常用单位:K(Kbps)、M(Mbps)、G(Gbps),如10M表示 10 Mbps。
22.4 实现原理详解
22.4.1 为什么在物理网卡限速
| 对比项 | veth 网卡 | 物理网卡 |
|---|---|---|
| 队列支持 | 简单队列 | 多队列 + 硬件队列 |
| 调度能力 | 有限 | FQ 公平调度 |
| 性能影响 | 大(buffer bloat) | 小 |
| 限速精度 | 低 | 高(EDT 时间戳) |
22.4.2 EDT 时间戳原理
限速计算示例:
原始带宽:1 Gbps = 每秒发送 1000M 数据
限制带宽:50 Mbps
计算:1000 / 50 = 20 倍
实现:每 20 个时间片才发送 1 个包
结果:带宽降低到 50 Mbps
22.5 测试验证
22.5.1 部署测试 Pod
# 10M 带宽限制
apiVersion: v1
kind: Pod
metadata:
name: netperf-10m
annotations:
kubernetes.io/egress-bandwidth: "10M"
spec:
containers:
- name: netperf
image: networkstatic/netperf
---
# 100M 带宽限制
apiVersion: v1
kind: Pod
metadata:
name: netperf-100m
annotations:
kubernetes.io/egress-bandwidth: "100M"
spec:
containers:
- name: netperf
image: networkstatic/netperf
22.5.2 使用 netperf 测试
# 获取服务端 IP
SERVER_IP=$(kubectl get pod netperf-server -o jsonpath='{.status.podIP}')
# 从限速 Pod 测试带宽
kubectl exec -it netperf-10m -- netperf -H $SERVER_IP -t TCP_STREAM
# 预期结果:~9.5 Mbps(接近 10M 限制)
22.5.3 测试结果对照
| 配置 | 预期带宽 | 实测带宽 |
|---|---|---|
egress-bandwidth: 10M |
10 Mbps | ~9.5 Mbps |
egress-bandwidth: 100M |
100 Mbps | ~93 Mbps |
| 无限制 | 网卡最大 | ~500+ Mbps |
22.6 与传统方案对比
22.6.1 与 CNI bandwidth 插件对比
| 特性 | CNI bandwidth 插件 | Cilium 带宽管理 |
|---|---|---|
| 限速位置 | veth 网卡 | 物理网卡 |
| 实现方式 | tc (tbf/htb) | eBPF + EDT |
| 性能影响 | 较大 | 较小 |
| 精度 | 一般 | 高 |
| TCP 优化 | 无 | TSQ 支持 |
22.7 重要限制
[!CAUTION]
使用限制:
- 不支持 Kind 环境:Kind 使用 veth pair,无法正确限速
- 需要物理/虚拟机环境:必须有真正的物理网卡驱动
- 内核版本要求:需要支持 EDT 的内核(5.x+)
- 主要限制 egress:入站限速场景较少
22.8 注意事项
22.8.1 环境要求
22.8.2 驱动检查
# 查看网卡驱动
ethtool -i eth0 | grep driver
# 物理网卡驱动示例
driver: vmxnet3 # VMware
driver: virtio_net # KVM
driver: ixgbe # Intel 万兆
22.9 章节小结
[!IMPORTANT]
核心要点总结:
为什么不在 veth 限速:Buffer Bloat、队列受限、精度差
Cilium 方案:在物理网卡使用 EDT 时间戳限速
启用方式:
- Helm:
bandwidthManager.enabled=true- Pod:
kubernetes.io/egress-bandwidth: "50M"工作原理:
- eBPF 为数据包打上 EDT 时间戳
- FQ 调度器根据 EDT 控制发包时机
MQ + FQ 协作:
- MQ(多队列)分散流量
- FQ(公平队列)按时间调度
环境要求:
- 需要真实物理/虚拟网卡
- 不支持 Kind(veth pair)
- 内核版本 5.x+
第二十三章 Cilium Ingress Controller
本章介绍 Cilium 原生的 Ingress Controller 功能,无需部署额外的 Ingress 控制器(如 Nginx Ingress),Cilium 可以直接提供七层负载均衡能力。
23.1 背景与概念
23.1.1 Ingress 回顾
Ingress 是 Kubernetes 中用于管理七层(HTTP/HTTPS)流量入口的资源:
四层 vs 七层负载均衡:
| 特性 | 四层(L4) | 七层(L7) |
|---|---|---|
| 工作层 | 传输层(TCP/UDP) | 应用层(HTTP/HTTPS) |
| 路由依据 | IP + 端口 | URL 路径、Host 头 |
| SSL 卸载 | ❌ | ✅ |
| 典型场景 | Service LoadBalancer | Ingress |
23.1.2 传统 Ingress 架构
需要额外部署 Nginx Ingress、Traefik 等控制器。
23.1.3 Cilium Ingress Controller
Cilium 内置 Ingress Controller,无需额外组件:
[!TIP]
Cilium 使用 Envoy 作为七层代理,提供 Ingress Controller 能力,同时为 Service Mesh 功能奠定基础。
23.2 前置要求
23.2.1 配置要求
| 要求 | 说明 |
|---|---|
| kubeProxyReplacement | 必须为 strict 或 true |
| L7 Proxy | 默认启用 |
| Kubernetes 版本 | ≥ 1.19 |
| Cilium 版本 | ≥ 1.13 |
23.2.2 启用 Ingress Controller
helm install cilium cilium/cilium --namespace kube-system \
--set kubeProxyReplacement=true \
--set ingressController.enabled=true \
--set ingressController.loadbalancerMode=dedicated
| 参数 | 说明 |
|---|---|
ingressController.enabled=true |
启用 Ingress Controller |
ingressController.loadbalancerMode |
dedicated(专用)或 shared(共享) |
23.3 HTTP 模式
23.3.1 工作流程
23.3.2 创建后端服务
# 部署后端应用
apiVersion: apps/v1
kind: Deployment
metadata:
name: productpage
spec:
replicas: 1
selector:
matchLabels:
app: productpage
template:
metadata:
labels:
app: productpage
spec:
containers:
- name: productpage
image: docker.io/istio/examples-bookinfo-productpage-v1:1.16.2
ports:
- containerPort: 9080
---
apiVersion: v1
kind: Service
metadata:
name: productpage
spec:
selector:
app: productpage
ports:
- port: 9080
23.3.3 创建 Ingress 资源
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: basic-ingress
spec:
ingressClassName: cilium # 使用 Cilium Ingress Controller
rules:
- http:
paths:
- path: /details
pathType: Prefix
backend:
service:
name: details
port:
number: 9080
- path: /
pathType: Prefix
backend:
service:
name: productpage
port:
number: 9080
23.3.4 验证配置
# 查看 Ingress
kubectl get ingress
# 输出示例
NAME CLASS HOSTS ADDRESS PORTS
basic-ingress cilium * 172.18.0.200 80
# 测试访问
curl http://172.18.0.200/details | jq
23.4 HTTPS 模式
23.4.1 TLS 卸载原理
[!NOTE]
信任域划分:
- 非信任域:客户端 ↔ Ingress Controller(HTTPS)
- 信任域:Ingress Controller ↔ 后端 Pod(HTTP)
23.4.2 创建 TLS 证书
方式一:使用 minica
# 安装 minica
go install github.com/jsha/minica@latest
# 生成证书
minica --domains demo.cilium.rocks
# 创建 Secret
kubectl create secret tls demo-cert \
--cert=demo.cilium.rocks/cert.pem \
--key=demo.cilium.rocks/key.pem
方式二:使用 OpenSSL
# 生成自签名证书
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout tls.key -out tls.crt \
-subj "/CN=demo.cilium.rocks"
# 创建 Secret
kubectl create secret tls demo-cert \
--cert=tls.crt --key=tls.key
方式三:使用 cert-manager
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: demo-cert
spec:
secretName: demo-cert
dnsNames:
- demo.cilium.rocks
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
23.4.3 创建 HTTPS Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: tls-ingress
spec:
ingressClassName: cilium
tls:
- hosts:
- demo.cilium.rocks
secretName: demo-cert # 引用 TLS Secret
rules:
- host: demo.cilium.rocks # 指定 Host
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: productpage
port:
number: 9080
23.4.4 验证 HTTPS
# 添加 hosts 解析
echo "172.18.0.200 demo.cilium.rocks" >> /etc/hosts
# 测试 HTTPS 访问
curl -k -v https://demo.cilium.rocks/
# 查看 TLS 握手信息
curl -k -v https://demo.cilium.rocks/ 2>&1 | grep -A5 "SSL connection"
23.5 与 LoadBalancer 配合
23.5.1 MetalLB 集成
Ingress Controller 需要 LoadBalancer 类型的 Service 暴露外部访问:
23.5.2 MetalLB 配置
# MetalLB L2 模式配置
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: default
namespace: metallb-system
spec:
addresses:
- 172.18.0.200-172.18.0.254
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
name: default
namespace: metallb-system
23.6 Ingress 资源详解
23.6.1 资源结构
23.6.2 关键字段
| 字段 | 说明 |
|---|---|
ingressClassName |
指定使用的 Ingress Controller |
tls.hosts |
TLS 证书适用的域名 |
tls.secretName |
包含证书的 Secret 名称 |
rules.host |
匹配的 Host 头(可选) |
rules.http.paths |
URL 路径匹配规则 |
backend.service |
后端 Service 配置 |
23.7 与传统方案对比
23.7.1 功能对比
| 特性 | Nginx Ingress | Cilium Ingress |
|---|---|---|
| 部署方式 | 独立 Deployment | CNI 内置 |
| L7 代理 | Nginx | Envoy |
| 配置方式 | 注解 + ConfigMap | 标准 Ingress |
| 与 CNI 集成 | 独立 | 原生集成 |
| Service Mesh | 需额外组件 | 原生支持 |
23.7.2 选择建议
23.8 注意事项
[!WARNING]
使用注意:
- HTTP 模式异常:部分版本可能出现 503 错误,建议创建资源后等待 2-3 分钟
- Host 必填(HTTPS):TLS 模式必须指定 host 字段
- LoadBalancer 依赖:需要 MetalLB 或云厂商 LB 提供 External IP
- 证书有效期:自签证书需注意过期问题,生产环境建议使用 cert-manager
23.9 章节小结
[!IMPORTANT]
核心要点总结:
什么是 Cilium Ingress:Cilium 内置的七层 Ingress Controller,基于 Envoy
前置要求:
kubeProxyReplacement=trueingressController.enabled=trueHTTP 模式:
ingressClassName: cilium- 定义 path 和后端 Service
HTTPS 模式:
- 添加
tls字段配置证书- 必须指定
host字段TLS 证书创建:
- minica / OpenSSL / cert-manager
- 创建为 Kubernetes Secret
依赖组件:
- 需要 LoadBalancer(MetalLB/云 LB)提供外部 IP
第二十四章 Cilium Gateway API
本章介绍 Cilium 对 Kubernetes Gateway API 的支持。Gateway API 是 Ingress 的下一代替代方案,提供更强大、更标准化的七层流量管理能力。
24.1 背景与概念
24.1.1 什么是 Gateway API
Gateway API 是 Kubernetes SIG-Network 设计的新一代流量管理 API,目标是替代 Ingress:
[!NOTE]
Gateway API 是 Kubernetes 官方推荐的 Ingress 替代方案,Cilium 通过 Envoy 原生支持 Gateway API。
24.1.2 与 Ingress 对比
| 特性 | Ingress | Gateway API |
|---|---|---|
| API 成熟度 | 稳定(GA) | 逐步稳定(部分 Beta) |
| 表达能力 | 有限 | 更丰富 |
| 角色分离 | 无 | 支持(Infra/Cluster/App) |
| TLS 管理 | 简单 | 更灵活 |
| 跨命名空间 | 复杂 | 原生支持 |
| 扩展性 | 注解(非标准) | 标准化扩展 |
24.1.3 核心资源对象
| 资源 | 说明 |
|---|---|
| GatewayClass | 定义使用的 Gateway 控制器(如 Cilium) |
| Gateway | 定义入口网关(监听端口、TLS 配置) |
| HTTPRoute | 定义 HTTP 路由规则(类似 Ingress Rules) |
24.2 前置要求
24.2.1 配置要求
| 要求 | 说明 |
|---|---|
| kubeProxyReplacement | 必须为 strict 或 true |
| L7 Proxy | 默认启用 |
| Gateway API CRDs | 必须预先安装 |
24.2.2 安装 Gateway API CRDs
必须预先安装四个 CRD:
# 安装 Gateway API CRDs
kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/v1.0.0/config/crd/standard/gateway.networking.k8s.io_gatewayclasses.yaml
kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/v1.0.0/config/crd/standard/gateway.networking.k8s.io_gateways.yaml
kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/v1.0.0/config/crd/standard/gateway.networking.k8s.io_httproutes.yaml
kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/v1.0.0/config/crd/standard/gateway.networking.k8s.io_referencegrants.yaml
[!CAUTION]
必须先安装 CRDs!如果不预先安装 Gateway API CRDs,Cilium 将无法启用 Gateway API 功能。
24.2.3 启用 Gateway API
helm install cilium cilium/cilium --namespace kube-system \
--set kubeProxyReplacement=true \
--set gatewayAPI.enabled=true
| 参数 | 说明 |
|---|---|
gatewayAPI.enabled=true |
启用 Gateway API 支持 |
24.3 HTTP 模式
24.3.1 工作流程
24.3.2 创建 Gateway
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: cilium-gateway
spec:
gatewayClassName: cilium # 使用 Cilium 作为控制器
listeners:
- name: http
protocol: HTTP
port: 80
allowedRoutes:
namespaces:
from: Same
24.3.3 创建 HTTPRoute
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: http-route
spec:
parentRefs:
- name: cilium-gateway # 关联 Gateway
rules:
- matches:
- path:
type: PathPrefix
value: /details
backendRefs:
- name: details
port: 9080
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: productpage
port: 9080
24.3.4 验证配置
# 查看 Gateway
kubectl get gateway
# 输出示例
NAME CLASS ADDRESS READY
cilium-gateway cilium 172.18.0.200 True
# 查看 HTTPRoute
kubectl get httproute
# 测试访问
LB_IP=$(kubectl get gateway cilium-gateway -o jsonpath='{.status.addresses[0].value}')
curl http://$LB_IP/details | jq
24.4 HTTPS 模式
24.4.1 TLS 配置架构
24.4.2 创建 TLS 证书
# 使用 minica 生成证书
minica --domains bookinfo.cilium.rocks
# 创建 Secret
kubectl create secret tls bookinfo-cert \
--cert=bookinfo.cilium.rocks/cert.pem \
--key=bookinfo.cilium.rocks/key.pem
24.4.3 创建 HTTPS Gateway
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: tls-gateway
spec:
gatewayClassName: cilium
listeners:
- name: https
protocol: HTTPS
port: 443
hostname: bookinfo.cilium.rocks
tls:
mode: Terminate
certificateRefs:
- name: bookinfo-cert # 引用 TLS Secret
allowedRoutes:
namespaces:
from: Same
24.4.4 创建 HTTPS HTTPRoute
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: https-route
spec:
parentRefs:
- name: tls-gateway
hostnames:
- bookinfo.cilium.rocks # 必须匹配 Gateway 的 hostname
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: productpage
port: 9080
24.4.5 验证 HTTPS
# 添加 hosts 解析
LB_IP=$(kubectl get gateway tls-gateway -o jsonpath='{.status.addresses[0].value}')
echo "$LB_IP bookinfo.cilium.rocks" >> /etc/hosts
# 测试 HTTPS 访问
curl -k -v https://bookinfo.cilium.rocks/
24.5 资源关系详解
24.5.1 资源层级
24.5.2 与 Ingress 映射
| Ingress | Gateway API |
|---|---|
ingressClassName |
GatewayClass + Gateway.gatewayClassName |
| Ingress 资源 | Gateway + HTTPRoute |
rules.host |
Gateway.listeners.hostname |
rules.http.paths |
HTTPRoute.rules.matches |
tls |
Gateway.listeners.tls |
24.6 与 Ingress 对比
24.6.1 配置对比
24.6.2 功能对比
| 场景 | Ingress | Gateway API |
|---|---|---|
| 基础 HTTP 路由 | ✅ | ✅ |
| TLS 终结 | ✅ | ✅ |
| Header 匹配 | 注解 | 原生支持 |
| 请求重写 | 注解 | 原生支持 |
| 流量拆分 | 注解 | 原生支持 |
| 跨命名空间 | 困难 | 简单 |
24.7 实际应用场景
24.7.1 场景一:多团队共享网关
24.7.2 场景二:Service Mesh 集成
[!TIP]
Gateway API 是 Service Mesh 的基础组件,Cilium 的 Gateway API 实现与其 Service Mesh 功能无缝集成。
24.8 注意事项
[!WARNING]
使用注意:
- CRDs 必须预装:Gateway API CRDs 必须在安装 Cilium 之前安装
- 版本兼容性:确保 CRDs 版本与 Cilium 版本兼容
- LoadBalancer 依赖:Gateway 会创建 LoadBalancer 类型的 Service
- hostname 匹配:HTTPS 模式下,HTTPRoute 的 hostnames 必须匹配 Gateway 的 hostname
24.9 章节小结
[!IMPORTANT]
核心要点总结:
什么是 Gateway API:Ingress 的下一代替代方案,更强大的七层流量管理
核心资源:
GatewayClass:定义控制器Gateway:定义入口网关HTTPRoute:定义路由规则前置要求:
- 必须预装 Gateway API CRDs
gatewayAPI.enabled=trueHTTP 模式:
- 创建 Gateway + HTTPRoute
gatewayClassName: ciliumHTTPS 模式:
- Gateway 配置 TLS 证书
- HTTPRoute 配置 hostnames
与 Ingress 区别:
- 更丰富的表达能力
- 原生支持流量拆分、Header 匹配等
- 更好的跨命名空间支持
第二十五章 BGP 基础知识
本章介绍 BGP(Border Gateway Protocol)协议的基础知识。BGP 是 Cilium 实现跨网络路由通告的核心技术,理解 BGP 原理对于配置 Cilium BGP 功能至关重要。
25.1 背景与概念
25.1.1 为什么需要 BGP
在 Kubernetes 环境中,每个 Pod 都有独立的 IP 地址,导致路由条目数量急剧增加:
路由条目膨胀:
| 场景 | 节点数 | 路由条目数 |
|---|---|---|
| 传统虚拟机 | 200 | ~200 |
| Kubernetes(100 Pod/节点) | 200 | ~20,000 |
[!NOTE]
当路由条目达到数万甚至数十万时,传统 IGP 协议(如 OSPF)难以高效管理,需要使用 BGP 协议。
25.1.2 路由协议分类
| 协议类型 | 代表 | 适用规模 | 特点 |
|---|---|---|---|
| 静态路由 | - | 小型 | 手动配置,简单直观 |
| IGP | OSPF、IS-IS | 中型园区网 | 自动学习,成百上千条 |
| BGP | iBGP、eBGP | 大型互联网 | 成千上万条,更灵活 |
25.1.3 BGP 定义
BGP(Border Gateway Protocol):边界网关协议
- 边界:用于不同自治系统(AS)之间的路由交换
- 网关:运行 BGP 的路由器称为 BGP 网关
- 协议:基于 TCP 179 端口通信
25.2 核心概念
25.2.1 自治系统(AS)
AS(Autonomous System):自治系统号,用于标识一个独立管理的网络域。
| 概念 | 说明 |
|---|---|
| AS 号 | 全局唯一标识符(如 AS 100、AS 65000) |
| 私有 AS | 64512 - 65534(类似私有 IP) |
| 公有 AS | 需向 IANA 申请 |
25.2.2 iBGP 与 eBGP
| 类型 | 全称 | 说明 |
|---|---|---|
| iBGP | Internal BGP | 同一 AS 内部的 BGP 邻居 |
| eBGP | External BGP | 不同 AS 之间的 BGP 邻居 |
关键区别:
| 特性 | iBGP | eBGP |
|---|---|---|
| AS 号 | 相同 | 不同 |
| 直连要求 | 不需要直连 | 通常需要直连 |
| TTL | 大于 1(可跨路由器) | 默认 1(直连) |
| Next-Hop | 不修改 | 修改为自己 |
25.2.3 BGP 邻居建立
BGP 使用 TCP 179 端口建立邻居关系:
[!TIP]
BGP 邻居不要求物理直连,只要 TCP 179 端口可达即可。这是 BGP 的重要特性。
25.3 水平分割原则
25.3.1 问题背景
水平分割(Split Horizon):从 iBGP 对等体学习到的路由,不再通告给其他 iBGP 对等体。
目的:防止路由环路
问题:R1 无法学习到 R3 的路由
25.3.2 解决方案
为了让所有路由器学习到完整路由,需要建立全互联(Full Mesh):
问题:邻居数量 = N × (N-1) / 2,规模增大时开销巨大
25.4 路由反射器
25.4.1 概念
RR(Route Reflector):路由反射器,用于解决 iBGP 全互联问题。
| 角色 | 说明 |
|---|---|
| RR(Route Reflector) | 路由反射器 |
| Client | RR 的客户端 |
| Non-Client | 非客户端的普通 iBGP 邻居 |
25.4.2 反射规则
| 路由来源 | 反射目标 |
|---|---|
| Non-Client | 所有 Client(不包括其他 Non-Client) |
| Client | Non-Client + 其他 Client |
| eBGP Peer | 所有 Client + Non-Client |
25.4.3 反射示例
路由流向:
| 场景 | 路由来源 | 反射目标 |
|---|---|---|
| R5 → R2 | Non-Client | R3、R4(Client) |
| R3 → R2 | Client | R4(Client)+ R5(Non-Client) |
| R1 → R2 | eBGP | R3、R4、R5(所有) |
25.4.4 理解技巧
[!TIP]
记忆技巧:
- Client = RR 的一部分(对内分彼此,对外是整体)
- Non-Client = 普通邻居(遵守水平分割)
- eBGP = 外部来源(无水平分割限制)
25.5 BGP 在 Kubernetes 中的应用
25.5.1 典型拓扑
25.5.2 Cilium BGP 实现
Cilium 支持多种 BGP 后端:
| 后端 | 说明 |
|---|---|
| GoBGP | 当前推荐,原生 Go 实现 |
| BIRD | 早期版本使用 |
| MetalLB BGP | 与 MetalLB 集成 |
25.6 章节小结
[!IMPORTANT]
核心要点总结:
什么是 BGP:
- 边界网关协议,用于大规模路由管理
- 基于 TCP 179 端口
AS 自治系统:
- 独立管理的网络域
- 私有 AS:64512 - 65534
iBGP vs eBGP:
- iBGP:同一 AS 内部
- eBGP:不同 AS 之间
水平分割:
- 防止路由环路
- 导致全互联问题
路由反射器:
- 解决全互联问题
- Client 和 Non-Client 角色区分
- 三条反射规则
Kubernetes 应用:
- 用于 Pod/Service IP 通告
- Cilium 使用 GoBGP 实现
第二十六章 Cilium BGP Control Plane
本章介绍 Cilium BGP Control Plane 的配置与实践。通过 Kind + ContainerLab 构建完整的 BGP 测试环境,实现 Kubernetes Pod 网络与数据中心网络的互通。
26.1 背景与架构
26.1.1 Spine-Leaf 网络架构
现代数据中心普遍采用 Spine-Leaf 架构:
| 层级 | 角色 | 说明 |
|---|---|---|
| Spine | 核心交换机 | 负责跨 Leaf 流量转发 |
| Leaf | 接入交换机 | 连接服务器、充当 BGP RR |
| Node | K8s 节点 | 运行 Cilium BGP |
26.1.2 BGP 邻居关系
| 邻居类型 | 场景 | AS 关系 |
|---|---|---|
| iBGP | Leaf ↔ Node | 同一 AS |
| eBGP | Spine ↔ Leaf | 不同 AS |
26.2 环境搭建
26.2.1 整体架构
26.2.2 网络复用原理
Kind 创建的容器通过网络复用方式与 ContainerLab 连接:
关键配置:
# ContainerLab Server 配置
network-mode: container:clab-bgp-control-plane
exec:
- ip route replace default via 10.1.5.1 # 替换默认路由
[!NOTE]
核心原理:通过container网络模式复用 Kind 容器的网络命名空间,再添加新网卡并替换默认路由,实现流量从 Kind 到 ContainerLab 的引导。
26.3 Cilium BGP 配置
26.3.1 Helm 安装参数
helm install cilium cilium/cilium --namespace kube-system \
--set bgpControlPlane.enabled=true \
--set ipam.mode=kubernetes \
--set ipv4NativeRoutingCIDR=10.0.0.0/8 \
--set tunnel=disabled
| 参数 | 说明 |
|---|---|
bgpControlPlane.enabled=true |
启用 BGP Control Plane |
ipam.mode=kubernetes |
使用 Kubernetes IPAM |
ipv4NativeRoutingCIDR |
直接路由的 CIDR |
tunnel=disabled |
禁用隧道模式 |
26.3.2 CiliumBGPPeeringPolicy
apiVersion: cilium.io/v2alpha1
kind: CiliumBGPPeeringPolicy
metadata:
name: bgp-peering-policy
spec:
nodeSelector:
matchLabels:
rack: rack0 # 选择节点
virtualRouters:
- localASN: 65005 # 本地 AS 号
exportPodCIDR: true # 宣告 Pod CIDR
neighbors:
- peerAddress: 10.1.5.1/32 # 邻居地址
peerASN: 65005 # 邻居 AS 号
关键字段说明:
| 字段 | 说明 |
|---|---|
nodeSelector |
选择应用策略的节点 |
localASN |
本节点的 AS 号 |
exportPodCIDR |
是否宣告 Pod CIDR |
peerAddress |
BGP 邻居地址 |
peerASN |
BGP 邻居的 AS 号 |
26.4 交换机配置
26.4.1 Leaf 交换机(路由反射器)
# Leaf 0 配置示例
router bgp 65005
router-id 10.1.5.1
# iBGP 邻居 - K8s 节点(作为 RR Client)
neighbor 10.1.5.10 remote-as 65005
neighbor 10.1.5.10 route-reflector-client
neighbor 10.1.5.11 remote-as 65005
neighbor 10.1.5.11 route-reflector-client
# eBGP 邻居 - Spine 交换机
neighbor 10.1.10.2 remote-as 500
neighbor 10.1.12.2 remote-as 800
26.4.2 Spine 交换机
# Spine 0 配置示例
router bgp 500
router-id 10.1.10.1
# eBGP 邻居 - Leaf 交换机
neighbor 10.1.10.1 remote-as 65005
neighbor 10.1.11.1 remote-as 65008
26.5 ECMP 负载分担
26.5.1 工作原理
ECMP(Equal-Cost Multi-Path):当有多条等价路径时,进行负载分担。
26.5.2 配置启用
# 在 Leaf 交换机上启用
router bgp 65005
maximum-paths 2
bestpath as-path multipath-relax
26.5.3 验证 ECMP
# 查看路由表
ip route show
# 输出示例 - 多个 nexthop
10.98.1.0/24 proto bgp
nexthop via 10.1.10.2 dev eth1 weight 1
nexthop via 10.1.12.2 dev eth2 weight 1
26.6 验证与测试
26.6.1 查看 BGP 邻居状态
# 在 Leaf 交换机上
show ip bgp summary
# 或在 K8s 节点上
cilium bgp peers
26.6.2 查看路由表
# 查看 BGP 路由
show ip bgp
# 验证 Pod 网络路由
ip route | grep 10.98
26.6.3 跨节点 Pod 通信测试
# 从一个节点的 Pod ping 另一个节点的 Pod
kubectl exec -it test-pod -- ping <target-pod-ip>
26.7 生产环境考量
26.7.1 网络规划
| 网络类型 | 用途 | 示例 |
|---|---|---|
| 管理网络 | SSH、监控 | 172.18.0.0/16 |
| 业务网络 | Pod 流量 | 10.1.0.0/16 |
26.7.2 高可用设计
26.8 章节小结
[!IMPORTANT]
核心要点总结:
Spine-Leaf 架构:
- Spine 负责跨 Leaf 转发
- Leaf 作为 BGP RR
环境搭建:
- Kind + ContainerLab 联合
- 网络复用实现流量引导
Cilium 配置:
bgpControlPlane.enabled=trueCiliumBGPPeeringPolicy定义邻居关键参数:
localASN:本地 AS 号peerASN:邻居 AS 号exportPodCIDR:宣告 Pod 网络交换机配置:
- Leaf 作为 RR,配置
route-reflector-client- 启用
maximum-paths实现 ECMP生产考量:
- 管理网络与业务网络分离
- 多路径高可用设计
第二十七章 Cilium BGP with LB-IPAM
本章介绍如何将 Cilium BGP Control Plane 与 LoadBalancer IPAM 结合,实现 Service 的 External IP 可路由,从而让外部网络能够直接访问 Kubernetes 集群内的服务。
27.1 背景与问题
27.1.1 问题场景
在生产环境中,外部客户端需要访问 Kubernetes 集群内的服务:
核心问题:外部网络设备如何知道 Service 的 LB IP 在哪里?
27.1.2 传统方案对比
| 方案 | 工作层级 | 说明 |
|---|---|---|
| MetalLB L2 | 二层 | ARP 响应,仅限同一子网 |
| MetalLB BGP | 三层 | 需要额外部署 |
| Cilium BGP | 三层 | CNI 原生集成 |
27.2 Service Announcement 原理
27.2.1 工作流程
27.2.2 核心概念
| 概念 | 说明 |
|---|---|
| Service Announcement | 将 Service 的 LB IP 通过 BGP 宣告 |
| serviceSelector | 选择哪些 Service 进行宣告 |
| exportPodCIDR | 宣告 Pod 网段(已在 26 章介绍) |
27.3 CiliumBGPPeeringPolicy 配置
27.3.1 完整配置示例
apiVersion: cilium.io/v2alpha1
kind: CiliumBGPPeeringPolicy
metadata:
name: bgp-peering-policy
spec:
nodeSelector:
matchLabels:
rack: rack0
virtualRouters:
- localASN: 65005
exportPodCIDR: true
# 服务宣告配置(1.13+ 新增)
serviceSelector:
matchExpressions:
- key: somekey
operator: NotIn
values:
- never-used-value
neighbors:
- peerAddress: 10.1.5.1/32
peerASN: 65005
27.3.2 serviceSelector 详解
宣告所有 LoadBalancer Service:
serviceSelector:
matchExpressions:
- key: somekey
operator: NotIn
values:
- never-used-value
[!NOTE]
原理解释:使用一个不存在的 key-value 组合,NotIn表示"不在列表中",由于没有 Service 有这个标签,所以所有 Service 都满足条件。
只宣告特定 Service:
serviceSelector:
matchLabels:
expose-bgp: "true" # 只宣告带此标签的 Service
27.4 MetalLB 集成方案
27.4.1 架构图
27.4.2 配置步骤
1. 安装 MetalLB:
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/main/config/manifests/metallb-native.yaml
2. 配置 IP 池:
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: default
namespace: metallb-system
spec:
addresses:
- 172.18.0.200-172.18.0.254
3. 创建 LoadBalancer Service:
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
type: LoadBalancer
selector:
app: my-app
ports:
- port: 80
targetPort: 8080
4. 应用 BGP Peering Policy(包含 serviceSelector)
27.4.3 验证路由宣告
# 在路由器上查看路由表
show ip route
# 应能看到类似输出
B 172.18.0.200/32 via 10.1.5.10 ...
27.5 Cilium LB-IPAM 方案
27.5.1 与 MetalLB 的区别
| 特性 | MetalLB | Cilium LB-IPAM |
|---|---|---|
| IP 分配 | MetalLB 管理 | Cilium 管理 |
| 路由宣告 | MetalLB BGP 或 Cilium | Cilium BGP |
| 组件数量 | 需要额外部署 | CNI 原生 |
27.5.2 配置 Cilium LB-IPAM
1. 创建 IP Pool:
apiVersion: cilium.io/v2alpha1
kind: CiliumLoadBalancerIPPool
metadata:
name: blue-pool
spec:
blocks:
- cidr: 30.0.10.0/24
serviceSelector:
matchLabels:
color: blue
2. 创建 Service 使用指定池:
apiVersion: v1
kind: Service
metadata:
name: test-service
labels:
color: blue # 匹配 IP Pool 的 serviceSelector
spec:
type: LoadBalancer
selector:
app: test
ports:
- port: 80
3. BGP 自动宣告:
# 在路由器上验证
show ip route | grep 30.0
# 输出示例
B 30.0.10.22/32 via 10.1.5.10 ...
27.6 完整流程演示
27.6.1 从创建到访问
27.6.2 验证命令
# 1. 查看 Service External IP
kubectl get svc
# 输出示例
NAME TYPE EXTERNAL-IP PORT(S)
my-service LoadBalancer 30.0.10.22 80:31234/TCP
# 2. 在路由器上验证路由
show ip bgp
# 3. 从外部访问测试
curl http://30.0.10.22/
27.7 生产环境考量
27.7.1 宣告策略选择
27.7.2 安全建议
| 建议 | 说明 |
|---|---|
| 精准宣告 | 只宣告需要对外的 Service |
| 网络隔离 | LB IP 与内部 IP 分开规划 |
| 访问控制 | 配合防火墙/NetworkPolicy |
27.8 Cluster IP vs LB IP 宣告对比
27.8.1 两种方式对比
| 项目 | Calico 方式 | Cilium 方式 |
|---|---|---|
| 宣告内容 | Cluster IP | LoadBalancer IP |
| 适用场景 | 内部互通 | 对外服务 |
| 安全性 | 较低 | 较高 |
| 推荐程度 | ⭐⭐ | ⭐⭐⭐⭐ |
[!WARNING]
Cluster IP 本身是集群内部 IP,将其通过 BGP 宣告到外部网络存在安全隐患,不推荐在生产环境使用。
27.9 章节小结
[!IMPORTANT]
核心要点总结:
问题本质:
- LoadBalancer IP 默认只在集群内有效
- 外部网络设备不知道如何路由
解决方案:
- 使用 BGP 将 LB IP 宣告到网络
- Cilium 1.13+ 支持 Service Announcement
配置要点:
serviceSelector控制宣告哪些 Service- 使用
NotIn+ 不存在的值宣告所有两种集成:
- MetalLB(分配 IP)+ Cilium BGP(宣告)
- Cilium LB-IPAM(分配)+ Cilium BGP(宣告)
验证方法:
- 路由器查看
show ip route- 外部
curl测试访问最佳实践:
- 宣告 LB IP 而非 Cluster IP
- 配合标签精细控制宣告范围
第二十八章 Cilium ClusterMesh
本章介绍 Cilium ClusterMesh 多集群网络互联功能。通过 ClusterMesh,多个 Kubernetes 集群可以形成逻辑上的统一集群,实现跨集群的服务发现、负载均衡和故障切换。
28.1 背景与概念
28.1.1 什么是 ClusterMesh
ClusterMesh 是 Cilium 提供的多集群连接方案:
| 特性 | 说明 |
|---|---|
| 多集群连接 | 多个物理集群形成逻辑统一 |
| 跨集群服务发现 | Service 可发现其他集群的 Pod |
| 统一负载均衡 | 请求可负载到多集群的 Pod |
28.1.2 应用场景
| 场景 | 说明 |
|---|---|
| 高可用容灾 | 一个集群故障,自动切换到另一个 |
| 地理分布 | 多地域部署,就近访问 |
| 水平扩展 | 突破单集群规模限制 |
28.2 架构原理
28.2.1 核心组件
| 组件 | 作用 |
|---|---|
| ClusterMesh API Server | 提供跨集群状态同步接口 |
| etcd | 存储集群状态信息 |
| Cilium Agent | 同步信息到本地 |
28.2.2 信息同步原理
核心要点:
- 每个集群的 Agent 将本地 Pod/Service 信息上报到 ClusterMesh API
- API Server 之间相互同步
- Service 因此能"知道"其他集群的后端 Pod
28.3 Global Service
28.3.1 普通 Service vs Global Service
28.3.2 配置方式
只需在 Service 上添加注解:
apiVersion: v1
kind: Service
metadata:
name: my-service
annotations:
# 标记为 Global Service
io.cilium/global-service: "true"
spec:
selector:
app: my-app
ports:
- port: 80
[!NOTE]
只有添加了io.cilium/global-service: "true"注解的 Service 才会享有跨集群能力。普通 Service 仍然只在本集群内有效。
28.4 Service Affinity(服务亲和性)
28.4.1 三种模式
| 模式 | 注解值 | 行为 |
|---|---|---|
| Local | local |
优先访问本集群 Pod |
| Remote | remote |
优先访问远端集群 Pod |
| 无标注 | 不设置 | 所有 Pod 随机负载 |
28.4.2 配置示例
apiVersion: v1
kind: Service
metadata:
name: echo-local
annotations:
io.cilium/global-service: "true"
io.cilium/service-affinity: "local" # 本地优先
spec:
selector:
app: echo
ports:
- port: 80
---
apiVersion: v1
kind: Service
metadata:
name: echo-remote
annotations:
io.cilium/global-service: "true"
io.cilium/service-affinity: "remote" # 远端优先
spec:
selector:
app: echo
ports:
- port: 80
28.4.3 应用场景
28.5 故障切换(Failover)
28.5.1 工作原理
28.5.2 测试验证
# 1. 正常状态 - 两个集群都响应
for i in {1..10}; do curl http://service; done
# 输出: Cluster1, Cluster2, Cluster1, Cluster2...
# 2. 模拟 Cluster2 故障(缩容到 0)
kubectl --context=cluster2 scale deploy backend --replicas=0
# 3. 再次测试 - 只有 Cluster1 响应
for i in {1..10}; do curl http://service; done
# 输出: Cluster1, Cluster1, Cluster1...
28.6 环境搭建
28.6.1 前置条件
| 要求 | 说明 |
|---|---|
| Pod CIDR | 各集群不能重叠 |
| 网络互通 | 集群间需能相互访问 |
| CA 证书 | 需共享或继承 |
28.6.2 安装步骤
1. 安装 Cluster 1:
cilium install --context kind-cluster1 --cluster-name cluster1 --cluster-id 1
2. 安装 Cluster 2(继承 CA):
cilium install --context kind-cluster2 --cluster-name cluster2 --cluster-id 2 \
--inherit-ca kind-cluster1
[!NOTE]
--inherit-ca参数让 Cluster2 继承 Cluster1 的 CA 证书,这是最简单的方式。如果使用 Helm 安装则需要手动配置证书。
3. 启用 ClusterMesh:
# 在两个集群上启用
cilium clustermesh enable --context kind-cluster1 --service-type NodePort
cilium clustermesh enable --context kind-cluster2 --service-type NodePort
4. 连接集群:
cilium clustermesh connect --context kind-cluster1 --destination-context kind-cluster2
5. 验证状态:
cilium clustermesh status --context kind-cluster1
28.6.3 连接方式
| 方式 | 适用场景 |
|---|---|
| NodePort | 开发测试环境 |
| LoadBalancer | 生产环境(需要 MetalLB 等) |
28.7 网络通信模式
28.7.1 两种模式
| 模式 | 说明 | 适用 |
|---|---|---|
| VXLAN | Overlay 封装 | 网络设备无法配置 |
| 直接路由 | 需要底层路由支持 | 可控制网络设备 |
28.7.2 跨集群通信
从 ClusterMesh 角度看,两个集群相当于一个逻辑集群,Pod 间通信与单集群内跨节点通信类似。
# 查看 tunnel 信息
cilium bpf tunnel list
# 会显示所有节点(包括其他集群的节点)
28.8 验证与调试
28.8.1 查看 ClusterMesh 状态
# 查看连接状态
cilium clustermesh status
# 查看节点列表(包含所有集群)
cilium node list
28.8.2 查看 Service 后端
# 登录 Cilium Pod
kubectl exec -it cilium-xxx -- bash
# 查看 Service 后端列表
cilium service list
# 查看 Endpoint 详情(会显示 preferred 状态)
cilium bpf lb list
28.8.3 验证输出示例
# preferred = 本集群的 Pod(affinity=local 时优先)
Backend State
10.1.1.1:80 active, preferred
10.1.1.2:80 active, preferred
10.2.1.1:80 active # 其他集群的 Pod
10.2.1.2:80 active
28.9 限制与注意事项
| 限制 | 说明 |
|---|---|
| 集群数量 | 最多 255 个集群 |
| Pod CIDR | 不能重叠 |
| Cluster ID | 每个集群需唯一 |
| CNI 要求 | 必须使用 Cilium |
28.10 章节小结
[!IMPORTANT]
核心要点总结:
ClusterMesh 本质:
- 多个物理集群 → 一个逻辑集群
- Service 能发现所有集群的 Pod
Global Service:
- 注解:
io.cilium/global-service: "true"- 必须添加才有跨集群能力
Service Affinity:
local:本地优先,降低延迟remote:远端优先,测试用途- 无标注:随机负载
故障切换:
- 一个集群 Pod 消失
- 自动切换到其他集群
部署关键:
- Pod CIDR 不能重叠
- 使用
--inherit-ca共享证书clustermesh enable+connectCilium 独有:
- ClusterMesh 是 Cilium CNI 特有功能
- 其他 CNI 不具备此能力
Cilium CNI 部分总结
至此,Cilium CNI 的全部章节已完成。以下是 Cilium 各功能模块的概览:
第三部分:Calico-CNI
第二十九章 Calico 基础介绍
本章介绍 Calico CNI 的基础知识,包括其支持的多种网络模式、核心组件和架构设计。Calico 是目前使用最广泛的 Kubernetes CNI 之一,在生产环境中积累了大量经验。
29.1 Calico 概述
29.1.1 什么是 Calico
Calico 是一个开源的网络和安全解决方案,提供:
| 特性 | 说明 |
|---|---|
| 多种网络模式 | IPIP、VXLAN、BGP |
| 网络策略 | 支持 Kubernetes NetworkPolicy |
| 自有 IPAM | calico-ipam,支持 IP 池管理 |
| 成熟稳定 | 在 OpenStack 时代即已存在 |
29.1.2 官方资源
| 资源 | 地址 |
|---|---|
| 官网 | https://www.tigera.io/project-calico/ |
| 文档 | https://docs.tigera.io/ |
| 版本 | 社区版 3.25+,企业版另有 |
29.2 网络模式对比
29.2.1 支持的网络模式
29.2.2 模式对比
| 模式 | 封装方式 | 额外开销 | 适用场景 |
|---|---|---|---|
| IPIP | IP 包封装在 IP 中 | 20 字节 | 跨子网通信 |
| VXLAN | MAC 包封装在 UDP 中 | 50 字节 | 大二层网络 |
| BGP | 无封装,纯路由 | 0 | 同子网或可控网络 |
29.2.3 Cross Subnet 模式
Calico 支持智能选择封装方式:
[!NOTE]
Cross Subnet 模式:节点在同一子网时使用直接路由(节省开销),跨子网时使用 Overlay 封装(保证连通)。这是生产环境常见的配置方式。
29.3 核心组件
29.3.1 组件架构
| 组件 | 作用 |
|---|---|
| Felix | 核心代理,管理路由、ACL、接口 |
| BIRD | BGP 守护进程,路由交换 |
| Confd | 监听配置变化,生成 BIRD 配置 |
| Typha | 大规模集群数据缓存 |
29.3.2 BIRD 进程
| 特性 | BIRD(Calico) | GoBGP(Cilium) |
|---|---|---|
| 语言 | C | Go |
| 成熟度 | 非常成熟 | 较新 |
| 资源占用 | 较低 | 适中 |
29.4 BGP 网络架构
29.4.1 AS per Rack 模式
特点:
- 同一机架的节点共享同一 AS 号
- ToR 交换机作为 BGP 路由反射器(RR)
- Spine 与 ToR 之间建立 eBGP
29.4.2 AS per Node 模式
特点:
- 每个节点都是独立的 AS
- 节点间都是 eBGP 关系
- 配置更简单,适合小规模
29.4.3 Downward Default 模式
特点:
- 节点只向上通告默认路由
- 降低端侧设备压力
- 全网路由集中在 Spine 层
29.5 VPP 高性能方案
29.5.1 VPP 简介
| 概念 | 说明 |
|---|---|
| VPP | 用户态协议栈,等价于内核协议栈 |
| DPDK | 绕过内核直接访问网卡 |
| 用途 | 高带宽场景(10G/25G/40G+) |
29.5.2 VPP 与 Calico 集成
# VPP 支持的功能
- 路由 (Routing)
- 负载均衡 (Load Balancing)
- 策略 (Policy)
- VXLAN / IPIP / IPsec / MPLS
[!WARNING]
VPP 方案门槛较高,需要深入理解用户态协议栈和 DPDK。目前主要用于电信/媒体处理等高带宽行业。
29.6 calicoctl 工具
29.6.1 基本用法
# 安装
curl -O -L https://github.com/projectcalico/calico/releases/download/v3.25.0/calicoctl-linux-amd64
mv calicoctl-linux-amd64 /usr/local/bin/calicoctl
chmod +x /usr/local/bin/calicoctl
# 常用命令
calicoctl get nodes
calicoctl get ippool
calicoctl get bgpconfig
calicoctl get bgppeer
29.6.2 功能对比
| 工具 | 说明 |
|---|---|
| calicoctl | Calico 资源管理 |
| cilium | Cilium 资源管理 |
| kubectl | 通用 K8s 资源 |
29.7 IP 池管理
29.7.1 IP Pool 配置
apiVersion: crd.projectcalico.org/v1
kind: IPPool
metadata:
name: default-pool
spec:
cidr: 10.244.0.0/16
ipipMode: CrossSubnet
vxlanMode: Never
nodeSelector: all()
| 字段 | 说明 |
|---|---|
cidr |
Pod 使用的 IP 段 |
ipipMode |
IPIP 模式:Always/CrossSubnet/Never |
vxlanMode |
VXLAN 模式:Always/CrossSubnet/Never |
nodeSelector |
哪些节点使用此池 |
29.7.2 指定 Pod IP
apiVersion: v1
kind: Pod
metadata:
name: test-pod
annotations:
# 指定 Pod 使用特定 IP
cni.projectcalico.org/ipAddrs: '["172.16.0.2"]'
spec:
containers:
- name: nginx
image: nginx
29.8 IPIP vs VXLAN 的区别
29.8.1 与 Flannel 迁移
| 模式 | BIRD 进程 | 说明 |
|---|---|---|
| IPIP | 需要 | 使用 BGP 分发路由 |
| VXLAN | 不需要 | 兼容 Flannel 迁移 |
[!NOTE]
VXLAN 模式不使用 BIRD 进程,是为了支持从 Flannel 迁移。因为 Flannel 的 VXLAN 模式也不使用 BGP。
29.9 与 Cilium 对比
| 特性 | Calico | Cilium |
|---|---|---|
| BGP 实现 | BIRD | GoBGP |
| eBPF 支持 | 有(可选) | 原生 |
| 高性能方案 | VPP/DPDK | XDP |
| 多集群 | Federation | ClusterMesh |
| 成熟度 | 非常成熟 | 较新但活跃 |
29.10 章节小结
[!IMPORTANT]
核心要点总结:
网络模式:
- IPIP:IP 封装,20 字节开销
- VXLAN:MAC 封装,50 字节开销
- BGP:纯路由,无开销
Cross Subnet:
- 同子网走路由
- 跨子网走 Overlay
- 智能选择,兼顾性能
BGP 架构:
- AS per Rack:机架共享 AS
- AS per Node:节点独立 AS
- Downward Default:减轻端侧压力
BIRD vs 无 BIRD:
- IPIP 模式需要 BIRD
- VXLAN 模式不需要 BIRD
高性能方案:
- VPP:用户态协议栈
- DPDK:绕过内核
- 适用于高带宽场景
工具:
- calicoctl 管理 Calico 资源
- 类似 Cilium CLI
第三十章 Calico 环境准备
本章介绍 Calico CNI 的环境准备工作,包括集群创建、Calico 安装、管理工具配置和环境验证。这是后续学习 Calico 各种网络模式的基础。
30.1 背景与目标
30.1.1 学习环境需求
| 组件 | 版本 | 说明 |
|---|---|---|
| Kubernetes | 1.25+ | 使用 kind 创建 |
| Calico | 3.23.x | 默认 IPIP 模式 |
| calicoctl | 与 Calico 版本匹配 | 管理 Calico 资源 |
30.1.2 环境目标
| 目标 | 说明 |
|---|---|
| Pod 互通 | Pod 之间可以相互 ping 通 |
| Service 可达 | Service 可正常访问 |
| 跨节点通信 | 不同节点的 Pod 可互通 |
30.2 集群创建
30.2.1 使用 kind 创建集群
# calico-cluster.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
networking:
disableDefaultCNI: true # 禁用默认 CNI
podSubnet: "10.244.0.0/16" # Pod CIDR
nodes:
- role: control-plane
- role: worker
- role: worker
# 创建集群
kind create cluster --name calico-demo --config calico-cluster.yaml
# 去除 master 节点污点(可选)
kubectl taint nodes --all node-role.kubernetes.io/control-plane-
30.2.2 创建流程
30.3 安装 Calico
30.3.1 安装方式对比
| 方式 | 适用场景 | 特点 |
|---|---|---|
| Quick Start | 快速体验 | 使用 YAML Manifest |
| Helm | 生产环境 | 更灵活的配置 |
| Operator | 企业环境 | 声明式管理 |
30.3.2 Quick Start 安装
# 安装 Calico Operator 和 CRD
kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.23.2/manifests/tigera-operator.yaml
# 安装 Calico 自定义资源
kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.23.2/manifests/custom-resources.yaml
# 等待 Pod 就绪
kubectl wait --for=condition=Ready pods --all -n calico-system --timeout=300s
kubectl wait --for=condition=Ready pods --all -n calico-apiserver --timeout=300s
30.3.3 查看安装结果
# 查看 Calico Pod
kubectl get pods -n calico-system
kubectl get pods -n calico-apiserver
# 输出示例
NAME READY STATUS RESTARTS AGE
calico-kube-controllers-xxx 1/1 Running 0 5m
calico-node-xxx 1/1 Running 0 5m
calico-typha-xxx 1/1 Running 0 5m
30.4 calicoctl 工具
30.4.1 安装 calicoctl
# 下载 calicoctl(需与 Calico 版本匹配)
curl -L https://github.com/projectcalico/calico/releases/download/v3.23.2/calicoctl-linux-amd64 -o calicoctl
# 添加执行权限
chmod +x calicoctl
mv calicoctl /usr/local/bin/
# 验证版本
calicoctl version
30.4.2 版本匹配重要性
[!WARNING]
版本匹配非常重要! calicoctl 版本必须与集群中 Calico 版本一致,否则会出现mismatched versions警告。虽然可以使用--allow-version-mismatch强制运行,但不推荐在生产环境中这样做。
30.4.3 常用命令
# 查看节点
calicoctl get nodes
# 查看 IP 池
calicoctl get ippool -o yaml
# 查看 BGP 配置
calicoctl get bgpconfig
# 查看 BGP Peer
calicoctl get bgppeer
30.5 IPPool 配置详解
30.5.1 默认 IPPool
# 查看 IPPool
calicoctl get ippool -o yaml
apiVersion: projectcalico.org/v3
kind: IPPool
metadata:
name: default-ipv4-ippool
spec:
cidr: 10.244.0.0/16 # Pod CIDR
ipipMode: Always # IPIP 模式
vxlanMode: Never # 不使用 VXLAN
natOutgoing: true # 出站 NAT
nodeSelector: all() # 所有节点
disabled: false # 启用状态
30.5.2 关键字段说明
| 字段 | 可选值 | 说明 |
|---|---|---|
ipipMode |
Always/CrossSubnet/Never | IPIP 封装策略 |
vxlanMode |
Always/CrossSubnet/Never | VXLAN 封装策略 |
natOutgoing |
true/false | Pod 出站是否 NAT |
nodeSelector |
all()/标签表达式 | 哪些节点使用此池 |
disabled |
true/false | 是否禁用此池 |
30.5.3 封装模式对比
[!NOTE]
IPIP 和 VXLAN 互斥:不能同时设置ipipMode: Always和vxlanMode: Always。通常只启用其中一种,另一种设为Never。
30.6 环境验证
30.6.1 验证流程
30.6.2 部署测试资源
# test-pods.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod1
labels:
app: test
spec:
containers:
- name: busybox
image: busybox
command: ["sleep", "3600"]
---
apiVersion: v1
kind: Pod
metadata:
name: pod2
labels:
app: test
spec:
containers:
- name: busybox
image: busybox
command: ["sleep", "3600"]
---
apiVersion: v1
kind: Service
metadata:
name: test-svc
spec:
selector:
app: test
ports:
- port: 80
targetPort: 80
# 创建测试资源
kubectl apply -f test-pods.yaml
# 等待 Pod 就绪
kubectl wait --for=condition=Ready pods --all --timeout=120s
30.6.3 执行验证
# 获取 Pod IP
POD1_IP=$(kubectl get pod pod1 -o jsonpath='{.status.podIP}')
POD2_IP=$(kubectl get pod pod2 -o jsonpath='{.status.podIP}')
# Pod 间 ping 测试
kubectl exec pod1 -- ping -c 3 $POD2_IP
# Service 访问测试
SVC_IP=$(kubectl get svc test-svc -o jsonpath='{.spec.clusterIP}')
kubectl exec pod1 -- wget -qO- http://$SVC_IP --timeout=5
# 验证成功标志
echo "Pod 互通: OK"
echo "Service 可达: OK"
30.7 ContainerLab 拓扑规划
30.7.1 IPIP 学习拓扑
为后续学习 IPIP 模式,建议搭建以下拓扑:
30.7.2 拓扑说明
| 节点 | 子网 | 说明 |
|---|---|---|
| Node 1 | 192.168.1.0/24 | 与 Node 2 同子网 |
| Node 2 | 192.168.1.0/24 | 与 Node 1 同子网 |
| Node 3 | 192.168.2.0/24 | 跨子网,需 IPIP |
通信路径:
- Node 1 ↔ Node 2:同子网,可直接路由
- Node 1 ↔ Node 3:跨子网,经过网关,需要 IPIP 封装
30.8 IPAM 问题排查
30.8.1 常见问题
| 问题 | 原因 | 解决方案 |
|---|---|---|
| IP 重复 | IPAM 数据库不一致 | 使用 calicoctl ipam check |
| 无法分配 IP | IPPool 禁用或耗尽 | 检查 IPPool 状态 |
| Pod 无 IP | CNI 配置错误 | 检查 /etc/cni/net.d/ |
30.8.2 IPAM 命令
# 检查 IPAM 状态
calicoctl ipam check
# 释放未使用的 IP
calicoctl ipam release --ip=10.244.x.x
# 显示 IPAM 使用情况
calicoctl ipam show
# 显示某个 IP 的分配信息
calicoctl ipam show --ip=10.244.x.x
[!TIP]
IBM 文档中有详细的 Calico IPAM 故障排查指南,搜索 "IBM Calico IPAM" 可以找到更多实用信息。
30.9 章节小结
[!IMPORTANT]
核心要点总结:
集群创建:
- 使用 kind 创建集群
- 禁用默认 CNI:
disableDefaultCNI: true- 指定 Pod CIDR
Calico 安装:
- Quick Start 适合快速体验
- 安装 Operator + CustomResources
- 等待所有 Pod 就绪
calicoctl 工具:
- 版本必须与 Calico 匹配
- 避免使用
--allow-version-mismatch- 可用于查看/管理 Calico 资源
IPPool 配置:
ipipMode:Always/CrossSubnet/NevervxlanMode:与 ipipMode 互斥nodeSelector:all() 表示全部节点环境验证:
- Pod 间 ping 测试
- Service 访问测试
- 两项都通过 = CNI 工作正常
后续学习:
- 搭建 ContainerLab 拓扑
- 准备同子网/跨子网场景
- 实践 IPIP/CrossSubnet 模式
第三十一章 Calico 同节点通信
本章深入分析 Calico 同节点 Pod 之间的通信原理,这是所有 Calico 网络模式(IPIP/VXLAN/BGP)的共同基础。同节点通信采用 L3 路由转发方式,核心技术是 Proxy ARP。
31.1 背景与概述
31.1.1 同节点通信的重要性
| 特点 | 说明 |
|---|---|
| 模式通用 | IPIP、VXLAN、BGP 同节点通信方式相同 |
| L3 转发 | 采用三层路由方式,非二层交换 |
| Proxy ARP | 核心技术,打通 Pod 与 Root Namespace |
31.1.2 与其他 CNI 对比
| CNI | 转发层级 | 设备 | 特点 |
|---|---|---|---|
| Calico | L3 路由 | Host 作为路由器 | 无广播,效率高 |
| Flannel | L2 交换 | Linux Bridge | 有广播,传统方式 |
31.2 网络拓扑结构
31.2.1 veth pair 连接
关键点:
- 每个 Pod 的 eth0 通过 veth pair 连接到 Host
- Host 端的网卡名为
cali-xxx格式 - Pod IP 使用
/32掩码(与任何地址都不同网段)
31.2.2 网卡特征
# 在 Pod 内查看网卡
ip link show eth0
# eth0@if5: ...
# 在 Host 上查看对应的 cali 网卡
ip link show | grep "^5:"
# 5: cali-xxx@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> ...
| 网卡 | 位置 | MAC 地址 | 说明 |
|---|---|---|---|
eth0 |
Pod 内 | 随机 MAC | Pod 的主网卡 |
cali-xxx |
Host | ee:ee:ee:ee:ee:ee |
全 1 固定 MAC |
31.3 路由转发原理
31.3.1 Pod 内路由表
# 在 Pod 内查看路由
ip route
# 输出示例
default via 169.254.1.1 dev eth0
169.254.1.1 dev eth0 scope link
路由表解读:
- 默认路由:所有流量发往
169.254.1.1(网关) - 出接口:通过
eth0发出 - scope link:本地链路范围
31.3.2 为什么使用 /32 掩码
| 掩码 | 含义 | 结果 |
|---|---|---|
/32 |
只有自己在网段内 | 任何目的 IP 都走 L3 |
/24 |
256 个地址同网段 | 同网段走 L2 交换 |
[!NOTE]
使用/32掩码强制所有流量走三层路由,这是 Calico L3 网络模型的核心设计。
31.3.3 Host 路由表
# 在 Host 上查看路由
ip route | grep 10.244
# 输出示例
10.244.66.65 dev cali-xxx1 scope link
10.244.66.66 dev cali-xxx2 scope link
解读:到达每个 Pod IP 的路由,出接口就是连接该 Pod 的 cali 网卡。
31.4 数据包转发流程
31.4.1 同节点通信流程
31.4.2 数据包封装详解
第一跳(Pod → Host):
| 字段 | 值 | 说明 |
|---|---|---|
| Src IP | 10.244.66.66 | 源 Pod IP |
| Dst IP | 10.244.66.65 | 目的 Pod IP |
| Src MAC | Pod 1 的 MAC | eth0 的 MAC |
| Dst MAC | ee:ee:ee:ee:ee:ee |
网关(Proxy ARP 返回) |
第二跳(Host → Pod 2):
| 字段 | 值 | 说明 |
|---|---|---|
| Src IP | 10.244.66.66 | 保持不变 |
| Dst IP | 10.244.66.65 | 保持不变 |
| Src MAC | ee:ee:ee:ee:ee:ee |
cali-xxx2 的 MAC |
| Dst MAC | Pod 2 的 MAC | 目的 Pod 的 MAC |
31.5 Proxy ARP 机制
31.5.1 什么是 Proxy ARP
Proxy ARP 原理:
- Pod 发送 ARP 请求查询
169.254.1.1的 MAC - cali 网卡开启了
proxy_arp功能 - cali 网卡用自己的 MAC 地址(全 1)回复
- Pod 得到 MAC 后即可封装数据包
31.5.2 169.254.1.1 的意义
# 这个地址在 Host 上找不到
ip addr | grep 169.254
# 无输出
# 但 ARP 可以获得响应
arping -I eth0 169.254.1.1
# ARPING 169.254.1.1
# 60 bytes from ee:ee:ee:ee:ee:ee: index=0 ...
| 问题 | 答案 |
|---|---|
| 为什么找不到 169.254.1.1? | 这个 IP 从未分配给任何接口 |
| 为什么 ARP 能成功? | Proxy ARP 代为响应 |
| 为什么用链路本地地址? | 避免与其他 IP 冲突 |
[!IMPORTANT]
169.254.0.0/16 是链路本地地址(Link-Local),RFC 3927 规定这个网段用于无 DHCP 时的自动配置。Calico 使用它作为虚拟网关,因为:
- 不会与真实 IP 冲突
- 不需要实际分配给接口
- 通过 Proxy ARP 即可工作
31.5.3 查看 Proxy ARP 配置
# 查看 cali 网卡的 proxy_arp 设置
cat /proc/sys/net/ipv4/conf/cali*/proxy_arp
# 1 (启用状态)
# 或使用 sysctl
sysctl net.ipv4.conf.cali*.proxy_arp
31.6 全 1 MAC 地址的原因
31.6.1 为什么使用 ee:ee:ee:ee:ee:ee
| 原因 | 说明 |
|---|---|
| 内核限制 | 内核不能为 veth 生成持久稳定的 MAC |
| 不冲突 | 全 1 不会与任何厂商 OUI 冲突 |
| 点对点 | veth pair 是点对点,MAC 只需本地有效 |
31.6.2 MAC 地址作用域
[!TIP]
MAC 地址只需在冲突域/广播域内唯一。每个 Pod 与其 cali 网卡组成独立的点对点链路,所以全部使用相同的 MAC 地址不会冲突。
31.6.3 Calico 官方 FAQ
Calico 官网 FAQ 解答了这些常见疑问:
-
Why do my containers have a route to 169.254.1.1?
- 这是虚拟网关,通过 Proxy ARP 工作
-
Why can't I see 169.254.1.1 on my host?
- 不需要实际配置,Proxy ARP 代为响应
-
Why do all cali interfaces have the same MAC?*
- 点对点链路,无需唯一 MAC
31.7 与 Flannel 对比
31.7.1 架构对比
31.7.2 特性对比
| 特性 | Calico | Flannel |
|---|---|---|
| 转发层级 | L3(路由) | L2(交换) |
| 广播域 | 隔离(每 Pod 独立) | 共享(同节点共享) |
| ARP 广播 | 无(Proxy ARP) | 有 |
| 效率 | 高(无广播开销) | 较低 |
| 隔离性 | 强 | 弱 |
31.8 抓包验证
31.8.1 在 Pod 内抓包
# 进入 Pod
kubectl exec -it pod1 -- sh
# 安装 tcpdump(如果没有)
apk add tcpdump
# 抓取 ICMP 包
tcpdump -i eth0 icmp -n
31.8.2 在 Host 抓包
# 找到对应的 cali 网卡
ip link | grep cali
# 抓包
tcpdump -i cali-xxx -n icmp
31.8.3 验证 MAC 地址
# Ping 测试后查看 ARP 缓存
ip neigh
# 示例输出
169.254.1.1 dev eth0 lladdr ee:ee:ee:ee:ee:ee REACHABLE
31.9 章节小结
[!IMPORTANT]
核心要点总结:
网络拓扑:
- Pod eth0 ↔ Host cali 网卡(veth pair)
- Pod IP 使用 /32 掩码
- 强制所有流量走 L3
路由转发:
- Pod 默认路由指向 169.254.1.1
- Host 路由表有到每个 Pod 的明细路由
- 出接口就是对应的 cali 网卡
Proxy ARP:
- cali 网卡开启 proxy_arp
- 代替 169.254.1.1 响应 ARP
- 返回自己的 MAC(全 1)
全 1 MAC:
- 所有 cali 网卡使用相同 MAC
- 点对点链路,本地有效
- 不与厂商 OUI 冲突
与 Flannel 对比:
- Calico: L3 路由,无广播
- Flannel: L2 交换,有广播
排查技巧:
- 不懂就查路由表
ip route- 确认 ARP 缓存
ip neigh- 抓包验证
tcpdump
第三十二章 Calico-Proxy-ARP 实践
本章通过手工实现 Proxy ARP 来深入理解 Calico 同节点通信的核心机制。通过实践,你将掌握 veth pair、路由配置、Proxy ARP 开关等关键技术点。
32.1 背景与目标
32.1.1 实验目标
| 目标 | 说明 |
|---|---|
| 理解 Proxy ARP | 手工实现虚拟网关 169.254.1.1 |
| 掌握路由配置 | 先配网关路由,再配默认路由 |
| 解决回程问题 | 添加回程路由确保双向通信 |
| 外网访问 | 通过 SNAT 实现出公网 |
32.1.2 实验拓扑
32.2 查看 Calico Proxy ARP 配置
32.2.1 Proxy ARP 开关位置
# 查看 cali 网卡的 proxy_arp 状态
cat /proc/sys/net/ipv4/conf/cali*/proxy_arp
# 输出: 1 (已启用)
# 查看 eth0 的 proxy_arp 状态
cat /proc/sys/net/ipv4/conf/eth0/proxy_arp
# 输出: 0 (未启用)
[!NOTE]
Proxy ARP 是针对单个网卡的配置:
cali*网卡开启了 proxy_arp(值为 1)eth0等其他网卡默认关闭
32.2.2 开关含义
| 值 | 含义 |
|---|---|
0 |
禁用 Proxy ARP |
1 |
启用 Proxy ARP |
32.3 手工实现 Proxy ARP
32.3.1 第一步:创建网络命名空间
# 创建命名空间 ns1
ip netns add ns1
# 验证
ip netns list
32.3.2 第二步:创建 veth pair
# 创建 veth pair
# 一端: veth (留在 root namespace)
# 另一端: c-eth0 (放入 ns1)
ip link add veth type veth peer name c-eth0
# 启用两端网卡
ip link set c-eth0 up
ip link set veth up
# 将 c-eth0 移入 ns1
ip link set c-eth0 netns ns1
# 在 ns1 中启用并配置 IP
ip netns exec ns1 ip link set c-eth0 up
ip netns exec ns1 ip addr add 1.1.1.2/24 dev c-eth0
32.3.3 第三步:配置路由(关键!)
[!IMPORTANT]
路由配置顺序非常重要:必须先配置到网关的路由,再配置默认路由!
# 进入 ns1
ip netns exec ns1 bash
# 第一步:配置到网关 169.254.1.1 的路由
ip route add 169.254.1.1 dev c-eth0 scope link
# 第二步:配置默认路由,指向网关
ip route add default via 169.254.1.1 dev c-eth0
# 查看路由表
ip route
# default via 169.254.1.1 dev c-eth0
# 169.254.1.1 dev c-eth0 scope link
为什么必须先配置网关路由?
| 错误示例 | 原因 |
|---|---|
ip route add default via 169.254.1.1 |
系统不知道 169.254.1.1 怎么走 |
32.3.4 第四步:启用 Proxy ARP
# 在 root namespace 中对 veth 启用 proxy_arp
echo 1 > /proc/sys/net/ipv4/conf/veth/proxy_arp
# 验证
cat /proc/sys/net/ipv4/conf/veth/proxy_arp
# 输出: 1
32.3.5 第五步:验证 ARP 响应
# 在 ns1 中测试 ARP
ip netns exec ns1 arping -I c-eth0 169.254.1.1
# 输出示例
# ARPING 169.254.1.1 from 1.1.1.2 c-eth0
# Unicast reply from 169.254.1.1 [9B:12:0C:xx:xx:xx] ...
至此,第一跳(Pod → Host)已经打通!
32.4 解决回程路由问题
32.4.1 问题现象
# 在 ns1 中 ping Host 地址
ip netns exec ns1 ping -I 1.1.1.2 192.168.2.66
# 结果:不通!
32.4.2 抓包分析
# 在 veth 上抓包
tcpdump -i veth icmp -n
# 输出
# ICMP echo request 1.1.1.2 -> 192.168.2.66
# (无 reply)
# 在 eth0 上抓包
tcpdump -i eth0 icmp -n
# 输出
# ICMP echo request 1.1.1.2 -> 192.168.2.66
# ICMP echo reply 192.168.2.66 -> 1.1.1.2 (发往默认网关!)
32.4.3 问题原因
原因:Host 收到包后,要回复给 1.1.1.2,但 Host 路由表没有到 1.1.1.2 的路由,匹配默认路由发给了外部网关。
32.4.4 解决方案:添加回程路由
# 在 root namespace 添加到 1.1.1.2 的路由
ip route add 1.1.1.2 dev veth scope link
# 或添加整个网段
ip route add 1.1.1.0/24 dev veth scope link
# 验证路由表
ip route | grep 1.1.1
# 1.1.1.2 dev veth scope link
# 再次测试
ip netns exec ns1 ping -I 1.1.1.2 192.168.2.66
# 结果:通了!
32.5 实现外网访问
32.5.1 问题:无法访问外网
ip netns exec ns1 ping 114.114.114.114
# 不通
原因:缺少 SNAT,外部网络不知道如何回复私有 IP 1.1.1.2
32.5.2 解决方案:配置 SNAT
# 启用 IP 转发
echo 1 > /proc/sys/net/ipv4/ip_forward
# 添加 SNAT 规则
iptables -t nat -A POSTROUTING -s 1.1.1.0/24 -j MASQUERADE
# 或指定出接口
iptables -t nat -A POSTROUTING -s 1.1.1.0/24 -o eth0 -j MASQUERADE
# 测试外网访问
ip netns exec ns1 ping 114.114.114.114
# 通了!
32.6 完整实现脚本
#!/bin/bash
# proxy-arp-demo.sh - 手工实现 Proxy ARP
# 1. 创建命名空间
ip netns add ns1
# 2. 创建 veth pair
ip link add veth type veth peer name c-eth0
ip link set veth up
ip link set c-eth0 netns ns1
ip netns exec ns1 ip link set c-eth0 up
ip netns exec ns1 ip addr add 1.1.1.2/24 dev c-eth0
# 3. 配置路由(顺序重要!)
ip netns exec ns1 ip route add 169.254.1.1 dev c-eth0 scope link
ip netns exec ns1 ip route add default via 169.254.1.1 dev c-eth0
# 4. 启用 Proxy ARP
echo 1 > /proc/sys/net/ipv4/conf/veth/proxy_arp
# 5. 添加回程路由
ip route add 1.1.1.2 dev veth scope link
# 6. 启用 IP 转发和 SNAT(如需外网访问)
echo 1 > /proc/sys/net/ipv4/ip_forward
iptables -t nat -A POSTROUTING -s 1.1.1.0/24 -j MASQUERADE
echo "Proxy ARP 配置完成!"
echo "测试:ip netns exec ns1 ping 114.114.114.114"
32.7 与 Calico 实现对比
| 步骤 | 手工实现 | Calico 实现 |
|---|---|---|
| 创建 veth | ip link add |
CNI 插件自动创建 |
| 配置路由 | ip route add |
Felix 自动注入 |
| Proxy ARP | 写 /proc/sys |
Felix 自动配置 |
| 回程路由 | 手动添加 | Felix 监听并维护 |
| SNAT | iptables 规则 | Felix 管理 iptables |
32.8 扩展:东西向流量
32.8.1 实验思路
要实现两个节点上的命名空间互通(东西向流量):
需要添加的配置:
- Node 1:添加到
1.1.2.0/24的路由,下一跳为 Node 2 - Node 2:添加到
1.1.1.0/24的路由,下一跳为 Node 1 - 两边都配置 Proxy ARP
[!TIP]
这就是 Calico Host-GW 模式的原理!通过路由表实现跨节点通信。
32.9 关键技术总结
| 技术 | 作用 | 命令 |
|---|---|---|
| veth pair | 连接命名空间 | ip link add type veth peer |
| 网关路由 | 定义如何到达网关 | ip route add 169.254.1.1 dev xxx scope link |
| 默认路由 | 定义默认出口 | ip route add default via 169.254.1.1 |
| Proxy ARP | 代理 ARP 响应 | echo 1 > /proc/sys/.../proxy_arp |
| 回程路由 | 解决回包问题 | ip route add x.x.x.x dev xxx |
| SNAT | 外网访问 | iptables -t nat -A POSTROUTING -j MASQUERADE |
32.10 章节小结
[!IMPORTANT]
核心要点总结:
Proxy ARP 开关:
- 位置:
/proc/sys/net/ipv4/conf/<iface>/proxy_arp- 值 1 启用,值 0 禁用
- 针对单个网卡配置
路由配置顺序:
- 必须先配置网关路由:
ip route add 169.254.1.1 dev xxx scope link- 再配置默认路由:
ip route add default via 169.254.1.1- 顺序错误会报错!
回程路由:
- Host 需要知道如何回到 Pod
- 否则包会走默认路由发到外部网关
- 解决:添加到 Pod IP 的明细路由
外网访问四要素:
- veth pair 连接
- Proxy ARP 响应
- 路由表配置
- SNAT 地址转换
与 Calico 对比:
- 手工实现帮助理解原理
- Calico 通过 Felix 自动化这些配置
- 核心技术完全相同
第三十三章 Calico-IPIP 跨节点通信
本章详细讲解 Calico 在 IPIP 模式下如何实现跨节点 Pod 通信。IPIP 是一种 IP-in-IP 的 Overlay 封装技术,通过在原始 IP 包外再封装一层 IP 头来实现隧道传输。
33.1 背景与概述
33.1.1 为什么需要 IPIP
| 场景 | 问题 | 解决方案 |
|---|---|---|
| 同节点 | L3 路由 + Proxy ARP | 直接转发 |
| 跨节点 | 节点间网络不认识 Pod IP | IPIP 隧道封装 |
33.1.2 IPIP 模式特点
| 特性 | 说明 |
|---|---|
| 封装方式 | IP-in-IP(协议号 4) |
| 额外开销 | 20 字节(外层 IP 头) |
| 二层信息 | 无(只有 IP 头) |
| 隧道设备 | tunl0 |
33.2 跨节点通信流程
33.2.1 整体数据流
33.2.2 详细步骤
| 步骤 | 位置 | 动作 |
|---|---|---|
| 1 | Pod A | 发送 ICMP 包,DST 为 Pod B IP |
| 2 | Node 1 路由表 | 匹配到 Pod B 网段,下一跳 172.18.0.2,出接口 tunl0 |
| 3 | tunl0 设备 | IPIP 封装,外层 IP: 172.18.0.4 → 172.18.0.2 |
| 4 | eth0 | 二层封装 MAC,发送到物理网络 |
| 5 | Node 2 eth0 | 接收 IPIP 包 |
| 6 | Node 2 tunl0 | 解封装,露出原始包 |
| 7 | Node 2 路由表 | 匹配 Pod B 的 /32 路由,转发到 cali 网卡 |
| 8 | Pod B | 收到原始包 |
33.3 路由表分析
33.3.1 Node 1 路由表
# 查看 Node 1 路由表
ip route
# 输出示例
10.24.79.0/26 via 172.18.0.2 dev tunl0 proto bird onlink
10.24.197.0/26 dev cali-xxx scope link
| 路由条目 | 含义 |
|---|---|
10.24.79.0/26 via 172.18.0.2 dev tunl0 |
到 Node 2 Pod 网段,下一跳 172.18.0.2,出接口 tunl0 |
proto bird |
由 BIRD 协议注入 |
onlink |
下一跳在链路上(不需 ARP) |
33.3.2 路由匹配流程
33.4 tunl0 设备详解
33.4.1 查看 tunl0 设备
ip link show tunl0
# 输出示例
tunl0@NONE: <NOARP,UP,LOWER_UP> mtu 1480 qdisc noqueue state UNKNOWN
link/ipip 0.0.0.0 brd 0.0.0.0
| 属性 | 说明 |
|---|---|
NOARP |
不处理 ARP(三层设备) |
link/ipip |
IPIP 类型隧道 |
mtu 1480 |
MTU = 1500 - 20(IPIP 头) |
33.4.2 tunl0 配置
ip -d link show tunl0
# 输出示例
tunl0@NONE: ...
ipip remote any local any ttl inherit nopmtudisc
| 参数 | 说明 |
|---|---|
remote any |
远端 IP 不固定(动态) |
local any |
本端 IP 不固定(使用默认路由出接口 IP) |
ttl inherit |
TTL 继承自原始包 |
[!NOTE]
当local和remote为any时,实际使用的 IP 由路由表决定:
- local: 默认路由对应的接口 IP(如 eth0 的 IP)
- remote: 路由条目中指定的下一跳 IP
33.5 IPIP 封装原理
33.5.1 IP-in-IP 结构
| 字段 | 外层 IP | 内层 IP |
|---|---|---|
| Source IP | 172.18.0.4(Node 1) | 10.24.197.x(Pod A) |
| Dest IP | 172.18.0.2(Node 2) | 10.24.79.x(Pod B) |
| Protocol | 4(IPIP) | 1(ICMP)等 |
33.5.2 协议号
| 协议号 | 协议名 |
|---|---|
| 1 | ICMP |
| 4 | IPIP |
| 6 | TCP |
| 17 | UDP |
33.6 抓包验证
33.6.1 在 tunl0 上抓包
# 在 tunl0 上抓包
tcpdump -i tunl0 -nn -e
# 输出示例
IP 10.24.197.x > 10.24.79.x: ICMP echo request
[!NOTE]
在 tunl0 上抓包显示的是 Raw IP(裸 IP),没有 MAC 地址。
这是因为 tunl0 是三层设备,不处理二层信息。
33.6.2 在 eth0 上抓包
# 过滤 IPIP 协议
tcpdump -i eth0 -nn 'ip proto 4'
# 输出示例
IP 172.18.0.4 > 172.18.0.2: IP 10.24.197.x > 10.24.79.x: ICMP echo request
抓包结果说明:
| 层次 | 源地址 | 目的地址 |
|---|---|---|
| 外层 IP | 172.18.0.4(Node 1) | 172.18.0.2(Node 2) |
| 内层 IP | 10.24.197.x(Pod A) | 10.24.79.x(Pod B) |
33.6.3 Wireshark 分析
# 导出抓包文件
tcpdump -i eth0 -nn 'ip proto 4' -w ipip.pcap
在 Wireshark 中可以看到:
Ethernet II
├── IPv4 (外层): 172.18.0.4 -> 172.18.0.2, Protocol: IPIP (4)
│ └── IPv4 (内层): 10.24.197.x -> 10.24.79.x, Protocol: ICMP
│ └── ICMP: Echo request
33.7 IPIP vs VXLAN
| 对比项 | IPIP | VXLAN |
|---|---|---|
| 额外开销 | 20 字节 | 50 字节 |
| 二层信息 | 无 | 有(内层 MAC) |
| VNI 隔离 | 无 | 有 |
| 协议类型 | IP 协议 4 | UDP 端口 4789 |
| 适用场景 | 简单隧道 | 多租户隔离 |
33.8 手工创建 IPIP 设备
33.8.1 创建 IPIP 隧道
# 在 Node 1 上创建
ip link add ipip0 type ipip local 172.18.0.4 remote 172.18.0.2
ip link set ipip0 up
ip addr add 1.1.1.1/24 dev ipip0
# 在 Node 2 上创建
ip link add ipip0 type ipip local 172.18.0.2 remote 172.18.0.4
ip link set ipip0 up
ip addr add 1.1.1.2/24 dev ipip0
33.8.2 验证
# 查看设备
ip -d link show ipip0
# 输出示例
ipip0@NONE: <NOARP,UP,LOWER_UP> mtu 1480
link/ipip 172.18.0.4 peer 172.18.0.2
# 测试连通性
ping 1.1.1.2
33.8.3 与 Calico tunl0 对比
| 对比项 | 手工创建 | Calico tunl0 |
|---|---|---|
| local/remote | 指定固定 IP | any(动态) |
| 路由 | 手动添加 | BIRD 自动注入 |
| 管理 | 手动 | Felix 自动 |
33.9 kind 环境 MAC 地址规律
[!TIP]
在 kind 创建的集群中,容器的 MAC 地址有规律:
- 格式:
02:42:ac:xx:xx:xx- 最后一位与 IP 地址最后一位相同
- 例如:IP
172.18.0.4对应 MAC02:42:ac:xx:xx:04
33.10 章节小结
[!IMPORTANT]
核心要点总结:
IPIP 封装原理:
- IP-in-IP,在原始 IP 包外再封装一层 IP 头
- 外层 IP:Node IP(local → remote)
- 内层 IP:Pod IP(src → dst)
- 协议号 4 表示 IPIP
tunl0 设备:
- Calico 自动创建的 IPIP 隧道设备
NOARP:三层设备,不处理 ARPlocal any remote any:动态选择 IP- MTU 1480(1500 - 20)
路由决策:
- 目标 Pod 网段匹配对应路由
- 下一跳为目标节点 IP
- 出接口为 tunl0 触发 IPIP 封装
抓包技巧:
tcpdump -i tunl0:显示 Raw IPtcpdump -i eth0 'ip proto 4':过滤 IPIP 包- Wireshark 可看到两层 IP
与 VXLAN 对比:
- IPIP 开销 20 字节 < VXLAN 50 字节
- IPIP 无二层信息、无 VNI
- IPIP 更简单,VXLAN 支持多租户
第三十四章 Calico-IPIP CrossSubnet 模式
本章详细讲解 Calico IPIP 的 CrossSubnet 模式,这是一种智能混合模式:同网段节点间走纯路由,跨网段节点间走 IPIP 封装,从而在性能和兼容性之间取得平衡。
34.1 背景与概述
34.1.1 什么是 CrossSubnet
| 通信场景 | 模式 | 性能 |
|---|---|---|
| 同网段节点 | 纯路由转发 | 高(无封装开销) |
| 跨网段节点 | IPIP 封装 | 中(20 字节开销) |
34.1.2 三种 ipipMode 对比
| ipipMode | 同网段 | 跨网段 | 适用场景 |
|---|---|---|---|
| Never | 路由 | 路由 | 底层网络支持 Pod IP 路由 |
| Always | IPIP | IPIP | 所有场景都封装 |
| CrossSubnet | 路由 | IPIP | 混合场景,性能优化 |
34.2 CrossSubnet 工作原理
34.2.1 决策流程
34.2.2 路由表差异
同网段路由条目:
# Node 1 (10.1.5.10) 到 Node 2 (10.1.5.11) 的 Pod 网段
10.24.x.x/26 via 10.1.5.11 dev net0
跨网段路由条目:
# Node 1 (10.1.5.10) 到 Node 3 (10.1.8.10) 的 Pod 网段
10.24.x.x/26 via 10.1.8.10 dev tunl0 onlink
| 关键差异 | 同网段 | 跨网段 |
|---|---|---|
| 出接口 | net0(物理网卡) |
tunl0(IPIP 隧道) |
| 封装 | 无 | IPIP |
| 下一跳可达性 | 直接可达 | onlink(不检查) |
34.3 配置 CrossSubnet 模式
34.3.1 IPPool 配置
apiVersion: crd.projectcalico.org/v1
kind: IPPool
metadata:
name: default-ipv4-ippool
spec:
cidr: 10.24.0.0/16
ipipMode: CrossSubnet # 关键配置
vxlanMode: Never
natOutgoing: true
nodeSelector: all()
| 参数 | 值 | 说明 |
|---|---|---|
ipipMode |
CrossSubnet |
跨网段时使用 IPIP |
vxlanMode |
Never |
不使用 VXLAN |
34.3.2 修改现有 IPPool
# 查看当前配置
calicoctl get ippool default-ipv4-ippool -o yaml
# 修改 ipipMode
calicoctl patch ippool default-ipv4-ippool -p '{"spec":{"ipipMode":"CrossSubnet"}}'
34.4 ContainerLab 跨网段拓扑
34.4.1 拓扑架构
34.4.2 Kind 集群配置
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
kubeadmConfigPatches:
- |
kind: InitConfiguration
nodeRegistration:
kubeletExtraArgs:
node-ip: 10.1.5.10
- role: worker
kubeadmConfigPatches:
- |
kind: JoinConfiguration
nodeRegistration:
kubeletExtraArgs:
node-ip: 10.1.5.11
- role: worker
kubeadmConfigPatches:
- |
kind: JoinConfiguration
nodeRegistration:
kubeletExtraArgs:
node-ip: 10.1.8.10
- role: worker
kubeadmConfigPatches:
- |
kind: JoinConfiguration
nodeRegistration:
kubeletExtraArgs:
node-ip: 10.1.8.11
34.4.3 网关配置要点
网关需要配置 SNAT 以确保集群能访问外网:
# VyOS 网关配置
set interfaces ethernet eth1 address '10.1.5.1/24'
set interfaces ethernet eth2 address '10.1.8.1/24'
# SNAT 配置
set nat source rule 100 outbound-interface 'eth0'
set nat source rule 100 source address '10.1.0.0/16'
set nat source rule 100 translation address 'masquerade'
34.5 通信验证
34.5.1 同网段通信(纯路由)
# 在 Node 1 (10.1.5.10) 上的 Pod 访问 Node 2 (10.1.5.11) 上的 Pod
# 查看路由表
ip route | grep "10.1.5.11"
# 10.24.x.x/26 via 10.1.5.11 dev net0 <- 出接口是 net0
# 抓包验证
tcpdump -i net0 -nn icmp
抓包结果:
IP 10.24.x.x > 10.24.y.y: ICMP echo request
[!NOTE]
同网段通信时,数据包是普通的 IP 包,没有 IPIP 封装。
MAC 地址会逐跳变化,IP 地址保持不变。
34.5.2 跨网段通信(IPIP 封装)
# 在 Node 1 (10.1.5.10) 上的 Pod 访问 Node 3 (10.1.8.10) 上的 Pod
# 查看路由表
ip route | grep "10.1.8.10"
# 10.24.x.x/26 via 10.1.8.10 dev tunl0 onlink <- 出接口是 tunl0
# 抓包验证
tcpdump -i net0 -nn 'ip proto 4'
抓包结果:
IP 10.1.5.10 > 10.1.8.10: IP 10.24.x.x > 10.24.y.y: ICMP echo request
[!NOTE]
跨网段通信时,数据包有两层 IP:
- 外层 IP:10.1.5.10 → 10.1.8.10(Node IP)
- 内层 IP:10.24.x.x → 10.24.y.y(Pod IP)
34.6 MAC 地址变化分析
34.6.1 跨网段通信的 MAC 地址
关键点:
-
Node 1 发包时:
- SRC MAC:Node 1 net0 的 MAC
- DST MAC:网关 10.1.5.1 的 MAC(因为 10.1.8.10 不在同网段)
-
网关转发时:
- SRC MAC:网关 eth2 的 MAC
- DST MAC:Node 3 net0 的 MAC
-
IP 地址不变:外层 IP 始终是 10.1.5.10 → 10.1.8.10
34.6.2 为什么 ARP 表没有对端 IP
# 在 Node 1 上查看 ARP 表
arp -n | grep 10.1.8.10
# 无结果!
原因:Node 1 (10.1.5.10) 和 Node 3 (10.1.8.10) 不在同一子网,无法直接 ARP 解析。
实际过程:
- Node 1 查路由,发现 10.1.8.10 的下一跳是网关 10.1.5.1
- Node 1 ARP 解析 10.1.5.1 的 MAC
- 数据包发给网关,由网关负责后续转发
34.7 Always vs CrossSubnet
| 对比项 | Always | CrossSubnet |
|---|---|---|
| 同网段开销 | 20 字节 | 0 字节 |
| 跨网段开销 | 20 字节 | 20 字节 |
| 配置复杂度 | 低 | 中 |
| 适用场景 | 简单环境 | 大规模混合环境 |
| 性能 | 一般 | 同网段更优 |
34.8 章节小结
[!IMPORTANT]
核心要点总结:
CrossSubnet 模式:
- 同网段节点:纯路由转发,无封装开销
- 跨网段节点:IPIP 封装,20 字节开销
- 配置:
ipipMode: CrossSubnet路由表识别:
- 同网段:
dev net0(物理网卡)- 跨网段:
dev tunl0 onlink(隧道设备)抓包验证:
- 同网段:
tcpdump -i net0 icmp- 跨网段:
tcpdump -i net0 'ip proto 4'MAC 地址规律:
- 跨网段时,DST MAC 是网关的 MAC
- ARP 表中不会有对端节点的 MAC
- 每一跳 MAC 变化,IP 不变
适用场景:
- 大规模集群,节点分布在多个子网
- 同机房节点走高速路由
- 跨机房节点走 IPIP 穿透
第三十五章 Calico-IPIP 手工实践
本章通过手工创建 IPIP 隧道设备,深入理解 IPIP 封装的底层原理。同时介绍 SBR(Source-Based Routing)策略路由的概念,为理解复杂网络场景打下基础。
35.1 背景与目标
35.1.1 为什么要手工实践
手工创建 IPIP 隧道的目的:
- 加深理解:通过亲手操作,理解 Calico 自动创建
tunl0设备的底层机制 - 排障能力:掌握 IPIP 设备的配置方式,便于问题排查
- 扩展思维:理解如何将 Pod 流量引导至 Overlay 设备
35.1.2 实验拓扑
| 节点 | 物理 IP | IPIP 接口地址 | Local | Remote |
|---|---|---|---|---|
| Node 1 | 172.18.0.4 | 1.1.1.1/24 | 172.18.0.4 | 172.18.0.2 |
| Node 2 | 172.18.0.2 | 1.1.2.1/24 | 172.18.0.2 | 172.18.0.4 |
35.2 手工创建 IPIP 隧道
35.2.1 Node 1 配置
# 1. 创建 IPIP 设备
ip link add ipip0 type ipip local 172.18.0.4 remote 172.18.0.2
# 2. 启用接口
ip link set ipip0 up
# 3. 配置隧道内部地址
ip addr add 1.1.1.1/24 dev ipip0
# 4. 添加到对端网段的路由
ip route add 1.1.2.0/24 dev ipip0
35.2.2 Node 2 配置
# 1. 创建 IPIP 设备(local/remote 互换)
ip link add ipip0 type ipip local 172.18.0.2 remote 172.18.0.4
# 2. 启用接口
ip link set ipip0 up
# 3. 配置隧道内部地址
ip addr add 1.1.2.1/24 dev ipip0
# 4. 添加到对端网段的路由
ip route add 1.1.1.0/24 dev ipip0
35.2.3 配置命令详解
| 参数 | 说明 | 示例 |
|---|---|---|
type ipip |
设备类型为 IPIP | - |
local |
本端物理 IP | 172.18.0.4 |
remote |
对端物理 IP | 172.18.0.2 |
| 隧道地址 | 隧道内部通信地址 | 1.1.1.1/24 |
| 路由 | 指向对端隧道网段 | 1.1.2.0/24 |
35.3 验证与抓包
35.3.1 连通性测试
# 在 Node 1 上 ping Node 2 的隧道地址
ping -I 1.1.1.1 1.1.2.1
35.3.2 抓包验证
# 在 Node 1 的物理网卡上抓包
tcpdump -i eth0 -nn 'ip proto 4'
抓包结果:
IP 172.18.0.4 > 172.18.0.2: IP 1.1.1.1 > 1.1.2.1: ICMP echo request
IP 172.18.0.2 > 172.18.0.4: IP 1.1.2.1 > 1.1.1.1: ICMP echo reply
35.3.3 查看设备信息
# 查看 IPIP 设备详情
ip -d link show ipip0
# 输出示例
# ipip0@NONE: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1480 ...
# link/ipip 172.18.0.4 peer 172.18.0.2
| 属性 | 含义 |
|---|---|
link/ipip |
设备类型为 IPIP |
172.18.0.4 peer 172.18.0.2 |
Local 和 Remote IP |
mtu 1480 |
MTU = 1500 - 20 (IPIP Header) |
NOARP |
三层设备,无 ARP |
35.4 IPIP vs VXLAN 配置对比
| 对比项 | IPIP | VXLAN |
|---|---|---|
| 配置复杂度 | 简单 | 较复杂 |
| VNI | 无 | 需要 |
| 端口 | 无 | 4789/8472 |
| FDB 表 | 无 | 需要 |
| 封装开销 | 20 字节 | 50 字节 |
| 多租户 | 不支持 | 支持 |
35.5 生产环境扩展思考
35.5.1 Pod 流量如何到达 IPIP 设备
在实际生产环境中,Pod 的流量需要被引导到 IPIP 设备进行封装:
关键步骤:
- Pod 到 Host:通过 veth pair + Proxy ARP
- Host 到 IPIP:通过路由表(Calico 自动注入)
- IPIP 封装:设备自动封装外层 IP
35.5.2 Calico 的自动化
Calico 自动完成的工作:
- 创建
tunl0设备(remote any local any) - 通过 BGP 学习其他节点的 Pod CIDR
- 注入路由:
10.24.x.x/26 via <Node IP> dev tunl0 onlink - 根据
ipipMode决定是否走隧道
35.6 SBR (Source-Based Routing) 策略路由
35.6.1 什么是 SBR
传统路由基于目的地址 (DST),而 SBR 基于源地址 (SRC):
35.6.2 SBR 配置示例
# 创建策略路由规则
ip rule add from 192.168.1.0/24 table 100
# 在表 100 中添加默认路由
ip route add default via 10.0.0.1 table 100
# 查看规则
ip rule show
输出示例:
0: from all lookup local
32765: from 192.168.1.0/24 lookup 100
32766: from all lookup main
32767: from all lookup default
35.6.3 SBR 应用场景
| 场景 | 说明 |
|---|---|
| 多网卡 (Multi-Homing) | 不同网卡的流量走不同出口 |
| Cilium CNI | 使用 SBR 实现复杂策略 |
| VPN 分流 | 特定源走 VPN 隧道 |
| 多 ISP | 根据源选择出口 |
35.6.4 多网卡场景的回程路由问题
问题:没有 SBR 时,回复包可能从错误的接口发出
解决:为每个接口配置 SBR,确保回程路径正确
35.7 CNI 网络实现套路总结
[!IMPORTANT]
核心套路:
- 创建设备:赋予封装能力(IPIP/VXLAN/GRE)
- 引导流量:通过路由表将流量导向设备
- 封装转发:设备自动封装 Overlay 包
- 对端解封:对端设备解封装后路由到目标
35.8 章节小结
[!TIP]
实践要点总结:
IPIP 设备创建:
ip link add ipip0 type ipip local <本端IP> remote <对端IP>三步配置:
- 启用接口:
ip link set ipip0 up- 配置地址:
ip addr add x.x.x.x/24 dev ipip0- 添加路由:
ip route add <对端网段> dev ipip0抓包验证:
tcpdump -i eth0 'ip proto 4'生产扩展:
- Pod → veth → 路由表 → IPIP 设备
- Calico 自动完成设备创建和路由注入
SBR 策略路由:
- 基于源地址选择路由表
- 解决多网卡回程路由问题
- Cilium 中有广泛应用
第三十六章 Calico-VxLAN 模式
本章介绍 Calico 的 VxLAN 封装模式,重点讲解 FDB(Forwarding Database)表机制以及与 IPIP 模式的区别。
36.1 背景与概述
36.1.1 VxLAN vs IPIP
| 对比项 | IPIP | VxLAN |
|---|---|---|
| 封装层级 | Layer 3 (IP-in-IP) | Layer 2 over Layer 3 |
| 协议号 | IP Protocol 4 | UDP 4789 |
| 封装开销 | 20 字节 | 50 字节 |
| BGP 依赖 | 需要(bird 组件) | 不需要 |
| FDB 表 | 不需要 | 需要 |
| 多租户 | 不支持 | 支持(VNI) |
| 防火墙友好 | 可能被阻挡 | UDP 更易穿透 |
36.1.2 何时选择 VxLAN
- 网络限制:底层网络不支持 IP Protocol 4
- 防火墙穿透:UDP 端口更易开放
- 无 BGP 需求:不想维护 BGP 组件
- 大二层网络:需要二层可达的场景
36.2 VxLAN 模式配置
36.2.1 IPPool 配置
apiVersion: crd.projectcalico.org/v1
kind: IPPool
metadata:
name: default-pool
spec:
cidr: 10.244.0.0/16
ipipMode: Never # 禁用 IPIP
vxlanMode: Always # 启用 VxLAN
natOutgoing: true
[!IMPORTANT]
关键配置:
ipipMode和vxlanMode只能选一个- VxLAN 模式下需禁用 bird 相关组件
36.2.2 禁用 BGP/bird 组件
VxLAN 模式不依赖 BGP,需要修改 Calico 部署配置:
# calico-node DaemonSet 中
env:
- name: CALICO_NETWORKING_BACKEND
value: "vxlan" # 原来是 "bird"
# 移除或注释 bird 相关探针
livenessProbe:
exec:
command:
# - /bin/calico-node
# - -bird-live
readinessProbe:
exec:
command:
# - /bin/calico-node
# - -bird-ready
36.2.3 网卡自动检测(Auto-Detect)
多网卡环境需指定业务网卡:
env:
- name: IP_AUTODETECTION_METHOD
value: "interface=eth1" # 指定网卡
# 或使用 CIDR
# value: "cidr=10.1.0.0/16"
检测方法:
| 方法 | 示例 | 说明 |
|---|---|---|
first-found |
默认 | 自动选择默认路由网卡 |
interface= |
eth1 |
指定网卡名 |
cidr= |
10.1.0.0/16 |
匹配 CIDR |
can-reach= |
8.8.8.8 |
能到达的地址 |
36.3 MTU 配置
36.3.1 不同模式的 MTU
| 模式 | MTU 计算 | 结果 |
|---|---|---|
| IPIP | 1500 - 20 | 1480 |
| VxLAN | 1500 - 50 | 1450 |
| WireGuard + VxLAN | 1500 - 60 | 1440 |
| IPv6 + VxLAN | 1500 - 70 | 1430 |
36.3.2 巨型帧环境
数据中心常用 9000 MTU 巨型帧:
# 通过 calicoctl 修改 MTU
calicoctl patch felixconfiguration default \
--patch='{"spec": {"mtu": 8981}}'
# 验证
ip -d link show vxlan.calico
# mtu 8950 ...
36.4 VxLAN 设备与 FDB 表
36.4.1 vxlan.calico 设备
# 查看 VxLAN 设备
ip -d link show vxlan.calico
# 输出示例
# vxlan.calico: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 ...
# link/ether 66:c0:d0:7a:17:07 brd ff:ff:ff:ff:ff:ff
# vxlan id 4096 local 172.18.0.3 dev eth0 srcport 0 0 dstport 4789 ...
| 属性 | 含义 |
|---|---|
vxlan id 4096 |
VNI (VxLAN Network Identifier) |
local 172.18.0.3 |
本端 VTEP IP |
dstport 4789 |
VxLAN UDP 端口 |
mtu 1450 |
1500 - 50 |
36.4.2 FDB 表详解
FDB (Forwarding Database) 是 VxLAN 的核心机制,用于解决 外层目的 IP 的问题:
查看 FDB 表:
# 方法 1: bridge fdb show
bridge fdb show dev vxlan.calico | grep -v permanent
# 方法 2: ip monitor 观察动态添加
ip monitor all
# 输出示例
# 66:c0:d0:7a:17:07 dev vxlan.calico dst 172.18.0.4 self permanent
36.4.3 FDB 表的核心含义
[!IMPORTANT]
FDB 表的作用:
- 回答问题:这个 MAC 地址属于哪个主机?
- 解决问题:VxLAN 封装时,外层 DST IP 用谁?
- 自动学习:类似 ARP,系统自动维护
两层理解:
- 深层理解:FDB 表示 MAC 地址与主机的映射,该 MAC 不一定是主机物理网卡的 MAC,只要在该主机的任意网卡上存在即可
- 简单理解:FDB 是自动维护的,可通过
ip monitor all观察添加过程
36.5 VxLAN 包封装分析
36.5.1 完整封装结构
36.5.2 内层 MAC 地址变化
[!WARNING]
关键点:内层 MAC 地址不是原始 Pod 的 MAC!
- Inner SRC MAC:本地
vxlan.calico接口的 MAC- Inner DST MAC:远端
vxlan.calico接口的 MAC(通过 ARP 表获取)
36.5.3 抓包验证
# 在 eth0 上抓 VxLAN 包
tcpdump -i eth0 -nn udp port 4789 -w vxlan.pcap
# 或直接查看
tcpdump -i eth0 -nn -e udp port 4789
# 输出示例
# 02:42:ac:12:00:03 > 02:42:ac:12:00:04, IP 172.18.0.3.random > 172.18.0.4.4789: VXLAN...
# 66:c0:d0:xx > 66:c0:d0:yy, IP 10.244.1.69 > 10.244.17.65: ICMP...
36.6 VxLAN vs IPIP 路由表对比
# IPIP 模式
10.244.17.64/26 via 172.18.0.4 dev tunl0 proto bird onlink
# VxLAN 模式
10.244.17.64/26 via 10.244.17.64 dev vxlan.calico onlink
| 对比项 | IPIP | VxLAN |
|---|---|---|
| 下一跳 | 物理节点 IP | Pod CIDR 网关 |
| 设备 | tunl0 | vxlan.calico |
| extern IP 来源 | 路由表直接给出 | FDB 表查询 |
| proto | bird (BGP) | 无 |
36.7 同节点通信
VxLAN 模式下,同节点 Pod 通信与 IPIP 模式完全相同:
- 无 VxLAN 封装
- 纯三层路由 + Proxy ARP
36.8 章节小结
[!TIP]
VxLAN 模式要点总结:
配置:
vxlanMode: Always,ipipMode: Never- 需禁用 bird 相关组件
FDB 表核心:
- 解决:外层 DST IP 用谁?
- 查询:
bridge fdb show dev vxlan.calico- 机制:MAC 地址 → 主机 IP
内层 MAC 变化:
- 不是原始 Pod MAC
- SRC: 本地
vxlan.calicoMAC- DST: 远端
vxlan.calicoMAC抓包:
tcpdump -i eth0 udp port 4789MTU:1500 - 50 = 1450
第三十七章 Calico-VxLAN-CrossSubnet 模式
本章介绍 Calico VxLAN 的 CrossSubnet 模式,这是生产环境中最推荐的混合部署方案——同网段走高性能路由,跨网段走 overlay 封装。
37.1 背景与概述
37.1.1 为什么需要 CrossSubnet
在大型数据中心中,节点通常分布在不同的网段:
- 同机架:节点在同一二层,可直接路由
- 跨机架:节点在不同网段,需要三层转发
37.1.2 CrossSubnet 模式原理
| 场景 | 封装方式 | 性能 | 依赖 |
|---|---|---|---|
| 同网段 | 无(纯路由) | 高 | - |
| 跨网段 | VxLAN | 较低 | FDB 表 |
37.1.3 Always vs CrossSubnet
| 模式 | 同网段 | 跨网段 | 适用场景 |
|---|---|---|---|
Always |
VxLAN | VxLAN | 统一封装,简单 |
CrossSubnet |
路由 | VxLAN | 混合环境,性能优先 |
Never |
路由 | 路由 | 需要 BGP + 外部路由 |
37.2 配置详解
37.2.1 IPPool 配置
apiVersion: crd.projectcalico.org/v1
kind: IPPool
metadata:
name: default-pool
spec:
cidr: 10.244.0.0/16
ipipMode: Never # 禁用 IPIP
vxlanMode: CrossSubnet # 跨网段 VxLAN
natOutgoing: true
[!IMPORTANT]
关键配置项:
vxlanMode: CrossSubnet— 只在跨网段时封装ipipMode: Never— 必须禁用 IPIP(二者互斥)- 仍需禁用 bird 相关组件
37.2.2 Calico 如何判断"同网段"
Calico 根据节点 IP 和网络掩码判断:
# 假设节点 IP
Node1: 10.1.5.10/24
Node2: 10.1.5.11/24 # 同网段
Node3: 10.1.8.10/24 # 不同网段
# Calico 判断逻辑
10.1.5.10/24 & 10.1.5.11/24 → 同网段 (10.1.5.0) → 路由
10.1.5.10/24 & 10.1.8.10/24 → 不同网段 → VxLAN
37.3 实验拓扑
37.3.1 ContainerLab 跨网段环境
37.3.2 路由器配置
# ContainerLab 路由器配置
interface net0
ip address 10.1.5.1/24
interface net1
ip address 10.1.8.1/24
# SNAT for internet access
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
37.4 同网段通信验证
37.4.1 测试场景
# 从 control-plane (10.1.5.10) 上的 Pod
# ping worker1 (10.1.5.11) 上的 Pod
kubectl exec -it pod-on-control -- ping <pod-on-worker1-ip>
37.4.2 抓包分析
# 在 control-plane 节点的 net0 接口抓包
tcpdump -i net0 -nn icmp
# 输出示例
# 10.244.1.69 > 10.244.175.1: ICMP echo request
# 10.244.175.1 > 10.244.1.69: ICMP echo reply
特点:
- 无 VxLAN 封装
- 裸 ICMP 包
- MAC 地址逐跳变化(标准路由行为)
37.5 跨网段通信验证
37.5.1 测试场景
# 从 control-plane (10.1.5.10) 上的 Pod
# ping worker2 (10.1.8.10) 上的 Pod
kubectl exec -it pod-on-control -- ping <pod-on-worker2-ip>
37.5.2 抓包分析
# 抓 VxLAN 包
tcpdump -i net0 -nn udp port 4789 -w vxlan-cross.pcap
Wireshark 分析:
Frame: 外层 Ethernet
├── SRC MAC: control-plane net0
├── DST MAC: router interface
├── Outer IP Header
│ ├── SRC: 10.1.5.10 (control-plane)
│ └── DST: 10.1.8.10 (worker2)
├── UDP Header
│ ├── SRC Port: random
│ └── DST Port: 4789
├── VxLAN Header
│ └── VNI: 4096
└── Inner Frame
├── Inner Ethernet
│ ├── SRC MAC: vxlan.calico (local)
│ └── DST MAC: vxlan.calico (remote)
└── Inner IP
├── SRC: 10.244.1.69 (Pod1)
└── DST: 10.244.73.x (Pod2)
37.6 与 IPIP CrossSubnet 对比
| 对比项 | IPIP CrossSubnet | VxLAN CrossSubnet |
|---|---|---|
| 同网段 | 路由 | 路由 |
| 跨网段 | IP-in-IP 封装 | VxLAN 封装 |
| 封装开销 | 20 字节 | 50 字节 |
| 协议 | IP Protocol 4 | UDP 4789 |
| BGP 依赖 | 需要 bird | 不需要 |
| FDB 表 | 不需要 | 需要 |
| 防火墙 | 可能受阻 | UDP 易穿透 |
37.7 Overlay 的安全性优势
Overlay 安全优势:
- Pod IP 隐藏:外部设备只看到节点 IP,不知道内部 Pod IP
- 网络隔离:中间网络设备无法直接访问 Pod
- 零改造成本:无需在外部网络配置 Pod 网段路由
37.8 故障排查
37.8.1 确认模式生效
# 查看 IPPool 配置
calicoctl get ippool -o yaml
# 确认 vxlanMode
spec:
vxlanMode: CrossSubnet
37.8.2 验证路由表
# 同网段节点 - 直接路由
ip route | grep <same-subnet-node-pod-cidr>
# 10.244.175.0/26 via 10.1.5.11 dev net0
# 跨网段节点 - VxLAN 路由
ip route | grep <cross-subnet-node-pod-cidr>
# 10.244.73.0/26 via 10.244.73.0 dev vxlan.calico onlink
37.8.3 常见问题
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 跨网段不通 | 路由器未配置 | 确保三层设备能转发 |
| FDB 表为空 | CNI 未正常启动 | 重启 calico-node |
| 同网段走 VxLAN | 掩码配置错误 | 检查节点 IP/掩码 |
37.9 章节小结
[!TIP]
VxLAN CrossSubnet 要点总结:
核心原理:
- 同网段走路由(高性能)
- 跨网段走 VxLAN(兼容性好)
- Calico 根据节点 IP/掩码自动判断
配置:
vxlanMode: CrossSubnet验证方法:
- 同网段:
tcpdump -i net0 icmp(普通包)- 跨网段:
tcpdump -i net0 udp port 4789(VxLAN 包)适用场景:
- 多机架/多网段的生产环境
- 追求性能与兼容性平衡
- 不想改造外部网络
生产建议:节点数 <200 时推荐使用
第三十八章 Calico-BGP-Fullmesh 模式
本章介绍 Calico 的 BGP Fullmesh 模式,这是 Calico 最"原生"的网络方案——不使用任何 overlay 封装,完全依赖 BGP 协议进行路由通告。
38.1 背景与概述
38.1.1 BGP 基础回顾
BGP 核心概念:
| 概念 | 说明 |
|---|---|
| AS (自治系统) | 统一管理的网络域 |
| iBGP | 同一 AS 内的 BGP |
| eBGP | 不同 AS 间的 BGP |
| 水平分割 | iBGP 学到的路由不再通告给其他 iBGP peer |
| TCP 179 | BGP 使用 TCP 179 端口建立会话 |
38.1.2 为什么需要 Fullmesh
由于 BGP 水平分割规则:
- iBGP peer 学到的路由不会通告给其他 iBGP peer
- 要让所有节点学到完整路由,必须两两建立 BGP 邻居关系
- 这就是 Fullmesh(全网状连接)
38.1.3 Fullmesh 的适用场景
| 节点数 | 连接数 | 推荐方案 |
|---|---|---|
| 10 | 45 | Fullmesh ✅ |
| 50 | 1,225 | Fullmesh ✅ |
| 100 | 4,950 | Fullmesh ✅ |
| 200 | 19,900 | 边界 |
| 500 | 124,750 | Route Reflector ✅ |
[!IMPORTANT]
官方建议:
- 节点数 < 200:使用 Fullmesh
- 节点数 ≥ 200:使用 Route Reflector (RR)
38.2 Fullmesh vs Overlay
| 对比项 | BGP Fullmesh | IPIP/VxLAN |
|---|---|---|
| 封装 | 无 | 有 |
| 性能 | 最高 | 有开销 |
| MTU | 无损失 | 有损失 |
| 外部路由 | 需要配合 | 无需 |
| 复杂度 | 高 | 低 |
| 调试 | 需了解 BGP | 相对简单 |
38.3 配置详解
38.3.1 IPPool 配置
apiVersion: crd.projectcalico.org/v1
kind: IPPool
metadata:
name: default-pool
spec:
cidr: 10.244.0.0/16
ipipMode: Never # 禁用 IPIP
vxlanMode: Never # 禁用 VxLAN
natOutgoing: true
[!IMPORTANT]
关键配置:
ipipMode: Never— 禁用 IPIP 封装vxlanMode: Never— 禁用 VxLAN 封装- 禁用 overlay 后,Calico 自动使用 BGP 路由
38.3.2 BGP 配置(默认)
Calico 默认启用 BGP Fullmesh:
apiVersion: crd.projectcalico.org/v1
kind: BGPConfiguration
metadata:
name: default
spec:
nodeToNodeMeshEnabled: true # 启用 Fullmesh
asNumber: 64512 # 默认 AS 号
38.4 路由分析
38.4.1 路由表特征
# 查看路由表
ip route show
# 输出示例
10.244.73.128/26 via 172.18.0.2 dev eth0 proto bird
10.244.83.128/26 via 172.18.0.3 dev eth0 proto bird
关键特征:
- proto bird:路由由 bird 进程(BGP daemon)维护
- 下一跳是 peer 节点 IP:直接路由到对端节点
- 无 tunl0/vxlan.calico:不使用隧道设备
38.4.2 与 Overlay 路由对比
| 模式 | 路由示例 | 设备 |
|---|---|---|
| BGP | via 172.18.0.2 dev eth0 proto bird |
eth0 |
| IPIP | via 172.18.0.2 dev tunl0 proto bird onlink |
tunl0 |
| VxLAN | via 10.244.73.0 dev vxlan.calico onlink |
vxlan.calico |
38.5 BGP 状态查看
38.5.1 calicoctl node status
# 查看 BGP peer 状态
calicoctl node status
# 输出示例
Calico process is running.
IPv4 BGP status
+----------------+-------------------+-------+----------+-------------+
| PEER ADDRESS | PEER TYPE | STATE | SINCE | INFO |
+----------------+-------------------+-------+----------+-------------+
| 172.18.0.2 | node-to-node mesh | up | 10:30:00 | Established |
| 172.18.0.3 | node-to-node mesh | up | 10:30:00 | Established |
+----------------+-------------------+-------+----------+-------------+
字段说明:
| 字段 | 说明 |
|---|---|
| PEER ADDRESS | BGP 邻居 IP |
| PEER TYPE | node-to-node mesh = Fullmesh |
| STATE | up = 正常 |
| INFO | Established = TCP 179 连接已建立 |
38.5.2 验证 TCP 179 连接
# 查看 BGP 端口连接
netstat -an | grep 179
# 输出
tcp 0 0 172.18.0.4:179 172.18.0.2:xxxxx ESTABLISHED
tcp 0 0 172.18.0.4:179 172.18.0.3:xxxxx ESTABLISHED
38.6 bird 命令详解
38.6.1 进入 bird 控制台
# 进入 calico-node Pod
kubectl exec -it calico-node-xxxxx -n kube-system -- /bin/sh
# 进入 bird 控制台
birdcl
# 或
birdc
38.6.2 常用 bird 命令
# 查看接口
birdc> show interfaces
# 查看路由表
birdc> show route
# 查看 BGP 协议路由
birdc> show route protocol Mesh_172_18_0_2
# 查看协议状态
birdc> show protocols
show route 输出示例:
10.244.73.128/26 via 172.18.0.2 on eth0 [Mesh_172_18_0_2 10:30:00] * (100/0) [i]
10.244.83.128/26 via 172.18.0.3 on eth0 [Mesh_172_18_0_3 10:30:00] * (100/0) [i]
38.7 AS Number 与配置文件
38.7.1 查看 AS Number
# 通过 calicoctl
calicoctl get nodes -o yaml | grep asNumber
# 或查看 BGPConfiguration
calicoctl get bgpconfig default -o yaml
38.7.2 bird 配置文件
# 配置文件位置
cat /etc/calico/confd/config/bird.cfg
# bird.cfg 示例
router id 172.18.0.4;
protocol bgp Mesh_172_18_0_2 {
local as 64512;
neighbor 172.18.0.2 as 64512;
# ...
}
protocol bgp Mesh_172_18_0_3 {
local as 64512;
neighbor 172.18.0.3 as 64512;
# ...
}
38.8 抓包验证
38.8.1 跨节点 Pod 通信
# 抓包
tcpdump -i eth0 -nn icmp
# 输出示例
172.18.0.4 > 172.18.0.2: ICMP echo request
# 注意:这是节点 IP,不是 Pod IP!
实际抓包:
# Pod 间 ping
10.244.140.132 > 10.244.88.128: ICMP echo request
特点:
- 无封装:直接看到 Pod IP
- MAC 地址变化:每跳变化
- 纯三层转发
38.9 Route Reflector 简介
当节点数超过 200 时,应使用 Route Reflector:
RR 优势:
- RR 之间 Fullmesh(少量连接)
- 普通节点只连接 RR(减少连接数)
- 多 RR 保证高可用
38.10 章节小结
[!TIP]
BGP Fullmesh 要点总结:
核心原理:
- BGP 水平分割 → 需要 Fullmesh
- 连接数:N*(N-1)/2
- 官方建议:<200 节点
配置:禁用 IPIP/VxLAN,默认启用 BGP
关键命令:
calicoctl node status— 查看 BGP peerip route— 看到proto birdbirdc show route— BGP 路由详情抓包特点:无封装,直接看到 Pod IP
生产建议:
- 小集群:Fullmesh
- 大集群:Route Reflector
第三十九章 Calico-BGP-RR 模式
本章介绍 Calico 的 BGP Route Reflector (RR) 模式,这是大规模集群(>200 节点)的推荐 BGP 方案,采用 AS per Rack 拓扑设计。
39.1 背景与概述
39.1.1 为什么需要 Route Reflector
回顾 Fullmesh 的问题:
| 节点数 | Fullmesh 连接数 |
|---|---|
| 100 | 4,950 |
| 200 | 19,900 |
| 500 | 124,750 |
| 1000 | 499,500 |
Route Reflector 解决方案:
- RR 节点:接收所有路由,反射给所有客户端
- 客户端:只与 RR 建立连接
- 连接数:从 N*(N-1)/2 降低到 N
39.1.2 AS per Rack 拓扑
生产环境推荐的 Leaf-Spine 架构:
架构说明:
| 层级 | 说明 |
|---|---|
| Spine | 核心交换机,eBGP 互联 |
| Leaf | 接入交换机,作为 RR |
| Node | K8s 节点,RR Client |
| iBGP | 同 AS 内部(Node ↔ Leaf) |
| eBGP | 不同 AS 之间(Leaf ↔ Spine) |
39.2 配置详解
39.2.1 禁用 Node-to-Node Mesh
apiVersion: crd.projectcalico.org/v1
kind: BGPConfiguration
metadata:
name: default
spec:
nodeToNodeMeshEnabled: false # 关闭 Fullmesh!
asNumber: 65005
或通过命令:
calicoctl patch bgpconfig default -p \
'{"spec": {"nodeToNodeMeshEnabled": false}}'
39.2.2 配置节点 BGP 属性
apiVersion: crd.projectcalico.org/v1
kind: Node
metadata:
name: node1
labels:
rack: rack0 # 机架标签
spec:
bgp:
ipv4Address: 10.1.5.10/24
asNumber: 65005
39.2.3 创建 BGP Peer
apiVersion: crd.projectcalico.org/v1
kind: BGPPeer
metadata:
name: rack0-to-leaf0
spec:
peerIP: 10.1.5.1 # Leaf 交换机 IP
asNumber: 65005 # 同 AS = iBGP
nodeSelector: rack == 'rack0' # 只匹配 rack0 的节点
nodeSelector 机制:
# 查看节点标签
kubectl get nodes --show-labels | grep rack
# 输出
node1 rack=rack0
node2 rack=rack0
node3 rack=rack1
node4 rack=rack1
39.3 Leaf 交换机配置
39.3.1 VyOS 配置示例
# Leaf0 配置
set interfaces ethernet eth0 address 10.1.5.1/24
set interfaces ethernet eth1 address 10.1.10.2/24 # to Spine0
set interfaces ethernet eth2 address 10.1.12.2/24 # to Spine1
# BGP 配置
set protocols bgp 65005 router-id 10.1.5.1
# 宣告本地网段
set protocols bgp 65005 network 10.1.5.0/24
# 配置 RR Client (iBGP)
set protocols bgp 65005 neighbor 10.1.5.10 remote-as 65005
set protocols bgp 65005 neighbor 10.1.5.10 route-reflector-client
set protocols bgp 65005 neighbor 10.1.5.11 remote-as 65005
set protocols bgp 65005 neighbor 10.1.5.11 route-reflector-client
# 配置 eBGP (to Spine)
set protocols bgp 65005 neighbor 10.1.10.1 remote-as 500
set protocols bgp 65005 neighbor 10.1.12.1 remote-as 800
# SNAT for Internet
set nat source rule 10 outbound-interface eth0
set nat source rule 10 source address 10.1.5.0/24
set nat source rule 10 translation address masquerade
[!IMPORTANT]
关键配置:
route-reflector-client:指定节点为 RR 客户端- 同 AS(65005)= iBGP
- 不同 AS(500, 800)= eBGP
39.4 ECMP 等价多路径
39.4.1 什么是 ECMP
ECMP (Equal Cost Multi-Path):
- 等价开销多路径
- 多条路径同时使用
- 负载分担 + 故障备份
39.4.2 VyOS ECMP 配置
# 启用 ECMP
set protocols bgp 65005 maximum-paths ebgp 2
set protocols bgp 65005 maximum-paths ibgp 2
39.4.3 验证 ECMP
# 查看路由表
show ip bgp
# 输出示例
Network Next Hop Metric Path
*> 10.1.8.0/24 10.1.10.1 0 500 65008 i
*= 10.1.12.1 0 800 65008 i
*>= 有效且最佳*== 有效且等价(ECMP)
39.5 验证步骤
39.5.1 calicoctl node status
calicoctl node status
# 输出
IPv4 BGP status
+----------------+---------------+-------+----------+-------------+
| PEER ADDRESS | PEER TYPE | STATE | SINCE | INFO |
+----------------+---------------+-------+----------+-------------+
| 10.1.5.1 | node specific | up | 10:30:00 | Established |
+----------------+---------------+-------+----------+-------------+
关键变化:
| Fullmesh | Route Reflector |
|---|---|
node-to-node mesh |
node specific |
| 多个 peer(所有节点) | 单个 peer(仅 RR) |
39.5.2 验证 TCP 179
netstat -an | grep 179
# 输出
tcp 0 0 10.1.5.10:xxxxx 10.1.5.1:179 ESTABLISHED
39.5.3 查看 Leaf 上的 BGP 状态
# VyOS
show ip bgp summary
# 输出
Neighbor AS MsgRcvd MsgSent State/PfxRcd
10.1.5.10 65005 100 100 Established
10.1.5.11 65005 98 98 Established
10.1.10.1 500 200 200 Established
10.1.12.1 800 195 195 Established
39.6 Service IP Advertisement
39.6.1 宣告 ClusterIP
apiVersion: crd.projectcalico.org/v1
kind: BGPConfiguration
metadata:
name: default
spec:
nodeToNodeMeshEnabled: false
asNumber: 65005
serviceClusterIPs:
- cidr: 10.96.0.0/16 # ClusterIP 范围
或通过命令:
calicoctl patch bgpconfig default -p \
'{"spec": {"serviceClusterIPs": [{"cidr": "10.96.0.0/16"}]}}'
39.6.2 验证路由传播
# 在 Leaf 上查看
show ip route bgp
# 输出
B 10.96.0.0/16 [200/0] via 10.1.5.10, eth0, ...
# 在 Spine 上查看
show ip bgp 10.96.0.0/16
# 路由已传播
效果:外部设备可以直接通过 BGP 路由访问 ClusterIP!
39.7 故障排查
39.7.1 常见问题
| 问题 | 排查命令 | 解决方案 |
|---|---|---|
| BGP 未建立 | calicoctl node status |
检查 IP/AS 配置 |
| 路由缺失 | ip route |
检查 BGP 宣告 |
| ECMP 不生效 | show ip bgp |
检查 maximum-paths |
| 跨机架不通 | tcpdump |
检查 Spine 转发 |
39.7.2 调试命令
# 查看 bird 日志
kubectl logs -n kube-system calico-node-xxxxx -c bird
# 进入 bird 控制台
kubectl exec -it calico-node-xxxxx -n kube-system -- birdcl
# 查看 BGP 路由
birdc> show route protocol Mesh_10_1_5_1
39.8 二层隔离的安全优势
为什么使用不同网段(跨机架):
优势:
- 广播域隔离
- 故障范围控制
- 安全性增强
39.9 章节小结
[!TIP]
BGP RR 模式要点总结:
核心原理:
- Leaf 交换机作为 Route Reflector
- K8s 节点作为 RR Client
- 连接数从 N² 降低到 N
拓扑设计:
- Leaf-Spine 架构
- AS per Rack(每机架一个 AS)
- iBGP(Node ↔ Leaf)+ eBGP(Leaf ↔ Spine)
关键配置:
nodeToNodeMeshEnabled: false— 禁用 FullmeshBGPPeer+nodeSelector— 按机架配置route-reflector-client— Leaf 配置ECMP:等价多路径,实现负载分担和高可用
Service 宣告:ClusterIP 可通过 BGP 向外宣告
适用场景:大规模集群(>200 节点)
第四十章 Calico-eBPF-with-DSR 模式
本章介绍 Calico 的 eBPF 数据平面模式及 DSR(Direct Server Return)功能,这是一种高性能的替代方案,可以绑过 iptables 和 kube-proxy。
40.1 背景与概述
40.1.1 什么是 eBPF
eBPF (extended Berkeley Packet Filter):
- 内核中安全运行的虚拟机
- 无需修改或重编内核
- 动态加载/卸载程序
- 广泛用于:网络、监控、安全、存储
40.1.2 Calico 与 eBPF 的结合
背景:
- 早期 Calico 不支持 eBPF
- 随着 Cilium 的崛起和 eBPF 技术火热
- Calico 3.x 版本引入 eBPF 数据平面
eBPF 数据平面优势:
| 对比项 | 传统 iptables | eBPF 数据平面 |
|---|---|---|
| Service 实现 | kube-proxy + iptables | eBPF map |
| 性能 | O(n) 规则匹配 | O(1) hash 查找 |
| conntrack | 内核 conntrack | eBPF conntrack |
| CPU 开销 | 较高 | 较低 |
| 功能 | 完整 | 部分限制 |
40.2 eBPF 数据平面原理
40.2.1 Bypass kube-proxy
工作机制:
- Service ClusterIP → eBPF map
- 直接查找后端 Pod
- 绕过 iptables/IPVS
40.2.2 限制与适用场景
不推荐使用 eBPF 的场景:
- Service Mesh control plane(仍需 iptables)
- Packet-by-packet 处理
- 需要完整 iptables 功能
推荐使用 eBPF 的场景:
- Connect-time Load Balancing
- XDP 加速
- 高性能需求
平台兼容性:
| 支持 | 不支持 |
|---|---|
| OpenShift, AKS, EKS | GKE, RKE |
| Ubuntu 20.04+ (5.4+ 内核) | 旧内核 (<4.18) |
| kubeadm 安装 | IPv6, SCTP |
40.3 DSR 模式原理
40.3.1 什么是 DSR
DSR (Direct Server Return):
- 响应包不经过 Load Balancer
- 后端直接返回给客户端
- 减少 LB 负载,提升性能
40.3.2 Tunnel vs DSR 对比
TCP 三次握手流程对比:
| 步骤 | Tunnel 模式 | DSR 模式 |
|---|---|---|
| SYN | Client → LB → Backend | Client → LB → Backend |
| SYN-ACK | Backend → LB → Client | Backend → Client (直接) |
| ACK | Client → LB → Backend | Client → LB → Backend |
| 总包数 | 6 个 | 4 个 |
40.4 配置与实现
40.4.1 启用 eBPF 数据平面
步骤 1:创建 ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: kubernetes-services-endpoint
namespace: kube-system
data:
KUBERNETES_SERVICE_HOST: "<API_SERVER_IP>"
KUBERNETES_SERVICE_PORT: "6443"
步骤 2:禁用 kube-proxy
# Kind 集群:创建时设置
kubeProxyMode: none
# 现有集群:scale down
kubectl scale deployment kube-proxy -n kube-system --replicas=0
步骤 3:启用 eBPF
calicoctl patch felixconfiguration default \
--patch='{"spec": {"bpfEnabled": true}}'
40.4.2 启用 DSR 模式
calicoctl patch felixconfiguration default \
--patch='{"spec": {"bpfExternalServiceMode": "DSR"}}'
DSR 模式值:
| 模式 | 说明 |
|---|---|
Tunnel |
默认,VxLAN 封装双向 |
DSR |
响应直接返回 |
40.4.3 验证配置
# 检查 Felix 配置
calicoctl get felixconfiguration default -o yaml
# 检查 BPF 状态
kubectl exec -n kube-system calico-node-xxx -- \
calico-node -bpf conntrack dump
40.5 抓包分析
40.5.1 Tunnel 模式抓包
特点:
- 中间节点进行 VxLAN 封装
- 所有流量经过 LB 节点
- 共 6 个包完成握手
40.5.2 DSR 模式抓包
特点:
- SYN-ACK 从 Backend 直接返回 Client
- 减少 LB 负载
- 共 4 个包完成握手
40.5.3 VxLAN 封装分析
# 外层 IP 头
Src: 172.18.0.2 (LB)
Dst: 172.18.0.3 (Backend)
# VxLAN 头
Port: 4789
VNI: xxx
# 内层 IP 头 (原始包)
Src: 172.18.0.1 (Client)
Dst: 172.18.0.2 (Service ClusterIP)
[!IMPORTANT]
DSR 如何实现直接返回:
- VxLAN 内层包含原始 Client IP
- Backend 解封装后获取 Client 地址
- 直接构造响应包发送给 Client
- 响应包源 IP 为 Service IP(非 Backend IP)
40.6 验证步骤
40.6.1 检查 eBPF 启用状态
# 查看 Felix 配置
calicoctl get felixconfiguration default -o yaml | grep bpf
# 输出
bpfEnabled: true
bpfExternalServiceMode: DSR
40.6.2 验证无 kube-proxy
# 检查 iptables 规则(应该没有 Service 规则)
iptables -t nat -L KUBE-SERVICES
# 检查 conntrack(eBPF 模式下为空)
conntrack -L | grep <service-port>
40.6.3 使用 BPF 工具查看
# 进入 calico-node Pod
kubectl exec -it calico-node-xxx -n kube-system -- sh
# 使用 calico-node 内置 BPF 工具
calico-node -bpf conntrack dump
calico-node -bpf routes dump
calico-node -bpf arp dump
40.7 故障排查
40.7.1 常见问题
| 问题 | 原因 | 解决方案 |
|---|---|---|
| Service 不通 | eBPF 未正确加载 | 检查 Felix 日志 |
| 性能没提升 | kube-proxy 仍运行 | 禁用 kube-proxy |
| 部分功能失效 | 平台不支持 | 检查兼容性列表 |
| conntrack 查不到 | 正常现象 | eBPF 有自己的 conntrack |
40.7.2 调试命令
# 查看 BPF 程序加载情况
tc -s qdisc show dev eth0
# 查看丢包统计
tc -s filter show dev eth0
# 查看 Felix 日志
kubectl logs -n kube-system calico-node-xxx -c felix | grep -i bpf
40.8 章节小结
[!TIP]
eBPF + DSR 模式要点总结:
eBPF 优势:
- 绕过 iptables/kube-proxy
- O(1) 查找性能
- 降低 CPU 开销
DSR 原理:
- 响应包直接从 Backend 返回 Client
- 减少 LB 节点负载
- TCP 握手从 6 包减少到 4 包
关键配置:
bpfEnabled: truebpfExternalServiceMode: DSR- 禁用 kube-proxy
验证方法:
calico-node -bpf命令- 抓包分析 TCP 握手流程
- conntrack 表为空(eBPF 管理)
注意限制:
- 内核版本要求 (>= 5.4)
- 部分平台不支持
- 功能不如 iptables 完整
第四十一章 Calico-VPP 模式
本章介绍 Calico 的 VPP(Vector Packet Processing)数据平面模式,这是一种高性能的用户态协议栈方案。
41.1 背景与概述
41.1.1 什么是 VPP
VPP (Vector Packet Processing):
- 矢量包处理框架
- 用户态协议栈
- 思科开源项目 (fd.io)
- 高性能网络处理
矢量处理优势:
| 特性 | 传统逐包处理 | VPP 矢量处理 |
|---|---|---|
| 处理方式 | 单包遍历所有层 | 批量处理同类包 |
| Cache 命中 | 低(频繁切换) | 高(连续访问) |
| 性能 | 较低 | 极高(百万 pps) |
| 扩展性 | 内核态修改 | Plugin 机制 |
41.1.2 VPP 与 DPDK 的关系
组合说明:
- VPP:提供用户态协议栈
- DPDK:提供快速数据通道
- SR-IOV + DPDK:生产环境最强组合
41.2 VPP 工作原理
41.2.1 Node Graph 处理模型
每个 Node 代表一个处理单元:
dpdk-input:从网卡接收ip4-input:IPv4 处理ip4-lookup:路由查找interface-output:发送
41.2.2 传统模式 vs VPP 模式
传统模式(内核协议栈):
Pod → veth → Bridge/Route → 内核协议栈 → NIC → 交换机
VPP 模式(用户态协议栈):
Pod → tun/tap → VPP 协议栈 → DPDK/驱动 → NIC → 交换机
41.3 环境配置
41.3.1 前置要求
HugePages(大页内存):
# 查看当前配置
cat /proc/meminfo | grep Huge
# 配置 HugePages(2MB)
echo 1024 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages
# 或配置 1GB HugePages
echo 4 > /sys/kernel/mm/hugepages/hugepages-1048576kB/nr_hugepages
Kubernetes 中查看 HugePages:
kubectl describe node <node-name> | grep hugepages
# 输出
# hugepages-1Gi: 0
# hugepages-2Mi: 0
41.3.2 网卡驱动绑定
驱动类型对比:
| 驱动 | 性能 | HugePages | 适用场景 |
|---|---|---|---|
af_packet |
低 | 不需要 | 测试/兼容 |
uio_pci_generic |
中 | 需要 | 通用 |
igb_uio |
高 | 需要 | 已淘汰 |
vfio-pci |
高 | 需要 | 生产推荐 |
绑定网卡到 DPDK:
# 查看当前状态
dpdk-devbind.py --status
# 绑定到 vfio-pci
dpdk-devbind.py --bind=vfio-pci 0000:82:00.0
# 绑定后,网卡在内核中不可见
ip link show # 看不到该网卡
[!WARNING]
网卡绑定到 DPDK 后,将从内核中消失。需要 VPP 创建 tap 设备维持 SSH 连接。
41.4 安装与配置
41.4.1 Calico VPP 安装
步骤 1:准备空集群
# kubeadm init 后,不安装 CNI
kubeadm init --pod-network-cidr=192.168.0.0/16
步骤 2:安装 Calico Operator
kubectl apply -f https://docs.projectcalico.org/manifests/calico.yaml
步骤 3:安装 VPP 数据平面
# vpp-dataplane.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: calico-vpp-config
namespace: calico-vpp-dataplane
data:
CALICOVPP_INTERFACE: eth0
CALICOVPP_NATIVE_DRIVER: af_packet # 或 dpdk
SERVICE_PREFIX: 10.96.0.0/16
kubectl apply -f vpp-dataplane.yaml
41.4.2 VPP 启动配置
# /etc/vpp/startup.conf
unix {
nodaemon
full-coredump
}
cpu {
workers 1
main-core 0
}
dpdk {
dev 0000:82:00.0
}
plugins {
plugin dpdk_plugin.so { enable }
plugin ping_plugin.so { enable }
}
buffers {
buffers-per-numa 131072
}
关键配置说明:
| 配置项 | 说明 |
|---|---|
cpu.workers |
转发线程数 |
dpdk.dev |
网卡 PCI 地址 |
plugins |
启用的插件 |
buffers |
内存缓冲区 |
41.5 调试与验证
41.5.1 进入 VPP 控制台
# Calico VPP 环境
kubectl exec -it calico-vpp-node-xxx -n calico-vpp-dataplane -- vppctl
# 或直接执行命令
kubectl exec calico-vpp-node-xxx -n calico-vpp-dataplane -- vppctl show interface
41.5.2 常用 vppctl 命令
# 查看接口
vppctl show interface
# 查看接口统计
vppctl show interface addr
# 查看路由表
vppctl show ip fib
# 查看 ARP 表
vppctl show ip neighbor
# 查看隧道
vppctl show vxlan tunnel
vppctl show ipip tunnel
# 查看运行状态
vppctl show runtime
输出示例:
vpp# show interface
Name Idx State MTU RX packets TX packets
GigabitEthernet82/0/0 1 up 9000 12345678 87654321
tap0 2 up 1500 54321 12345
tun0 3 up 1500 11111 22222
41.6 Pod 通信原理
41.6.1 Pod 到 VPP 的连接
tun vs tap 设备:
| 类型 | 层级 | 用途 |
|---|---|---|
| tun | L3(IP) | Pod 连接 VPP |
| tap | L2(Ethernet) | 需要 MAC 地址场景 |
41.6.2 跨节点通信
41.7 限制与注意事项
41.7.1 已知限制
| 功能 | 状态 |
|---|---|
| BGP | 必须启用 |
| Service Affinity | 不支持 |
| EndpointSlice | 不支持 |
| WireGuard | 部分限制 |
| IPv6 | 支持 |
41.7.2 生产建议
[!CAUTION]
VPP 模式仍为实验性功能:
- 官方标记为 "Not Production Ready"
- 建议在测试环境充分验证
- 生产环境考虑使用 Multus + VPP
适用场景:
- 高性能网络需求(流媒体、AI/ML)
- SR-IOV 环境
- 专用硬件加速
41.8 章节小结
[!TIP]
VPP 模式要点总结:
VPP 是什么:
- 矢量包处理框架
- 用户态高性能协议栈
- 通过 Plugin 扩展功能
与 DPDK 关系:
- VPP 提供协议栈
- DPDK 提供快速数据通道
- 生产推荐:SR-IOV + DPDK
关键配置:
- HugePages 内存
- 网卡驱动绑定(vfio-pci)
- VPP startup.conf
Pod 连接方式:
- 通过 tun 设备连接到 VPP
- VPP 替代内核协议栈转发
限制注意:
- 必须启用 BGP
- 实验性功能,非生产就绪
- 需要物理机或虚拟机(非容器化)
第四十二章 Calico-Service-DataPath
本章深入分析 Calico 环境下 Kubernetes Service 的数据路径,包括 ClusterIP、NodePort、LoadBalancer 三种类型的 NAT 转换过程。
42.1 背景与概述
42.1.1 Service 类型回顾
| 类型 | 访问范围 | 使用场景 |
|---|---|---|
| ClusterIP | 集群内部 | 默认类型,微服务通信 |
| NodePort | 集群外部 | 通过节点端口访问 |
| LoadBalancer | 外部 LB | 云环境,生产推荐 |
| Headless | 集群内部 | StatefulSet,直连 Pod |
42.1.2 NAT 转换基础
NAT 类型说明:
| NAT 类型 | 说明 | 场景 |
|---|---|---|
| SNAT | 修改源地址 | 内网访问外网 |
| DNAT | 修改目的地址 | 外网访问内网 |
| Reverse NAT | 反向还原 | 响应包还原地址 |
42.2 ClusterIP 数据路径
42.2.1 工作原理
ClusterIP 是 Kubernetes 默认的 Service 类型,只能在集群内部访问。
42.2.2 DNAT 详解
请求阶段:
Pod A 发送请求:
原始包: SRC=10.244.1.10 (PodA), DST=10.96.0.100 (ClusterIP)
经过 iptables DNAT:
转换后: SRC=10.244.1.10 (PodA), DST=10.244.2.20 (PodB)
响应阶段:
Pod B 返回响应:
原始包: SRC=10.244.2.20 (PodB), DST=10.244.1.10 (PodA)
经过 conntrack Reverse DNAT:
转换后: SRC=10.96.0.100 (ClusterIP), DST=10.244.1.10 (PodA)
[!IMPORTANT]
为什么需要 Reverse DNAT?
Pod A 发送的请求目的是 ClusterIP,如果响应的源地址是 PodB IP,Pod A 会因为地址不匹配而丢弃该包。
42.2.3 抓包验证
# 在 Client Pod 网卡抓包
kubectl exec client-pod -- tcpdump -i eth0 -nn
# 观察第一个 SYN 包
# SRC: 10.244.1.10 (PodA)
# DST: 10.96.0.100 (ClusterIP)
# 在 Host 网卡抓包
tcpdump -i eth0 -nn host 10.244.2.20
# 观察同一个 SYN 包 (DNAT 已完成)
# SRC: 10.244.1.10 (PodA)
# DST: 10.244.2.20 (PodB) ← 目的地址已变化
42.3 NodePort 数据路径
42.3.1 工作原理
NodePort 允许从集群外部通过节点 IP + 端口访问服务。
42.3.2 为什么需要 SNAT?
解决方案:SNAT
- 请求时:将源地址改为 Node1 IP
- 响应时:PodB 将响应发回 Node1
- Node1 再做 Reverse NAT 发回 Client
42.3.3 完整 NAT 过程
| 阶段 | 源地址 | 目的地址 | NAT 操作 |
|---|---|---|---|
| Client→Node1 | ClientIP | Node1:30000 | - |
| Node1→PodB | Node1 IP | PodB:80 | SNAT + DNAT |
| PodB→Node1 | PodB IP | Node1 IP | - |
| Node1→Client | Node1:30000 | ClientIP | Reverse NAT |
42.3.4 抓包验证
# 在 Node1 上抓包
tcpdump -i eth0 -nn port 30000 -w nodeport.pcap
# 从外部访问
curl 172.18.0.2:30000
# 分析 pcap 文件
# 可以看到 4 种不同的地址组合
42.4 LoadBalancer 数据路径
42.4.1 工作原理
LoadBalancer 在 NodePort 基础上增加了外部负载均衡器。
42.4.2 与 NodePort 的关系
LoadBalancer = 外部 L4 LB + NodePort
数据流:
Client → L4 LB (DNAT选择节点) → NodePort (SNAT+DNAT) → Pod
[!TIP]
L4 LB 的作用:
- 隐藏后端节点 IP
- 负载均衡到多个节点
- 提供统一入口(外部 IP/VIP)
42.5 iptables 规则分析
42.5.1 Service 相关规则链
# 查看 Service 规则
iptables -t nat -L KUBE-SERVICES -n --line-numbers
# 查看特定 Service 的规则链
iptables -t nat -L KUBE-SVC-XXXXX -n
规则链结构:
42.5.2 iptables trace 调试
# 添加 trace 规则
iptables -t raw -A PREROUTING -p tcp --dport 80 -j TRACE
iptables -t raw -A OUTPUT -p tcp --dport 80 -j TRACE
# 查看 trace 日志
dmesg -T | grep TRACE
# 清除 trace 规则
iptables -t raw -D PREROUTING -p tcp --dport 80 -j TRACE
iptables -t raw -D OUTPUT -p tcp --dport 80 -j TRACE
42.6 conntrack 连接跟踪
42.6.1 查看连接表
# 查看所有连接
conntrack -L
# 过滤特定端口
conntrack -L -p tcp --dport 80
# 查看 NAT 连接
conntrack -L -n
42.6.2 conntrack 表项解读
tcp 6 117 TIME_WAIT
src=10.244.1.10 dst=10.96.0.100 sport=45678 dport=80
src=10.244.2.20 dst=10.244.1.10 sport=80 dport=45678
解读:
- 原始方向: PodA(10.244.1.10) → ClusterIP(10.96.0.100)
- 回复方向: PodB(10.244.2.20) → PodA(10.244.1.10)
- DNAT 映射: ClusterIP → PodB
42.7 实验环境搭建
42.7.1 测试 Pod 部署
# test-pods.yaml
apiVersion: v1
kind: Pod
metadata:
name: client-pod
spec:
nodeName: node1 # 固定到 node1
containers:
- name: client
image: curlimages/curl
command: ["sleep", "infinity"]
---
apiVersion: v1
kind: Pod
metadata:
name: backend-pod
labels:
app: backend
spec:
nodeName: node2 # 固定到 node2,跨节点测试
containers:
- name: backend
image: nginx
---
apiVersion: v1
kind: Service
metadata:
name: backend-svc
spec:
type: NodePort
selector:
app: backend
ports:
- port: 80
targetPort: 80
nodePort: 30000
42.7.2 验证步骤
# 1. ClusterIP 测试
kubectl exec client-pod -- curl backend-svc
# 2. NodePort 测试 (从集群外)
curl <node-ip>:30000
# 3. 抓包分析
# 在不同位置抓包对比
42.8 章节小结
[!TIP]
Service DataPath 要点总结:
ClusterIP:
- 仅做 DNAT(ClusterIP → PodIP)
- conntrack 记录映射,响应时 Reverse DNAT
NodePort:
- SNAT + DNAT 双重转换
- SNAT 确保响应能回到入口节点
LoadBalancer:
- L4 LB 选择后端节点
- 后续流程等同 NodePort
调试技巧:
tcpdump抓包:注意抓包位置影响看到的地址iptables trace:追踪规则命中conntrack -L:查看 NAT 映射关系注意事项:
- 抓包时 iptables 已处理完成
- 生产环境谨慎修改 iptables 规则
- 使用 BGP Fullmesh 简化分析(无 overlay)
第四十三章 Calico 生产实践
本章介绍 Calico 在生产环境中的网络设计和最佳实践,包括数据中心网络拓扑、BGP 架构选型、AS 模式对比。
43.1 背景与概述
43.1.1 参考文档
[!IMPORTANT]
生产环境必读文档:
- Calico 官方文档:
Reference → Architecture → Calico IP Fabric- IETF RFC 7938:Use of BGP for Routing in Large-Scale Data Centers
建议:生产环境至少阅读 10 遍,理解每一句话的含义和技术依据。
43.1.2 数据中心网络演进
| 时代 | 架构 | 特点 |
|---|---|---|
| 传统 | 接入-汇聚-核心 | 生成树 + VRRP + BFD |
| 现代 | Spine-Leaf (Clos) | BGP 路由 + ECMP |
43.2 BGP 网络拓扑模式
Calico 支持三种主要的 BGP 网络拓扑模式:
43.2.1 模式对比
| 模式 | AS 分配 | 复杂度 | 适用场景 |
|---|---|---|---|
| AS Per Rack | 每机架一个 AS | 中 | 大型数据中心 |
| AS Per Node | 每节点一个 AS | 高 | 特殊需求 |
| Downward Default | 默认路由下发 | 低 | 推荐生产 |
43.3 AS Per Rack 模式
43.3.1 架构原理
每个机架(Rack)作为一个独立的自治系统(AS)。
43.3.2 工作原理
-
机架内部(iBGP):
- ToR 交换机作为 Route Reflector
- 计算节点与 ToR 建立 iBGP
- 同机架节点共享同一 AS 号
-
机架之间(eBGP):
- ToR 与 Spine 建立 eBGP
- 不同机架使用不同 AS 号
- Spine 交换机汇聚全网路由
43.3.3 优势与劣势
| 方面 | 优势 | 劣势 |
|---|---|---|
| 扩展性 | 机架级隔离 | 需要规划 AS 号 |
| 故障域 | 故障隔离在机架 | ToR 压力较大 |
| 管理 | 结构清晰 | 配置复杂 |
43.4 AS Per Node 模式
43.4.1 架构原理
每个计算节点作为一个独立的自治系统。
43.4.2 特点分析
优点:
- 每个节点完全独立
- 路由隔离彻底
缺点:
- 节点 BGP 压力大(Bird 进程)
- AS 号管理复杂
- 开源 BGP 实现不如商业设备稳定
[!WARNING]
不推荐在生产环境使用计算节点使用 Bird 提供 BGP 能力,其稳定性和性能不如商业路由设备。每个节点最多 200 个 Pod,路由条目有限,没必要每节点独立 AS。
43.5 Downward Default 模式(推荐)
43.5.1 架构原理
下级设备只向上级通告默认路由,全网路由只在 Spine 维护。
43.5.2 工作原理
路由通告方向(上行):
Node → ToR → Spine
每级只通告本级路由,汇聚后上报
默认路由方向(下行):
Spine → ToR → Node
每级下发默认路由,指向上级
43.5.3 优势分析
| 方面 | 说明 |
|---|---|
| 设备负载 | 专业设备做专业的事,Spine 处理复杂路由 |
| 节点压力 | 计算节点只维护少量路由,释放资源 |
| 配置简化 | 下级设备配置简单,默认路由即可 |
| 可扩展性 | Spine 交换机能力强,支持大规模路由表 |
[!TIP]
生产环境推荐使用 Downward Default 模式这种模式让专业设备处理复杂的 BGP 路由,计算节点专注于运行工作负载,是最合理的分工。
43.6 Route Reflector 配置策略
43.6.1 选择 Route Reflector
选择建议:
| 方案 | 适用场景 | 说明 |
|---|---|---|
| ToR 交换机 | 大型数据中心 | 性能最好,需交换机支持 |
| 专用 RR 节点 | 中型集群 | 灵活,需额外资源 |
| 控制节点 | 小型集群 | 简单,复用现有资源 |
43.6.2 Route Reflector 数量
# 推荐配置: 至少 2 个 RR 实现高可用
# 使用 calicoctl 配置 RR
apiVersion: projectcalico.org/v3
kind: BGPConfiguration
metadata:
name: default
spec:
nodeToNodeMeshEnabled: false # 禁用 Full Mesh
asNumber: 65001
43.7 AS 号规划
43.7.1 私有 AS 号范围
| 类型 | 范围 | 说明 |
|---|---|---|
| 2 字节私有 | 64512-65534 | 常用,1023 个 |
| 4 字节私有 | 4200000000-4294967294 | 扩展,约 9500 万个 |
43.7.2 规划示例
数据中心 AS 规划示例:
Spine 层:
- Spine-1: AS 65000
- Spine-2: AS 65000 (同 AS,iBGP)
Leaf/ToR 层:
- Rack-A: AS 65001
- Rack-B: AS 65002
- Rack-C: AS 65003
...
- Rack-N: AS 6500N
注意: 每个 Rack 使用不同 AS 号
43.8 生产环境选型建议
43.8.1 决策流程
43.8.2 模式选型建议
| 规模 | 推荐模式 | 理由 |
|---|---|---|
| 小型 (<50) | Full Mesh | 简单,无需额外配置 |
| 中型 (50-200) | AS Per Rack + RR | 平衡复杂度和扩展性 |
| 大型 (>200) | Downward Default | 专业设备处理路由 |
43.9 ContainerLab 实验
43.9.1 验证不同 BGP 架构
# 使用 ContainerLab 模拟 Spine-Leaf 架构
# 可以验证:
# 1. AS Per Rack 模式
# 2. AS Per Node 模式
# 3. Downward Default 模式
# 查看 BGP 邻居
calicoctl node status
# 查看路由表
ip route show
43.10 章节小结
[!TIP]
Calico 生产实践要点总结:
必读文档:
- Calico IP Fabric 设计文档
- RFC 7938 数据中心 BGP 路由
架构选型:
- Downward Default:推荐生产使用
- AS Per Rack:大型数据中心
- AS Per Node:不推荐
设计原则:
- 专业设备做专业的事
- Spine 处理复杂路由
- 计算节点专注工作负载
Route Reflector:
- 小型:控制节点兼任
- 中大型:ToR 或专用节点
- 至少 2 个实现高可用
验证测试:
- 使用 ContainerLab 模拟
- 理解每种模式的路由流向
- 生产上线前充分测试
第四十四章 Calico-IPAM 高级用法
本章介绍 Calico IPAM(IP Address Management)的高级配置,包括基于拓扑分配 IP、固定 IP、IP 池迁移、CNI 插件链等。
44.1 背景与概述
44.1.1 CNI 的两大核心组件
一个完备的 CNI 包含两大核心组件:
| 组件 | 职责 | 重要性 |
|---|---|---|
| IPAM | IP 地址分配和管理 | ⭐⭐⭐⭐⭐ |
| Network | 网络连通性 | ⭐⭐⭐⭐⭐ |
[!IMPORTANT]
两者并重:不要只关注网络通路,IPAM 在生产环境中同样重要!
44.1.2 Calico IPAM 类型
| 类型 | 说明 | 功能 |
|---|---|---|
| calico-ipam | Calico 原生 IPAM | 支持高级特性 |
| host-local | 主机本地 IPAM | 功能有限 |
44.2 基于拓扑分配 IP(Node Selector)
44.2.1 背景
在大型数据中心,需要根据物理拓扑(如机架/Rack)分配 IP 地址,便于管理和路由汇聚。
44.2.2 原理
通过 IPPool 的 nodeSelector 字段,限制 IP 池只能在特定节点上使用。
默认配置:
# 默认 IPPool - 所有节点
apiVersion: projectcalico.org/v3
kind: IPPool
metadata:
name: default-pool
spec:
cidr: 10.244.0.0/16
nodeSelector: all() # 所有节点都使用此池
44.2.3 实现步骤
步骤 1:为节点打标签
# 标记机架
kubectl label node node1 rack=rack-0
kubectl label node node2 rack=rack-0
kubectl label node node3 rack=rack-1
kubectl label node node4 rack=rack-1
步骤 2:创建基于拓扑的 IP 池
# Rack 0 的 IP 池
apiVersion: projectcalico.org/v3
kind: IPPool
metadata:
name: rack-0-pool
spec:
cidr: 192.168.0.0/24
ipipMode: Always
natOutgoing: true
nodeSelector: rack == "rack-0"
---
# Rack 1 的 IP 池
apiVersion: projectcalico.org/v3
kind: IPPool
metadata:
name: rack-1-pool
spec:
cidr: 192.168.1.0/24
ipipMode: Always
natOutgoing: true
nodeSelector: rack == "rack-1"
步骤 3:验证
# 创建测试 Pod
kubectl create deployment test --image=nginx --replicas=4
# 查看 Pod IP 分配
kubectl get pods -o wide
# 预期结果:
# node1/node2 上的 Pod: 192.168.0.x
# node3/node4 上的 Pod: 192.168.1.x
44.2.4 关键点
| 要点 | 说明 |
|---|---|
| 标签一致性 | 节点标签必须与 nodeSelector 匹配 |
| 路由汇聚 | 同机架 IP 相同网段,便于路由聚合 |
| 管理清晰 | 根据 IP 可快速定位物理位置 |
| 故障隔离 | 网段隔离有助于故障排查 |
44.3 固定 IP 地址(Static IP)
44.3.1 背景
某些应用(如数据库)需要固定 IP,即使 Pod 重建也保持相同 IP。
44.3.2 原理
通过 Pod 注解指定固定 IP 地址。
44.3.3 实现
创建带固定 IP 的 Pod:
apiVersion: v1
kind: Pod
metadata:
name: static-ip-pod
annotations:
cni.projectcalico.org/ipAddrs: '["192.168.0.100"]'
spec:
containers:
- name: nginx
image: nginx
验证:
kubectl get pod static-ip-pod -o wide
# NAME READY STATUS IP NODE
# static-ip-pod 1/1 Running 192.168.0.100 node1
44.3.4 固定 IP 的局限性
[!WARNING]
Pod 重建时的 IP 冲突问题当 Pod 重建时,旧 Pod 的 IP 可能尚未释放,导致新 Pod 无法获取相同 IP。
解决方案:使用 IP 池(多个 IP)
# 为有状态应用分配 IP 范围
apiVersion: projectcalico.org/v3
kind: IPPool
metadata:
name: db-pool
spec:
cidr: 192.168.100.0/28 # 16 个 IP
nodeSelector: app == "database"
[!TIP]
Spiderpool 项目DaoCloud 开源的 Spiderpool 项目对 Calico 的静态 IP 功能进行了增强,支持:
- IP 池绑定到 Deployment
- Pod 重建时自动复用 IP
- 双栈 IP 地址管理
44.4 IP 池迁移(Migration)
44.4.1 背景
当需要更换 Pod 网段时,需要从旧 IP 池迁移到新 IP 池。
44.4.2 原理
44.4.3 实现步骤
步骤 1:创建新 IP 池
apiVersion: projectcalico.org/v3
kind: IPPool
metadata:
name: new-pool
spec:
cidr: 10.0.0.0/16
ipipMode: Always
natOutgoing: true
步骤 2:禁用旧 IP 池
# 查看当前 IP 池
calicoctl get ippool -o yaml
# 编辑旧池,添加 disabled: true
calicoctl patch ippool old-pool -p '{"spec": {"disabled": true}}'
# 验证
calicoctl get ippool
# NAME CIDR SELECTOR DISABLED
# old-pool 10.244.0.0/16 all() true
# new-pool 10.0.0.0/16 all() false
步骤 3:重启 Pod
# 方法1: 滚动更新
kubectl rollout restart deployment <name>
# 方法2: 删除 Pod
kubectl delete pod --all -n <namespace>
# 方法3: 使用 drain 节点
kubectl drain <node> --ignore-daemonsets --delete-emptydir-data
步骤 4:验证新 IP
kubectl get pods -o wide
# Pod IP 现在应该是 10.0.x.x
44.4.4 关键点
| 要点 | 说明 |
|---|---|
| 不可回滚 | 迁移后旧 IP 池已禁用 |
| 需重启 Pod | 需要 Pod 重建才能获取新 IP |
| 类似 DHCP | 类似 DHCP Server 更换后重新获取 IP |
44.5 指定 IP 池(Specific Pool)
44.5.1 背景
在同一机架内,不同应用需要从不同 IP 池分配地址。
44.5.2 原理
通过 Pod 注解指定从哪个 IP 池分配地址。
44.5.3 实现
创建多个 IP 池:
# 数据库专用池
apiVersion: projectcalico.org/v3
kind: IPPool
metadata:
name: db-pool
spec:
cidr: 192.168.100.0/28
ipipMode: Always
natOutgoing: true
---
# 应用专用池
apiVersion: projectcalico.org/v3
kind: IPPool
metadata:
name: app-pool
spec:
cidr: 192.168.200.0/24
ipipMode: Always
natOutgoing: true
使用注解指定 IP 池:
apiVersion: v1
kind: Pod
metadata:
name: db-pod
annotations:
cni.projectcalico.org/ipv4pools: '["db-pool"]'
spec:
containers:
- name: mysql
image: mysql:8.0
---
apiVersion: v1
kind: Pod
metadata:
name: app-pod
annotations:
cni.projectcalico.org/ipv4pools: '["app-pool"]'
spec:
containers:
- name: nginx
image: nginx
44.5.4 与 Node Selector 的区别
| 功能 | Node Selector | 指定 IP 池 |
|---|---|---|
| 粒度 | 节点级别 | Pod 级别 |
| 配置位置 | IPPool | Pod 注解 |
| 用途 | 机架隔离 | 应用隔离 |
| 灵活性 | 较低 | 较高 |
44.6 CNI 插件链
44.6.1 背景
Calico 支持 CNI 插件链,可以组合多个插件实现更多功能。
44.6.2 插件链架构
44.6.3 常用辅助插件
| 插件 | 类型 | 功能 |
|---|---|---|
| portmap | 端口映射 | 支持 hostPort |
| bandwidth | 带宽限制 | QoS 流量控制 |
| sbr | 源路由 | Source Based Routing |
| tuning | 内核调优 | 设置 sysctl 参数 |
44.6.4 配置示例
查看 CNI 配置:
cat /etc/cni/net.d/10-calico.conflist
带插件链的配置:
{
"name": "k8s-pod-network",
"cniVersion": "0.3.1",
"plugins": [
{
"type": "calico",
"ipam": {
"type": "calico-ipam"
}
},
{
"type": "portmap",
"capabilities": {
"portMappings": true
}
},
{
"type": "bandwidth",
"capabilities": {
"bandwidth": true
}
},
{
"type": "tuning",
"sysctl": {
"net.core.somaxconn": "1024"
}
}
]
}
44.6.5 带宽限制示例
apiVersion: v1
kind: Pod
metadata:
name: bandwidth-limited-pod
annotations:
kubernetes.io/ingress-bandwidth: "10M"
kubernetes.io/egress-bandwidth: "10M"
spec:
containers:
- name: nginx
image: nginx
44.7 IPAM 配置参考
44.7.1 IPPool 完整字段
apiVersion: projectcalico.org/v3
kind: IPPool
metadata:
name: example-pool
spec:
# 必填字段
cidr: 192.168.0.0/24
# 可选字段
blockSize: 26 # 每节点分配的子网大小
ipipMode: Always # IPIP 模式: Always/CrossSubnet/Never
vxlanMode: Never # VxLAN 模式
natOutgoing: true # 出站 SNAT
disabled: false # 是否禁用
nodeSelector: all() # 节点选择器
allowedUses: # 允许的用途
- Workload
- Tunnel
44.7.2 常用注解
| 注解 | 用途 | 示例 |
|---|---|---|
cni.projectcalico.org/ipAddrs |
指定固定 IP | ["192.168.0.100"] |
cni.projectcalico.org/ipv4pools |
指定 IPv4 池 | ["my-pool"] |
cni.projectcalico.org/ipv6pools |
指定 IPv6 池 | ["my-ipv6-pool"] |
44.8 章节小结
[!TIP]
Calico IPAM 高级用法要点总结:
CNI 两大核心:
- IPAM:IP 地址管理
- Network:网络通路
基于拓扑分配(Node Selector):
- 节点打标签 + IPPool nodeSelector
- 同机架使用同网段,便于管理
固定 IP:
- 使用
cni.projectcalico.org/ipAddrs注解- 注意 Pod 重建时的 IP 冲突问题
IP 池迁移:
- 创建新池 → 禁用旧池 → 重启 Pod
- 类似 DHCP Server 更换
指定 IP 池:
- 使用
cni.projectcalico.org/ipv4pools注解- Pod 级别的精细控制
CNI 插件链:
- 多个插件组合使用
- 扩展功能:端口映射、带宽限制、内核调优
第四部分:Flannel-CNI
第四十五章 Flannel-TUN-TAP 精讲
本章深入讲解 Linux TUN/TAP 虚拟网络设备,这是理解 Flannel UDP 模式等网络方案的基础。
45.1 背景与概述
45.1.1 Flannel 简介
Flannel 是由 CoreOS 开源的轻量级 CNI 方案,适合快速部署 Kubernetes 网络。
资源获取:
| 资源 | 地址 |
|---|---|
| GitHub | https://github.com/flannel-io/flannel |
| Slack | Kubernetes Slack #flannel 频道 |
45.1.2 Flannel 与 Calico 的核心区别
| 特性 | Flannel | Calico |
|---|---|---|
| 同节点通信 | L2 交换(Bridge) | L3 路由 |
| Pod 网络 | 挂在交换机上 | 挂在路由器上 |
| Pod 掩码 | /24(同网段) | /32(独立主机路由) |
| 复杂度 | 简单 | 较复杂 |
45.2 TUN/TAP 设备原理
45.2.1 什么是 TUN/TAP 设备
TUN/TAP 是 Linux 内核提供的虚拟网络设备,用于在用户空间和内核空间之间传递网络数据。
45.2.2 TUN 与 TAP 的区别
| 特性 | TUN 设备 | TAP 设备 |
|---|---|---|
| 工作层次 | L3 网络层 | L2 数据链路层 |
| 处理数据 | IP 包(裸 IP) | 以太网帧(含 MAC) |
| MAC 地址 | 无 | 有 |
| 典型应用 | VPN、Flannel UDP | 虚拟机网桥、OpenVPN TAP |
| 抓包格式 | raw IP packet | 完整以太网帧 |
45.2.3 TUN/TAP 数据传输流程
数据流经过程说明:
- 应用发包:用户空间应用产生数据包
- 协议栈处理:经过 TCP/IP 协议栈
- 路由到 TUN:根据路由表转发到 TUN 设备
- 字符设备读取:数据写入
/dev/net/tun - 用户空间处理:flanneld 进程读取数据
- 封装转发:flanneld 封装后重新发送
- 物理发送:通过物理网卡发出
45.3 TUN/TAP 与内核模块的效率对比
45.3.1 效率差异
| 方式 | 数据路径 | 上下文切换 | 效率 |
|---|---|---|---|
| TUN/TAP | 用户空间 ↔ 内核空间 多次 | 多次 | 较低 |
| 内核模块 | 全程内核空间 | 无 | 较高 |
45.3.2 为什么 TUN/TAP 效率低
效率低的原因:
- 数据拷贝开销:内核 ↔ 用户空间多次拷贝
- 上下文切换:用户态/内核态切换消耗 CPU
- 中断处理:额外的中断开销
- 调度延迟:用户空间进程调度不确定
[!NOTE]
典型案例:OpenVPN 使用 TUN/TAP 设备,效率低于 WireGuard(内核模块实现)
45.4 Flannel 中的 TUN 设备实践
45.4.1 查看 TUN 设备
# 查看网络设备详情
ip -d link show flannel0
# 输出示例
# flannel0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1472 qdisc pfifo_fast
# link/none
# tun type tun pi off vnet_hdr off persist off
关键信息解读:
| 字段 | 含义 |
|---|---|
link/none |
无 MAC 地址(TUN 设备特征) |
tun type tun |
TUN 类型设备 |
POINTOPOINT |
点对点连接 |
45.4.2 查看 TUN/TAP 设备类型
# 列出所有 TUN/TAP 设备
ip tuntap list
# 输出示例
# flannel0: tun
45.4.3 查看设备关联进程
# 查看 TUN 设备关联的进程
ip -d tuntap show
# 输出示例
# flannel0: tun persist
# Attached to processes: flanneld(1220)
这说明:
flannel0设备一端连接 TCP/IP 协议栈- 另一端连接用户空间的
flanneld进程
45.4.4 验证进程
# 查看 flanneld 进程
ps aux | grep flanneld
# 输出示例
# root 1220 0.0 0.5 1234567 12345 ? Sl 10:00 0:01 /opt/bin/flanneld
# 查看网络连接
netstat -anp | grep flanneld
45.5 Flannel UDP 模式配置
45.5.1 配置示例
# ConfigMap 配置
apiVersion: v1
kind: ConfigMap
metadata:
name: kube-flannel-cfg
namespace: kube-flannel
data:
net-conf.json: |
{
"Network": "10.244.0.0/16",
"Backend": {
"Type": "udp"
}
}
45.5.2 挂载 TUN 设备
在非特权模式下,需要显式挂载 /dev/net/tun:
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: kube-flannel-ds
spec:
template:
spec:
containers:
- name: kube-flannel
volumeMounts:
- name: dev-net-tun
mountPath: /dev/net/tun
volumes:
- name: dev-net-tun
hostPath:
path: /dev/net/tun
[!WARNING]
非特权模式注意事项在非特权模式(
privileged: false)下:
- 必须显式挂载
/dev/net/tun- 需要添加
NET_ADMIN和NET_RAWcapabilities- 特权模式下无需额外配置
45.5.3 验证 UDP 模式
# 查看 flanneld 日志
kubectl logs -n kube-flannel -l app=flannel
# 应该看到
# Found network config - Backend type: udp
45.6 其他 TUN/TAP 应用场景
45.6.1 常见应用
| 应用 | 设备类型 | 用途 |
|---|---|---|
| OpenVPN | TUN/TAP | VPN 隧道 |
| Flannel UDP | TUN | 容器网络 |
| Calico VPP | TUN | 高性能转发 |
| 虚拟机网桥 | TAP | VM 网络 |
| WireGuard | 内核模块 | 高效 VPN |
45.6.2 TUN 设备在 Calico VPP 中的应用
45.7 章节小结
[!TIP]
TUN/TAP 设备要点总结:
设备类型:
- TUN:L3 网络层,处理 IP 包,无 MAC 地址
- TAP:L2 数据链路层,处理以太网帧,有 MAC 地址
工作原理:
- 一端连接 TCP/IP 协议栈(内核空间)
- 一端连接用户空间进程(如 flanneld)
- 通过
/dev/net/tun字符设备通信效率对比:
- TUN/TAP:用户空间处理,效率较低
- 内核模块(VxLAN):全程内核处理,效率较高
Flannel 配置:
- UDP 模式使用 TUN 设备
- 非特权模式需挂载
/dev/net/tun- 需要
NET_ADMIN和NET_RAW权限验证命令:
ip tuntap list:列出 TUN/TAP 设备ip -d tuntap show:查看设备关联进程
第四十六章 Flannel-UDP 模式
本章深入讲解 Flannel 的 UDP 封装模式,包括同节点和跨节点的 Pod 通信原理及数据包封装机制。
46.1 背景与概述
46.1.1 Flannel 后端类型
Flannel 支持多种后端(Backend)类型:
| 后端类型 | 封装方式 | 性能 | 适用场景 |
|---|---|---|---|
| UDP | TUN + UDP 封装 | 较低 | 通用/测试 |
| VxLAN | 内核 VxLAN | 较高 | 生产推荐 |
| host-gw | 主机路由 | 最高 | 同子网 |
| IPIP | IP-in-IP | 中等 | 跨子网 |
46.1.2 UDP 模式特点
UDP 模式核心特点:
- 使用 TUN 设备连接内核与用户空间
- flanneld 进程在用户空间处理封装
- 通过 UDP 协议封装原始 IP 包
- 监听端口:8285
46.2 同节点 Pod 通信
46.2.1 通信架构
同节点 Pod 通信通过 Linux Bridge 实现 L2 交换。
46.2.2 通信原理
路由表分析(Pod 内部):
# Pod 内查看路由
ip route
# 输出示例
10.244.2.0/24 dev eth0 scope link # 同网段走 L2
default via 10.244.2.1 dev eth0 # 跨网段走网关
关键点:
| 特征 | 说明 |
|---|---|
| 掩码 | /24(同网段) |
| 网关 | 0.0.0.0(无网关) |
| 通信层次 | L2 交换 |
| 设备 | cni0 Bridge |
46.2.3 Bridge MAC 地址学习
# 查看网卡所属 Bridge
ip -d link show veth-pod
# 输出示例
# veth-pod: ... master cni0 ...
# 查看 Bridge MAC 表
bridge fdb show dev cni0
# 输出示例
# 8e:b1:82:xx:xx:xx dev veth-podA master cni0
# 7a:c3:91:xx:xx:xx dev veth-podB master cni0
46.3 跨节点 Pod 通信
46.3.1 通信架构
46.3.2 路由表分析
# 宿主机路由表
ip route
# 输出示例
10.244.0.0/24 via 10.244.2.1 dev flannel0 # 目标网段走 flannel0
10.244.2.0/24 dev cni0 proto kernel # 本地网段走 cni0
路由匹配过程:
- 目标地址
10.244.0.27 - 匹配路由
10.244.0.0/24 via 10.244.2.1 dev flannel0 - 数据包发送到
flannel0TUN 设备
46.3.3 TUN 设备与 flanneld
# 查看 flannel0 设备
ip -d link show flannel0
# 输出示例
# flannel0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP>
# link/none
# tun type tun pi off
# 查看关联进程
ip -d tuntap show
# 输出示例
# flannel0: tun persist
# Attached to processes: flanneld(1220)
46.3.4 flanneld 工作原理
flanneld 职责:
| 职责 | 说明 |
|---|---|
| 监听 TUN | 从 flannel0 读取原始 IP 包 |
| 查询路由 | 从 API Server/etcd 获取 Pod 所在节点 |
| UDP 封装 | 将原始包封装为 UDP 数据 |
| 发送数据 | 通过 8285 端口发送到目标节点 |
46.4 数据包封装分析
46.4.1 封装结构
46.4.2 封装字段详解
| 层次 | 字段 | 值 |
|---|---|---|
| 外层 IP | Src IP | 源节点物理 IP |
| Dst IP | 目标节点物理 IP | |
| UDP | Src Port | 随机高端口 |
| Dst Port | 8285(flanneld) | |
| 内层(原始) | Src IP | 源 Pod IP |
| Dst IP | 目标 Pod IP | |
| Payload | 原始数据 |
46.4.3 抓包验证
# 在 flannel0 上抓包(裸 IP)
tcpdump -i flannel0 -nn
# 输出示例(raw IP,无 MAC)
# IP 10.244.2.3 > 10.244.0.27: ICMP echo request
# 在 eth0 上抓包(UDP 封装)
tcpdump -i eth0 -nn port 8285
# 输出示例
# IP 172.18.0.3.xxxxx > 172.18.0.2.8285: UDP, length xxx
Wireshark 解码技巧:
# 将 8285 端口的 UDP 数据解码为 IPv4
Analyze -> Decode As -> UDP port 8285 -> IPv4
46.5 MTU 设置
46.5.1 为什么 MTU 是 1472
MTU 计算:
| 组成 | 大小 |
|---|---|
| 物理网卡 MTU | 1500 bytes |
| - 外层 IP Header | 20 bytes |
| - UDP Header | 8 bytes |
| = flannel0 MTU | 1472 bytes |
[!WARNING]
MTU 不匹配问题如果内层 MTU 设置过大,会导致:
- 数据包分片
- 传输效率降低
- 可能出现通信失败
46.6 UDP 模式配置
46.6.1 ConfigMap 配置
apiVersion: v1
kind: ConfigMap
metadata:
name: kube-flannel-cfg
namespace: kube-flannel
data:
net-conf.json: |
{
"Network": "10.244.0.0/16",
"Backend": {
"Type": "udp"
}
}
46.6.2 DaemonSet 配置(非特权模式)
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: kube-flannel-ds
spec:
template:
spec:
containers:
- name: kube-flannel
securityContext:
privileged: false
capabilities:
add: ["NET_ADMIN", "NET_RAW"]
volumeMounts:
- name: dev-net-tun
mountPath: /dev/net/tun
volumes:
- name: dev-net-tun
hostPath:
path: /dev/net/tun
[!IMPORTANT]
非特权模式必须挂载 /dev/net/tun否则 flanneld 无法创建 flannel0 TUN 设备,Pod 会 CrashLoopBackOff
46.7 UDP 模式 vs VxLAN 模式
46.7.1 效率对比
46.7.2 对比表
| 特性 | UDP 模式 | VxLAN 模式 |
|---|---|---|
| 封装位置 | 用户空间(flanneld) | 内核空间 |
| 数据拷贝 | 多次(内核↔用户) | 少(内核内) |
| 上下文切换 | 有 | 无 |
| 性能 | 较低 | 较高 |
| 调试难度 | 较易 | 较难 |
| 生产推荐 | ❌ | ✅ |
46.8 章节小结
[!TIP]
Flannel UDP 模式要点总结:
同节点通信:
- 通过
cni0Linux Bridge 实现 L2 交换- Pod 使用 /24 掩码,在同一网段
- 无需封装,直接二层转发
跨节点通信:
- 数据包发送到
flannel0TUN 设备- flanneld 进程读取并 UDP 封装
- 通过 8285 端口发送到目标节点
封装机制:
- 外层:节点 IP + UDP 8285
- 内层:原始 Pod IP + 数据
- MTU 设置为 1472(1500-20-8)
配置要点:
- Backend Type 设置为
udp- 非特权模式需挂载
/dev/net/tun- 需要
NET_ADMIN和NET_RAW权限生产建议:
- UDP 模式效率低于 VxLAN
- 生产环境推荐使用 VxLAN 或 host-gw
第四十七章 Flannel-VxLAN 模式
本章深入讲解 Flannel 的 VxLAN 封装模式,包括配置方法、内核封装机制、ARP/FDB 表工作原理,以及 DirectRouting 优化模式。
47.1 背景与概述
47.1.1 VxLAN 模式定位
Flannel VxLAN 模式是生产环境推荐的后端类型:
47.1.2 与 Calico VxLAN 的关系
Flannel 官方明确表示 VxLAN 模式设计目标是支持从 Calico 迁移:
| 特性 | Flannel VxLAN | Calico VxLAN |
|---|---|---|
| 封装机制 | 相同 | 相同 |
| FDB 表 | 相同 | 相同 |
| VNI | 1 | 4096 |
| 端口 | 8472 | 4789 |
| 网络策略 | 不支持 | 支持 |
[!TIP]
生产建议
- 如需网络策略功能,使用 Calico
- 如追求简单稳定,使用 Flannel
- 可使用 Flannel + Calico 组合:Flannel 负责网络,Calico 负责策略
47.2 VxLAN 配置
47.2.1 ConfigMap 配置
apiVersion: v1
kind: ConfigMap
metadata:
name: kube-flannel-cfg
namespace: kube-flannel
data:
net-conf.json: |
{
"Network": "10.244.0.0/16",
"Backend": {
"Type": "vxlan"
}
}
47.2.2 验证 VxLAN 设备
# 查看 flannel.1 VxLAN 设备
ip -d link show flannel.1
# 输出示例
# flannel.1: <BROADCAST,MULTICAST,UP,LOWER_UP>
# link/ether 7c:a2:9a:xx:xx:xx brd ff:ff:ff:ff:ff:ff
# vxlan id 1 local 172.18.0.2 dev eth0 srcport 0 0 dstport 8472 ...
# 查看 FDB 表
bridge fdb show dev flannel.1
47.3 VxLAN 通信原理
47.3.1 通信架构
47.3.2 路由表分析
# 宿主机路由表
ip route
# 输出示例
10.244.0.0/24 dev cni0 proto kernel scope link # 本地网段
10.244.2.0/24 via 10.244.2.0 dev flannel.1 # 远端网段走 flannel.1
路由决策过程:
- Pod 发送数据包到
10.244.2.2 - 匹配路由
10.244.2.0/24 via 10.244.2.0 dev flannel.1 - 下一跳
10.244.2.0,出接口flannel.1
47.4 ARP 与 FDB 表机制
47.4.1 工作流程
47.4.2 ARP 表作用
# 查看 ARP 缓存
arp -n
# 或在 Pod 内查看
ip neigh
# 输出示例
10.244.2.0 dev flannel.1 lladdr 24:81:58:xx:xx:xx PERMANENT
ARP 表功能:
- 存储下一跳 IP 对应的 MAC 地址
- 用于封装内层以太网帧
47.4.3 FDB 表作用
# 查看 FDB 表
bridge fdb show dev flannel.1
# 输出示例
24:81:58:xx:xx:xx dev flannel.1 dst 172.18.0.4 self permanent
FDB 表功能:
| 作用 | 说明 |
|---|---|
| MAC → Node IP | 将目标 MAC 映射到节点 IP |
| 确定封装目标 | 外层 IP 的目标地址 |
| 由 flanneld 维护 | 通过 API Server 同步 |
47.5 内核空间封装优势
47.5.1 VxLAN vs UDP 模式
47.5.2 性能对比
| 指标 | VxLAN 模式 | UDP 模式 |
|---|---|---|
| 封装位置 | 内核空间 | 用户空间 |
| 上下文切换 | 无 | 有 |
| 数据拷贝 | 少 | 多 |
| CPU 开销 | 低 | 高 |
| 性能 | 高 | 低 |
| 端口 | 8472 | 8285 |
47.5.3 识别内核进程
# 查看 8472 端口
netstat -anp | grep 8472
# 输出示例
udp 0 0 0.0.0.0:8472 0.0.0.0:* -
# 注意: 进程名为 "-" 表示内核空间进程
# 用户空间进程会显示如: flanneld(1220)
47.6 抓包分析
47.6.1 抓包命令
# 在 eth0 上抓取 VxLAN 流量
tcpdump -i eth0 -nn port 8472 -w vxlan.pcap
# 测试跨节点通信
kubectl exec -it test-pod -- curl http://10.244.2.2:80
47.6.2 Wireshark 解码
# 将 8472 端口解码为 VxLAN
Analyze -> Decode As -> UDP port 8472 -> VxLAN
47.6.3 封装结构
47.7 DirectRouting 模式
47.7.1 概念说明
DirectRouting 是 VxLAN 模式的优化选项:
| 场景 | 通信方式 |
|---|---|
| 节点在同一二层 | 直接路由(无封装) |
| 节点跨二层 | VxLAN 封装 |
[!NOTE]
与 Calico CrossSubnet 相同DirectRouting 功能等同于 Calico 的
ipipMode: CrossSubnet或vxlanMode: CrossSubnet
47.7.2 配置方法
apiVersion: v1
kind: ConfigMap
metadata:
name: kube-flannel-cfg
namespace: kube-flannel
data:
net-conf.json: |
{
"Network": "10.244.0.0/16",
"Backend": {
"Type": "vxlan",
"DirectRouting": true
}
}
47.7.3 路由表变化
# 启用 DirectRouting 后的路由表
ip route
# 同二层节点 - 直接路由
10.244.1.0/24 via 10.1.5.11 dev eth0 # 下一跳是节点 IP
# 跨二层节点 - VxLAN 封装
10.244.2.0/24 via 10.244.2.0 dev flannel.1 # 走 flannel.1
47.7.4 拓扑示例
47.7.5 抓包验证
# 同二层通信 - 抓 eth0
tcpdump -i eth0 host 10.244.1.2 -w direct.pcap
# 结果: 无 VxLAN 封装,直接 IP 路由
# 跨二层通信 - 抓 flannel.1 或 eth0
tcpdump -i eth0 port 8472 -w vxlan.pcap
# 结果: VxLAN 封装
47.8 VxLAN MTU 设置
47.8.1 MTU 计算
| 组成 | 大小 |
|---|---|
| 物理网卡 MTU | 1500 bytes |
| - 外层 IP Header | 20 bytes |
| - UDP Header | 8 bytes |
| - VxLAN Header | 8 bytes |
| - 外层 MAC | 14 bytes |
| = flannel.1 MTU | 1450 bytes |
47.9 章节小结
[!TIP]
Flannel VxLAN 模式要点总结:
配置方式:
Backend.Type: "vxlan"- 可选
DirectRouting: true优化同二层通信核心机制:
- ARP 表:下一跳 IP → MAC 地址
- FDB 表:MAC 地址 → 目标节点 IP
- 内核封装:UDP 8472 端口
与 UDP 模式对比:
- VxLAN 在内核空间封装,效率更高
- 无上下文切换,无多次数据拷贝
- 端口 8472(vs UDP 的 8285)
DirectRouting 模式:
- 同二层节点:直接路由,不封装
- 跨二层节点:VxLAN 封装
- 等同于 Calico CrossSubnet
生产建议:
- VxLAN 是 Flannel 生产推荐模式
- 如需网络策略,可结合 Calico 使用
第四十八章 Flannel-IPIP 模式
本章深入讲解 Flannel 的 IPIP 封装模式,包括配置方法、裸 IP 设备特性、NOARP 标志原理,以及 DirectRouting 优化模式。
48.1 背景与概述
48.1.1 IPIP 模式定位
IPIP(IP-in-IP)是一种轻量级的隧道封装方式:
48.1.2 与 Calico IPIP 的关系
| 特性 | Flannel IPIP | Calico IPIP |
|---|---|---|
| 封装机制 | IP-in-IP | IP-in-IP |
| 默认模式 | 需手动配置 | 默认开启 |
| BGP 路由 | 不支持 | 支持 |
| 网络策略 | 不支持 | 支持 |
[!NOTE]
Flannel vs Calico IPIP
- Calico IPIP 模式结合了 BGP 路由宣告
- Flannel IPIP 仅做封装,路由由 flanneld 静态维护
48.2 IPIP 配置
48.2.1 ConfigMap 配置
apiVersion: v1
kind: ConfigMap
metadata:
name: kube-flannel-cfg
namespace: kube-flannel
data:
net-conf.json: |
{
"Network": "10.244.0.0/16",
"Backend": {
"Type": "ipip"
}
}
48.2.2 验证 IPIP 设备
# 查看 flannel.ipip 设备
ip -d link show flannel.ipip
# 输出示例
# flannel.ipip: <POINTOPOINT,NOARP,UP,LOWER_UP>
# link/ipip 172.18.0.3 brd 0.0.0.0
# ipip remote any local 172.18.0.3 ...
# 查看路由表
ip route | grep flannel.ipip
# 10.244.1.0/24 via 172.18.0.4 dev flannel.ipip
48.3 IPIP 通信原理
48.3.1 通信架构
48.3.2 路由表分析
# 宿主机路由表
ip route
# 输出示例
10.244.0.0/24 dev cni0 proto kernel scope link # 本地网段
10.244.1.0/24 via 172.18.0.4 dev flannel.ipip # 远端走 IPIP
路由决策过程:
- Pod 发送数据包到
10.244.1.2 - 匹配路由
10.244.1.0/24 via 172.18.0.4 dev flannel.ipip - 下一跳
172.18.0.4(目标节点),出接口flannel.ipip
48.4 裸 IP 设备特性
48.4.1 Raw 设备说明
flannel.ipip 是一个 裸 IP(Raw IP) 设备:
特点:
| 特性 | 普通网卡 (eth0) | Raw 设备 (flannel.ipip) |
|---|---|---|
| MAC 层 | 有 | 无 |
| 抓包显示 | 有 MAC 地址 | 无 MAC 地址 |
| tcpdump 类型 | LINUX_SLL | Raw |
48.4.2 抓包验证
# 在 flannel.ipip 上抓包 - 无 MAC 层
tcpdump -i flannel.ipip -nn
# 输出示例 - 只有 IP 信息,无 MAC
# IP 10.244.0.5 > 10.244.1.2: ICMP echo request
# 在 eth0 上抓包 - 有完整 MAC 层
tcpdump -i eth0 -nn ip proto 4
# 输出示例 - IPIP 封装
# IP 172.18.0.3 > 172.18.0.4: IP 10.244.0.5 > 10.244.1.2 ...
48.5 NOARP 标志详解
48.5.1 标志含义
# 查看设备标志
ip link show flannel.ipip
# 输出示例
# flannel.ipip: <POINTOPOINT,NOARP,UP,LOWER_UP>
| 标志 | 含义 |
|---|---|
| POINTOPOINT | 点对点设备 |
| NOARP | 禁用 ARP 协议 |
| UP | 设备启用 |
| LOWER_UP | 底层链路可用 |
48.5.2 为什么禁用 ARP
禁用 ARP 的场景:
- 裸 IP 设备:flannel.ipip 没有 MAC 层,ARP 无意义
- 避免 GARP 冲突:多节点配置相同隧道网段时,避免 GARP 告警
- 隧道封装:IPIP/GRE 隧道直接使用 IP 封装,不需要 MAC 解析
[!TIP]
NOARP 实际应用在 FE/BE(前端/后端)负载均衡架构中,BE 节点可能需要配置与 VIP 相同的地址用于响应。
此时需要设置 NOARP 避免 GARP 冲突告警。
48.6 封装结构对比
48.6.1 IPIP vs VxLAN
48.6.2 开销对比
| 封装方式 | 额外开销 | MTU (1500基础) |
|---|---|---|
| IPIP | 20 bytes | 1480 |
| VxLAN | 50 bytes | 1450 |
| host-gw | 0 bytes | 1500 |
48.7 DirectRouting 模式
48.7.1 配置方法
apiVersion: v1
kind: ConfigMap
metadata:
name: kube-flannel-cfg
namespace: kube-flannel
data:
net-conf.json: |
{
"Network": "10.244.0.0/16",
"Backend": {
"Type": "ipip",
"DirectRouting": true
}
}
48.7.2 工作原理
48.7.3 路由表变化
# 启用 DirectRouting 后的路由表
ip route
# 同二层节点 - 直接路由
10.244.1.0/24 via 10.1.5.11 dev eth0 # 下一跳是节点 IP,走 eth0
# 跨二层节点 - IPIP 封装
10.244.2.0/24 via 10.1.8.10 dev flannel.ipip # 走 flannel.ipip
48.7.4 抓包验证
# 同二层通信 - 抓 eth0,port 80
tcpdump -i eth0 port 80 -nn -w direct.pcap
# 结果: 普通 TCP 包,无 IPIP 封装
# 跨二层通信 - 抓 IPIP 协议
tcpdump -i eth0 -nn ip proto 4 -w ipip.pcap
# 结果: IPIP 封装包
48.8 抓包分析技巧
48.8.1 IPIP 协议过滤
# IPIP 协议号为 4
tcpdump -i eth0 -nn ip proto 4
# 或使用协议名
tcpdump -i eth0 -nn ipip
48.8.2 查看 HTTP 内容
# 使用 -X 或 -A 查看内容
tcpdump -i eth0 -nn -A port 80
# 输出示例
# GET / HTTP/1.1
# Host: 10.244.1.2
# ...
# HTTP/1.1 200 OK
# flannel-pod-name: xxx
48.9 章节小结
[!TIP]
Flannel IPIP 模式要点总结:
配置方式:
Backend.Type: "ipip"- 可选
DirectRouting: true优化同二层通信设备特性:
flannel.ipip是 Raw IP 裸设备- 无 MAC 层,抓包只有 IP 信息
- NOARP 标志禁用 ARP 协议
封装开销:
- 仅 20 bytes(外层 IP Header)
- 比 VxLAN 少 30 bytes
- MTU = 1500 - 20 = 1480
DirectRouting 模式:
- 同二层节点:直接路由,走 eth0
- 跨二层节点:IPIP 封装,走 flannel.ipip
- 等同于 Calico CrossSubnet
抓包技巧:
- IPIP 协议:
tcpdump ip proto 4- flannel.ipip 上只能看到裸 IP,无 MAC
第四十九章 Flannel-HostGW 模式
本章深入讲解 Flannel 的 Host-Gateway 模式,包括配置方法、纯路由通信原理、MAC 地址变化规律,以及为什么只能在同二层网络中使用。
49.1 背景与概述
49.1.1 HostGW 模式定位
Host-Gateway 是 Flannel 中性能最高的后端类型:
49.1.2 核心特点
| 特性 | 说明 |
|---|---|
| 封装方式 | 无封装 |
| MTU | 1500(无损耗) |
| 通信方式 | 纯三层路由 |
| 适用场景 | 同二层网络 |
| 性能 | 最高 |
49.2 HostGW 配置
49.2.1 ConfigMap 配置
apiVersion: v1
kind: ConfigMap
metadata:
name: kube-flannel-cfg
namespace: kube-flannel
data:
net-conf.json: |
{
"Network": "10.244.0.0/16",
"Backend": {
"Type": "host-gw"
}
}
49.2.2 路由表验证
# 查看宿主机路由表
ip route
# 输出示例
10.244.0.0/24 dev cni0 proto kernel scope link # 本地网段
10.244.1.0/24 via 172.18.0.4 dev eth0 # 远端走物理网卡
10.244.2.0/24 via 172.18.0.5 dev eth0 # 远端走物理网卡
关键观察:
- 远端网段的出接口是
eth0(物理网卡) - 下一跳是目标节点的物理 IP
- 没有
flannel.1、flannel.ipip等隧道设备
49.3 HostGW 通信原理
49.3.1 通信架构
49.3.2 路由核心原则
[!IMPORTANT]
纯路由转发的黄金法则在没有 NAT 和 Overlay 封装的路由网络中:
- IP 地址不变:源 IP 和目标 IP 全程不变
- MAC 地址变化:每经过一跳,MAC 地址都会改变
49.3.3 数据包变化过程
变化规律:
| 字段 | 变化情况 |
|---|---|
| 源 IP | ❌ 不变 |
| 目标 IP | ❌ 不变 |
| 源 MAC | ✅ 变为出接口 MAC |
| 目标 MAC | ✅ 变为下一跳 MAC |
49.4 为什么只能在同二层使用
49.4.1 同二层场景
同二层时:
- 节点之间可以直接通过 MAC 地址转发
- 只需要配置静态路由,无需封装
- 每个节点都知道如何到达其他节点
49.4.2 跨二层场景
跨二层时的问题:
| 问题 | 说明 |
|---|---|
| 路由器不知道 Pod 网段 | 中间路由器没有 10.244.x.0/24 的路由 |
| 无法转发 | 数据包到达路由器后会被丢弃或走默认路由 |
| 需要动态路由协议 | 必须使用 BGP 等协议宣告 Pod 网段 |
[!CAUTION]
host-gw 限制如果节点不在同一二层网络,host-gw 模式不能工作!
跨二层场景请使用:
- VxLAN 模式(推荐)
- IPIP 模式
- VxLAN/IPIP + DirectRouting 混合模式
49.5 抓包分析
49.5.1 抓包验证
# 在 eth0 上抓包
tcpdump -i eth0 -nn port 80
# 输出示例 - 无封装,直接是原始 IP 包
# 10.244.0.5.xxxxx > 10.244.1.2.80: Flags [S], ...
# 10.244.1.2.80 > 10.244.0.5.xxxxx: Flags [S.], ...
49.5.2 包结构对比
49.6 性能对比
49.6.1 各模式对比
| 模式 | 封装开销 | MTU | 性能 | 适用场景 |
|---|---|---|---|---|
| host-gw | 0 bytes | 1500 | ⭐⭐⭐ | 同二层 |
| IPIP | 20 bytes | 1480 | ⭐⭐ | 跨二层 |
| VxLAN | 50 bytes | 1450 | ⭐ | 跨二层 |
| UDP | 28 bytes | 1472 | ❌ | 不推荐 |
49.6.2 性能优势原因
49.7 与 Calico 对比
| 特性 | Flannel host-gw | Calico (无封装) |
|---|---|---|
| 路由方式 | 静态路由 | BGP 动态路由 |
| 跨子网支持 | ❌ 不支持 | ✅ 支持 |
| 网络策略 | ❌ 不支持 | ✅ 支持 |
| 复杂度 | 简单 | 较复杂 |
[!TIP]
选择建议
- 如果节点在同二层且不需要网络策略:Flannel host-gw
- 如果需要跨子网或网络策略:Calico
49.8 章节小结
[!TIP]
Flannel host-gw 模式要点总结:
配置方式:
Backend.Type: "host-gw"- 路由表直接指向目标节点 IP
核心原理:
- 无封装:直接三层路由转发
- IP 不变 MAC 变:每跳 MAC 地址更新
- 出接口是物理网卡
eth0使用限制:
- 只能在同二层网络使用
- 跨二层需要封装(VxLAN/IPIP)
- 或使用 BGP 宣告路由(Calico)
性能优势:
- MTU 1500,无损耗
- 零封装开销
- CPU 开销最小
适用场景:
- 所有节点在同一二层网络
- 追求最高网络性能
- 不需要跨子网通信
第五十章 Flannel-IPsec 模式
本章深入讲解 Flannel 的 IPsec 模式,包括预共享密钥(PSK)配置、ESP 封装原理、xfrm 状态与策略、Wireshark 解密技巧,以及与其他模式的对比。
50.1 背景与概述
50.1.1 IPsec 模式定位
IPsec(IP Security)是 Flannel 中提供加密传输的后端类型:
50.1.2 核心特点
| 特性 | 说明 |
|---|---|
| 加密协议 | ESP(Encapsulating Security Payload) |
| 密钥类型 | PSK(Pre-Shared Key) |
| 封装模式 | Tunnel Mode |
| 适用场景 | 跨不可信网络的安全通信 |
| 性能开销 | 加解密消耗 CPU |
50.2 IPsec 配置
50.2.1 生成 PSK
# 生成 96 位(12 字节)预共享密钥
dd if=/dev/urandom bs=12 count=1 2>/dev/null | base64
# 输出示例:iVzSdJHgXWNqpuJE
50.2.2 ConfigMap 配置
apiVersion: v1
kind: ConfigMap
metadata:
name: kube-flannel-cfg
namespace: kube-flannel
data:
net-conf.json: |
{
"Network": "10.244.0.0/16",
"Backend": {
"Type": "ipsec",
"PSK": "iVzSdJHgXWNqpuJE"
}
}
[!IMPORTANT]
PSK 配置要点
- PSK 是必选参数,至少 96 位(12 字节 Base64)
- 所有节点必须使用相同的 PSK
- 生产环境建议使用更长的密钥
50.3 IPsec 工作原理
50.3.1 IPsec Tunnel 模式架构
50.3.2 ESP 包结构
关键特点:
- 内层无 MAC:IPsec 是三层协议,封装时不包含内层 MAC
- 整体加密:内层 IP 包被完全加密
- 完整性校验:ESP 提供认证功能
50.4 xfrm 状态与策略
50.4.1 查看 xfrm 状态
# 查看 IPsec SA(Security Association)
ip xfrm state
# 输出示例
src 172.18.0.3 dst 172.18.0.4
proto esp spi 0x0000a1b2 reqid 1 mode tunnel
replay-window 0
auth-trunc hmac(sha256) 0x... 128
enc cbc(aes) 0x...
50.4.2 查看 xfrm 策略
# 查看 IPsec 策略
ip xfrm policy
# 输出示例
src 10.244.0.0/24 dst 10.244.1.0/24
dir out priority 100
tmpl src 172.18.0.3 dst 172.18.0.4
proto esp reqid 1 mode tunnel
50.4.3 xfrm 工作流程
[!TIP]
IPsec 不依赖路由表与 host-gw 不同,IPsec Tunnel 模式通过
xfrm policy决定转发,不需要在路由表中配置到远端 Pod 网段的路由。
50.5 抓包与解密
50.5.1 抓取 ESP 包
# 在 eth0 上抓取 ESP 协议包
tcpdump -i eth0 -nn esp -w ipsec.pcap
50.5.2 Wireshark 解密配置
解密步骤:
- 从
ip xfrm state获取 SPI、算法、密钥 - 在 Wireshark 中:
Edit → Preferences → Protocols → ESP - 添加 SA 条目:
- Source/Destination IP
- SPI 值
- 加密算法(如 AES-GCM-128)
- 密钥
50.6 与其他模式对比
50.6.1 封装方式对比
| 模式 | 封装 | 加密 | MTU | CPU 开销 |
|---|---|---|---|---|
| host-gw | 无 | ❌ | 1500 | 最低 |
| IPIP | IP-in-IP | ❌ | 1480 | 低 |
| VxLAN | UDP+VxLAN | ❌ | 1450 | 中 |
| IPsec | ESP+Tunnel | ✅ | ~1400 | 高 |
| WireGuard | UDP | ✅ | ~1420 | 中 |
50.6.2 IPsec vs WireGuard
| 特性 | IPsec | WireGuard |
|---|---|---|
| 复杂度 | 高 | 低 |
| 密钥管理 | PSK/证书 | 公钥 |
| 性能 | 中等 | 较高 |
| 内核集成 | 传统 | Linux 5.6+ 原生 |
50.7 生产注意事项
[!CAUTION]
IPsec 生产使用注意
- 性能开销:每个数据包都需要加解密,高流量场景下 CPU 消耗显著
- MTU 调整:ESP 封装会减少可用 MTU,注意调整应用配置
- 密钥安全:PSK 需要安全分发和存储
- 调试复杂:加密后的数据包难以直接分析
50.8 章节小结
[!TIP]
Flannel IPsec 模式要点总结:
配置方式:
Backend.Type: "ipsec"- PSK 是必选参数(至少 96 位)
核心原理:
- 使用 ESP Tunnel 模式封装和加密
- 通过 xfrm policy 决定转发(不依赖路由表)
- 三层协议,封装时不包含内层 MAC
xfrm 命令:
ip xfrm state:查看 SA(密钥、算法)ip xfrm policy:查看转发策略抓包解密:
- 抓包:
tcpdump esp- Wireshark 需配置 ESP SA 才能解密
适用场景:
- 跨不可信网络的安全通信
- 对数据传输有加密要求的场景
第五十一章 Flannel-WireGuard 模式
本章深入讲解 Flannel 的 WireGuard 模式,包括配置方法、公钥/私钥加密机制、flannel-wg 设备原理、peer 概念,以及与 IPsec 的对比。
51.1 背景与概述
51.1.1 WireGuard 模式定位
WireGuard 是 Flannel 中提供现代加密传输的后端类型:
51.1.2 核心特点
| 特性 | 说明 |
|---|---|
| 加密算法 | ChaCha20-Poly1305 |
| 密钥类型 | 公钥/私钥对 |
| 传输协议 | UDP |
| 默认端口 | 51820 |
| 内核集成 | Linux 5.6+ 原生支持 |
| 性能 | 优于 IPsec |
51.2 WireGuard 配置
51.2.1 ConfigMap 配置
apiVersion: v1
kind: ConfigMap
metadata:
name: kube-flannel-cfg
namespace: kube-flannel
data:
net-conf.json: |
{
"Network": "10.244.0.0/16",
"Backend": {
"Type": "wireguard"
}
}
[!TIP]
最简配置WireGuard 后端只需要指定
Type: "wireguard",无需像 IPsec 那样手动配置 PSK。密钥对由 Flannel 自动生成和分发。
51.2.2 可选参数
| 参数 | 类型 | 说明 |
|---|---|---|
| PSK | string | 可选的预共享密钥 |
| ListenPort | int | 监听端口(默认 51820) |
| MTU | int | 自动协商 |
51.3 WireGuard 工作原理
51.3.1 设备与路由
# 查看 WireGuard 设备
ip link show type wireguard
# 输出示例
5: flannel-wg: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1420 qdisc noqueue
link/none
# 路由表
ip route
# 输出示例
10.244.0.0/16 dev flannel-wg
51.3.2 通信架构
51.3.3 Peer 概念
Peer 配置说明:
| 字段 | 说明 |
|---|---|
| Public Key | 对端的公钥(用于加密) |
| Endpoint | 对端的 IP:Port |
| AllowedIPs | 允许通过此 Peer 的 Pod 网段 |
51.4 wg 命令详解
51.4.1 安装工具
# Debian/Ubuntu
apt-get install wireguard-tools
# CentOS/RHEL
yum install wireguard-tools
51.4.2 查看配置
# 查看 WireGuard 接口配置
wg show flannel-wg
# 输出示例
interface: flannel-wg
public key: ABC123...
private key: (hidden)
listening port: 51820
peer: XYZ789...
endpoint: 172.18.0.4:51820
allowed ips: 10.244.1.0/24
latest handshake: 5 seconds ago
transfer: 1.2 MiB received, 800 KiB sent
51.4.3 关键字段解读
51.5 加密机制
51.5.1 公钥/私钥
加密过程:
| 步骤 | 说明 |
|---|---|
| 1 | Node1 用 Node2 的公钥加密数据 |
| 2 | 数据通过网络传输 |
| 3 | Node2 用 自己的私钥解密数据 |
51.5.2 内核态处理
# 检查端口监听
ss -ulnp | grep 51820
# 输出示例
udp UNCONN 0 0 0.0.0.0:51820 0.0.0.0:*
users:(("kernel",pid=-))
[!IMPORTANT]
内核态进程WireGuard 是 in-kernel 实现,端口由内核直接监听(pid 显示为
-或kernel),无用户态进程,性能更高。
51.6 与其他模式对比
51.6.1 加密模式对比
| 特性 | IPsec | WireGuard |
|---|---|---|
| 配置复杂度 | 高(需手动 PSK) | 低(自动密钥) |
| 加密协议 | ESP (AES-GCM) | ChaCha20-Poly1305 |
| 内核支持 | 传统模块 | 5.6+ 原生 |
| 性能 | 中等 | 较高 |
| 代码行数 | ~400,000 | ~4,000 |
51.6.2 Flannel 后端总结
| 模式 | 封装 | 加密 | 推荐场景 |
|---|---|---|---|
| VxLAN | ✅ | ❌ | 通用场景 |
| host-gw | ❌ | ❌ | 同二层高性能 |
| IPIP | ✅ | ❌ | 轻量跨子网 |
| IPsec | ✅ | ✅ | 传统安全需求 |
| WireGuard | ✅ | ✅ | 现代安全需求 |
51.7 章节小结
[!TIP]
Flannel WireGuard 模式要点总结:
配置方式:
Backend.Type: "wireguard"- 密钥自动生成,无需手动配置 PSK
核心原理:
- 使用 公钥/私钥加密
- 通过 Peer 概念管理对端信息
- UDP 51820 端口传输
关键设备:
flannel-wg:WireGuard 虚拟接口- 内核态运行,无用户态进程
核心命令:
wg show flannel-wg:查看配置ip link show type wireguard:查看设备与 IPsec 对比:
- 配置更简单
- 性能更高
- Linux 5.6+ 原生支持
第五十二章 Multus 多网卡方案
本章深入讲解 Multus CNI 多网卡方案,包括核心架构、组件体系、NetworkAttachmentDefinition 配置、Pod 注解使用方式,以及典型应用场景。
52.1 背景与概述
52.1.1 为什么需要多网卡
传统 Pod 只有一个网卡(eth0),在以下场景存在局限:
多网卡需求场景:
| 场景 | 说明 |
|---|---|
| 转控分离 | 控制平面和数据平面使用不同网卡 |
| 高性能网络 | SR-IOV/DPDK 直通网卡 |
| 多租户隔离 | 不同业务使用不同网络 |
| 电信/金融 | NFV、流媒体处理 |
52.1.2 Multus 定位
[!IMPORTANT]
Multus 核心功能Multus CNI 是一个 Meta CNI,不直接提供网络功能,而是协调多个 CNI 插件为 Pod 添加多张网卡。
52.2 核心组件
52.2.1 组件架构
52.2.2 组件职责
| 组件 | 职责 | 说明 |
|---|---|---|
| Multus CNI | 多网卡编排 | Meta CNI,协调多个 CNI 插件 |
| SR-IOV Device Plugin | VF 资源管理 | 发现和通告 VF/PF 资源 |
| SR-IOV CNI | 网络通路搭建 | 构建 SR-IOV 网络连接 |
| WhereAbouts | 集群级 IPAM | 跨节点 IP 地址管理 |
52.2.3 SR-IOV 概念
| 术语 | 全称 | 说明 |
|---|---|---|
| PF | Physical Function | 物理网卡功能 |
| VF | Virtual Function | 虚拟化子网卡 |
52.3 NetworkAttachmentDefinition
52.3.1 NAD 概念
NetworkAttachmentDefinition(NAD)是 Multus 的核心 CRD:
apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
name: macvlan-conf
namespace: default
spec:
config: '{
"cniVersion": "0.3.1",
"type": "macvlan",
"master": "eth0",
"mode": "bridge",
"ipam": {
"type": "whereabouts",
"range": "192.168.1.0/24"
}
}'
52.3.2 NAD 结构解析
52.4 Pod 多网卡配置
52.4.1 注解方式
apiVersion: v1
kind: Pod
metadata:
name: multi-net-pod
annotations:
k8s.v1.cni.cncf.io/networks: macvlan-conf
spec:
containers:
- name: app
image: nginx
52.4.2 多网卡配置
apiVersion: v1
kind: Pod
metadata:
name: multi-net-pod
annotations:
# 添加多张网卡
k8s.v1.cni.cncf.io/networks: |
[
{"name": "macvlan-conf"},
{"name": "ipvlan-conf", "interface": "net2"}
]
spec:
containers:
- name: app
image: nginx
52.4.3 Pod 网络结构
52.5 安装部署
52.5.1 安装 Multus
# 克隆项目
git clone https://github.com/k8snetworkplumbingwg/multus-cni.git
cd multus-cni
# 安装
kubectl apply -f deployments/multus-daemonset.yml
52.5.2 安装 WhereAbouts
# 安装 WhereAbouts IPAM
kubectl apply -f https://raw.githubusercontent.com/k8snetworkplumbingwg/whereabouts/master/doc/crds/whereabouts.cni.cncf.io_ippools.yaml
kubectl apply -f https://raw.githubusercontent.com/k8snetworkplumbingwg/whereabouts/master/doc/crds/whereabouts.cni.cncf.io_overlappingrangeipreservations.yaml
52.5.3 验证安装
# 检查 Multus DaemonSet
kubectl get pods -n kube-system | grep multus
# 检查 CNI 插件
ls /opt/cni/bin/ | grep -E "macvlan|ipvlan|multus"
52.6 使用场景
52.6.1 常用后端 CNI
| CNI 类型 | 特点 | 适用场景 |
|---|---|---|
| macvlan | 虚拟 MAC 地址 | 裸机环境 |
| ipvlan | 共享 MAC 地址 | 云环境/OpenStack |
| SR-IOV | 硬件直通 | 高性能/DPDK |
| bridge | 软件桥接 | 通用场景 |
52.6.2 macvlan vs ipvlan
| 对比项 | macvlan | ipvlan |
|---|---|---|
| MAC 地址 | 每个子接口独立 MAC | 共享父接口 MAC |
| 云环境 | ❌ 可能被拦截 | ✅ 推荐 |
| 裸机环境 | ✅ 推荐 | ✅ 可用 |
| 性能 | 高 | 更高 |
52.7 章节小结
[!TIP]
Multus 多网卡方案要点总结:
核心概念:
- Multus 是 Meta CNI,协调多个 CNI 插件
- 为 Pod 添加除 eth0 外的额外网卡
核心组件:
- Multus CNI:多网卡编排
- SR-IOV Device Plugin:VF 资源管理
- WhereAbouts:集群级 IPAM
配置方式:
- 创建 NetworkAttachmentDefinition CRD
- Pod 通过 注解 引用 NAD
常用后端:
- macvlan:裸机环境
- ipvlan:云环境(共享 MAC)
- SR-IOV:高性能直通
典型场景:
- 转控分离(控制面/数据面隔离)
- 电信 NFV、金融高频交易
- 流媒体处理
第五十三章 Multus IPVLAN L2 模式
本章深入讲解 IPVLAN L2 模式的工作原理、配置方法、适用场景,以及与 MACVLAN 的区别。
53.1 背景与概述
53.1.1 IPVLAN 简介
IPVLAN 是一种网卡复用技术,区别于 MACVLAN 的核心特点是:子接口与父接口共享同一 MAC 地址。
[!IMPORTANT]
IPVLAN 核心特性
- 子接口与父接口 MAC 地址相同
- 不同子接口通过 IP 地址 区分
- 内核要求:Linux 4.18+
53.1.2 IPVLAN 命名由来
| 类型 | 区分方式 | MAC 地址 | IP 地址 |
|---|---|---|---|
| IPVLAN | IP 区分 | 相同 | 不同 |
| MACVLAN | MAC 区分 | 不同 | 不同 |
53.1.3 云环境适配
IPVLAN 在 公有云/OpenStack 环境中特别适用:
[!NOTE]
云环境兼容性OpenStack 默认只允许与端口 MAC 匹配的流量通过。IPVLAN 由于 MAC 相同,无需额外配置即可正常工作。MACVLAN 则需要设置
allow_address_pair为0.0.0.0/0。
53.2 L2 模式原理
53.2.1 L2 模式工作方式
在 L2 模式下,父接口相当于一个 虚拟交换机:
53.2.2 L2 vs L3 模式
| 对比项 | L2 模式 | L3 模式 |
|---|---|---|
| 工作层级 | 二层(交换) | 三层(路由) |
| 父接口角色 | 虚拟交换机 | 虚拟路由器 |
| 广播域 | 共享 | 隔离 |
| 适用场景 | 同子网通信 | 跨子网通信 |
53.3 配置实现
53.3.1 NetworkAttachmentDefinition
创建 IPVLAN L2 模式的 NAD:
apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
name: ipvlan-l2-whereabouts-conf
spec:
config: '{
"cniVersion": "0.3.1",
"name": "ipvlan-l2-whereabouts",
"type": "ipvlan",
"master": "eth0",
"mode": "l2",
"ipam": {
"type": "whereabouts",
"range": "172.18.0.200/24",
"range_start": "172.18.0.200",
"range_end": "172.18.0.205"
}
}'
53.3.2 NAD 配置解析
53.3.3 Pod 配置
apiVersion: v1
kind: Pod
metadata:
name: ipvlan-pod1
annotations:
k8s.v1.cni.cncf.io/networks: ipvlan-l2-whereabouts-conf@eth1
spec:
containers:
- name: app
image: busybox
command: ["sleep", "3600"]
53.3.4 注解语法
| 语法 | 说明 |
|---|---|
nad-name |
使用默认接口名(net0, net1...) |
nad-name@eth1 |
指定接口名为 eth1 |
53.4 通信验证
53.4.1 验证 MAC 地址
# 查看节点 eth0 的 MAC
ip link show eth0
# 输出: link/ether 02:42:ac:12:00:02
# 进入 Pod 查看 eth1 的 MAC
kubectl exec ipvlan-pod1 -- ip link show eth1
# 输出: link/ether 02:42:ac:12:00:02 (与父接口相同)
53.4.2 同节点通信
# Pod1 ping Pod2 (同节点)
kubectl exec ipvlan-pod1 -- ping -I eth1 172.18.0.201
53.4.3 跨节点通信
# Pod1 ping Pod3 (跨节点)
kubectl exec ipvlan-pod1 -- ping -I eth1 172.18.0.202
[!WARNING]
多网卡环境下的 ping 命令在多网卡环境中,务必使用
-I <interface>指定源接口,否则可能走错网络路径。
53.4.4 ARP 表验证
# 查看 ARP 表
kubectl exec ipvlan-pod1 -- arp -n
# 同节点 Pod:MAC 相同
# 172.18.0.201 02:42:ac:12:00:02
# 跨节点 Pod:MAC 为对端节点的父接口
# 172.18.0.202 02:42:ac:12:00:03
53.5 IPVLAN vs MACVLAN
53.5.1 对比总结
| 对比项 | MACVLAN | IPVLAN |
|---|---|---|
| MAC 地址 | 子接口独立 | 子接口相同 |
| 云环境 | 可能被拦截 | ✅ 兼容 |
| 适用场景 | 裸机环境 | 云/虚拟化环境 |
| 内核要求 | 3.x+ | 4.18+ |
| 区分方式 | MAC | IP |
53.6 章节小结
[!TIP]
IPVLAN L2 模式要点总结:
核心特性:
- 子接口与父接口 MAC 地址相同
- 通过 IP 地址 区分不同子接口
适用场景:
- 云环境(OpenStack、公有云)
- 避免 MAC 地址被安全策略拦截
L2 模式:
- 父接口充当 虚拟交换机
- 子接口在同一广播域
配置要点:
type: ipvlanmode: l2master: eth0(父接口)注意事项:
- 多网卡环境下 ping 需指定
-I <interface>- 内核版本要求 Linux 4.18+
第五十四章 Multus IPVLAN L3 模式
本章讲解 IPVLAN L3 模式的工作原理、配置方法、回程路由设置,以及与 L2 模式的区别。
54.1 背景与概述
54.1.1 L3 模式简介
在 L3 模式下,父接口充当 虚拟路由器,子接口可以配置 不同子网 的 IP 地址。
[!IMPORTANT]
L3 模式核心特性
- 父接口充当 路由器(L2 模式是交换机)
- 子接口可配置 不同子网 的地址
- 需要添加 回程路由 实现跨子网通信
54.1.2 L2 vs L3 模式对比
| 对比项 | L2 模式 | L3 模式 |
|---|---|---|
| 父接口角色 | 虚拟交换机 | 虚拟路由器 |
| 子接口子网 | 必须相同 | 可以不同 |
| 通信方式 | 二层交换 | 三层路由 |
| 广播域 | 共享 | 隔离 |
| 路由配置 | 无需 | 需要回程路由 |
54.2 L3 模式原理
54.2.1 工作方式
54.2.2 路由查找过程
54.2.3 无网关路由
L3 模式的一个关键特性是 无网关路由(仅指定出接口):
# 传统路由(带网关)
ip route add 15.1.2.0/24 via 15.1.1.1 dev eth1
# 无网关路由(仅出接口)
ip route add 15.1.2.0/24 dev eth1
[!NOTE]
无网关路由原理当出接口直连路由器时,只需指定出接口,无需指定下一跳网关。数据包从出接口发出后,直接到达路由器进行转发。
54.3 配置实现
54.3.1 NetworkAttachmentDefinition
创建 IPVLAN L3 模式的 NAD:
apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
name: ipvlan-l3-conf-1
spec:
config: '{
"cniVersion": "0.3.1",
"name": "ipvlan-l3-net1",
"type": "ipvlan",
"master": "eth0",
"mode": "l3",
"ipam": {
"type": "whereabouts",
"range": "15.1.1.0/24",
"range_start": "15.1.1.10",
"range_end": "15.1.1.20"
}
}'
---
apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
name: ipvlan-l3-conf-2
spec:
config: '{
"cniVersion": "0.3.1",
"name": "ipvlan-l3-net2",
"type": "ipvlan",
"master": "eth0",
"mode": "l3",
"ipam": {
"type": "whereabouts",
"range": "15.1.2.0/24",
"range_start": "15.1.2.10",
"range_end": "15.1.2.20"
}
}'
54.3.2 配置要点
| 配置项 | 说明 |
|---|---|
mode: l3 |
启用 L3 路由模式 |
| 不同 NAD | 配置不同子网 |
| 同一 master | 共享父接口 |
54.3.3 Pod 配置
apiVersion: v1
kind: Pod
metadata:
name: ipvlan-l3-pod1
annotations:
k8s.v1.cni.cncf.io/networks: ipvlan-l3-conf-1@eth1
spec:
nodeName: node1 # 确保同节点
containers:
- name: app
image: busybox
command: ["sleep", "3600"]
---
apiVersion: v1
kind: Pod
metadata:
name: ipvlan-l3-pod2
annotations:
k8s.v1.cni.cncf.io/networks: ipvlan-l3-conf-2@eth1
spec:
nodeName: node1 # 确保同节点
containers:
- name: app
image: busybox
command: ["sleep", "3600"]
54.4 回程路由配置
54.4.1 为什么需要回程路由
默认情况下,Pod 只知道自己所在子网的路由:
# Pod1 (15.1.1.10) 默认路由表
15.1.1.0/24 dev eth1 # 只知道本子网
# Pod2 (15.1.2.20) 默认路由表
15.1.2.0/24 dev eth1 # 只知道本子网
Pod1 无法直接访问 Pod2,因为没有去往 15.1.2.0/24 的路由。
54.4.2 添加回程路由
# 在 Pod1 中添加去往 Pod2 子网的路由
kubectl exec ipvlan-l3-pod1 -- ip route add 15.1.2.0/24 dev eth1
# 在 Pod2 中添加去往 Pod1 子网的路由
kubectl exec ipvlan-l3-pod2 -- ip route add 15.1.1.0/24 dev eth1
54.4.3 验证通信
# Pod1 ping Pod2(需指定源接口)
kubectl exec ipvlan-l3-pod1 -- ping -I eth1 15.1.2.20
[!WARNING]
关键注意事项
- 必须使用
-I eth1指定源接口- L3 模式需要 手动添加回程路由
- 同节点 才可通过回程路由互通
54.5 同节点 vs 跨节点
54.5.1 限制说明
[!CAUTION]
跨节点限制L3 模式下,不同节点的 Pod 无法直接通过回程路由通信,因为:
- 每个节点的父接口是独立的路由器
- 没有统一的路由平面
如需跨节点通信,需要额外的 Underlay 网络 或 BGP 路由宣告。
54.5.2 适用场景
| 场景 | 是否支持 |
|---|---|
| 同节点不同子网 Pod | ✅ 支持(需回程路由) |
| 跨节点不同子网 Pod | ❌ 不支持(需额外配置) |
| 同节点同子网 Pod | ✅ 支持(使用 L2 模式更佳) |
54.6 章节小结
[!TIP]
IPVLAN L3 模式要点总结:
核心原理:
- 父接口充当 虚拟路由器
- 子接口可配置 不同子网 IP
回程路由:
- 必须手动添加
ip route add <对端子网> dev eth1- 无需指定网关,仅需出接口
限制:
- 仅 同节点 Pod 可通过回程路由通信
- 跨节点需要额外网络配置
与 L2 对比:
- L2:交换机,同子网
- L3:路由器,跨子网
第五十五章 Multus IPVLAN SBR 模式
本章讲解 IPVLAN 的 SBR(Source-Based Routing,源地址路由)模式,实现多网卡同时访问外网的场景。
55.1 背景与概述
55.1.1 什么是 SBR
SBR(Source-Based Routing) 是一种基于 源地址 进行路由决策的技术,也称为"原地路由"或"策略路由"。
[!IMPORTANT]
SBR 核心价值
- 允许 Pod 拥有 多张网卡同时访问外网
- 根据 源 IP 决定出口路由
- 适用于 多网络接入 场景
55.1.2 应用场景
| 场景 | 说明 |
|---|---|
| 多网卡上网 | 不同网卡走不同出口 |
| 网络隔离 | 内网/外网分离访问 |
| 流量分流 | 按源地址分配带宽 |
55.2 SBR 原理
55.2.1 核心组件
SBR 依赖 Linux 的 策略路由 机制,包含两个关键组件:
| 组件 | 命令 | 作用 |
|---|---|---|
| ip rule | ip rule add from <src> table <n> |
定义策略:源地址 → 路由表 |
| ip route table | ip route add default via <gw> table <n> |
定义路由表内容 |
55.2.2 工作流程
55.2.3 优先级机制
[!NOTE]
SBR 优先级更高当配置了 SBR 后,即使目的地址在 同一子网,也会优先走 SBR 指定的网关,而非直接二层通信。
55.3 配置实现
55.3.1 手动配置 SBR
在 Pod 内手动添加 SBR 规则:
# 1. 添加路由表(table 100)
ip route add default via 172.18.0.1 dev eth1 table 100
# 2. 添加路由策略(从 172.18.0.0/24 来的包走 table 100)
ip rule add from 172.18.0.0/24 table 100
验证配置:
# 查看路由策略
ip rule show
# 输出: from 172.18.0.0/24 lookup 100
# 查看路由表
ip route show table 100
# 输出: default via 172.18.0.1 dev eth1
55.3.2 NAD 自动配置 SBR
通过 NetworkAttachmentDefinition 自动配置 SBR:
apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
name: ipvlan-sbr
spec:
config: '{
"cniVersion": "0.3.1",
"name": "ipvlan-sbr-net",
"type": "ipvlan",
"master": "eth0",
"mode": "l2",
"ipam": {
"type": "whereabouts",
"range": "172.18.0.0/24"
},
"plugins": [
{
"type": "sbr"
}
]
}'
55.3.3 配置对比
| 方式 | 优点 | 缺点 |
|---|---|---|
| 手动配置 | 灵活可控 | 需要 init 容器或手动执行 |
| NAD 自动 | 自动化,无需干预 | 需要 SBR CNI 插件支持 |
55.4 多网卡上外网验证
55.4.1 场景说明
55.4.2 验证步骤
# 1. 从 eth0 访问外网(默认路由)
ping 114.114.114.114
# 2. 从 eth1 访问外网(SBR 路由)
ping -I 172.18.0.200 114.114.114.114
# 3. 抓包验证
tcpdump -i eth1 icmp
[!TIP]
指定源地址的重要性使用
ping -I <IP地址>而非ping -I <接口名>,确保 SBR 规则正确匹配。
55.4.3 抓包分析
# 在 eth1 上抓包
tcpdump -i eth1 -n icmp
# 预期输出(走 SBR):
# 172.18.0.200 > 114.114.114.114: ICMP echo request
# 114.114.114.114 > 172.18.0.200: ICMP echo reply
55.5 SBR 与 ARP 缓存交互
55.5.1 首包行为
55.5.2 后续包行为
当 ARP 缓存存在后,行为可能改变:
| 情况 | 首包 | 后续包 |
|---|---|---|
| 无 ARP 缓存 | 走 SBR → 网关 | 走 SBR → 网关 |
| 有 ARP 缓存 | 走 SBR → 网关 | 可能直接二层(ARP 优先级更高) |
[!WARNING]
ARP 缓存影响当 Pod 学习到对端的真实 MAC 地址后,后续包可能 绕过 SBR 直接走二层。这是因为:
- 本地 ARP 缓存优先级高于 SBR
- 已知 MAC 时无需查询路由策略
清除 ARP 缓存验证:
ip neigh del 172.18.0.201 dev eth1
55.5.3 双端 SBR 配置
当两端都配置 SBR 时:
两端都会经过网关,确保 SBR 策略生效。
55.6 章节小结
[!TIP]
IPVLAN SBR 模式要点总结:
SBR 机制:
ip rule:定义源地址 → 路由表映射ip route table:定义路由表内容配置方式:
- 手动:
ip route add+ip rule add- 自动:NAD 中
plugins: [{"type": "sbr"}]验证方法:
ping -I <源IP>指定源地址tcpdump -i eth1抓包确认注意事项:
- SBR 优先级高于普通路由
- ARP 缓存可能影响后续包路径
- 双端配置 SBR 可确保策略生效
第五十六章 IPVLAN-SBR 深度解析
本章深入分析 SBR 的底层行为机制,包括首包处理、ICMP Redirect、单边与双边 SBR 差异,以及典型应用场景。
56.1 背景与问题
56.1.1 上章回顾
上一章介绍了 SBR 的基本配置和使用,但在实际抓包分析中发现了一些 意外行为:
- 首包发往网关,但后续包可能直接二层通信
- 存在 ICMP Redirect 消息
- 不同网络环境行为不同
56.1.2 本章目标
56.2 SBR 首包行为分析
56.2.1 首包封装过程
56.2.2 为什么首包走网关
| 阶段 | 说明 |
|---|---|
| 无 ARP 缓存 | Pod1 不知道 Pod2 的 MAC |
| SBR 规则生效 | 指向 via 172.18.0.1 |
| ARP 请求网关 | 而非请求 Pod2 |
| 封装网关 MAC | dst_mac = 网关 MAC |
[!NOTE]
关键点SBR 规则指定了下一跳网关,因此 Pod1 会 ARP 解析网关的 MAC,而非目的 Pod 的 MAC。
56.2.3 抓包验证
# Pod1 发送首包
tcpdump -i eth1 -en
# 首包内容:
# src_mac: 12:00:00:02 (Pod1)
# dst_mac: dc:cd:40:xx (网关)
# src_ip: 172.18.0.200
# dst_ip: 172.18.0.201
56.3 ICMP Redirect 机制
56.3.1 什么是 ICMP Redirect
ICMP Redirect(重定向)是网关发送的一种通知消息:
"你发给我的包,目的地和你在同一网段,你应该直接发给它,不需要经过我。"
56.3.2 Redirect 消息格式
ICMP Type: 5 (Redirect)
Code: 1 (Redirect for Host)
Message: Redirect to 172.18.0.201
56.3.3 Redirect 触发条件
| 条件 | 说明 |
|---|---|
| 源和目的同子网 | 172.18.0.200 ↔ 172.18.0.201 |
| 包经过网关 | 网关发现"绕路" |
| 网关启用 Redirect | Linux Bridge 默认启用 |
[!WARNING]
ICMP Redirect 对 SBR 的影响收到 Redirect 后,Pod 可能 绕过 SBR 策略,直接二层通信。这可能不是期望的行为!
56.4 单边 vs 双边 SBR
56.4.1 单边 SBR(仅 Pod1 配置)
行为特点:
- 首包:Pod1 → 网关 → Pod2
- 回包:Pod2 直接发给 Pod1(无 SBR)
- 后续:可能绕过 SBR
56.4.2 双边 SBR(两端都配置)
行为特点:
- 首包:双方都走网关
- 两次 Redirect:各触发一次
- 后续:恢复直接通信
56.4.3 对比总结
| 场景 | 首包路径 | Redirect 次数 | 后续包路径 |
|---|---|---|---|
| 单边 SBR | Pod1→GW→Pod2 | 1 次 | 可能直接二层 |
| 双边 SBR | 双方都走网关 | 2 次 | 恢复直接通信 |
56.5 Linux Bridge vs 真实交换机
56.5.1 行为差异
56.5.2 差异对比
| 特性 | Linux Bridge | 真实交换机(H3C) |
|---|---|---|
| ICMP Redirect | ✅ 启用 | ❌ 通常不发 |
| SBR 首包 | 走网关 | 走网关 |
| SBR 后续包 | 可能绕过 | 始终走网关 |
| 行为一致性 | 可能变化 | 稳定可预期 |
[!CAUTION]
生产环境注意Linux Bridge 环境下的 SBR 行为可能与真实交换机不同。测试时需要在 目标生产环境 中验证!
56.6 SBR 典型应用场景
56.6.1 多网卡多网关
56.6.2 问题:多网关冲突
场景:一台机器有两张网卡,只能配置一个默认路由。
# 默认路由只能有一个出接口
ip route add default via 192.168.0.1 dev eth1
# SSH 从 eth0 进来,回包却走 eth1
# 导致:非对称路由,连接失败!
56.6.3 SBR 解决方案
# 1. eth0(管理网)使用 SBR
ip route add default via 10.0.0.1 dev eth0 table 100
ip rule add from 10.0.0.0/24 table 100
# 2. eth1(业务网)使用默认路由
ip route add default via 192.168.0.1 dev eth1
56.6.4 典型使用场景
| 场景 | eth0 (管理网) | eth1 (业务网) |
|---|---|---|
| 运维管理 | SSH、监控 | - |
| 业务流量 | - | 应用数据 |
| 告警上报 | SBR 路由 | - |
| 外网访问 | - | 默认路由 |
56.7 章节小结
[!TIP]
IPVLAN-SBR 深度解析要点总结:
首包行为:
- SBR 优先于普通路由
- 首包 ARP 解析网关 MAC
ICMP Redirect:
- 网关发现同子网绕路时发送
- 可能导致后续包绕过 SBR
单边 vs 双边:
- 单边 SBR:1 次 Redirect
- 双边 SBR:2 次 Redirect
环境差异:
- Linux Bridge:有 Redirect
- 真实交换机:通常无 Redirect
应用场景:
- 多网卡多网关
- SSH 管理 + 业务分离
- 解决非对称路由问题
第五十七章 MACVLAN-SBR 实践
本章介绍 MACVLAN 与 SBR 结合的实践配置,包括 MACVLAN 与 IPVLAN 的差异、NAD 配置方法、以及生产级多网卡环境的搭建。
57.1 背景与概述
57.1.1 MACVLAN vs IPVLAN 核心差异
| 特性 | MACVLAN | IPVLAN |
|---|---|---|
| MAC 地址 | 各不相同 | 共享父接口 MAC |
| 内核支持 | 3.x 早期版本 | 4.x+ |
| 理解难度 | 简单(传统模式) | 需理解共享 MAC |
| 公有云兼容 | 可能受限(MAC 检查) | 更友好 |
| 典型场景 | 传统网卡复用 | 云原生环境 |
[!NOTE]
关键区别
- MACVLAN:每个子接口有 独立的 MAC 地址
- IPVLAN:所有子接口 共享父接口的 MAC 地址
57.1.2 MACVLAN 工作模式
| 模式 | 说明 | 使用场景 |
|---|---|---|
| bridge | 子接口间可直接通信(最常用) | 生产环境默认 |
| private | 子接口间完全隔离 | 安全隔离 |
| vepa | 流量必须经过外部交换机 | 硬件卸载 |
| passthrough | 直通模式 | SR-IOV |
57.2 基础 MACVLAN 配置
57.2.1 NAD 配置示例
apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
name: macvlan-basic
spec:
config: '{
"cniVersion": "0.3.1",
"name": "macvlan-net",
"type": "macvlan",
"master": "eth0",
"mode": "bridge",
"ipam": {
"type": "whereabouts",
"range": "15.15.1.0/24"
}
}'
57.2.2 配置要点
| 参数 | 说明 |
|---|---|
| type | macvlan - 网卡复用类型 |
| master | 父接口名称(复用哪张网卡) |
| mode | 工作模式,通常用 bridge |
| ipam | IP 地址管理,通常用 whereabouts |
57.3 MACVLAN 基础验证
57.3.1 创建测试 Pod
apiVersion: v1
kind: Pod
metadata:
name: macvlan-pod1
annotations:
k8s.v1.cni.cncf.io/networks: macvlan-basic
spec:
containers:
- name: test
image: nicolaka/netshoot
command: ["sleep", "infinity"]
57.3.2 验证网络
# 查看 Pod 网卡
kubectl exec macvlan-pod1 -- ip a
# 输出示例:
# eth1: <BROADCAST,MULTICAST,UP>
# link/ether 2a:b7:78:7c:xx:xx
# inet 15.15.1.20/24
# MACVLAN 的 MAC 地址与父接口不同!
57.3.3 同网段通信验证
[!TIP]
MACVLAN 同网段通信MACVLAN 子接口之间的通信走 二层直连,因为 MAC 地址不同,可以正常 ARP 解析。
57.4 MACVLAN + SBR 高级配置
57.4.1 为什么需要 SBR
问题:MACVLAN 接口默认没有默认路由,无法访问外网。
解决:使用 SBR 为 MACVLAN 接口添加专属路由表。
57.4.2 NAD + SBR 插件配置
apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
name: macvlan-sbr
spec:
config: '{
"cniVersion": "0.3.1",
"name": "macvlan-sbr-net",
"plugins": [
{
"type": "macvlan",
"master": "eth1",
"mode": "bridge",
"ipam": {
"type": "whereabouts",
"range": "15.15.1.0/24",
"gateway": "15.15.1.1",
"routes": [
{"dst": "0.0.0.0/0"}
]
}
},
{
"type": "sbr"
}
]
}'
57.4.3 plugins 链式配置
| 插件 | 作用 |
|---|---|
| macvlan | 创建 MACVLAN 子接口 |
| sbr | 自动添加 ip rule + ip route table |
[!IMPORTANT]
自动化 SBR通过
plugins数组配置sbr插件,Pod 启动时 自动 添加原地路由,无需手动配置!
57.5 Kind + ContainerLab 多网卡环境
57.5.1 架构设计
57.5.2 多网段设计
| 网卡 | 网段 | 网关 | 用途 |
|---|---|---|---|
| eth0 | 172.20.20.0/24 | Kind 默认 | K8s 集群通信 |
| eth1 | 15.15.1.0/24 | 15.15.1.1 | MACVLAN 网络1 |
| eth2 | 16.16.1.0/24 | 16.16.1.1 | MACVLAN 网络2 |
57.6 多网卡 Pod 配置
57.6.1 三网卡 Pod 示例
apiVersion: v1
kind: Pod
metadata:
name: multi-nic-pod
annotations:
k8s.v1.cni.cncf.io/networks: macvlan-sbr-net1, macvlan-sbr-net2
spec:
containers:
- name: test
image: nicolaka/netshoot
command: ["sleep", "infinity"]
57.6.2 Pod 网络结构
57.6.3 验证 SBR 自动配置
# 查看路由规则
kubectl exec multi-nic-pod -- ip rule
# 输出:
# 0: from all lookup local
# 100: from 15.15.1.0/24 lookup 100
# 101: from 16.16.1.0/24 lookup 101
# 32766: from all lookup main
# 32767: from all lookup default
# 查看路由表
kubectl exec multi-nic-pod -- ip route show table 100
# 输出:
# 15.15.1.0/24 dev eth1 scope link
# default via 15.15.1.1 dev eth1
57.7 多网卡通信验证
57.7.1 验证场景
57.7.2 验证命令
# 同网段 Pod 互通
ping -I 15.15.1.2 15.15.1.3
# 跨网段 Pod 互通(通过网关)
ping -I 15.15.1.2 16.16.1.3
# 每张网卡上外网
ping -I 15.15.1.2 114.114.114.114
ping -I 16.16.1.2 114.114.114.114
57.8 生产应用场景
57.8.1 网卡功能分离
57.8.2 典型行业应用
| 行业 | 网卡用途 | 说明 |
|---|---|---|
| 电信 | Control/Data 分离 | 控制面与数据面隔离 |
| 流媒体 | Media/Signal 分离 | 媒体流与信令分离 |
| 金融 | Trade/Admin 分离 | 交易网络与管理网络隔离 |
57.9 性能对比
| 方案 | 性能级别 | 说明 |
|---|---|---|
| MACVLAN Bridge | ⭐⭐⭐⭐ | 接近物理网卡性能 |
| IPVLAN L2 | ⭐⭐⭐⭐ | 与 MACVLAN 相当 |
| SR-IOV + DPDK | ⭐⭐⭐⭐⭐ | 最高性能(硬件虚拟化) |
| Overlay (VxLAN) | ⭐⭐ | 封装开销较大 |
57.10 章节小结
[!TIP]
MACVLAN-SBR 实践要点总结:
MACVLAN 特点:每个子接口有独立 MAC 地址,Bridge 模式最常用
与 IPVLAN 区别:MACVLAN MAC 独立,IPVLAN MAC 共享
SBR 自动配置:使用
plugins数组添加{"type": "sbr"}多网卡环境:Kind + ContainerLab 集成,多网卡可同时上外网
生产应用:网卡功能分离(管理/业务/媒体),电信、流媒体、金融行业常用
第五十八章 Multus-with-SRIOV-Kernel
本章介绍 SR-IOV Kernel 模式在 Kubernetes 中的应用,包括 SR-IOV 原理、硬件虚拟化技术、组件配置及与 Multus 的集成。
58.1 背景与概述
58.1.1 高性能网络需求
问题:当网卡带宽超过 10G 时,传统内核协议栈成为性能瓶颈。
解决方案:Kernel Bypass(内核旁路)技术。
58.1.2 SR-IOV 核心概念
| 术语 | 说明 |
|---|---|
| PF (Physical Function) | 物理网卡,完整的 PCIe 功能 |
| VF (Virtual Function) | 虚拟网卡,PF 划分出的轻量级功能 |
| SR-IOV | Single Root I/O Virtualization,硬件虚拟化标准 |
[!NOTE]
SR-IOV 优势
- 硬件虚拟化:VF 直接由硬件提供,不经过 Host OS 协议栈
- 高性能:接近物理网卡性能
- 资源隔离:每个 VF 独立的带宽和资源
58.2 SR-IOV vs DPDK
58.2.1 Kernel Bypass 层次
| 技术 | Bypass 层级 | 说明 |
|---|---|---|
| SR-IOV Kernel | Host OS 协议栈 | VF 直通到 Pod |
| DPDK/VPP | Pod 协议栈 | 用户态协议栈处理 |
| SR-IOV + DPDK | 双层 Bypass | 最高性能 |
58.2.2 适用场景
58.3 BIOS/内核预设置
58.3.1 BIOS 设置
| 设置项 | 说明 |
|---|---|
| VT-d | Intel 虚拟化技术,必须开启 |
| SR-IOV | 网卡 SR-IOV 功能,必须开启 |
58.3.2 内核参数
# 编辑 GRUB 配置
vi /etc/default/grub
# 添加内核参数
GRUB_CMDLINE_LINUX="intel_iommu=on iommu=pt"
# 更新 GRUB
grub2-mkconfig -o /boot/grub2/grub.cfg
# 重启生效
reboot
58.3.3 HugePages(大页内存)配置
# 编辑配置
vi /etc/sysctl.d/hugepages.conf
# 添加内容
vm.nr_hugepages = 16
vm.hugetlb_shm_group = 0
# 应用配置
sysctl -p /etc/sysctl.d/hugepages.conf
| 参数 | 说明 |
|---|---|
| nr_hugepages | 大页数量(1G 页 x 16 = 16G) |
| 用途 | 提高内存命中率,减少 TLB miss |
[!IMPORTANT]
HugePages 注意事项
- HugePages 是从系统内存中划分的
- 例如:800G 内存,配置 300G HugePages,剩余 500G 可用
- 需要根据 Pod 数量和单 Pod 内存需求规划
58.4 VF 创建与管理
58.4.1 创建 VF
# 查看网卡
ip link show
# 创建 VF(例如:eth2 创建 8 个 VF)
echo 8 > /sys/class/net/eth2/device/sriov_numvfs
# 验证
ip -d link show eth2
# 输出会显示 vf 0, vf 1, ..., vf 7
58.4.2 VF 数量规划
| 网卡带宽 | VF 数量 | 单 VF 带宽 |
|---|---|---|
| 10G | 8 | ~1.25G |
| 25G | 8 | ~3G |
| 40G | 16 | ~2.5G |
58.5 SR-IOV Network Device Plugin
58.5.1 组件作用
58.5.2 安装 Device Plugin
# 部署 SR-IOV Network Device Plugin
kubectl apply -f https://raw.githubusercontent.com/k8snetworkplumbingwg/sriov-network-device-plugin/master/deployments/k8s-v1.16/sriovdp-daemonset.yaml
58.5.3 ConfigMap 配置
apiVersion: v1
kind: ConfigMap
metadata:
name: sriovdp-config
namespace: kube-system
data:
config.json: |
{
"resourceList": [
{
"resourceName": "sriov_netdevice",
"selectors": {
"vendors": ["8086"],
"devices": ["154c"],
"drivers": ["i40evf"],
"pfNames": ["eth2", "eth3"]
}
}
]
}
| 字段 | 说明 |
|---|---|
| resourceName | K8s 资源名称 |
| vendors | 网卡厂商 ID(如 8086 = Intel) |
| devices | 设备 ID |
| drivers | VF 驱动名称 |
| pfNames | PF 网卡名称 |
58.5.4 验证资源
# 查看节点资源
kubectl describe node <node-name> | grep -A 10 "Allocatable"
# 输出示例:
# intel.com/sriov_netdevice: 16
# hugepages-1Gi: 64Gi
58.6 SR-IOV CNI
58.6.1 安装 SR-IOV CNI
# 部署 SR-IOV CNI
kubectl apply -f https://raw.githubusercontent.com/k8snetworkplumbingwg/sriov-cni/master/images/sriov-cni-daemonset.yaml
58.6.2 CNI 工作原理
58.7 NAD 配置
58.7.1 完整 NAD 示例
apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
name: sriov-net
annotations:
k8s.v1.cni.cncf.io/resourceName: intel.com/sriov_netdevice
spec:
config: '{
"cniVersion": "0.3.1",
"name": "sriov-network",
"type": "sriov",
"spoofchk": "on",
"trust": "on",
"vlan": 100,
"ipam": {
"type": "whereabouts",
"range": "192.168.100.0/24",
"range_start": "192.168.100.10",
"range_end": "192.168.100.200"
}
}'
58.7.2 关键参数说明
| 参数 | 说明 |
|---|---|
| spoofchk | MAC 欺骗检查,on 开启 |
| trust | 信任模式,on 允许接收 GARP |
| vlan | VLAN ID,可选 |
| resourceName | 引用 Device Plugin 定义的资源 |
[!TIP]
trust 参数
- 开启后 VF 可以接收 GARP(Gratuitous ARP)消息
- 避免 Pod 重启后因 MAC 地址变化导致的短暂不可达
58.8 Pod 配置
58.8.1 Pod 示例
apiVersion: v1
kind: Pod
metadata:
name: sriov-pod
annotations:
k8s.v1.cni.cncf.io/networks: sriov-net
spec:
containers:
- name: app
image: nicolaka/netshoot
command: ["sleep", "infinity"]
resources:
requests:
intel.com/sriov_netdevice: "1"
limits:
intel.com/sriov_netdevice: "1"
58.8.2 资源声明
[!IMPORTANT]
必须声明资源与 MACVLAN/IPVLAN 不同,SR-IOV 必须在 Pod 中声明资源请求,否则调度器无法分配 VF。
58.9 验证与调试
58.9.1 验证命令
# 查看 Pod 网卡
kubectl exec sriov-pod -- ip a
# 查看网卡驱动
kubectl exec sriov-pod -- ethtool -i net1
# driver: i40evf
# 查看 VF 分配
ip -d link show eth2 | grep vf
58.9.2 常见问题
| 问题 | 原因 | 解决 |
|---|---|---|
| VF 数量为 0 | 未创建 VF | echo N > sriov_numvfs |
| 资源不可见 | ConfigMap 配置错误 | 检查 pfNames、drivers |
| Pod 无法调度 | VF 不足 | 增加 VF 或减少 Pod |
58.10 章节小结
[!TIP]
SR-IOV Kernel 要点总结:
原理:PF 划分为多个 VF,VF 直通到 Pod,Bypass Host OS 协议栈
预设置:BIOS 开启 VT-d/SR-IOV,内核配置 IOMMU,配置 HugePages
组件:
- Device Plugin:管理 VF,上报资源
- SR-IOV CNI:搭建网络通路
- Multus:多网卡管理
配置流程:
- 创建 VF → 部署 Device Plugin → 部署 SR-IOV CNI → 创建 NAD → 创建 Pod
关键参数:
spoofchk、trust、vlan、资源请求
第五十九章 Multus-with-SRIOV-DPDK-VPP
本章介绍 SR-IOV 结合 DPDK/VPP 的高性能网络方案,涵盖驱动配置、PMD 原理、CPU 绑核隔离等关键技术。
59.1 背景与概述
59.1.1 SR-IOV Kernel vs SR-IOV DPDK
| 对比项 | SR-IOV Kernel | SR-IOV DPDK |
|---|---|---|
| 协议栈 | 内核协议栈 | 用户态协议栈(VPP) |
| Bypass 层级 | Host OS | Host OS + Pod 内核 |
| 驱动 | 原生驱动(i40evf, sfc_efx) | vfio-pci, igb_uio |
| 性能 | 高 | 极高 |
| 复杂度 | 低 | 高 |
59.1.2 双层 Bypass 架构
[!TIP]
双层 Bypass 理解
- 第一层:SR-IOV VF 直通,Bypass Host OS 内核协议栈
- 第二层:DPDK/VPP 用户态协议栈,Bypass Pod 内核协议栈
59.2 DPDK 驱动类型
59.2.1 驱动对比
| 驱动 | 说明 | 推荐 |
|---|---|---|
| vfio-pci | 现代推荐驱动,安全性好 | ✅ 生产推荐 |
| igb_uio | 早期驱动,需要更高权限 | ❌ 不推荐 |
| uio_generic | 通用驱动,性能较低 | ❌ 备选 |
59.2.2 驱动选择原因
[!IMPORTANT]
生产环境驱动选择
- 优先使用
vfio-pci,支持非特权模式运行- 避免使用
igb_uio,需要更高权限vfio-pci对 capabilities 要求更少,更安全
59.3 驱动绑定操作
59.3.1 加载驱动模块
# 加载 vfio-pci 驱动
modprobe vfio-pci
59.3.2 绑定 VF 到 DPDK 驱动
# 查看 VF 的 PCI 地址
lspci | grep -i ethernet
# 使用 dpdk-devbind 脚本绑定驱动
# 解绑原有驱动并绑定到 vfio-pci
for pci_addr in <VF_PCI_ADDR_LIST>; do
dpdk-devbind.py -u $pci_addr
dpdk-devbind.py -b vfio-pci $pci_addr
done
# 验证驱动绑定
dpdk-devbind.py --status
59.3.3 验证 VF 配置
# 查看 VF 状态
ip -d link show eth6
# 输出示例:
# eth6: ... link/ether ...
# vf 0 MAC ... spoof check on, trust on
# vf 1 MAC ... spoof check on, trust on
# ...
59.4 DPDK PMD 原理
59.4.1 中断模式 vs 轮询模式
| 模式 | 中断模式 | PMD 轮询模式 |
|---|---|---|
| 触发方式 | 被动(中断触发) | 主动(持续轮询) |
| CPU 使用 | 按需 | 100% 占用 |
| 延迟 | 较高 | 极低 |
| 适用场景 | 通用场景 | 高性能转发 |
59.4.2 PMD 工作原理
[!TIP]
PMD 核心特点
- CPU 持续轮询,不依赖中断
- CPU 显示 100% 使用率(正常现象)
- 实现纳秒级延迟
59.5 CPU 绑核与隔离
59.5.1 为什么需要 CPU 绑核
问题:PMD 需要 CPU 24 小时持续轮询,如果 CPU 被其他进程抢占,会导致数据包丢失。
解决:CPU 绑核隔离,确保 PMD 专用 CPU 不被抢占。
59.5.2 禁用 irqbalance
# 停止 irqbalance 服务
systemctl stop irqbalance
systemctl disable irqbalance
[!IMPORTANT]
irqbalance 说明
- irqbalance 会动态分配中断到不同 CPU
- 对于 PMD 专用场景,必须禁用
- 禁用后 CPU 使用更可控
59.5.3 配置 CPU 隔离
# 编辑 GRUB 配置
vi /etc/default/grub
# 添加 isolcpus 参数
GRUB_CMDLINE_LINUX="intel_iommu=on iommu=pt isolcpus=4-51,56-103"
# 更新 GRUB
grub2-mkconfig -o /boot/grub2/grub.cfg
# 重启生效
reboot
59.5.4 CPU 分配策略
| CPU 范围 | 用途 |
|---|---|
| 0-3, 52-55 | 系统预留(K8s、基础服务) |
| 4-51, 56-103 | Pod 专用(PMD、VPP) |
59.6 NAD 配置差异
59.6.1 DPDK NAD 特点
apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
name: sriov-dpdk-net
annotations:
k8s.v1.cni.cncf.io/resourceName: intel.com/sriov_dpdk
spec:
config: '{
"cniVersion": "0.3.1",
"name": "sriov-dpdk-network",
"type": "sriov",
"vlan": 100
}'
[!WARNING]
DPDK 模式 IPAM 注意事项
- DPDK 模式下,VPP 接管协议栈
- 传统 IPAM(如 whereabouts)无法直接使用
- IP 配置需通过 VPP 内部机制完成
59.6.2 ConfigMap 驱动配置
apiVersion: v1
kind: ConfigMap
metadata:
name: sriovdp-config
namespace: kube-system
data:
config.json: |
{
"resourceList": [
{
"resourceName": "sriov_dpdk",
"selectors": {
"vendors": ["8086"],
"drivers": ["vfio-pci"],
"pfNames": ["eth2", "eth3"]
}
},
{
"resourceName": "sriov_kernel",
"selectors": {
"vendors": ["8086"],
"drivers": ["i40evf"],
"pfNames": ["eth4", "eth5"]
}
}
]
}
59.7 Fast Path vs Slow Path
59.7.1 概念对比
| 路径 | 延迟 | 吞吐量 | 适用场景 |
|---|---|---|---|
| Slow Path | 高 | 一般 | 通用场景 |
| Fast Path | 极低 | 极高 | NFV、流媒体、金融 |
59.8 生产应用场景
59.8.1 典型应用领域
59.9 章节小结
[!TIP]
SR-IOV DPDK VPP 要点总结:
驱动选择:优先
vfio-pci,安全且支持非特权模式PMD 原理:Poll Mode Driver 持续轮询,CPU 100% 占用但延迟极低
CPU 隔离:
- 禁用 irqbalance
- 配置 isolcpus 参数
- NUMA 感知分配
配置差异:
- DPDK 模式下传统 IPAM 不适用
- 驱动类型区分 Kernel 和 DPDK 资源
性能优势:双层 Bypass + Fast Path = 极致性能
第六十章 K8s-CNI-IPAM 机制详解
本章系统介绍 Kubernetes CNI 中的 IPAM(IP Address Management)机制,涵盖各种 IPAM 类型、适用场景和选型建议。
60.1 背景与概述
60.1.1 CNI 的两大核心功能
| 功能 | 说明 | 示例技术 |
|---|---|---|
| Network Links | 搭建网络通路 | VXLAN, IPIP, BGP, VLAN |
| IPAM | IP 地址分配与管理 | host-local, whereabouts, DHCP |
[!IMPORTANT]
CNI = Network Links + IPAM任何 CNI 方案都包含这两部分:
- Network Links:解决 Pod 之间如何通信
- IPAM:解决 Pod 如何获取 IP 地址
60.1.2 为什么需要 IPAM
60.2 IPAM 类型概述
60.2.1 四种标准 IPAM
| IPAM 类型 | 分配范围 | 适用场景 | 使用频率 |
|---|---|---|---|
| DHCP | 外部 DHCP 服务器 | 传统网络对接 | 少 |
| host-local | 节点本地 | Flannel 等通用场景 | 高 |
| static | 手动指定 | 多网卡固定 IP | 中 |
| whereabouts | 集群级别 | Multus 多网卡 | 中 |
60.3 host-local IPAM
60.3.1 工作原理
核心特点:
- 每个节点分配一个固定子网(如 /24)
- Pod IP 从节点子网中分配
- IP 与节点强关联
60.3.2 配置示例
{
"ipam": {
"type": "host-local",
"subnet": "10.244.0.0/16",
"rangeStart": "10.244.0.10",
"rangeEnd": "10.244.0.250",
"routes": [
{ "dst": "0.0.0.0/0" }
],
"dataDir": "/var/lib/cni/networks"
}
}
60.3.3 优缺点分析
| 优点 | 缺点 |
|---|---|
| 简单易用 | IP 与节点绑定 |
| 性能好(本地分配) | 节点故障 IP 不可迁移 |
| 无外部依赖 | 每节点 IP 数量有限 |
[!TIP]
Flannel 默认使用 host-local
- 每节点分配 /24 子网(254 个可用 IP)
- 满足大多数场景(K8s 默认每节点最多 110 个 Pod)
60.4 static IPAM
60.4.1 适用场景
典型场景:
- 多网卡环境(Multus)
- VM 风格的 Pod
- 需要固定 IP 对接外部系统
60.4.2 配置示例
apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
name: static-ip-net
spec:
config: '{
"cniVersion": "0.3.1",
"name": "static-network",
"type": "macvlan",
"master": "eth0",
"mode": "bridge",
"ipam": {
"type": "static",
"addresses": [
{
"address": "192.168.1.100/24",
"gateway": "192.168.1.1"
}
],
"routes": [
{ "dst": "0.0.0.0/0" }
]
}
}'
60.4.3 为什么原生 K8s 不太需要 static IP
[!NOTE]
Service 机制解耦了 IP 变化问题
- Pod IP 变化 → Endpoints 自动更新
- Service ClusterIP/NodePort 保持不变
- 外部通过 Service 访问,无需关心 Pod IP
60.5 whereabouts IPAM
60.5.1 Cluster-Wide vs Host-Local
| 特性 | host-local | whereabouts |
|---|---|---|
| IP 分配范围 | 单节点 | 整个集群 |
| IP 迁移性 | 不支持 | 支持 |
| 实现复杂度 | 低 | 中 |
| 外部依赖 | 无 | 需要 CR 存储 |
60.5.2 配置示例
apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
name: whereabouts-net
spec:
config: '{
"cniVersion": "0.3.1",
"name": "whereabouts-network",
"type": "macvlan",
"master": "eth1",
"mode": "bridge",
"ipam": {
"type": "whereabouts",
"range": "192.168.100.0/24",
"exclude": [
"192.168.100.0/32",
"192.168.100.1/32",
"192.168.100.255/32"
]
}
}'
60.6 Cilium IPAM
60.6.1 块分配机制
Cilium 特点:
- 使用 /26 块(64 个 IP)而非 /24
- 块可以动态扩展
- 支持更灵活的 IP 管理
60.6.2 与 host-local 对比
| 特性 | host-local (/24) | Cilium (/26 块) |
|---|---|---|
| 每节点 IP 数 | 254 | 可扩展(多块) |
| IP 利用率 | 可能浪费 | 更高效 |
| 灵活性 | 低 | 高 |
60.7 公有云 VPC IPAM
60.7.1 云厂商方案
特点:
- 直接使用 VPC 子网 IP
- Pod IP 与 VPC 路由互通
- 无需 Overlay 封装
60.7.2 阿里云 Terway 示例
[!TIP]
Terway = VPC Network Links + Cilium Policy
- Network Links:基于 ENI 实现 VPC 互通
- Policy:使用 Cilium 实现网络策略
60.8 Spiderpool IPAM
60.8.1 新一代集群级 IPAM
Spiderpool 特性:
- 来自 DaoCloud 开源
- 解决 IP 固定问题
- 支持多 IP 池定义
- 与 whereabouts 功能对比增强
60.8.2 解决的问题
60.9 IPAM 选型建议
60.9.1 决策流程
60.9.2 选型对照表
| 场景 | 推荐 IPAM | 原因 |
|---|---|---|
| 通用 K8s 集群 | host-local | 简单、稳定、性能好 |
| Multus 多网卡 | whereabouts | 集群级 IP 池 |
| 固定 IP 需求 | static / Spiderpool | 支持 IP 固定 |
| 公有云 | 云厂商 IPAM | VPC 原生互通 |
| NFV/电信 | whereabouts + static | 复杂网络需求 |
60.10 章节小结
[!TIP]
CNI IPAM 要点总结:
CNI 双核心:Network Links(网络通路)+ IPAM(地址管理)
host-local:节点级分配,简单高效,Flannel 等通用 CNI 默认使用
static:固定 IP,适用于多网卡、VM 风格 Pod
whereabouts:集群级 IP 池,适用于 Multus 多网卡场景
云厂商:VPC 原生 IPAM,Pod 直接使用 VPC IP,无 Overlay
选型原则:根据场景选择,简单场景用 host-local,复杂多网卡用 whereabouts/Spiderpool

浙公网安备 33010602011771号