1.概述
kuberenes 容器网络控件工作粗略可以分为三个部分。
- 节点上容器网关创建和配置
- 节点上容器网络管理: 容器网络接口创建和删除, 容器IP添加和释放, IP网络访问控制
- 节点间网段管理和路由: 节点子网创建和释放,节点间子网路由表管理
host-gw
是kubernetes 网络组件之一flannel
一个性能最高一种模式。由于fannel
是kubenets上最基本组件
- 节点上容器网络管理: 由于没有IP网络控制,节点控制使用k8s源生cni组件分配网络。项目地址https://github.com/containernetworking/cni
- 节点间网段管理和路由
- 子网管理:
- 节点子网路由: 节点路由表配置才是
flannel
工作之一,host-gw
模式其实只有网桥配置以及路由表定时更新代码: https://github.com/flannel-io/flannel/tree/master/backend
2.节点上容器网络管理
容器网络和Host机器网络通过网络命名空间进行隔离。简单的说把容器虚拟网卡,以及需要路由配置,iptables放到指定容器网络空间里面。按照下面网络规划图, 下面各个节详细说明怎么一步步进行配置:
2.1 一切从网络命名空间开始
容器网络核心就是网络命名空间,命令空间创建以后,命名空间默认包含lo
接口, 空路由表, 空iptables
,如图:
如上图创建名字为container1
网络命名空间
$ ip netns add container1
2.2 构建容器网络空间和根网络空间桥梁
容器网络空间和主机根网络空间通过虚拟设备veth-pair
进行连接, veth-pair
由两个网络接口成对组成,一头接口容器网络空间,另外一头接入根网络空间网桥上, 这个网桥在flannel k8s网络一般命名为cni0
。下面是以主机Host1为网络规划为例子,进行配置。
2.2.1 创建veth-pair
创建veth-pair
默认在根网络空间上成对创建的, 创建以后如图:
创建网络接口对veth0
->veth0-tmp
, tmp一端后面用于移动到指定网络空间上
$ ip link add veth0 type veth peer name veth0-tmp
2.2.2 移动接口到指定网络空间
把veth-pair
一端名字为veth0-tmp
移动到container1
网络空间, 如图:
$ip link set dev veth0-tmp netns container1
2.2.3 配置网络空间接口
接口移动到网络命名空间以后, 配置对接口进行一些配置,配置步骤如下
- 修改veth接口名字:
vethx-tmp
->eth0
- veth接口添加ip地址
- 启动网络空间中
eth0
接口和lo
接口: 接口添加以后默认状态是down
上面这些操作都有一个共性,不是在默认根网络空间进行配置,而是在容器网络空间进行操作的。在网络空间执行命令可以通过这个方式执行
$ ip netns exec [网络空间名字] [cmd] [param1, parm2, ....]
# 例如在网络命名空间 container1 执行 ping 命令
$ ip netns exec container1 ping 127.0.0.1
2.2.3.1 修改网络空间接口名称
网络空间一端veth接口, 在主空间创建时候为了减少名字冲突,使用随机名称,移动到指定命名空间,为了后面配置脚本统一,把 接口随机名字修改eth0
,方面用户程序使用,用户理解。
把网络命名空间container1
接口veth0-tmp
修改为eth0
$ ip netns exec container1 ip link set veth0-tmp name eth0
2.2.3.2 配置网络空间接口ip地址
主机host1容器网络段是10.239.0.0/24
,其中10.239.0.1/24
留容器网关使用,这里容器1网络空间配置10.239.0.2/24
ip地址到接口eth0
上,eth0
是容器默认接口,需要在此接口配置ip地址,如图:
把网络命名空间container1
接口eth0
添加ip地址10.239.0.2/24
$ ip netns exec container1 ip addr add 10.239.0.2/24 dev eth0
2.2.3.3 启动网络空间接口
默认情况下,新建接口都是没有启动,需要手工启动以后才可以使用。网络空间里面两个接口都需要启动:
eth0
: 需要和外界通信lo
: 回环接口不启动自己ping自己就不通了
# 启动接口 eth0
$ ip netns exec container1 ip link set eth0 up
# 启动接口 lo
$ ip netns exec container1 ip link set lo up
2.2.3.4 测试容器空间接口
测试接口是否正确配置IP地址, 已经接口启动成功
下面测试容器网络命名空间1, eth0
接口
$ ip netns exec container1 ping 10.239.0.2
PING 10.239.0.2 (10.239.0.2) 56(84) bytes of data.
64 bytes from 10.239.0.2: icmp_seq=1 ttl=64 time=0.035 ms
下面测试容器网络命名空间1, lo
接口
$ ip netns exec container1 ping 127.0.0.1
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.240 ms
2.3 节点容器网关配置
同一台机器上多个容器网络空间怎么通信,需要有交换机switch
,然后把容器网络空间在root network namespace
一端veth接入这个设备上。Linux网桥作用类似于网络switch。它会在连接到其上的接口间转发网络包。并且因为它是switch,它是在L2层完成这些转发的。容器网关就是switch角色。
2.3.1 创建容器网关br0
容器网关是通过网桥实现的,flannel host-gw模式下容器网桥名字为cni0
,我们这边用br0
代表吧, 网桥是放在根网络空间上的,如图:
在根网络空间创建名字为br0
网桥接口
$ ip link add br0 type bridge
2.3.2 配置容器网关br0
2.3.2.1 配置网关网关IP
配置网桥br0
ip地址为10.239.0.1/24
$ ip addr add 10.239.0.1/24 dev br0
2.3.2.2 启动网段接口
容器switch需要启动以后才可以使用
$ ip link set br0 up
2.4 把容器网络连接网关上
把容器在根分区接口接入到根网络空间交换机上,多个容器之间可以通过交换转发数据了。
2.4.1 把接口对一端接入网桥
把veth接口根根网络空间一端接入到网桥接口上,并且启用这个接口
把veth0
接口接入网桥br0
上,启动veth0
接口
# 把接口接入到br0上
$ ip link set dev veth0 master br0
# 启动接口
$ ip link set dev veth0 up
2.4.2 配置容器空间默认网关
跨网段通信的时候需要通过路由器转发, 容器需要其他网段通信时候需要通过路由转发,需要在容器网络空间中配置默认路由,默认路由地址就是网桥地址
容器和容器网段外的节点通信需要通过br0
作为路由器转发,说最终去了br0
以后通过root network namespace
路由策略进行转发的。
在容器容器1添加默认路由, 路由器地址为br0
ip,
$ ip netns exec container1 ip route add default via 10.239.0.1 dev eth0
2.4.3 测试路由配置正确性
通过ping
不同网段ip地址测试路由配置正确性, 主机网络和容器网段不在同一网段, 可以通过ping主机测试
$ ip netns exec container1 ping 172.1.1.2
2.5 配置其他节点/其他容器
2.5.1 本机器添加其他容器网络
主机添加多个容器可以通过2.2
方法创建容器网络, 通过2.4
方法把容器网络接入网关上。注意配置容器命名空间一端veth
接口IP地址时候需要不同IP地址。如图:
2.5.2 在其他机器配置容器网络
在添加其他机器容器配置时候,需要使用独立网段配置,各个机器网段不能冲突。可以根据2.2
,2.3
,2.4
方法添加其他主机容器网络。
3 节点间路由配置
k8s 集群每添加一个节点,集群都自动为这个节点分配一个网段。flannel
等网络组件在每个k8s节点上都会启动一个agent
, 这个服务定时获取节点集群所有节点对应容器网络网段/获取网段添加,修改,删除信息,网络组件对把这些网段同步到主机路由表上。简单一点网络组件对主机容器网段路由进行增删改的操作。
3.1 各个节点上路由配置
路由配置:
- 主机对应容器网段:
br0
容器路由器网段在启动网桥时候系统自定配置了。 - 其他主机网段: 有网络组件配置的。每台机器需要配置自己主机以外所以节点容器网段路由
Host1机器上添加容器网络段路由: 10.239.1.0/24
路由到Host2 ip 172.1.1.3/24
$host1> ip route add 10.239.1.0/24 via 172.1.1.3 dev eth0
Host2机器上添加容器网络段路由: 10.239.0.0/24
路由到Host1 ip 172.1.1.2/24
$host2> ip route add 10.239.0.0/24 via 172.1.1.2 dev eth0
3.2 节点路由测试
3.2.1 主机->对端容器
Host1->Host2容器1
# 测试host1 到host2 容器网关是否正常
$host1> ping 10.239.1.1
# Host1到Host2容器1
$host1> ping 10.239.1.2
Host2->Host1容器1
# 测试host2 到host1 容器网关是否正常
$host2> ping 10.239.1.1
# Host1到Host2容器1
$host2> ping 10.239.1.2
3.2.2 测试容器->对端主机
Host1容器1->Host2
$host1> ip netns exec container1 ping 172.1.1.3
Host2容器1到Host1
$host2> ip netns exec container1 ping 172.1.1.2
3.2.3 跨主机间的容器
Host1容器1->Host2容器1
$host1> ip netns exec container ping 10.239.1.2
Host2容器1->Host1容器1
$host2> ip netns exec container ping 10.239.0.2
4 容器访问集群之外网络
集群内所以节点上容器之间通信容器网络段地址,容器访问集群之外网络,这些ip地址无法路由回来,容器访问外部主机网络都是使用地址伪装技术。容器方面使用源地址伪装SNAT
,这个SNAT
对应容器数据包往集群外部主机发送数据时候把容器网络地址IP替换为容器对应主机上IP地址。当外部主机回应数据包时候,系统根据NAT 表TCP/IP五元组信息,或者ICMP标识符号,把回应包目标地址修改为容器地址,这个过程就是目标地址替换DNAT
。
4.1 容器网络配置SNAT
容器网络访问外网SNAT,当时容器之间是使用容器段IP受影响,配置以后识别
容器-to-容器
:保持容器网络段地址,不进行SNAT容器-to-外部主机
:容器IP替换主机IP地址,进行SNAT其他主机-to-本主机网段容器
:保持源IP不变,不进行SNAT其他主机-to-其他主机容器网段
:把其他主机网段进行SNAT,保证容器数据可以正确防护。
这些配置都是通过iptables
进行配置。
主机1上的配置
# 容器网络段(container to container) 10.239.0.0/16 网段内部通信不做snat伪装
host1>$ iptables -t nat -A POSTROUTING -s 10.239.0.0/16 -d 10.239.0.0/16 -j RETURN
# 容器网络段访问外部主机( container to external) 10.239.0.0/16 网段出去外网,把容器地址转换为主机地址
host1>$ iptables -t nat -A POSTROUTING -s 10.239.0.0/16 ! -d 224.0.0.0/4 -j MASQUERADE
# 其他机器通过Host机器跳转本主机对应的容器网络网段请求不用做地址伪装
host1>$ iptables -t nat -A POSTROUTING ! -s 10.239.0.0/16 -d 10.239.0.0/24 -j RETURN
# 其他机器通过Host机器跳转容器网络段,当时不是当前Host网络容器网络段,需要进行地址伪装
host1>$ iptables -t nat -A POSTROUTING ! -s 10.239.0.0/16 -d 10.239.0.0/16 -j MASQUERADE
主机2上有一下区别
# 容器网络段(container to container) 10.239.0.0/16 网段内部通信不做snat伪装 [集群所以节点一样]
host2>$ iptables -t nat -A POSTROUTING -s 10.239.0.0/16 -d 10.239.0.0/16 -j RETURN
# 容器网络段访问外部主机( container to external) 10.239.0.0/16 网段出去外网,把容器地址转换为主机地址 [集群所以节点一样]
host2>$ iptables -t nat -A POSTROUTING -s 10.239.0.0/16 ! -d 224.0.0.0/4 -j MASQUERADE
# 其他机器通过Host机器跳转本主机对应的容器网络网段请求不用做地址伪装
host2>$ iptables -t nat -A POSTROUTING ! -s 10.239.0.0/16 -d 10.239.1.0/24 -j RETURN
# 其他机器通过Host机器跳转容器网络段,当时不是当前Host网络容器网络段,需要进行地址伪装 [集群所以节点一样]
host2>$ iptables -t nat -A POSTROUTING ! -s 10.239.0.0/16 -d 10.239.0.0/16 -j MASQUERADE