6

Kubernetes下Flannel网络

 2 years ago
source link: https://zhangrr.github.io/posts/20220317-kubernetes_flannel/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Kubernetes下Flannel网络

2022-03-17 6 分钟阅读

现在各大公有云的 k8s 网络插件基本用的都是 vxlan,我们也需要对这个进行一下详细了解,以便用于公司的正式生产环境。

首先,kubernetes的网络模型:

image-20220317101247744

包含三要素:

  • 所有的容器(container)之间能够在不使用 NAT 的情况下互相通讯

  • 所有的节点(Node)能够在不使用 NAT 的情况下跟所有的容器(container)互相通讯(反之容器访问节点亦然)

  • 容器(container)中的IP地址,在容器内和容器外面看起来是一样的

那来到 flannel,它是一种 Overlay network 覆盖网络,盖在原有的 Node 网络基础上: image-20220317101802355

上图要仔细分析, K8S 中存在三个+一个网络段:

  • node 网络段,上图是 172.20.32.0/19,这是基础网络段
  • pod 网络段,100.96.0.0/16,2¹⁶(65536)个IP,这是由 flannel 产生的 overlay network,所有的 pod 都站在一个大广场上,互相可见
  • svc 网络段,上图未提,我们需要知道,想要把 pod 的 ip 给固定下来,就得使用 svc 来分配固定的域名,这个是由 iptables 来维护的
  • In-Host docker network网络段,这是每个 Node 主机的单独网络段,flannel给每个 Node 主机划分了一个 100.96.x.0/24 段,然后在 etcd 内进行维护,来避免不同的 Node 主机分配IP冲突。

综述:flannel 为每个 Node 分配一个 subnet,容器(container)从此 subnet 中分配 IP,这些 IP 可以在 Node 间路由,容器间无需 NAT 和 port mapping 就可以跨主机通信。

每个 subnet 都是从一个更大的 IP 池中划分的,flannel 会在每个 Node 上运行一个叫 flanneld 的 agent,其职责就是从池子中分配 subnet。为了在各个主机间共享信息,flannel 用 etcd(与 consul 类似的 key-value 分布式数据库)存放网络配置、已分配的 subnet、host 的 IP 等信息。

数据包如何在主机间转发是由 backend 实现的。flannel 提供了多种 backend,最常用的有 vxlan 和 host-gw,udp 万万不可使用。

仔细分析一下两个不同主机上的container跨主机互相通讯的过程:

image-20220317105412532

container-1 首先建立 IP 包, src: 100.96.1.2 -> dst: 100.96.2.3, 包发到 docker0 网桥,docker0 是 container-1 的缺省 gateway 网关。

在每个 Node 上,flannel 都会跑一个flanneld的守护进程,它会在 Linux 的 kernel 路由表中建立若干条路由, Node1 的路由表如下:

admin@ip-172-20-33-102:~$ ip route
default via 172.20.32.1 dev eth0
100.96.0.0/16 dev flannel0  proto kernel  scope link  src 100.96.1.0
100.96.1.0/24 dev docker0  proto kernel  scope link  src 100.96.1.1
172.20.32.0/19 dev eth0  proto kernel  scope link  src 172.20.33.102
fallback

对照路由表,dst 100.96.2.3 的路由会落到 100.96.0.0/16 这一条上,也就是说会落到 flannel0 这个设备上继续转发出去。

flannel0 呢本质上是一个由flanneld 进程建立的 TUN 虚拟网卡设备:

  • 写TUN:当 IP 包写到flannel0后,会转发到 kernel,然后 kernel 查路由表再转发
  • 读TUN:当 IP 包首先到达核心,路由表显示下一跳是flannel0虚拟网卡,kernel会直接把包发到产生这个虚拟网卡的进程去,也就是flanneld进行读包

看上图,IP 包首先到达 Docker0,因为它是网关,然后查内核路由表,到达flannel0虚拟网卡,然后就到达flanneld进程读包, 这时候flanneld会做什么呢?

flanneld从 etcd 中获得各个主机网段对应的节点信息:

admin@ip-172-20-33-102:~$ etcdctl ls /coreos.com/network/subnets
/coreos.com/network/subnets/100.96.1.0-24
/coreos.com/network/subnets/100.96.2.0-24
/coreos.com/network/subnets/100.96.3.0-24

admin@ip-172-20-33-102:~$ etcdctl get /coreos.com/network/subnets/100.96.2.0-24
{"PublicIP":"172.20.54.98"}
fallback

于是乎 Node1 上面的 flanneld 进程得知 100.96.2.3 IP对应的网段是 100.96.2.0/24 ,再进一步对应到 Node2 的公网IP 172.20.54.98,然后它就会继续封装这个IP包,用UDP或者VXLAN,把这个 IP 包再包裹一层封起来送到 Node2 的 flanneld 进程去。

Node2:包首先从网卡到达 Node2 的核心 kernel,路由表显示下一跳是flannel0 虚拟网卡,包就转发到 flanneld 进程读包,flanneld接收到包后,就会做拆包,拆完包直接写包到 TUN,然后包到达本地核心路由,再查路由表转发到docker0,最终到达 container-2

我们同样可以查看 Node2 的路由表,应该显示如下结果:

admin@ip-172-20-54-98:~$ ip route
default via 172.20.32.1 dev eth0
100.96.0.0/16 dev flannel0  proto kernel  scope link  src 100.96.2.0
100.96.2.0/24 dev docker0  proto kernel  scope link  src 100.96.2.1
172.20.32.0/19 dev eth0  proto kernel  scope link  src 172.20.54.98
fallback

这样整个过程就明晰了。

道理明晰了,下面我们就需要来实际操作了。

直接在 k8s 里装就很简单,用 yaml 一步操作就行了,这里我们不做任何介绍。

我们下面说的是如何单独把 flannel 单独拿出来使用:

1、首先要装etcd

参照之前的帖子:Etcd单节点应用

这里也给出不用Docker,用systemctl来运行的方案:

准备工作,关闭selinux,打开包转发:

echo 1 > /proc/sys/net/ipv4/ip_forward
#或者修改/etc/sysctl.conf,然后sysctl -p
#net.ipv4.ip_forward = 1

systemctl disable firewalld.service
systemctl stop firewalld.service

iptables -P FORWARD ACCEPT

安装 etcd :

yum install etcd -y

cp /etc/etcd/etcd.conf/etc/etcd/etcd.conf.bak

vi /etc/etcd/etcd.conf
ETCD_LISTEN_PEER_URLS="http://172.16.9.110:2380"
ETCD_LISTEN_CLIENT_URLS=http://172.16.9.110:2379,http://127.0.0.1:2379
ETCD_NAME="default"
ETCD_INITIAL_ADVERTISE_PEER_URLS="http://172.16.9.110:2380"
ETCD_ADVERTISE_CLIENT_URLS=http://172.16.9.110:2379

systemctl enable --now etcd

配置 etcd:

vi /root/etcd.sh
{ "Network": "10.10.0.0/16","SubnetLen": 24,"Backend": {"Type":"vxlan"} }

etcdctl --endpoints=http://172.16.9.110:2379 set kubernetes‑cluster/network/config < /root/etcd.sh

etcdctl ls kubernetes‑cluster/network/config

curl -s http://172.16.9.110:2379/v2/keys/kubernetes‑cluster/network/subnets 

解释一下:pod 的网段是 10.10.0.0/16,掩码是 /16 ,本地 Node 主机的掩码是/24,也就是说一台宿主机上最多产256台container。

然后 etcd 的 key 是 kubernetes‑cluster/network

2、然后装 flannel:

注意 etcd 的配置跟上面一致:

yum install flannel -y

cp /etc/sysconfig/flanneld/etc/sysconfig/flanneld.bak

vi/etc/sysconfig/flanneld
FLANNEL_ETCD_ENDPOINTS=http://172.16.9.110:2379
FLANNEL_ETCD_PREFIX="kubernetes‑cluster/network"

systemctl enable --now flanneld

3、重启Docker

编辑docker启动配置文件:

cat /run/flannel/subnet.env
FLANNEL_NETWORK=10.10.0.0/16
FLANNEL_SUBNET=10.10.1.1/24
FLANNEL_MTU=1450
FLANNEL_IPMASQ=false

/usr/lib/systemd/system/docker.service

# Add: --bip= and --mtu=

vi /usr/lib/systemd/system/docker.service
dockerd --bip=$FLANNEL_SUBNET --mtu=$FLANNEL_MTU

或者
cat /run/flannel/docker
DOCKER_OPT_BIP="‑‑bip=10.10.1.1/24"
DOCKER_OPT_IPMASQ="‑‑ip‑masq=true"
DOCKER_OPT_MTU="‑‑mtu=1450"
DOCKER_NETWORK_OPTIONS=" ‑‑bip=10.10.1.1/24 ‑‑ip‑masq=false ‑‑mtu=1450 "

cat /etc/systemd/system/docker.service.d/docker.conf
ServiceEnvironmentFile=‑/etc/sysconfig/docker
EnvironmentFile=‑/etc/sysconfig/docker‑storage
EnvironmentFile=‑/etc/sysconfig/docker‑network
EnvironmentFile=‑/run/flannel/docker
ExecStart=
ExecStart=/usr/bin/dockerd $OPTIONS 
          $DOCKER_STORAGE_OPTIONS 
          $DOCKER_NETWORK_OPTIONS 
          $BLOCK_REGISTRY 
          $INSECURE_REGISTRY

然后就可以了。

那么为什么 flannel 不用 UDP 呢?

image-20220317130659132

看上图,包从用户空间到内核空间的流入流出,会经过1、2、3的来回拷贝翻转,性能损失较大,所以 UDP 只能用在测试环境。

最后 flannel 的 VXLAN 和 HOST-GW 又有什么区别呢?

  • 与 vxlan 不同,host-gw 不会封装数据包,而是在主机的路由表中创建到其他主机 subnet 的路由条目,从而实现容器跨主机通信
  • host-gw 把每个主机都配置成网关,主机知道其他主机的 subnet 和转发地址。
  • vxlan 则在主机间建立隧道,不同主机的容器都在一个大的网段内(比如 10.2.0.0/16)。
  • 虽然 vxlan 与 host-gw 使用不同的机制建立主机之间连接,但对于容器则无需任何改变。
  • 由于 vxlan 需要对数据进行额外打包和拆包,性能会稍逊于 host-gw。

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK