Kubernetes 网络与通信

本文最后更新于 2025年8月14日 上午

网桥

网桥是一个二层虚拟网络设备,它把若干个网络接口”连接“起来,使得网络接口之间的报文能够互相转发。网桥能够解析收发的报文,读取目标MAC地址的信息。

Linux内核通过通过一个虚拟网桥设备来实现桥接的。这个虚拟网桥设备可以绑定若干个以太接口设备,从而将它们桥接起来。

网桥的常用命令

新增一个网桥设备

1
# krctl addbr xxxxx

增加接口

1
# brctl addif xxxxx ethx

配置IP地址

1
ifconfig brxxx xx.xx.xx.xx

Netfilter和iptables

在Linux网络协议栈中有一组回调函数挂接点,通过这些挂接点挂接的钩子函数可以在Linux网络栈处理数据包的过程中对数据包进行一些操作。这种挂接点技术被叫做Netfilter和iptables

Netfilter负责在内核中执行各种挂接的规则,运行在内核模式中,而iptables是在用户模式下运行的进程,负责协助和维护内核中Netfilter的各种规则表。

Netfilter提供了多个挂载点用于执行Filter规则

在配置规则时,需要理解 Netfilter 提供的规则表(table)与链(chain)结构:

  • 表(Table)
    表是按照功能划分的规则集合,不同的表用于不同的处理目的:
    1. filter :数据包过滤(决定放行/拒绝/丢弃),是最常用的表
    2. nat :网络地址转换(SNAT、DNAT、MASQUERADE 等)
    3. mangle :修改数据包的特定字段(如 TOS、TTL)
    4. raw :控制数据包是否进入连接跟踪(conntrack)
    5. security :配合 SELinux 等安全框架进行包级别的安全策略控制
  • 链(Chain)
    链决定规则在数据包处理过程中的位置。常见内置链:
    1. PREROUTING :数据包刚到本机,还未进行路由判断前
    2. INPUT :目标是本机的入站数据包
    3. FORWARD :需要转发到其他主机的数据包
    4. OUTPUT :本机发出的数据包
    5. POSTROUTING :路由判断后、发出数据包前
  • 规则(Rule)
    每条规则由匹配条件(Match)和处理动作(Target)组成:
    • 匹配条件 :源/目的 IP、端口、协议、接口、数据包状态(conntrack)、MAC 地址等
    • 处理动作 :ACCEPT、DROP、REJECT、SNAT、DNAT、LOG、自定义链等

结合数据包路径,可以总结 Netfilter 的核心执行流程(简化):

1
2
3
4
5
6
7
8
9
less
复制编辑
[PREROUTING] --(路由判断)--> [INPUT] --> 本地进程
|
v |
[FORWARD] --------------------+
|
[POSTROUTING] --> 网络接口发送

在配置 iptables 规则时,本质上就是将匹配条件处理动作注册到这些链上,由 Netfilter 在对应的挂载点进行回调执行,从而实现包过滤、地址转换、流量控制等功能。

路由

Liunx系统包含一个完整的路由功能。IP层在处理数据发送或者转发时,会使用路由表来决定发往哪里。路由功能由IP维护的一张路由表来实现。当主机收到数据报文时,它用此表来决策接下来应该做什么操作。当从网络侧接收到数据报文时,IP层会先加插报文的IP地址,如果与主机自身的地址一致,那么报文将被发送到传输层相应的协议中,如果报文中的IP地址不是主机自身的地址,并且配置了路由功能,那么该报文被转发,否则丢弃报文。

命名空间与虚拟网络

请参考https://gadoid.github.io/2025/04/06/容器网络/中的 命名空间与虚拟网络的讨论

这里继续讨论Docker中的网络模式,和Docker部署前后的规则变化。

Docker的容器网络模式

Docker支持以下网络模式

  • host模式: 使用—net=host注定
  • container模式: 使用—net=container:NAME_or_ID指定
  • none模式:使用—net=none指定
  • bridge模式:使用—net=brideg指定

Kubernetes中使用了bridge模式。在bridge模式下,Docker Daemon 首次启动时会创建一个虚拟网桥,默认的名称是docker0,如何在私有网络命名空间中给这个网桥分配一个子网。针对由Docker创建的每一个容器,都会创建一个虚拟以太网设备,其中一端关联到网桥上,另一端使用Linux的网络命名空间技术映射到容器内的eth0设备。如何在网桥的地址段内给eth0接口分配一个IP地址

Docker启动后的系统响应

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# systemctl start docker
# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever

2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 00:1a:2b:3c:4d:5e brd ff:ff:ff:ff:ff:ff
inet 192.168.1.10/24 brd 192.168.1.255 scope global dynamic eth0
valid_lft 86392sec preferred_lft 86392sec
inet6 fe80::21a:2bff:fe3c:4d5e/64 scope link
valid_lft forever preferred_lft forever

3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:3d:94:8f:65 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
inet6 fe80::42:3dff:fe94:8f65/64 scope link
valid_lft forever preferred_lft forever

# iptables-save
# Generated by iptables-save v1.8.7 on Mon Aug 11 18:50:00 2025
*nat
:PREROUTING ACCEPT [7:878]
:INPUT ACCEPT [7:878]
:OUTPUT ACCEPT [3:536]
:POSTROUTING ACCEPT [3:536]
:DOCKER - [0:0]
-A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
-A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE

COMMIT
# Completed on Mon Aug 11 18:50:00 2025

*filter
:INPUT ACCEPT [113:11362]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [37:5000]
:DOCKER - [0:0]
-A FORWARD -o docker0 -j DOCKER
-A FORWARD -o docker0 -m conntrack --ctstate RELATED,established -j ACCEPT
-A FORWARD -i docker0 ! -o docker0 -j ACCEPT
-A FORWARD -i docker0 -o docker0 -j ACCEPT

COMMIT
# Completed on Mon Aug 11 18:50:00 2025

Docker创建了docker0网桥,并添加了iptables规则。docker0网桥和iptables规则都处于root网络命名空间中。通过解读这些规则,我们发现在没有启动任何容器时,如果启动了Docker Daemon,那么它已经做好了通信准备

  1. 在NAT表中有3条记录,在前两条匹配生效后,会继续执行DOCKER链,而此时DOCKER链未空,所以只是创建了框架
  2. NAT表第3条的含义是,若本地发出的数据包不是发往docker0的,而是发往主机之外的设备,则需要进行动态地修改,将源地址从容器的地址修改为宿主机网卡IP
  3. 在FILTER表中,第1条也是一个框架
  4. 在FILTER表中,第3条的含义是,docker0发出的包如果需要转发到非docker0本地IP地址的设备,则是允许的。这样docker0设备的包就可以根据路由规则中转到宿主机的网卡设备,从而访问外面的网络
  5. 在FILTER表中,第4条的含义是,docker0的包还可以被中转给docker0本身,即连接在docker0网桥上的不同容器之间的通信也是允许的
  6. 在FILTER表中,第2条的含义是,如果接收到的数据包属于以前已经建立好的连接,那么允许直接通过。

无端口映射创建容器后的网络配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
docker run --name register -d registry
# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 00:1a:2b:3c:4d:5e brd ff:ff:ff:ff:ff:ff
inet 192.168.1.10/24 brd 192.168.1.255 scope global dynamic eth0
valid_lft 86392sec preferred_lft 86392sec
inet6 fe80::21a:2bff:fe3c:4d5e/64 scope link
valid_lft forever preferred_lft forever
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:3d:94:8f:65 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
inet6 fe80::42:3dff:fe94:8f65/64 scope link
valid_lft forever preferred_lft forever
5: veth12a4b9c@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether 8a:12:34:56:78:9a brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet6 fe80::8812:34ff:fe56:789a/64 scope link
valid_lft forever preferred_lft forever

# iptables-save
# Generated by iptables-save v1.8.7 on Mon Aug 11 18:51:30 2025
*nat
:PREROUTING ACCEPT [8:958]
:INPUT ACCEPT [8:958]
:OUTPUT ACCEPT [4:616]
:POSTROUTING ACCEPT [4:616]
:DOCKER - [0:0]
-A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
-A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
COMMIT
# Completed on Mon Aug 11 18:51:30 2025

*filter
:INPUT ACCEPT [115:11562]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [39:5200]
:DOCKER - [0:0]
-A FORWARD -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -o docker0 -j DOCKER
-A FORWARD -i docker0 ! -o docker0 -j ACCEPT
-A FORWARD -i docker0 -o docker0 -j ACCEPT
COMMIT
# Completed on Mon Aug 11 18:51:30 2025

# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 192.168.1.1 0.0.0.0 UG 100 0 0 eth0
172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0
192.168.1.0 0.0.0.0 255.255.255.0 U 100 0 0 eth0

宿主机上的Netfilter 和路由表都没有变换,说明在进行端口映射时,Docker的默认网络是没有进行特殊处理的。

宿主机上的Veth设备对已经建立,并连接到容器内。而容器内的网络信息已经在虚拟端口配置了ip地址

1
2
3
4
5
6
7
8
9
# docker exec register ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
4: eth0@if5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever

配置端口映射的容器网络配置

1
docuer run --name register -d -p 1180:5000 registry

启动后的iptables

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# iptables-save
# Generated by iptables-save v1.8.7 on Mon Aug 11 18:51:30 2025
*nat
:PREROUTING ACCEPT [8:958]
:INPUT ACCEPT [8:958]
:OUTPUT ACCEPT [4:616]
:POSTROUTING ACCEPT [4:616]
:DOCKER - [0:0]
-A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
-A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
-A POSTROUTING -s 172.17.0.2/32 -d 172.17.0.2/32 -p tcp -m tcp --dport 5000 -j MASQUERADE
-A DOCKER ! -i docker0 -p tcp -m tcp --dport 1180 -j DNAT --to-destination 172.17.0.2:5000
COMMIT
# Completed on Mon Aug 11 18:51:30 2025
*filter
:INPUT ACCEPT [115:11562]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [39:5200]
:DOCKER - [0:0]
-A FORWARD -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -o docker0 -j DOCKER
-A FORWARD -i docker0 ! -o docker0 -j ACCEPT
-A FORWARD -i docker0 -o docker0 -j ACCEPT
-A DOCKER -d 172.17.0.2/32 ! -i docker0 -o docker0 -p tcp -m tcp --dport 5000 -j ACCEPT
COMMIT
# Completed on Mon Aug 11 18:51:30 2025

Docker服务在NAT和FILTER 两个表内添加的两个DOCKER子链都用于端口映射。

Kubernetes网络模型

Kubernetes网络模型设计的一个基本原则是,每个Pod都拥有一个独立的IP地址,并假定所有的Pod都在一个可以直接联通的,扁平网络空间中。用户不需要额外考虑如何建立Pod之间的连接,也不需要考虑如何将容器端口映射到主机端口等问题。

在Kubernetes网络里,IP是以Pod为单位进行分配的,一个Pod内部的所有容器共享一个网络堆栈。

这样的网络模型叫做IP-per-Pod模型。

通过IP-per-Pod 模型,Pod的内部和外部IP保持一致。这样Pod间互相访问不再需要进行转换,同时在同一个Pod内部,容器间共享网络协议栈,可以通过localhost的方式在容器间进行服务访问。

基于以上设计,Kubernetes对于网络实现有以下要求:

  1. 所有的Pod都可以在不用NAT的方式下与其他的Pod通信
  2. 在所有的Node上运行的代理程序都可以在不用NAT的方式下与所有的Pod通信
  3. 以hostnetwork模式运行的Pod都可以在不用NAT的方式下与其他的Pod通信

Kubernetes网络的实现机制

容器与容器间的通信

同一个Pod内的不同容器共享同一个网络命名空间,共享同一个Linux协议栈。

kubelet通过启动参数 —pod-infra-container-image 指定基础容器提供网络命名空间。再由CNI网络插件负责infra容器的地址管理。

Pod与Pod间的通信

每个Pod都有一个具体的全局唯一的IP地址。Pod间极可能在同一个Node上也可能在不同Node上进行通信。Pod通过infra容器连接到宿主机网络。再由CNI网络插件负责infra容器的地址管理

CNM网络模型

cnm网络主要由Network Sandbox ,Endpoint 和 Network 3个组件组合构建

  • Network Sandbox : 容器内部的网络栈,包括对网络接口,路由表,DNS等配置的管理。Sandbox可通过Linux网络命名空间,FreeBSD Jail等机制实现。 一个Sandbox可以包含多个Endpoint
  • Endpoint : 用于将容器内的Sandbox与外部网络相连的网络接口,可以使用Veth设备对,Open vSwitch 的内部port等技术进行实现。一个Endpoint 仅能加入一个Network
  • Network :可以直接互联的Endpoint的集合,可以通过Linux网桥,VLan等技术实现。一个Network可以包含多个Endpoint

CNI网络模型

CNI规范

CNI提供了一种应用容器的插件化网络解决方案,来定义对容器网络进行操作和配置的规范,通过插件的形式对CNI接口进行实现,尝试提供一个普适的容器网络解决方案,CNI仅关注在创建容器时分配网络资源与在销毁容器时删除网络资源,这使得CNI规范非常轻巧,易于实现。

概念

  • 容器: 是独立于Linux网络命名空间的环境。关键之处是容器需要拥有自己的Linux网络命名空间,这是加入网络的必要条件
  • 网络:表示可以互联的一组网络端点,这些实体拥有各自独立,唯一的IP地址,可以是容器,物理机或者其他网络设备。可以将容器添加到一个或多个网络,也可以从一个或多个网络中删除
  • 运行时:运行CNI插件的程序
  • 插件:设置特定网络配置的程序
  • CNI接口:运行时和插件之间的接口对容器网络的设置和操作都通过插件进行具体实现。CNI插件包括两种类型CNI Plugin和IPAM Plugin。 CNI Plugin 负责为容器配置网络资源,IPAM Plugin负责对容器的IP地址进行分配和管理。

容器运行时与CNI插件的关系

容器运行时与CNI插件之间的关系和工作机制遵循以下原则

  • 容器运行时必须在调用任意插件前为容器创建一个新的网络命名空间
  • 容器运行时必须确定此容器所属的网络,以及每个网络必须执行哪个插件
  • 网络配置为JSON格式,便于在文件中存储。网络配置包括必填字段,如name和type,以及插件特有的字段。网络配置允许在调用时更高字段的值,为此必须在可选字段args中包含需要变更的信息
  • 容器运行时必须按照先后顺序为每个网络运行插件将容器添加到每个网络中
  • 容器生命周期结束后,容器运行时必须以逆序执行插件,以使容器与网络断开连接
  • 容器运行时一定不能为同一个容器的调用执行并行操作,但可以为多个不同的容器的调用执行并行操作。
  • 容器运行时必须对容器的ADD和DEL操作设置顺序,以便ADD操作最终跟随相应的DEL操作,DEL操作后面可能会有其他DEL操作,但插件应自由处理多个DEL操作(DEL 幂等)
  • 容器必须由ContainerID进行唯一标识,存储状态的插件应使用联合主键进行存储
  • 容器运行时不得为同一个实例调用两次ADD操作。对同一个容器仅在每次ADD操作都使用不同的网络接口名称时,才可以多次添加到特定网络中
  • 除非明确标记为可选配置,CNI结构中的字段都是必填字段

CNI Plugin 详解

CNI Plugin必须是一个可执行程序,由容器管理系统调用

CNI Plugin负责将网络接口插入容器的网络命名空间,并在主机上进行必要的更改,如何调用适当的IPAM插件,将IP地址分配给容器网络接口,并设置正确的路由规则

CNI Plugin需要支持的操作包括ADD(添加),DEL(删除),CHECK(检查) 和 VERSION(版本查询)。这些操作的具体实现均由CNI Plugin可执行程序完成。容器运行时必须使用CNI Plugin网络配置参数中的type字段标识的文件名在环境变量CNI_PATH设定的路径下查找同名的可执行文件,一旦找到,容器运行时就将调用该可执行程序,并传入以下环境变量设置的网络配置参数,供该插件完成容器网络资源和参数的设置。

输入参数需要以操作系统环境变量的形式进行设置,CNI要求包括以下环境变量

  • CNI_COMMAND : 操作方法,包括ADD,DEL,CHECK和VERSION
  • CNI_CONTAINED : 容器ID
  • CNI_NETNS : 容器的网络命名空间路径,例如/proc/[pid]/ns/net
  • CNI_IFNAME : 待设置的网络接口名称
  • CNI_ARGS : 其他参数,key=value格式
  • CNI_PATH : 可执行文件的查找路径

网络配置文件由JSON格式表示,以标准输入的方式传递给可执行程序

操作方法

ADD

将容器添加到某个网络中,主要过程为在Container Runtime创建容器时,首先创建好容器内的网络命名空间,并在其中为容器创建一个网络接口,然后调用CNI插件为该接口完成网络配置如何

  • CNI_COMMAND 命令
  • CNI_CONTAINERID 容器ID
  • CNI_NETNS 容器的网络命名空间路径
  • CNI_IFNAME 容器网络接口名称

DEL

在容器销毁时,将容器从某个网络中删除,或者回退之前ADD操作的结果

  • CNI_COMMAND 命令
  • CNI_CONTAINERID 容器ID
  • CNI_IFNAME 容器网络接口名称

DEL操作通常应该返回成功,即使在某些条件不满足的情况下

DEL操作必须能够允许执行多次并返回成功,即使网络接口存在问题或已经被删除

CHECK

检查容器是否设置正确,返回结果为空,则表示检查成功

  • CNI_COMMAND 命令
  • CNI_CONTAINERID 容器ID
  • CNI_IFNAME 容器网络接口名称
  • CNI_NETNS 容器的网络命名空间路径

VERSION

查询网络插件支持的CNI规范版本号

  • CNI_COMMAND 命令
  • cniVersion , 以JSON格式表示

CNI网络配置

CNI规范的网络配置参数

cniVersion (string): CNI版本号

name(string): 网络名称,应在一个Node或一个管理域内唯一

disableCheck(string): 可设置为true或false

plugins(list): 插件列表及其配置信息

插件的主要配置参数

type(string): CNI Plugin 二进制文件的名称

capabilities: 插件特定的Linux能力列表

runtimeConfig,args,以cni.dev/开头的配置

ipMasq(boolean) : 是否设置IP Masquerade

ipam: IP地址管理的相关配置

dns: DNS服务的相关配置

  • nameserver(list of strings): 域名服务列表
  • domain(string):本地域名
  • search(list of strings): 按优先级排序的域名搜索后缀列表
  • options(list of strings): 传递给域名解析器的选项列表

配置示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
{
"cniVersion": "1.0.0",
"name": "dbnet",
"plugins": [
{
"type": "bridge",
"bridge": "cni0",
"keyA": ["some more","plugin specific","configuration"],
"ipam": {
"type": "host-local",
"subnet": "10.1.0.0/16",
"gateway": "10.1.0.1",
"route": [
{"dst": "0.0.0.0/0"}
]
},
"dns": {
"nameservers": ["10.1.0.1"]
}
},
{
"type": "tuning",
"capabilities": {
"mac": true
},
"sysctl": {
"net.core.somaxconn": "500"
}
},
{
"type": "portmap",
"capabilities": {"portMappings": true}
}
]
}

以上述配置观察CNI插件的加载过程

由容器运行时,执行ADD操作,按照描述顺序执行3个插件

首先调用bridge插件,以如下配置进行ADD操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
"cniVersion": "1.0.0",
"name": "dbnet",
"plugins": [
{
"type": "bridge",
"bridge": "cni0",
"keyA": ["some more","plugin specific","configuration"],
"ipam": {
"type": "host-local",
"subnet": "10.1.0.0/16",
"gateway": "10.1.0.1",
"route": [
{"dst": "0.0.0.0/0"}
]
},
"dns": {
"nameservers": ["10.1.0.1"]
}
}
}

在bridge插件执行的过程中,会调用host-local插件为容器分配IP地址,同样也是ADD操作。host-local执行成功后返回以下结果。host-local 会对已经分配的地址 进行持久化记录,在后续分配时,会先查询文件中哪些ip已被使用,再从剩余的地址池中挑选一个

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"ips": [
{
"address": "10.1.0.5/16",
"gateway": "10.1.0.1"
}
],
"routes": [
{
"dst": "0.0.0.0/0"
}
],
"dns": {
"namesevers": ["10.1.0.1"]
}
}

bridge插件在获取ipam插件的返回结果之后,会将容器网络接口的配置设置为以下信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
{
"ips": [
{
"address": "10.1.0.5/16",
"gateway": "10.1.0.1"
}
],
"routes": [
{
"dst": "0.0.0.0/0"
}
],
"interfaces": [
{
"name": "cni0",
"mac": "00:11:22:33:44:55"
},
{
"name": "veth3243",
"mac": "55:44:33:22:11:11"
},
{
"name": "etho0",
"mac": "99:88:77:66:55:44",
"sandbox": "/var/run/netns/blue"
}
],
"dns": {
"nameservers": [ "10.1.0.1" ]
}
}

bridge 运行结束,接下来运行tuning插件,基于其配置执行ADD操作,同时将上一个bridge 插件的运行结果设置在prevResult字段中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
{
"cniVersion": "1.0.0",
"name": "dbnet",
"type": "tuning",
"sysctl": {
"net.core.somaxconn": "500"
}
"runtimeConfig":{
"mac": "00:11:22:33:44:66"
},
"prevResult":{
"ips": [
{
"address": "10.1.0.5/16",
"gateway": "10.1.0.1"
}
],
"routes": [
{
"dst": "0.0.0.0/0"
}
],
"interfaces": [
{
"name": "cni0",
"mac": "00:11:22:33:44:55"
},
{
"name": "veth3243",
"mac": "55:44:33:22:11:11"
},
{
"name": "eth0",
"mac": "99:88:77:66:55:44",
"sandbox": "/var/run/netns/blue"
}
],
"dns": {
"nameservers": [ "10.1.0.1" ]
}
}
}

… 依次对容器内的网络进行配置。

Check过程

首先调用bridge插件执行CHECK操作,将之前执行了3个插件ADD操作的结果设置在prevResult字段中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
{
"cniVersion": "1.0.0",
"name": "dbnet",
"plugins": [
{
"type": "bridge",
"bridge": "cni0",
"keyA": ["some more","plugin specific","configuration"],
"ipam": {
"type": "host-local",
"subnet": "10.1.0.0/16",
"gateway": "10.1.0.1",
"route": [
{"dst": "0.0.0.0/0"}
]
},
"dns": {
"nameservers": ["10.1.0.1"]
}
},
"prevResult":{
"ips": [
{
"address": "10.1.0.5/16",
"gateway": "10.1.0.1"
}
],
"routes": [
{
"dst": "0.0.0.0/0"
}
],
"interfaces": [
{
"name": "cni0",
"mac": "00:11:22:33:44:55"
},
{
"name": "veth3243",
"mac": "55:44:33:22:11:11"
},
{
"name": "eth0",
"mac": "00:11:22:33:44:66",
"sandbox": "/var/run/netns/blue"
}
],
"dns": {
"nameservers": [ "10.1.0.1" ]
}
}
}

… 执行所有插件的Check操作,并使用prevResult字段保留上一个插件的结果内容

DEL操作与前面的ADD/CHECK 操作类似。

IPAM Plugin

为了减轻CNI Plugin在IP地址管理方面的负担,CNI规范设置了一个独立的插件来管理容器的IP地址。IPAM Plugin负责为容器分配IP地址,网关,路由和DNS,并负责将IP地址操作结果返回给主CNI Plugin。

host-local 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
{
"ipam": {
"type": "host-local",
"ranges": [
[
{
"subnet": "10.10.0.0/16",
"rangeStart": "10.10.1.20",
"rangeEnd": "10.10.3.50",
"gateway": "10.10.0.254"
},
{
"subnet": "172.16.5.0/24"
}
],
[
{
"subnet": "3ffe:ffff:0:01ff::/64",
"rangeStart": "3ffe:ffff:0:01ff::0010",
"rangeEnd": "3ffe:ffff:0:01ff::0020"
}
]
],
"routes": [
{"dst":"0.0.0.0/0"},
{"dst":"192.168.0.0/16", "gw":"10.10.51"},
{"dst":"3ffe:ffff:0:01ff::1/64"}
],
"dataDir": "/run/my-orchestrator/container-ipam-state"
}
}

主要参数

  • type : 插件类型
  • routes : 路由信息
  • ranges : 可分配地址范围
    • subnet : 子网CIDR地址范围
    • rangeStart: 范围的起始IP地址
    • rangeEnd: 范围的结束IP地址
    • getway: 网关地址
  • resolvConf: 域名解析服务配置
  • dataDir: 数据目录,用于保存已分配IP地址等信息

容器网络方案

Flannel

Flannel是一个轻量级的三层网络CNI插件,在每个Node上会运行一个flanneld代理来管理Node上Pod的网络配置。Flannel可以使用keubernetes或etcd 作为后端存储,以保存为每个Node分配一段互不冲突的PodIP CIDR地址池。

Flannel 在每个Node上运行一个flanneld进程,创建两个网络接口flannel.1 和 cni0,其中cni0作为Pod网络的网关管理容器网络与宿主机网络的通信,跨主机网络通信适用VXLAN协议实现。

Open vSwitch

Open vSwitch 是一个开源的虚拟交换机软件,与Linux中的bridge类似。Open vSwitch的网桥可以直接建立多种通信通道。

配置

  1. 删除docker0网桥,创建Linux网桥,并配置IP地址
  2. 建立Open vSwitch的OVS网桥,适用ovs-vsctl命令给OVS网桥增加gre接口,设置连接的NodeIP 为对端的IP地址
  3. 将OVS网桥作为网络接口加入Docekr网桥
  4. 重启OVS网桥和Docker网桥,将一个Docker的地址段添加到Docker网桥的路由规则中

实际过程

  1. 按照OVS
1
2
3
4
5
# yum localinstall openvswitch-2.4.0-1.x86_64.rpm
# systemctl start openvswitch
# service openswitch status
ovsdb-server is running with pid 2429
ovs-vswitchd is running with pid 2439
  1. 创建网桥和GRE隧道

    1. 创建网桥
    1
    # ovs-vsctl add-br br0 

    b. 创建GRE隧道连接对端网桥

    1
    2
    # ovs-vsctl add-port br0 gre1 -- set interface gre1 type=gre
    option:remote_ip=192.168.18.128
    1
    c.  添加br0到本地docker0
1
# brctl addif docker0 br0

d. 启动br0和docker0网桥

1
2
# ip link set dev br0 up
# ip link set dev docker0 up
1
e.  添加路由规则,将发往容器的网段路由到docker0网络
1
# ip route add 172.17.0.0/16 dev docker0
1
f.  清空Docker自带的iptables规则及Linux的规则
1
# iptables -t nat -F ; iptables -F

直接路由

通过部署MultiLayer ,配置每个Node 中的容器网络到其他Node的路由来完成容器间的通信。

1
2
# route add -net 10.1.20.0 netmask 255.255.255.0 gw 192.168.1.129  //Node1
# route add -net 10.1.10.0 netmask 255.255.255.0 gw 192.168.1.128 //Node2

通过容器进行docker0容器网络发现与路由

拉取Quagga

1
$ docker pull index.alauda.cn/georce/router

保证每个Node间的docker0 子网网段地址不重叠,启动Quagga容器

1
$ docker run -itd --name=router --privileged --net=host index.alauda.cn/georce/router

之后等待动态路由自动发现处于不同节点上的容器网络

Calico

Calico是一个基于BGP的纯三层网络方案,Calico 在每个Node上都利用Linux Kernel实现了一个高效的vRouter来负责数据转发。每个vRouter都通过BGP1协议把本Node上运行的容器的路由信息向这个那个Calico网络广播,并自动设置到达其他Node的路由转发规则,它保证了容器间的数据都是通过IP路由的方式完成的互通。不需要额外的封装结构,效率更高。

组件

Felix: Calico Agent,运行在每个Node上,负责为容器设置网络资源,保证跨主机容器间的网络互通

etcd: Calico使用的后端存储

BGP Client(BIRD) : 负责把Felix 在各Node上设置的路由信息通过BGP广播Calico网络

Route Reflector: 通过一个或者多个BGP Route Reflector完成大规模集群的分级路由分发

CalicoCtl: 命令行工具


Kubernetes 网络与通信
http://gadoid.io/2025/08/14/Kubernetes-网络与通信/
作者
Codfish
发布于
2025年8月14日
许可协议