乐趣区

关于后端:从-Flannel-学习-Kubernetes-overlay-网络

这是 Kubernetes 网络学习的第四篇笔记。

  • 深刻摸索 Kubernetes 网络模型和网络通信
  • 认识一下容器网络接口 CNI
  • 源码剖析:从 kubelet、容器运行时看 CNI 的应用
  • 从 Flannel 学习 Kubernetes VXLAN 网络(本篇)
  • Cilium CNI 与 eBPF

Flannel 介绍

Flannel 是一个非常简单的 overlay 网络(VXLAN),是 Kubernetes 网络 CNI 的解决方案之一。Flannel 在每台主机上运行一个简略的轻量级 agent flanneld 来监听集群中节点的变更,并对地址空间进行预配置。Flannel 还会在每台主机上安装 vtep flannel.1(VXLAN tunnel endpoints),与其余主机通过 VXLAN 隧道相连。

flanneld 监听在 8472 端口,通过 UDP 与其余节点的 vtep 进行数据传输。达到 vtep 的二层包会被一成不变地通过 UDP 的形式发送到对端的 vtep,而后拆出二层包进行解决。简略说就是用四层的 UDP 传输二层的数据帧。

在 Kubernetes 发行版 K3S 中将 Flannel 作为默认的 CNI 实现。K3S 集成了 flannel,在启动后 flannel 以 go routine 的形式运行。

环境搭建

Kubernetes 集群应用 k3s 发行版,但在装置集群的时候,禁用 k3s 集成的 flannel,应用独立装置的 flannel 进行验证。

装置 CNI 的 plugin,须要在所有的 node 节点上执行上面的命令,下载 CNI 的官网 bin。

sudo mkdir -p /opt/cni/bin
curl -sSL https://github.com/containernetworking/plugins/releases/download/v1.1.1/cni-plugins-linux-amd64-v1.1.1.tgz | sudo tar -zxf - -C /opt/cni/bin

装置 k3s 的管制立体。

export INSTALL_K3S_VERSION=v1.23.8+k3s2
curl -sfL https://get.k3s.io | sh -s - --disable traefik --flannel-backend=none --write-kubeconfig-mode 644 --write-kubeconfig ~/.kube/config

装置 Flannel。 这里留神,Flannel 默认的 Pod CIRD 是 10.244.0.0/16,咱们将其批改为 k3s 默认的 10.42.0.0/16

curl -s https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml | sed 's|10.244.0.0/16|10.42.0.0/16|g' | kubectl apply -f -

增加另一个节点到集群。

export INSTALL_K3S_VERSION=v1.23.8+k3s2
export MASTER_IP=<MASTER_IP>
export NODE_TOKEN=<TOKEN>
curl -sfL https://get.k3s.io | K3S_URL=https://${MASTER_IP}:6443 K3S_TOKEN=${NODE_TOKEN} sh -

查看节点状态。

kubectl get node
NAME          STATUS   ROLES                  AGE   VERSION
ubuntu-dev3   Ready    <none>                 13m   v1.23.8+k3s2
ubuntu-dev2   Ready    control-plane,master   17m   v1.23.8+k3s2

运行两个 pod:curlhttpbin,为了探寻

NODE1=ubuntu-dev2
NODE2=ubuntu-dev3
kubectl apply -n default -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
  labels:
    app: curl
  name: curl
spec:
  containers:
  - image: curlimages/curl
    name: curl
    command: ["sleep", "365d"]
  nodeName: $NODE1
---
apiVersion: v1
kind: Pod
metadata:
  labels:
    app: httpbin
  name: httpbin
spec:
  containers:
  - image: kennethreitz/httpbin
    name: httpbin
  nodeName: $NODE2
EOF

网络配置

接下来,一起看下 CNI 插件如何配置 pod 网络。

初始化

Flannel 是通过 Daemonset 的形式部署的,每台节点上都会运行一个 flannel 的 pod。通过挂载本地磁盘的形式,在 Pod 启动时会通过初始化容器将二进制文件和 CNI 的配置复制到本地磁盘中,别离位于 /opt/cni/bin/flannel/etc/cni/net.d/10-flannel.conflist

通过查看 kube-flannel.yml 中的 ConfigMap,能够找到 CNI 配置,flannel 默认委托(见 flannel-cni 源码 flannel_linux.go#L78)给 bridge 插件 进行网络配置,网络名称为 cbr0;IP 地址的治理,默认委托(见 flannel-cni 源码 flannel_linux.go#L40)host-local 插件 实现。

#cni-conf.json 复制到 /etc/cni/net.d/10-flannel.conflist
{
  "name": "cbr0",
  "cniVersion": "0.3.1",
  "plugins": [
    {
      "type": "flannel",
      "delegate": {
        "hairpinMode": true,
        "isDefaultGateway": true
      }
    },
    {
      "type": "portmap",
      "capabilities": {"portMappings": true}
    }
  ]
}

还有 Flannel 的网络配置,配置有咱们设置的 Pod CIDR 10.42.0.0/16 以及后端(backend)的类型 vxlan。这也是 flannel 默认的类型,此外还有 多种后端类型 可选,如 host-gwwireguardudpAllocIPIPIPSec

#net-conf.json 挂载到 pod 的 /etc/kube-flannel/net-conf.json
{
  "Network": "10.42.0.0/16",
  "Backend": {"Type": "vxlan"}
}

Flannel Pod 运行启动 flanneld 过程,指定了参数 --ip-masq--kube-subnet-mgr,后者开启了 kube subnet manager 模式。

运行

集群初始化时应用了默认的 Pod CIDR 10.42.0.0/16,当有节点退出集群,集群会从该网段上为节点调配 属于节点的 Pod CIDR 10.42.X.1/24

flannel 在 kube subnet manager 模式下,连贯到 apiserver 监听节点更新的事件,从节点信息中获取节点的 Pod CIDR。

kubectl get no ubuntu-dev2 -o jsonpath={.spec} | jq
{
  "podCIDR": "10.42.0.0/24",
  "podCIDRs": ["10.42.0.0/24"],
  "providerID": "k3s://ubuntu-dev2"
}

而后在主机上写子网配置文件,上面展现的是其中一个节点的子网配置文件的内容。另一个节点的内容差别在 FLANNEL_SUBNET=10.42.1.1/24,应用的是对应节点的 Pod CIDR。

#node 192.168.1.12
cat /run/flannel/subnet.env
FLANNEL_NETWORK=10.42.0.0/16
FLANNEL_SUBNET=10.42.0.1/24
FLANNEL_MTU=1450
FLANNEL_IPMASQ=true

CNI 插件执行

CNI 插件的执行是由容器运行时触发的,具体细节能够看上一篇《源码解析:从 kubelet、容器运行时看 CNI 的应用》。

flannel 插件

flannel CNI 插件(/opt/cni/bin/flannel)执行的时候,接管传入的 cni-conf.json,读取下面初始化好的 subnet.env 的配置,输入后果,委托给 bridge 进行下一步。

cat /var/lib/cni/flannel/e4239ab2706ed9191543a5c7f1ef06fc1f0a56346b0c3f2c742d52607ea271f0 | jq
{
  "cniVersion": "0.3.1",
  "hairpinMode": true,
  "ipMasq": false,
  "ipam": {
    "ranges": [
      [
        {"subnet": "10.42.0.0/24"}
      ]
    ],
    "routes": [
      {"dst": "10.42.0.0/16"}
    ],
    "type": "host-local"
  },
  "isDefaultGateway": true,
  "isGateway": true,
  "mtu": 1450,
  "name": "cbr0",
  "type": "bridge"
}

bridge 插件

bridge 应用下面的输入连同参数一起作为输出,依据配置实现如下操作:

  1. 创立网桥 cni0(节点的根网络命名空间)
  2. 创立容器网络接口 eth0(pod 网络命名空间)
  3. 创立主机上的虚构网络接口 vethX(节点的根网络命名空间)
  4. vethX 连贯到网桥 cni0
  5. 委托 ipam 插件调配 IP 地址、DNS、路由
  6. 将 IP 地址绑定到 pod 网络命名空间的接口 eth0
  7. 查看网桥状态
  8. 设置路由
  9. 设置 DNS

最初输入如下的后果:

cat /var/li/cni/results/cbr0-a34bb3dc268e99e6e1ef83c732f5619ca89924b646766d1ef352de90dbd1c750-eth0 | jq .result
{
  "cniVersion": "0.3.1",
  "dns": {},
  "interfaces": [
    {
      "mac": "6a:0f:94:28:9b:e7",
      "name": "cni0"
    },
    {
      "mac": "ca:b4:a9:83:0f:d4",
      "name": "veth38b50fb4"
    },
    {
      "mac": "0a:01:c5:6f:57:67",
      "name": "eth0",
      "sandbox": "/var/run/netns/cni-44bb41bd-7c41-4860-3c55-4323bc279628"
    }
  ],
  "ips": [
    {
      "address": "10.42.0.5/24",
      "gateway": "10.42.0.1",
      "interface": 2,
      "version": "4"
    }
  ],
  "routes": [
    {"dst": "10.42.0.0/16"},
    {
      "dst": "0.0.0.0/0",
      "gw": "10.42.0.1"
    }
  ]
}

port-mapping 插件

该插件会未来自主机上一个或多个端口的流量转发到容器。

Debug

让咱们在第一个节点上,应用 tcpdump 对接口 cni0 进行抓包。

tcpdump -i cni0 port 80 -vvv

从 pod curl 中应用 pod httpbin 的 IP 地址 10.42.1.2 发送申请:

kubectl exec curl -n default -- curl -s 10.42.1.2/get

cni0

从在 cni0 上的抓包后果来看,第三层的 IP 地址均为 Pod 的 IP 地址,看起来就像是两个 pod 都在同一个网段。

host eth0

文章结尾提到 flanneld 监听 udp 8472 端口。

netstat -tupln | grep 8472
udp        0      0 0.0.0.0:8472            0.0.0.0:*                           -

咱们间接在以太网接口上抓取 UDP 的包:

tcpdump -i eth0 port 8472 -vvv

再次发送申请,能够看到抓取到 UDP 数据包,传输的负载是二层的封包。

Overlay 网络下的跨节点通信

在系列的第一篇中,咱们钻研 pod 间的通信时提到不同 CNI 插件的解决形式不同,这次咱们摸索了 flannel 插件的工作原理。心愿通过上面的图能够对 overlay 网络解决跨节点的网络通信有个比拟直观的意识。

当发送到 10.42.1.2 流量达到节点 A 的网桥 cni0,因为指标 IP 并不属于以后阶段的网段。依据零碎的路由规定,进入到接口 flannel.1,也就是 VXLAN 的 vtep。这里的路由规定也由 flanneld 来保护,当节点上线或者下线时,都会更新路由规定。

#192.168.1.12
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
default         _gateway        0.0.0.0         UG    0      0        0 eth0
10.42.0.0       0.0.0.0         255.255.255.0   U     0      0        0 cni0
10.42.1.0       10.42.1.0       255.255.255.0   UG    0      0        0 flannel.1
192.168.1.0     0.0.0.0         255.255.255.0   U     0      0        0 eth0
#192.168.1.13
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
default         _gateway        0.0.0.0         UG    0      0        0 eth0
10.42.0.0       10.42.0.0       255.255.255.0   UG    0      0        0 flannel.1
10.42.1.0       0.0.0.0         255.255.255.0   U     0      0        0 cni0
192.168.1.0     0.0.0.0         255.255.255.0   U     0      0        0 eth0

flannel.1 将原始的以太封包应用 UDP 协定从新封装,将其发送到指标地址 10.42.1.0(指标的 MAC 地址通过 ARP 获取)。对端的 vtep 也就是 flannel.1 的 UDP 端口 8472 收到音讯,解帧出以太封包,而后对以太封包进行路由解决,发送到接口 cni0,最终达到指标 pod 中。

响应的数据传输与申请的解决也是相似,只是源地址和目标地址调换。

关注 ” 云原生指北 ” 微信公众号
(转载本站文章请注明作者和出处盛世浮生,请勿用于任何商业用途)

退出移动版