kubernetes 多网卡方案之 Multus_CNI 部署和基本使用
Multus CNI 部署和基本使用
背景
一个容器启动后,在默认情况下一般都会只存在两个虚拟网络接口(loopback 和 eth0),而 loopback 的流量始终都会在本容器内或本机循环,真正对业务起到支撑作用的只有 eth0,当然这对大部分业务场景而言已经能够满足。
但是如果一个应用或服务既需要对外提供 API 调用服务,也需要满足自身基于分布式特性产生的数据同步,那么这时候一张网卡的性能显然很难达到生产级别的要求,网络流量延时、阻塞便成为此应用的一项瓶颈。
基于上述痛点和需求,容器多网络方案不断涌现。k8s 有一个多网卡规范:K8sNetworkPlumbingWG/multi-net-spec。目前已知的多网卡方案:
k8s 多网卡规范:K8sNetworkPlumbingWG/multi-net-spec
华为开发的多网络 CNI 插件:Huawei-PaaS/CNI-Genie
Intel 开发的多网络 CNI 插件: Multus-CNI
而根据开源社区活跃度、是否实现 CNI 规范以及稳定性,我们采用 multus-cni 作为在 K8s 环境下的容器多网络方案。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ltfwB2Kk-1654692278128)(https://cdn.jsdelivr.net/gh/Fly0905/note-picture@main/imag/202206071418411.png)]
Multus CNI 简单来说是一种符合 CNI(Container Network Interface)规范的开源插件,旨在为实现 K8s(Kubernetes) 环境下容器多网卡而提出的解决方案。它作为一种“元插件”,可以与其他 CNI 插件搭配使用。
Multus CNI
Multus CNI enables attaching multiple network interfaces to pods in Kubernetes.
以上是 Multus CNI 项目官方对其存在意义的精简描述,它的存在就是帮助 K8s 的 Pod(可简单理解为一组容器的集合,是 K8s 可管理的最小“容器”单位)建立多网络接口。
Multus CNI 本身不提供网络配置功能,它是通过用其他满足 CNI 规范的插件进行容器的网络配置。
如下图所示,在此场景下我们可以把 Pod 抽象为单个容器。原本容器里应仅存在 eth0 接口(loopback 忽略不计),是由主插件产生创建并配置的;而当集群环境存在 Multus CNI 插件,并添加额外配置后,将会发现此容器内不再仅有 eth0 接口,你可以利用这些新增的接口去契合实际业务需求。
CNI 规范
CNI (Container Network Interface), a Cloud Native Computing Foundation project, consists of a specification and libraries for writing plugins to configure network interfaces in Linux containers, along with a number of supported plugins. CNI concerns itself only with network connectivity of containers and removing allocated resources when the container is deleted. Because of this focus, CNI has a wide range of support and the specification is simple to implement.
以上引用了官方对其的描述,简短来讲 CNI 是一组限于容器网络面的规范,定义了容器网络资源创建、管理的规则。
但它并不仅仅是规范,它也包含了库和实现,CNI 自身实现并提供了内置且通用的网络插件,同时为第三方实现其规范预留了扩展。
CNI 将插件分成三种类型:
到这里,我们已经明白,Multus CNI 属于 Meta 类, 它可以与其他第三方插件适配(也就是主插件),主插件来作为 Pod 的主网络并且被 K8s 所感知,它们可以搭配使用且不冲突。
适用场景
这边具体讲下 multus-cni 的使用场景,一般可以在需要网络隔离的情况下使用额外网络,包括分离数据平面流量与控制平面流量。隔离网络流量对以下性能和安全性是很有用的:
安全性:
用户可以将敏感的流量发送到专为安全考虑而管理的网络平面,也可隔离不能在租户或客户间共享的私密数据。
性能:
用户可以在两个不同的平面上发送流量,以管理每个平面上流量的多少。
Multus CNI 部署
集群已选用 Calico 作为网络插件并配置为 IPIP 模式。
Multus 的快速入门方法是使用 Daemonset(一种在集群中的每个节点上运行 pod 的方法)进行部署,这会启动安装 Multus 二进制文件并配置 Multus 以供使用的 pod。
1. 安装 Multus daemonset
首先,克隆这个 GitHub 存储库。
使用kubectl
从这个 repo 应用一个 YAML 文件。
Multus daemonset 作用
启动一个 Multus 守护程序集,这会在每个节点上运行一个 pod,它在每个节点上放置一个 Multus 二进制文件
/opt/cni/bin
读取 按字典顺序(按字母顺序)的第一个配置文件
/etc/cni/net.d
,并在每个节点上为 Multus 创建一个新的配置文件/etc/cni/net.d/00-multus.conf
,此配置是自动生成的,并且基于默认网络配置(假定为按字母顺序排列的第一个配置)在每个节点上创建一个
/etc/cni/net.d/multus.d
目录,其中包含 Multus 访问 Kubernetes API 的身份验证信息。
验证安装
你可以通过查看/etc/cni/net.d/
目录进一步验证它是否已运行,并确保自动生成的/etc/cni/net.d/00-multus.conf
存在对应于按字母顺序排列的第一个配置文件。
因为 Multus CNI 使用的是 DaemonSet 类型,所以默认在所有节点都有一个实例,以下 Whereabouts 同理。
经观察,Pod 运行不久后,将会在各节点上的/opt/cni/bin/下生成 multus 的可执行文件,/etc/cni/下生成网络定义文件以及用于配置集群访问的文件。
可执行文件的作用是配置 Pod 的网络栈,DaemonSet 的作用是实现网络互通。
注:
一个 Network Namespace 的网络栈包括:网卡(Network interface)、回环设备(Loopback Device)、路由表(Routing Table)和 iptables 规则。
打开 00-multus.conf,查看其内容:
其中 Multus CNI 在 K8s 环境下的调用关系下图所示,在 Kubernetes 中,处理容器网络相关的逻辑并不会在 kubelet 主干代码里执行,而是会在具体的 CRI(Container Runtime Interface,容器运行时接口)实现里完成。
CRI 将网络定义文件以 JSON 格式通过 STDIN 方式传递给 Multus CNI 插件可执行文件。文件中 delegates 的意义在于 Multus 会调用其 delegates 指定的插件来执行,这里还有一点需要说明下,如果/etc/cni/net.d/ 目录下有多个网络定义文件,CRI 只会加载按字典顺序排在第一位的文件(即插件),即默认情况下创建 Pod 时使用的是 Calico 插件配置网络。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5Uv1VfmJ-1654692278133)(https://cdn.jsdelivr.net/gh/Fly0905/note-picture@main/imag/202206072059282.jpeg)]
2. 创建 NetworkAttachmentDefinition
我们要做的第一件事是为附加到 pod 的每个附加网络接口创建配置(NetworkAttachmentDefinition)。我们将通过创建自定义资源来做到这一点。快速启动安装的一部分会创建一个“CRD”,它是我们保存这些自定义资源的主目录——我们将在其中存储每个接口的配置。
CNI 配置
我们将添加的是一个 CNI 配置。如果你不熟悉它们,让我们快速分解它们。这是一个示例 CNI 配置:
CNI 配置是 JSON,我们在这里有一个结构,其中包含一些我们感兴趣的东西:
cniVersion
:告诉每个 CNI 插件正在使用哪个版本,如果它使用的版本太晚(或太早),可以提供插件信息。type
:这告诉 CNI 在磁盘上调用哪个二进制文件。每个 CNI 插件都是一个被调用的二进制文件。通常,这些二进制文件存储在/opt/cni/bin
每个节点上,CNI 执行这个二进制文件。在这种情况下,我们指定了loopback
二进制文件(它创建了一个环回类型的网络接口)。如果这是你第一次安装 Multus,你可能需要验证“type 字段中的插件实际上是否在/opt/cni/bin
目录中的磁盘上。additional
:这里以这个字段为例,每个 CNI 插件都可以在 JSON 中指定他们想要的任何配置参数。这些特定于你在type
现场调用的二进制文件。
再举一个例子——看看bridge CNI plugin README,它显示了额外的细节。
如果你想了解有关 CNI 配置的更多信息,可以阅读整个 CNI 规范。查看CNI 参考插件并了解它们的配置方式也可能很有用。
当 CNI 配置发生变化时,你无需重新加载或刷新 Kubelet。每次创建和删除 pod 时都会读取这些内容。因此,如果你更改配置,它将在下次创建 pod 时应用。如果需要新配置,可能需要重新启动现有的 pod。
将配置存储为自定义资源
所以,我们要创建一个额外的接口。让我们创建一个 macvlan 接口供 pod 使用。我们将创建一个自定义资源来定义接口的 CNI 配置。
请注意,在以下命令中有一个kind: NetworkAttachmentDefinition
. 这是我们配置的自定义资源类型——它是 Kubernetes 的自定义扩展,它定义了我们如何将网络连接到我们的 pod。
其次,注意config
信息。你会看到这是一个 CNI 配置,就像我们之前解释的那样。
最后但非常重要的是,请注意metadata
字段下的name
- 这里是我们为这个配置命名的地方,也是我们告诉 pod 使用这个配置的方式。
这是创建此示例配置的命令:
注意:此示例master
使用的是eth0
,应与集群中主机上的网络接口名称匹配。
NAD 对象中使用到了 macvaln 和 ipam 里的 host-local 是什么?
containernetworking 团队维护的通用 CNI 网络插件(见https://www.cni.dev/plugins/current/),网路插件根据用途分成 Main 插件、Ipam 插件和 Meta 插件:
Main插件
:主要用来创建具体的网络设备的二进制文件;Ipam插件
:主要用来负责分配 IP 地址;Meta插件
:由 CNI 社区维护的内部插件。
macvlan 就是属于 Main 插件,host-local 属于 Ipam 插件,在这里我们也可以了解到,cni 插件中至少要包含 Main 插件和 Ipam 插件。
Main 插件是用于在集群中创建额外的网络:
bridge
:创建基于网桥的额外网络可让同一主机中的 Pod 相互通信,并与主机通信。host-device
:创建 host-device 额外网络可让 Pod 访问主机系统上的物理以太网网络设备。macvlan
:创建基于 macvlan 的额外网络可让主机上的 Pod 通过使用物理网络接口与其他主机和那些主机上的 Pod 通信。附加到基于 macvlan 的额外网络的每个 Pod 都会获得一个唯一的 MAC 地址。ipvlan
:创建基于 ipvlan 的额外网络可让主机上的 Pod 与其他主机和那些主机上的 Pod 通信,这类似于基于 macvlan 的额外网络。与基于 macvlan 的额外网络不同,每个 Pod 共享与父级物理网络接口相同的 MAC 地址。SR-IOV
:创建基于 SR-IOV 的额外网络可让 Pod 附加到主机系统上支持 SR-IOV 的硬件的虚拟功能 (VF) 接口。
Ipam(IP Address Management)插件主要用来负责分配 IP 地址:
dhcp插件
,节点上需要有 DHCP server;host-local插件
,是给定子网范围,在单个几点上基于这个子网进行 ip 地址分配;static插件
,静态地址管理,直接指定 ip 地址使用的。calico-ipam
,是 clalico cni 自己的 ip 地址分配插件,是一种集中式 ip 分配插件;whereabouts
,也是一个集中式 ip 分配插件,用的比较少,使用了 sriov 设备才用到的,这个是 k8snetworkplumbingwg 社区开源的。
Meta 插件:由 CNI 社区维护的内部插件
flannel
,这就是专门为 Flannel 项目提供的 CNI 插件;tunning
,是一个通过 sysctl 调整网络设备参数的二进制文件;portmap
,是一个通过 iptables 配置端口映射的二进制文件;bandwidth
,是一个使用 Token Bucket Filter(TBF)来进行限流的二进制文件;calico
,是专门为 Calico 项目提供的 CNI 插件。
你可以使用以下方法查看你创建kubectl
的配置:
你可以通过描述它们来获得更多详细信息:
3. Pod 配置附加网络接口
我们将创建一个 pod。这看起来就像你之前创建的任何 pod 一样熟悉,但是,我们将有一个特殊的annotations
字段——一个名为k8s.v1.cni.cncf.io/networks
的字段,它的值是我们在上面创建的NetworkAttachmentDefinition
的名称,多个时候可以使用逗号分隔。
让我们继续使用以下命令创建一个 pod(它只会休眠很长时间):
你现在可以检查 pod 并查看附加了哪些接口,如下所示:
你应该注意,有 3 个接口:
lo
环回接口eth0
我们的默认网络net1
我们使用 macvlan 配置创建的新界面。
网络状态注释信息
如需进一步确认,请使用kubectl describe pod samplepod
并且会有一个注释部分,类似于以下内容:
此数据告诉我们,我们有两个 CNI 插件成功运行。
如果我想要更多网络接口怎么办?
你可以通过创建更多自定义资源然后在 pod 的注解中引用它们来向 pod 添加更多网络接口。你还可以重用配置,例如,要将两个 macvlan 接口附加到一个 pod,你可以像这样创建一个 pod:
请注意,注释现在变为k8s.v1.cni.cncf.io/networks: macvlan-conf,macvlan-conf
. 我们有两次使用相同的配置,用逗号分隔。
如果你要创建另一个自定义资源foo
,你可以使用诸如:之类的名称k8s.v1.cni.cncf.io/networks: foo,macvlan-conf
。
参考链接
https://github.com/k8snetworkplumbingwg/multus-cni
https://networkbuilders.intel.com/social-hub/video/doxsehe0
https://networkbuilders.intel.com/solutionslibrary/multiple-network-interfaces-support-in-kubernetes-feature-brief
https://blog.crazytaxii.com/posts/multus_cni/
https://www.cni.dev/plugins/current/ipam/host-local/
http://119.23.219.145/posts/kubernetes-cni-%E7%9A%84%E5%9F%BA%E6%9C%AC%E5%8E%9F%E7%90%86/
https://access.redhat.com/documentation/zh-cn/openshift_container_platform/4.3/html/networking/multiple-networks
版权声明: 本文为 InfoQ 作者【琦彦】的原创文章。
原文链接:【http://xie.infoq.cn/article/e1d6c58939f6b1973221083fd】。文章转载请联系作者。
评论