用 CRD 把 API 拓宽
目标
亲手编写 CustomResourceDefinition 来扩展 Kubernetes API,确认 schema 验证会真正拒绝请求,并通过 Aggregated ClusterRole 为新资源添加权限。
为什么这很重要
CKA 考试范围包含 CRD,并不是要求你编写 operator,而是考查你能否运维已经安装了 operator 的集群。实际集群中通常已经安装了数十个 CRD。
这里需要理解一个结构:创建 CRD 后,apiserver 会出现新的 endpoint,并提供存储、验证和 watch 功能。但仅此而已。 真正执行操作的是 watch 该 CR 的控制器,而控制器是独立软件。如果创建 CR 后没有任何事情发生,通常是因为控制器不存在或已经停止运行。
schema 也是同一道理。在 apiextensions.k8s.io/v1 中,schema 不是可选项,而是必需项。schema 就是该 API 的契约,错误值会先被 apiserver 阻止,而不是留给控制器处理。
步骤
- 创建 CRD
widgets.labhub.io。group 为labhub.io,scope 为Namespaced,kind 为Widget,plural 为widgets,singular 为widget,shortNames 中包含wg;只有一个版本v1alpha1,served 与 storage 均为 true。 - 创建 namespace
cka-crd,并在其中创建 Widgetdemo。spec.replicas为 3,spec.tier为small。 - 修改 CRD 的 v1alpha1 schema,加入验证规则。
spec.replicas的 type 为integer,minimum 为 1,maximum 为 10。spec.tier的 type 为string,enum 为[small, large]。spec 对象的 required 为[replicas, tier]。 - 尝试创建
spec.replicas为 99 的 Widgettoo-big,并将拒绝错误输出保存到/root/cka-crd/reject.txt。too-big不应被创建。 - 为 v1alpha1 添加两个 additionalPrinterColumns。名称
REPLICAS(type 为integer,jsonPath 为.spec.replicas);名称TIER(type 为string,jsonPath 为.spec.tier)。 - 创建 CRD
clusterwidgets.labhub.io。scope 为Cluster,kind 为ClusterWidget,plural 为clusterwidgets,group 为labhub.io,版本为v1alpha1。然后创建 ClusterWidgetglobal。 - 创建 ClusterRole
cka-widget-viewer。添加标签rbac.labhub.io/aggregate-to-widget=true,规则允许对 apiGroupslabhub.io中的widgets执行get、list、watch。然后创建 ClusterRolecka-widget-aggregate,使其 aggregationRule 通过选择器选择该标签。
参考
- schema 的最小形式为
openAPIV3Schema: {type: object, properties: {spec: {type: object, x-kubernetes-preserve-unknown-fields: true}}}。第 3 步会具体定义这个 spec。 - 可以先用
kubectl get crd widgets.labhub.io -o yaml导出当前 schema,修改后重新应用,这样更快。 - 常见错误 1:将 CRD 名称写成
widget.labhub.io这样的单数形式。名称必须由 plural 与 group 拼接而成。 - 常见错误 2:使用 aggregationRule 的同时又填写 rules。控制器会覆盖 rules,因此手写规则会消失。
创建 CustomResourceDefinition
创建 CRD widgets.labhub.io。group 为 labhub.io,scope 为 Namespaced,kind 为 Widget,plural 为 widgets,singular 为 widget,shortNames 中包含 wg;只有一个版本 v1alpha1,served 与 storage 均为 true。
CRD 的 metadata.name 必须采用“复数形式.组”的格式。在 apiextensions.k8s.io/v1 中,versions 数组的每一项都必须包含 schema。
创建自定义资源
创建 namespace cka-crd,并在其中创建 Widget demo。spec.replicas 为 3,spec.tier 为 small。
CR 的 apiVersion 格式为“组/版本”。schema 较宽松时可以加入任意字段,因此一开始允许 spec 下的未知字段会更方便。
在 schema 中加入验证规则
修改 CRD 的 v1alpha1 schema,加入验证规则。spec.replicas 的 type 为 integer,minimum 为 1,maximum 为 10。spec.tier 的 type 为 string,enum 为 [small, large]。spec 对象的 required 为 [replicas, tier]。
在 openAPIV3Schema 的 properties.spec.properties 下,为每个字段设置 type 和 minimum/maximum/enum。required 不是字段值,而是该对象层级中的数组。
确认验证拒绝请求的瞬间
尝试创建 spec.replicas 为 99 的 Widget too-big,并将拒绝错误输出保存到 /root/cka-crd/reject.txt。too-big 不应被创建。
错误输出写入标准错误,而不是标准输出。重定向时不要忘记 2>&1。被拒绝的资源不应创建,这才是正常结果。
为 kubectl get 输出添加列
为 v1alpha1 添加两个 additionalPrinterColumns。名称 REPLICAS(type 为 integer,jsonPath 为 .spec.replicas);名称 TIER(type 为 string,jsonPath 为 .spec.tier)。
additionalPrinterColumns 位于 versions 数组的各个版本内部。需要 name、type、jsonPath 三个字段,且 jsonPath 以点号开头。
创建集群作用域 CRD
创建 CRD clusterwidgets.labhub.io。scope 为 Cluster,kind 为 ClusterWidget,plural 为 clusterwidgets,group 为 labhub.io,版本为 v1alpha1。然后创建 ClusterWidget global。
创建 CRD 后无法修改 scope。集群作用域资源不接受 -n 选项。
通过 Aggregated ClusterRole 扩展权限
创建 ClusterRole cka-widget-viewer。添加标签 rbac.labhub.io/aggregate-to-widget=true,规则允许对 apiGroups labhub.io 中的 widgets 执行 get、list、watch。然后创建 ClusterRole cka-widget-aggregate,使其 aggregationRule 通过选择器选择该标签。
不要直接编写带有 aggregationRule 的 ClusterRole 的 rules。控制器会查找带标签的其他 ClusterRole 并将其规则合并。