cni 插件 flannel-vxlan 使用流程分析
这里主要研究 flannel-vxlan ,flannel-vxlan 是 overlay 类型的网络。本次实操无需安装 Kubernetes,直接调用插件来配置容器网络。
CNI 插件的本质工作有两个:
工作一:配置容器网络命名空间的网络 工作二:路由各个网络命名空间的网络流量
工作一需要实现 CNI 相关的接口,从而可以通过外部对插件进行调用。工作二常常需要额外的守护进程来完成,在 Kubernetes 中一般以 DaemonSet 的形式部署,同时以 Kubernetes 的 etcd 作为数据存储的后端。
环境介绍
节点0,10.211.55.2(用于部署 etcd) 节点1,10.211.55.32 节点2,10.211.55.57
事前准备
在节点0上部署 etcd 为网络插件提供存储服务,这里使用 Docker 部署,暴露端口为 2379,相关官网教程见 https://etcd.io/docs/v3.4/op-guide/container/ ,启动参数见 https://etcd.io/docs/v3.4/op-guide/configuration/。
docker run -p 2379:2379 quay.io/coreos/etcd:v3.3.1 /usr/local/bin/etcd --advertise-client-urls http://10.211.55.2:2379 --listen-client-urls http://0.0.0.0:2379
安装 etcdctl 工具,相关教程见 https://gist.github.com/sanjid133/fffaae1c7deb7c3d6c5f6bae549d6380,当然,也可以使用 docker exec 登录到 etcd 容器中使用它自带的 etcdctl。
安装 cni 基础网络插件至 /opt/cni/bin 下,插件仓库在 https://github.com/containernetworking/plugins 上,直接下载编译好的 release https://github.com/containernetworking/plugins/releases 然后解压到 /opt/cni/bin 下即可。
直接使用 https://www.cni.dev/docs/cnitool/ 或者自己编写如下程序调用 CNI 插件,这里采用第二种方式,该程序最终会被编译为 cnidemo
package main
import (
"context"
"flag"
"fmt"
"log"
"os"
"os/signal"
"strconv"
gocni "github.com/containerd/go-cni"
)
var (
idF = flag.Int("id", 2, "")
confFF = flag.String("conf", "/etc/cni/net.d/11-flannel.conf", "")
)
func init() {
flag.Parse()
}
func main() {
id := *idF
netns := fmt.Sprintf("/var/run/netns/ns-%d", id)
l, err := gocni.New(
gocni.WithMinNetworkCount(2),
gocni.WithPluginDir([]string{"/opt/cni/bin"}),
gocni.WithInterfacePrefix("eth"))
if err != nil {
log.Fatalf("failed to initialize cni library: %v", err)
}
if err := l.Load(gocni.WithLoNetwork, gocni.WithConfListFile(*confFF)); err != nil {
log.Fatalf("failed to load cni configuration: %v", err)
}
ctx := context.Background()
defer func() {
if err := l.Remove(ctx, strconv.Itoa(id), netns); err != nil {
log.Fatalf("failed to teardown network: %v", err)
}
}()
result, err := l.Setup(ctx, strconv.Itoa(id), netns)
if err != nil {
log.Fatalf("failed to setup network for namespace: %v", err)
}
for key, iff := range result.Interfaces {
if len(iff.IPConfigs) > 0 {
IP := iff.IPConfigs[0].IP.String()
fmt.Printf("IP of the interface %s:%s\n", key, IP)
}
}
ch := make(chan os.Signal, 1)
signal.Notify(ch, os.Interrupt)
<-ch
}
flannel-vxlan
flannel 网络插件分为两个部分,第一部分为 https://github.com/containernetworking/plugins 自带的 flannel ,用于完成工作一,通过调用 bridge 插件完成对容器命名空间网络的配置,而 https://github.com/flannel-io/flannel 用于完成工作二,使用 vxlan 实现容器跨节点通信。
手动部署的官网文件见 https://github.com/flannel-io/flannel/blob/master/Documentation/running.md
首先,利用 etcdctl 向 etcd 中添加相关的网络配置,注意这里使用的 etcd api 版本为 v2。
etcdctl --endpoints=http://10.211.55.2:2379 set /coreos.com/network/config '{ "Network": "10.5.0.0/16", "Backend": {"Type": "vxlan"}}'
在节点1和节点2上下载 https://github.com/flannel-io/flannel/releases 二进制程序并启动。
./flanneld-amd64 -etcd-endpoints http://10.211.55.2:2379
在节点1和节点2的 /etc/cni/net.d/ 下编写 flannel 配置文件 11-flannel.conf。此处的配置信息参考 https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml 中 ConfigMap 的 cni-conf.json 部分。
{
"name":"cbr0",
"cniVersion":"0.3.1",
"plugins":[
{
"type":"flannel",
"delegate":{
"hairpinMode":true,
"isDefaultGateway":true
}
},
{
"type":"portmap",
"capabilities":{
"portMappings":true
}
}]
}
在 节点1 和 节点2 上分别创建网络命名空间 ns-2,ns-3。
ip netns add ns-x
使用自己编写的 cnidemo 调用 flannel 插件来配置 ns-2,ns-3。
# 节点1
./cnidemo -id 2 -conf /etc/cni/net.d/11-flannel.conf
# 节点2
./cnidemo -id 3 -conf /etc/cni/net.d/11-flannel.conf
程序会输出容器网络命名空间的 ip ,我这里是 10.5.67.4 和 10.5.7.2。可以在 ns-2 中启动一个 http server,在 ns-3 中测试是否可以访问。如果可以成功访问,则表示 flannel 网络插件正常运行了。
# 节点1
ip netns exec ns-2 python3 -m http.server 8080
# 节点2
ip netns exec ns-3 curl http://10.5.67.4:8080
分析
flannel 利用 etcd 来对数据进行存取,利用 etcdctl 插件它在 etcd 中存储的数据。
etcdctl --endpoints=http://10.211.55.2:2379 ls /coreos.com/network
输出如下:
/coreos.com/network/config
/coreos.com/network/subnets
第一个就是之前配置的内容,这里查看第二个 key 的数据:
etcdctl --endpoints=http://10.211.55.2:2379 ls /coreos.com/network/subnets
输出如下:
/coreos.com/network/subnets/10.5.67.0-24
/coreos.com/network/subnets/10.5.7.0-24
可以看出,这是当前两个节点在容器网络中的子网信息,挑选第一个进行查看:
etcdctl --endpoints=http://10.211.55.2:2379 get /coreos.com/network/subnets/10.5.67.0-24
输出如下:
{"PublicIP":"10.211.55.32","BackendType":"vxlan","BackendData":{"VtepMAC":"92:3a:42:4a:9a:a9"}}
由于 vxlan 构建的是一个二层 overlay 网络,所以这里 BackendData 数据记录的是 flannel 守护进程创建的网卡 flannel.1 的物理 MAC 地址。这个 flannel.1 是 VTEP(VXLAN Tunnel Endpoints),输入命令查看其相关信息:
ip -d link show flannel.1
输出如下:
flannel.1: mtu 1450 qdisc noqueue state UNKNOWN mode DEFAULT group default
link/ether 92:3a:42:4a:9a:a9 brd ff:ff:ff:ff:ff:ff promiscuity 0
vxlan id 1 local 10.211.55.32 dev enp0s5 srcport 0 0 dstport 8472 nolearning ageing 300 udpcsum addrgenmode none
在另一个节点节点2上查看转发表:
bridge fdb show dev flannel.1
输出如下:
92:3a:42:4a:9a:a9 dst 10.211.55.32 self permanent
原文链接:https://blog.schwarzeni.com/2021/05/02/CNI-%E6%8F%92%E4%BB%B6-flannel-vxlan-%E4%BD%BF%E7%94%A8%E5%AE%9E%E6%93%8D/