Kubernetes跨10个版本升级之旅-CNI改造
概述
近期准备对公司内部的 Kubernetes 从 v1.20.3 升级到 v1.30.4 版本,因为 v1.24 之后就移除了 dockershim 相关的代码,又因为公司内部魔改过一个不是非常流行的 CNI(估计很多人没有听过) Contiv Netplugin 会依赖 Docker 创建和清理容器 IP,所以要升级 Kubernetes 进去,就必须改造 Contiv Netplugin,将关于 Docker 的代码进行修改以支持 Kubernetes 直接调用 Containerd。
下面是 Kubelet, Dockershim 和 Contiv Netplugin 的几个组件交互的过程。
旧集群配置容器网络的过程
下面是当前 Kubernetes v1.20.3 版本下,通过 Contiv Netplugin 配置容器网络的日志。先分析一下,在旧环境依赖 Docker 的情况下,容器的网络是如何被配置的。
|
|
从上面的代码看,最重要的一步是 netlink.LinkSetNsPid(link, pid)
,这个方法会把 OVS 创建出来的 Port(OVS概念)和 sandbox 的 PID 的网络命名空间关联,简单理解就是 Veth Pair 的一头会被放到这个 PID 的网络命名空间,然后就是一系列的 nsenter
的操作,进入到这个 PID 的命名空间之后,就可以把虚拟网卡的名字修改,并且配置 IP 地址和配置默认网关,这就是 Contiv Netplugin 的原理。
新版本Kubernetes配置容器网络
从旧集群配置容器网络的过程可以了解 dockershim 的作用主要有:
- 创建sandbox
- 启动sandbox获取pause容器的PID
- PID传递到CNI,CNI根据PID确定容器的网络命名空间
新版本 Kubernetes 没有了 dockershim 之后,容器的网络应该如何配置呢,下面是 Containerd 调用 CNI 的过程。
|
|
从上面代码的关键区别,可以看到,不依赖 dockershim 之后,Containerd 调用 CNI 的过程中,是先生成网络命名空间的名字,然后再启动 sandbox 容器,在启动 sandbox 容器之前,是无法获取容器的 PID 的,也就是说如果还是按照之前的方式调用 Contiv Netplugin 的话,是无法通过 PID 来获取网络命名空间的,我们可以从下面的日志中得到验证。
在不修改 Contiv Netplugin 的情况下,直接升级 Kubernetes 版本到 v1.30.4,且移除 Docker 进程后,无法通过 Contiv Netplugin 正常配置和清理网络配置,具体日志如下。可以看到,Netplugin 发送请求到 Netmaster 成功常见 Endpoint(Contiv的概念),Netmaster 根据绑定的网络配置,已经将网络配置的具体内容,包括 IP 和网关等返回给 Netplugin 了,OVS 也创建了 Veth Pairs,但是在接下来的步骤中遇到问题 Invalid nw name space
。
|
|
Contiv Netplugin适配Kubernetes v1.30.4
在了解了旧集群配置容器网络的过程和新版本 Kubernetes 配置容器网络的过程之后,就可以理解了,在 Kubernetes 1.30.4 中,在没有 dockershim 的情况下,如何直接获取 sandbox 的命名空间,然后配置好容器网络之后再启动容器。
首先 pInfo.NwNameSpace
在 Kubernetes v1.30.4 下,会直接传递 sandbox 的网络命名空间,格式类似为 /var/run/netns/cni-a4be4aed-0ec5-d43a-2f44-7e918282a09c
,这个区别于依赖 dockershim 时候的格式 /proc/<PID>/ns/net
,所以我们不能像原来需要 Netplugin 的方法 netlink.LinkSetNsPid(link, pid)
来将 Veth Pairs 在容器一段的 link 绑定到 sandbox 容器的网络命名空间下,但我们通过 ip
命令直接将 Veth Pairs 在容器一段的 link 直接配置到 /var/run/netns/cni-a4be4aed-0ec5-d43a-2f44-7e918282a09c
这样的网络命名空间下,然后就是配置 IP 地址和默认网关、路由的操作了。
|
|
至此 Contiv Netplugin 适配 Kubernetes v1.30.4 的工作就完成了,从下面的日志可以看到 Netplugin 去 Netmaster 请求 Endpoint 的分配,Netmaster 给 Netplugin 返回了 Endpoint 的网络配置,IPAddress:10.189.52.138
,然后 Netplugin 就负责把 sandbox 的网络配置配置好就可以了。
|
|
关于删除容器的流程
CNI Del 的操作没有依赖 Docker,所以这里不讨论正常的删除容器的操作。但 Contiv Netplugin 在处理异常情况的容器 IP 残留是需要依赖 Docker 的,那么这个过程又要如何适配在没有 Docker 的情况下回收 IP 呢。下面是 Contiv Netplugin 在清理 IP 时候的过程。
|
|
从上面的过程中可以发现 Contiv Netplugin 在清理 IP 的时候,会依赖 Docker,主要是通过类似 docker inspect <contianerID>
来获取还在运行中的容器的 PID,然后通过 nsenter
进入容器进程中获取 IP 地址,之后会跟 Netmaster 上记录的这个节点上占用的 IP 做对比,如果 Netmaster 上有的 IP 在本机的 Netplugin 上发现不到,就会判断为过期的 IP,需要释放 Netmaster 上记录的这个 IP,这样在后面的分配中,这个 IP 才能被重新使用。
因为 Netplugin 使用 Docker 的 Client 是比较轻度的,所以这里只要换个方式获取运行中容器的 ID 即可,比如可以使用 crictl ps
获取,有可以正常的回收无效的 IP。
|
|
测试
|
|
总结
Kubernetes 集群升级需要排查很多问题,如果是跨多个版本升级,就更需要仔细的摸排潜在的坑了,在这些坑里,最恐怖就是网络,比网络更可怕的就是网络插件的一些历史包袱问题了。