文章部分内容整理自:
更多内容请阅读博主下面的博文:
按照处理类型的不同,一般可以将其分为两类:一类可能会修改传入对象,称为 mutating webhook;一类则会只读传入对象,称为 validating webhook。
工作流程:
首先介绍一下 operator framework。 它实际上给用户提供了 webhook 和 controller 的框架,它的主要意义在于帮助开发者屏蔽了一些通用的底层细节,不需要开发者再去实现消息通知触发、失败重新入队等,只需关注被管理应用的运维逻辑实现即可。
主流的 operator framework 主要有两个:
两者实际上并没有本质的区别,它们的核心都是使用官方的 controller-tools 和 controller-runtime。不过细节上稍有不同,比如 kubebuilder 有着更为完善的测试与部署以及代码生成的脚手架等;而 operator-sdk 对 ansible operator 这类上层操作的支持更好一些。
案例选用的是阿里云对外开放的 kruise 项目下的 SidercarSet。
SidercarSet 的功能就是负责给 Pod 插入 sidecar 容器(也称为辅助容器),例如可以插入一些监控,日志采集来丰富这个 Pod 的功能,然后根据插入的状态、Pod 的状态反过来更新 SidercarSet 以记录这些辅助容器的状态。
Step 1: 初始化
操作:新建一个 gitlab 项目,运行 “kubebuilder init –domain=kruise.io”。
参数解读:domain 指定了后续注册 CRD 对象的 Group 域名。
效果解读:拉取依赖代码库、生成代码框架、生成 Makefile/Dockerfile 等工具文件。
Step 2: 创建 API
操作:运行 “kubebuilder create api –group apps –version v1alpha1 –kind SidecarSet –namespace=false”
实际上不仅会创建 API,也就是 CRD,还会生成 Controller 的框架。
参数解读:- group 加上之前的 domian 即此 CRD 的 Group: apps.kruise.io;
version 一般分三种,按社区标准:
kind: 此 CRD 的类型,类似于社区原生的 Service 的概念;
namespaced: 此 CRD 是全局唯一还是 namespace 唯一,类似 node 和 Pod。
它的参数基本可以分为两类。group, version, kind 基本上对应了 CRD 元信息的三个重要组成部分。这里给出了一些常见的标准,大家实际使用的时候可以参考一下。namespaced 则用于指定我们刚刚创建的 CRD 时全局唯一的(如 node)还是 namespace 唯一的(如 Pod)。这里用了 false,即指定 SidecarSet 为全局唯一的。
效果解读:
生成了 CRD 和 controller 的框架,后面需要手工填充代码。
Step 3: 填充 CRD
(1) 调整注释
code generator 依赖注释生成代码,因此有时需要调整注释。以下列出了本次实战中 SidecarSet 需要调整的注释:
+genclient:nonNamespaced: 生成非 namespace 对象;
+kubebuilder:subresource:status: 生成 status 子资源;
+kubebuilder:printcolumn:name=”MATCHED”,type=’integer’,JSONPath=”.status.matchedPods”,description=”xxx”: kubectl get sidecarset: 后续展示相关。
(2) 填充字段
填充字段是令用户的 CRD 实际生效、实际有意义的重要部分。
SidecarSetSpec: 填充 CRD 描述信息;
SidecarSetStatus: 填充 CRD 状态信息。
需要注意的是,研发人员无需参与 CRD 的 grpc 接口、编解码等 controller 的底层实现。
SidecarSet 的功能是给 Pod 注入 Sidecar,为了完成该功能,我们在 SidecarSetSpec(左图) 定义了两个主要信息:一个是用于选择哪些 Pod 需要被注入的 Selector;一个是定义 Sidecar 容器的 Containers。
在 SidecarSetStatus(右图)中定义了状态信息,MatchedPods 反映的是该 SidecarSet 实际匹配了多少 Pod,UpdatedPods 反映的是已经注入了多少,ReadyPods 反映的则是有多少 Pod 已经正常工作了。
Step 4: 生成 webhook 框架
“kubebuilder alpha webhook –group apps –version v1alpha1 –kind SidecarSet –type=mutating –operations=create”
“kubebuilder alpha webhook –group core –version v1 –kind Pod –type=mutating –operations=create”
“kubebuilder alpha webhook –group apps –version v1alpha1 –kind SidecarSet –type=validating –operations=create,update”
参数解读:
效果解读:
Step 5: 填充 webhook
生成的 webhook handler 分别位于:
需要改写、填充的一般包括以下两个部分:
因为第四步我们定义了三个:sidecarset mutating、sidecarset mutaing、pod mutating。
先来看上图左侧的 sidecarset mutating,首先是 setDefaultSidecarSet 把默认值设置好,这也是 mutaing 最常做的事情。
上图右侧 validating 也是非常的标准,也是对 SidecarSet 一些字段进行校验。
关于 pod mutaing 这里没有做展示,这里面有些不同,这里面的 mutaingSidecarSetFn 不是进行默认值设置,而是获取 setDefaultSidecarSet 的数值,然后注入到 Pod 里面。
Step 6: 填充 controller
生成的 controller 框架位于 pkg/controller/sidecarset/sidecarset_controller.go。主要有三点需要进行修改:
用户创建一个 SidecarSet;
webhook 收到该 SidecarSet 之后,会进行缺省值设置和配置项校验。这两个操作完成之后,会完成真正的入库,并返回给用户;
用户创建一个 Pod;
webhook 会拿回对应的 SidecarSet,并从中取出 container 注入 Pod 中,因此 Pod 在实际入库时就已带有了刚刚的 sidecar;
controller 在后台不停地轮询,查看集群的状态变化。第 4 步中的注入会触发 SidecarSet 的入队,controller 就会令 SidecarSet 的 UpdatedPods 加 1。
以上就是 SidecarSet 前期一个简单的功能实现。
这里我们再补充一个问题。一般的 webhook 由 controller 来完成业务逻辑、状态更新,但这个不是一定的,两者之一可以不是必须的。在以上的示例中就是由 webhook 完成主要的业务逻辑,无需 controller 的参与。
本文的主要内容就到此为止了,这里为大家简单总结一下: