Kata Containers 网络模型(二)

CNI 是如何为容器设置网络的

书接上回,在 Kata Containers 网络模型(一) 中介绍了一些基础知识和 containerd 关于网路配置的相关知识,这篇文章单独讲讲 CNI 插件是如何获取配置信息的,最后又以 "bridge + host-local" 插件为例,将网络形态继续向终态推进。

  • Kata Containers 网络模型(一)针对 containerd 如何配置网络这一主题详细展开,使用大量篇幅陈述如何初始化 CNI 插件,这部分内容有助于理解 CNI 插件的配置文件与网络的关系。然后详细描述了 containerd 在创建 sandbox 的时候是如何创建 netns 以及如何调用 CNI 插件为 pod 配置网络。
  • Kata Containers 网络模型(二)详细展开了 CNI 插件的基本使用方法,以 bridge 插件为例剖析了代码实现,在最后展示了网络配置效果。
  • Kata Containers 网络模型(三)将会聚焦 Kata containers 如何为安全容器连接网络,这里我们以 Go(2.0)版本的 runtime 作为例子,Rust(3.0)版本在原理上大同小异。还有部分网络更新的逻辑在 agent 组件中,不可避免的还是要看一些 Rust 源码。
Table of Contents

CNI 插件

CNI 插件和配置

CNI 插件是一个相对独立的部分,理解后才能更好的理解 containerd 的行为。CNI 插件被分为三类,为了节省篇幅这类只列出一部分有特点的插件,其他的可以参考 containernetworking/plugins

  • Main 插件:用于创建网络设备的插件
    • bridge: 创建一个网桥并将容器添加到该网桥。
  • IPAM 插件:用于分配 IP 地址的插件
    • dhcp: 代表容器发出 dhcp 请求。
    • host-local: 维护一个分配 ip 的本地数据库。
  • Meta 插件:一般是指调用前两类插件实现创建网络的插件
    • flannel: 一个非常经典的跨主机通讯插件,它调用了 bridge 等插件。

CNI 插件本质上就是一堆二进制可执行文件,任何程序只要按照要求调用都可以让 CNI 插件创建网络。CNI 插件的行为是靠环境变量控制的,典型的环境变量是:

环境变量 作用
CNI_COMMAND CNI 命令,比较典型的是 ADD 和 DEL,分别表示增加网络和删除网络。
CNI_NETNS netns 路径,由上图可以知道在调用 CNI 插件之前 netns 就已经被创建好了。
CNI_PATH CNI 插件的路径。
CNI_IFNAME 容器内部接口的名称。
CNI_CONTIANERID 容器 ID。
CNI_VERSION CNI 版本。

除了控制信息外 CNI 执行还需要网络配置信息,以网桥为例,还需要告知 CNI 插件 IP 子网等信息,这样才能调用 IPAM 插件分配 IP 地址。这个信息是上层 runtime 以标准输入(stdin)的方式传入 CNI 插件的,数据以 json 格式传递,其结构与 NetworkConfig 结构体一致,关于该结构体更详细信息请参考《Kata Containers 网络模型(一):CNI 插件配置数据结构 》

DIY 一个 CNI 插件

CNI 库的地址是 containernetworking/cni,用于开发 CNI 插件,在这个仓库的 "pkg/skel/skel.go" 中给出了一个 CNI 插件的骨干代码 [4],它已经解析了各种必需的环境变量、标准输入,这样我们只需要实现给出的 3 个函数就能快速完成一个自制 CNI 插件:

  • cmdAdd: 对应 ADD 指令,用于添加网络。
  • cmdCheck: 对应 CHECK 指令。
  • cmdDel: 对应 DEL 指令,用于删除网络。

每个函数都是接收一个 CmdArgs 结构体,它的定义如下所示。

请对照上一节介绍的内容,可以很容易发现这里就是捕获环境变量和标准输入。关于如何手写一个 CNI 插件已经超越这篇文章的主题,如果感兴趣可以参见 [5]。

Bridge 插件

不要着急,这里已经是 CNI 插件的最后一部分了,介绍一个被我们使用的具体的插件,其实一切都会变得顺理成章了。这部分我们只关注网络是如何被创建的(cmdAdd 函数),全部源码参见 plugins/main/bridge/bridge.go

总结一下,bridge 插件做的事情是(1)创建网桥如果不存在的话;(2)在两个 netns 下设置 veth 设备;(3)如果需要分配 IP 地址,则调用 IPAM 插件;(4)返回执行结果。

我们还是以上一篇文章中 bridge 插件配置为例。

下图展示的是 bridge 插件执行后的效果。左边是在初始状态下,只有两个 netns,但是里面都没有网络设备。右边是执行了 ADD 命令后,创建了一个网桥,一对 veth 设备。根据上面的设置,我们使用的私有网段是 172.19.0.0/24,那么网桥作为网关被设置了 172.19.0.1/24,在 host netns 的 veth 设备直接接入到网桥上,因此不给它设置 IP 地址,在 pod netns 下的 veth 设备的 IP 地址被 host-local 设置为 172.19.0.95/24,当然在这个网段的任何一个地址都是符合要求的。

kata-containers-networking-CNI 插件.drawio

References

  1. https://time.geekbang.org/column/article/64948
  2. http://liubin.org/kata-dev-book/src/kata-arch.html
  3. https://blog.frognew.com/2021/06/relearning-container-17.html
  4. https://blog.frognew.com/2021/06/relearning-container-16.html
  5. https://morningspace.github.io/tech/k8s-net-cni-coding-go/
All rights reserved
Except where otherwise noted, content on this page is copyrighted.