隐蔽的角落 - 这次我们只聊 Cilium IPAM
礼记《学者有四失》里说“人之学也,或失则多”,这是给我提醒每篇推文最好只聊一个概念。前一篇文章着重介绍了一下 Cilium 的各种炫酷的花式玩法,今天我们来看一个最最基本的功能:IPAM(IP Address Management)。
IPAM 的概念很简单,就是 IP 地址管理。DHCP 就是 IPAM 常用的一种工具。可为什么我们要在这里单独聊它呢?因为概念虽然简单,但在容器网络这个场景里,有它特殊的实现方式和业务挑战。
图 1:Cluster 内,Pod 间通信示意图
在开始聊 IPAM 之前,我们先来看看图 1。这是一张示意图,它画出了一个 cluster 内部,Pod 内容器间通信,Pod 间(跨主机)通信的典型场景。这张图里面还画出了一些额外的信息:
一个 Pod 内部可能有若干个 container,其中一定会包含一个 Infra-container。
一个 container 内部是可以同时跑多个 process 的。
每个 Pod 都有一个在容器网络环境独一无二的 IP 地址。容器网络环境和宿主机网络环境的区别与联系请阅读二哥之前的推文“镜子-或许我们也和 Pod 一样生活在虚拟世界”。
Pod 内部所有的容器都共享其所在 Pod 的 IP 地址,但是这些容器需要使用不同的端口。
我们可以看到除了 Pod 内容器间通信是可以直接用 localhost 之外,不同的 Pod 之间通信是需要用到 pod IP 的。那随之而来的问题是:pod IP 地址是谁给分配的?又是在什么时候安排上的?如果 Pod 没了,这个 IP 地址被谁收走了?
你肯定觉得在隐秘的角落,应该有一个类似 DHCP 的东西在控制着这一切,但好像 K8s 里面又没有提到 DHCP 这个事情。其实答案很简单:CNI 插件。
CRD
Cilium 用到了一个叫 K8s CRD(Custom Resouce Definition)的技术。所谓 CRD,就是有一些功能 K8s 没有提供,但是呢 K8s 通过插件的方式外包给第三方。CRD 是 K8s 生态中核心扩展机制(另外一个核心扩展机制是 Custom API Server)。
图 2:CRD 在 K8s apiserver 中的位置示意图(图片取自书籍《Programming Kubernetes》)
如图 2 所示,在 kube-apiserver 中有一个模块叫 apiextensions-apiserver 来单独服务 CRD。它的位置仅靠着 K8s native-resource。
CRD 是 K8s 的一个 resource,用来描述对 Custom Resouce(CR)的定义,而后者则是基于该 CRD 而创建的资源。来一句绕口令吧:CRD 是 CR 的定义,CR 是 CRD 的实例。
下面的一小段示例用来向 K8s 注册 CR definition,好让 K8s 知道有一个第三方定义的 resouce 存在。
而下面这段另外的示例用来基于前一步注册的 CRD 来创建 CR。这段示例仅展示了部分内容。K8s 收到这样的请求会创建一个数据结构,填充内容并存放到 etcd 中,数据的结构从 CRD 中得到,如 spec.group、spec.names、spec.ipam 等等,而数据内容其实就是由下面这段声明式 yaml 来填充。每一份这样的数据被叫作 CR 实例(CR instance)。
上面这两段示例也说明了使用 CRD 的标准方式:先注册 CRD,再创建 CR 实例。
CR 和 K8s 自带的 resource 如 Pod,Namespace,Deployment 一样,也是一种 resource,只不过它是由第三方自定义的,用于提供和 K8s native-resource 一样的使用体验,如果不注意,你甚至都不会在意这个 resource 是第三方提供的。为啥能有这么自然的使用体验呢?因为 CRD 也是被 K8s apiserver 一起无差别处理的,CR 和 native-resource 存放的位置都一样,都是存放在 etcd 中。
但和 K8s 自带的 resource 位于 Core group 不同,CR 一般位于第三方自己的 Group(Group-Version-Resource 中的 Group)内。具体到 Cilium,它定义了若干个 CR,其中一个叫CiliumNodes
,位于 Group cilium.io
,Version v2
。
你可以通过如下命令获取 CiliumNodes 这个 CRD 的详细内容,如果你的环境恰好使用了 Cilium 的话。
CRD 的背后有一个叫 K8s Controller 的服务以 Pod 方式在 K8s 环境里运行,以响应 K8s 外包过来的各种请求。
图 3:Customer Controller 内部结构图
如图 3 所示,对于一个 recource,一旦 API server 端有针对它的实例创建、删除或者更新的操作,Informer 都会收到"事件通知"。
于此同时 Controller 内部会运行有一个 Control Loop,这个 loop 的作用很明显,就是消化掉与此 resource 相关的各个通知事件。
注意区分 resource 相关的增加、更新、删除相关的 event 和 top-level Event 对象的区别。前者是事件通知机制,而后者则和 Pod 一样也是一种 resource,可以将其看成是一个反映系统运行状态的日志(Log)系统。
IPAM
聊完 Controller,我们来看看 Cilium 是如何利用 Controller 来完成 IP 地址管理的。
图 4:Cilium IPAM 示意图
图 4 的右方出现了一个cilium-operator
的图示。Operator 作为 K8s 里的一个概念,于 2016 年被 CoreDNS 提出。它是一个 Controller,它的出现大大简化了“有状态应用”的部署复杂度,我们大致了解到这个地方即可,后续二哥会单独开一篇聊聊 Operator。
整个 K8s cluster 只运行有一份 cilium-operator Pod,而 cilium-agent 则在每个 Node 上都运行一份。
当 cilium-operator 发现有一个新的CiliumNodes
资源被创建后,它会从它的供货商那里获取一批与此 Node 相关的 IP 地址块,Node 的如 hostname 等信息则通过读取 CR 资源实例得到。
前面我们提到每个 Node 上都会运行有 cilium-agent,它会从这个新建 CR 的spec.ipam.available
这里拿到一个 IP 地址。这个过程中 cilium-operator 就像是一个公益超市,批发进货,散卖加回收,还没有赚差价。
你也可以执行下面的命令,看看与每个 Node 相关的 IPAM 信息。cilium-agent 通过 client-go 即可非常方便地拿到和你看到的一模一样的数据。二哥为了你能更快速地得到更直观的了解,贴心地截了一张图(下图 5)放在这里。你可以看到这个 CR 的实例大概长什么样子。
图 5:kubectl get cn cilium-2 -o yaml 输出截图
图 4 仅仅是一个示意图,实际上 Controller 的 IP 供货商有很多种,除了图中所示的 AWS ENI(Elastic Network Interface)之外,还有 Azure,GKE 等等可供选择。从不同的供货商那里获取 IP 地址块的方式也不尽相同。
如果你的 cluster 环境是完全自己管理的,我们知道 K8s 自身就是可以通过 kubeadmin 来设置 CIDR 的,像下面的命令一样,你也可以按照自己的喜好随意设置。
图 6:Cilium IPAM 时序图
说了这么多,我觉得还是有点抽象,所以我把图 6 放上来了。因为图 4 主要涉及到的是 AWS ENI,所以我在图 6 中将涉及到 AWS ENI 的部分用红框标识出来了。
图 6 这张时序图初看起来比较复杂,一眼望去不知道重点是什么。因为它是对图 4 的细节放大,所以包含了很多的信息。放大到什么程度呢?它把从容器创建到 IP 分配这个过程中所有的参与者和各自参与时机都画出来了。
另外从这张图里面,我们也可以看到在容器创建过程中,kubelet,CRI,还有 CNI plugin 之间是如何分工的。可以很明显地看到 CNI 插件其实做的事情非常简单,大部分情况下,它只是个摆设,真正的活还是得靠它背后勤劳的小蜜蜂们来完成,比如这里的 cilium-agent。
以上就是本文的全部内容。码字不易,更多内容请关注二哥的微信公众号。您的举手之劳是对二哥莫大的鼓励。感谢有你!
版权声明: 本文为 InfoQ 作者【Lance】的原创文章。
原文链接:【http://xie.infoq.cn/article/ec8282756ee5b01f736cb4d04】。文章转载请联系作者。
评论