1.概述

kuberenes 容器网络控件工作粗略可以分为三个部分。

  • 节点上容器网关创建和配置
  • 节点上容器网络管理: 容器网络接口创建和删除, 容器IP添加和释放, IP网络访问控制
  • 节点间网段管理和路由: 节点子网创建和释放,节点间子网路由表管理

host-gw 是kubernetes 网络组件之一flannel一个性能最高一种模式。由于fannel是kubenets上最基本组件

通过下面手动配置命令,便于去理解容器网络管理源码。

2.节点上容器网络管理

容器网络和Host机器网络通过网络命名空间进行隔离。简单的说把容器虚拟网卡,以及需要路由配置,iptables放到指定容器网络空间里面。按照下面网络规划图, 下面各个节详细说明怎么一步步进行配置:
跨主机网络和容器网络.jpg

2.1 一切从网络命名空间开始

容器网络核心就是网络命名空间,命令空间创建以后,命名空间默认包含lo接口, 空路由表, 空iptables,如图:
容器网络模式: host-gw 手动搭建 - 图2
如上图创建名字为container1网络命名空间

  1. $ ip netns add container1

2.2 构建容器网络空间和根网络空间桥梁

容器网络空间和主机根网络空间通过虚拟设备veth-pair 进行连接, veth-pair由两个网络接口成对组成,一头接口容器网络空间,另外一头接入根网络空间网桥上, 这个网桥在flannel k8s网络一般命名为cni0。下面是以主机Host1为网络规划为例子,进行配置。

2.2.1 创建veth-pair

创建veth-pair默认在根网络空间上成对创建的, 创建以后如图:
创建veth对.jpg
创建网络接口对veth0->veth0-tmp, tmp一端后面用于移动到指定网络空间上

  1. $ ip link add veth0 type veth peer name veth0-tmp

2.2.2 移动接口到指定网络空间

veth-pair一端名字为veth0-tmp移动到container1网络空间, 如图:
移动veth接口指定网络空间.jpg

  1. $ip link set dev veth0-tmp netns container1

2.2.3 配置网络空间接口

接口移动到网络命名空间以后, 配置对接口进行一些配置,配置步骤如下

  • 修改veth接口名字: vethx-tmp -> eth0
  • veth接口添加ip地址
  • 启动网络空间中eth0接口和lo接口: 接口添加以后默认状态是down

上面这些操作都有一个共性,不是在默认根网络空间进行配置,而是在容器网络空间进行操作的。在网络空间执行命令可以通过这个方式执行

  1. $ ip netns exec [网络空间名字] [cmd] [param1, parm2, ....]
  2. # 例如在网络命名空间 container1 执行 ping 命令
  3. $ ip netns exec container1 ping 127.0.0.1

2.2.3.1 修改网络空间接口名称

网络空间一端veth接口, 在主空间创建时候为了减少名字冲突,使用随机名称,移动到指定命名空间,为了后面配置脚本统一,把 接口随机名字修改eth0,方面用户程序使用,用户理解。
修改网络空间接口名字.jpg
把网络命名空间container1接口veth0-tmp修改为eth0

  1. $ 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/24ip地址到接口eth0上,eth0是容器默认接口,需要在此接口配置ip地址,如图:
修改网络空间接口IP.jpg
把网络命名空间container1接口eth0添加ip地址10.239.0.2/24

  1. $ ip netns exec container1 ip addr add 10.239.0.2/24 dev eth0

2.2.3.3 启动网络空间接口

默认情况下,新建接口都是没有启动,需要手工启动以后才可以使用。网络空间里面两个接口都需要启动:

  • eth0: 需要和外界通信
  • lo: 回环接口不启动自己ping自己就不通了

启动网络空间接口.jpg

  1. # 启动接口 eth0
  2. $ ip netns exec container1 ip link set eth0 up
  3. # 启动接口 lo
  4. $ ip netns exec container1 ip link set lo up

2.2.3.4 测试容器空间接口

测试接口是否正确配置IP地址, 已经接口启动成功

下面测试容器网络命名空间1, eth0接口

  1. $ ip netns exec container1 ping 10.239.0.2
  2. PING 10.239.0.2 (10.239.0.2) 56(84) bytes of data.
  3. 64 bytes from 10.239.0.2: icmp_seq=1 ttl=64 time=0.035 ms

下面测试容器网络命名空间1, lo接口

  1. $ ip netns exec container1 ping 127.0.0.1
  2. PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
  3. 64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.240 ms

如果都可以ping通代表配置正常

2.3 节点容器网关配置

同一台机器上多个容器网络空间怎么通信,需要有交换机switch,然后把容器网络空间在root network namespace一端veth接入这个设备上。Linux网桥作用类似于网络switch。它会在连接到其上的接口间转发网络包。并且因为它是switch,它是在L2层完成这些转发的。容器网关就是switch角色。

2.3.1 创建容器网关br0

容器网关是通过网桥实现的,flannel host-gw模式下容器网桥名字为cni0,我们这边用br0代表吧, 网桥是放在根网络空间上的,如图:
添加容器网关br0.jpg
在根网络空间创建名字为br0网桥接口

  1. $ ip link add br0 type bridge

2.3.2 配置容器网关br0

网桥接口创建以后也和容器接口一样,需要配置ip地址和启动.

2.3.2.1 配置网关网关IP

配置容器网关IP.jpg
配置网桥br0ip地址为10.239.0.1/24

  1. $ ip addr add 10.239.0.1/24 dev br0

2.3.2.2 启动网段接口

容器switch需要启动以后才可以使用
启动容器网关.jpg

  1. $ ip link set br0 up

网桥接口启动成功以后,系统自动添加路由策略:
启动容器网关-第 2 页.jpg

2.4 把容器网络连接网关上

把容器在根分区接口接入到根网络空间交换机上,多个容器之间可以通过交换转发数据了。

2.4.1 把接口对一端接入网桥

把veth接口根根网络空间一端接入到网桥接口上,并且启用这个接口
容器接口对接入到网桥-第 1 页.jpg
veth0接口接入网桥br0上,启动veth0接口

  1. # 把接口接入到br0上
  2. $ ip link set dev veth0 master br0
  3. # 启动接口
  4. $ ip link set dev veth0 up

2.4.2 配置容器空间默认网关

跨网段通信的时候需要通过路由器转发, 容器需要其他网段通信时候需要通过路由转发,需要在容器网络空间中配置默认路由,默认路由地址就是网桥地址

容器和容器网段外的节点通信需要通过br0作为路由器转发,说最终去了br0以后通过root network namespace路由策略进行转发的。

在容器容器1添加默认路由, 路由器地址为br0ip,

  1. $ ip netns exec container1 ip route add default via 10.239.0.1 dev eth0

配置容器命名空间默认路由-第 2 页.jpg

2.4.3 测试路由配置正确性

通过ping不同网段ip地址测试路由配置正确性, 主机网络和容器网段不在同一网段, 可以通过ping主机测试

  1. $ ip netns exec container1 ping 172.1.1.2

如果网络通了,代表路由容器到路由器配置正确。

2.5 配置其他节点/其他容器

2.5.1 本机器添加其他容器网络

主机添加多个容器可以通过2.2方法创建容器网络, 通过2.4方法把容器网络接入网关上。注意配置容器命名空间一端veth接口IP地址时候需要不同IP地址。如图:
添加其他容器.jpg

2.5.2 在其他机器配置容器网络

在添加其他机器容器配置时候,需要使用独立网段配置,各个机器网段不能冲突。可以根据2.2,2.3,2.4方法添加其他主机容器网络。
添加多主机的容器网络.jpg

3 节点间路由配置

k8s 集群每添加一个节点,集群都自动为这个节点分配一个网段。flannel等网络组件在每个k8s节点上都会启动一个agent, 这个服务定时获取节点集群所有节点对应容器网络网段/获取网段添加,修改,删除信息,网络组件对把这些网段同步到主机路由表上。简单一点网络组件对主机容器网段路由进行增删改的操作。

3.1 各个节点上路由配置

路由配置:

  • 主机对应容器网段: br0容器路由器网段在启动网桥时候系统自定配置了。
  • 其他主机网段: 有网络组件配置的。每台机器需要配置自己主机以外所以节点容器网段路由

添加多主机的容器网络-添加路由.jpg

Host1机器上添加容器网络段路由: 10.239.1.0/24路由到Host2 ip 172.1.1.3/24

  1. $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

  1. $host2> ip route add 10.239.0.0/24 via 172.1.1.2 dev eth0

3.2 节点路由测试

通过测试定位配置问题

3.2.1 主机->对端容器

Host1->Host2容器1

  1. # 测试host1 到host2 容器网关是否正常
  2. $host1> ping 10.239.1.1
  3. # Host1到Host2容器1
  4. $host1> ping 10.239.1.2

Host2->Host1容器1

  1. # 测试host2 到host1 容器网关是否正常
  2. $host2> ping 10.239.1.1
  3. # Host1到Host2容器1
  4. $host2> ping 10.239.1.2

3.2.2 测试容器->对端主机

Host1容器1->Host2

  1. $host1> ip netns exec container1 ping 172.1.1.3

Host2容器1到Host1

  1. $host2> ip netns exec container1 ping 172.1.1.2

3.2.3 跨主机间的容器

Host1容器1->Host2容器1

  1. $host1> ip netns exec container ping 10.239.1.2

Host2容器1->Host1容器1

  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进行配置。
配置容器访问外部网络.jpg

主机1上的配置

  1. # 容器网络段(container to container) 10.239.0.0/16 网段内部通信不做snat伪装
  2. host1>$ iptables -t nat -A POSTROUTING -s 10.239.0.0/16 -d 10.239.0.0/16 -j RETURN
  3. # 容器网络段访问外部主机( container to external) 10.239.0.0/16 网段出去外网,把容器地址转换为主机地址
  4. host1>$ iptables -t nat -A POSTROUTING -s 10.239.0.0/16 ! -d 224.0.0.0/4 -j MASQUERADE
  5. # 其他机器通过Host机器跳转本主机对应的容器网络网段请求不用做地址伪装
  6. host1>$ iptables -t nat -A POSTROUTING ! -s 10.239.0.0/16 -d 10.239.0.0/24 -j RETURN
  7. # 其他机器通过Host机器跳转容器网络段,当时不是当前Host网络容器网络段,需要进行地址伪装
  8. host1>$ iptables -t nat -A POSTROUTING ! -s 10.239.0.0/16 -d 10.239.0.0/16 -j MASQUERADE

主机2上有一下区别

  1. # 容器网络段(container to container) 10.239.0.0/16 网段内部通信不做snat伪装 [集群所以节点一样]
  2. host2>$ iptables -t nat -A POSTROUTING -s 10.239.0.0/16 -d 10.239.0.0/16 -j RETURN
  3. # 容器网络段访问外部主机( container to external) 10.239.0.0/16 网段出去外网,把容器地址转换为主机地址 [集群所以节点一样]
  4. host2>$ iptables -t nat -A POSTROUTING -s 10.239.0.0/16 ! -d 224.0.0.0/4 -j MASQUERADE
  5. # 其他机器通过Host机器跳转本主机对应的容器网络网段请求不用做地址伪装
  6. host2>$ iptables -t nat -A POSTROUTING ! -s 10.239.0.0/16 -d 10.239.1.0/24 -j RETURN
  7. # 其他机器通过Host机器跳转容器网络段,当时不是当前Host网络容器网络段,需要进行地址伪装 [集群所以节点一样]
  8. host2>$ iptables -t nat -A POSTROUTING ! -s 10.239.0.0/16 -d 10.239.0.0/16 -j MASQUERADE