用 CRD 造平台 API
目标
扩展 Kubernetes API,亲手创建名为 WebService 的平台 API,并在真实集群中完整实现 schema 验证、服务端默认值、租户边界和自助式 RBAC。
为什么重要
如果把平台 API 做成企业内部 Web 应用,就必须重新实现状态存储、并发控制、身份认证与授权、审计和 watch。注册 CRD 后,这些能力会全部随之而来——这就是 Kubernetes API 成为平台通用语言的原因。OpenAPI schema 尤其能以最低成本实现自助服务三要素中的“快速反馈”。只需一行 maximum: 10,错误请求就不会等到 30 分钟后才出现在流水线日志中,而会立即以人能读懂的语句被拒绝。default 同样能够让服务器强制应用“安全的默认值”。但仅有 CRD 还不能构成完整平台——只有同时具备租户之间互不侵犯的边界(命名空间、配额和 RBAC),才能真正做到无需工单即可授予权限。本实验中的所有资源都是 Kubernetes 内置资源,因此会实际应用,并通过 kubectl 评分。
步骤
- 在
/root/cnpa-platform/crd.yaml中编写并应用 CustomResourceDefinition——设置metadata.name: webservices.platform.labhub.io、spec.group: platform.labhub.io、spec.scope: Namespaced;spec.names中 kind 为WebService、plural 为webservices、singular 为webservice、shortNames 第一项为ws;版本v1alpha1设置served: true、storage: true。 - 完成同一 CRD 的
v1alpha1schema——在spec对象中定义image(string,必填)、replicas(integer,default: 2、minimum: 1、maximum: 10)、public(boolean,default: false)。然后在additionalPrinterColumns中添加两列:名称Image(jsonPath.spec.image,type string)和Replicas(jsonPath.spec.replicas,type integer)。 - 创建命名空间
tenant-blue——添加标签platform.labhub.io/tenant: blue和pod-security.kubernetes.io/enforce: baseline。 - 在
tenant-blue中创建 ResourceQuotatenant-blue-quota——设置requests.cpu: "2"、requests.memory: 4Gi、limits.cpu: "4"、limits.memory: 8Gi、pods: "10"。在同一命名空间中创建 LimitRangetenant-blue-limits——type 为Container,default中 cpu 为200m、memory 为256Mi,defaultRequest中 cpu 为100m、memory 为128Mi。 - 在
tenant-blue中创建 WebServiceshop——只写spec.image: ghcr.io/labhub/shop:1.0.0,不要写replicas和public。创建后重新读取,确认这两个值已经自动填充。 - 验证 schema 违规会被拒绝。尝试创建
spec.replicas: 20的 WebServicebad,并将其放在tenant-blue中,并将失败输出(包括标准错误)保存到/root/cnpa-platform/reject.txt。bad不得残留在集群中。 - 创建 ServiceAccount
blue-dev,并将其放在tenant-blue中,并在同一命名空间中创建 Rolewebservice-editor(apiGroups 为platform.labhub.io、resources 为webservices、verbs 为get,list,watch,create,update,patch,delete)和 RoleBindingblue-devs(将该 Role 绑定到blue-devServiceAccount)。不要授予修改配额的权限。 - 按照相同模式创建第二个租户——命名空间
tenant-green(标签platform.labhub.io/tenant: green)、ResourceQuotatenant-green-quota(包含pods: "10"),以及 WebServiceapi(spec.image: ghcr.io/labhub/api:1.0.0,不指定 replicas)。tenant-blue中的blue-dev不应有权在tenant-green中创建 WebService。
参考
- 应用 CRD 后,
kubectl get ws -n tenant-blue这样的缩写形式应当立即可用。 - 使用
kubectl auth can-i <verb> <resource> --as=system:serviceaccount:tenant-blue:blue-dev -n <네임스페이스>检查权限。 - 将失败输出保存到文件时,还必须重定向标准错误。
- 常见错误 1:把 CRD 的
metadata.name写成webservice.platform.labhub.io这样的单数形式。这里必须使用复数。 - 常见错误 2:
default应写在各个属性内部,而不是写在spec对象本身。另外,required是由属性名称组成的数组。
注册 CRD——组、作用域与名称
在 /root/cnpa-platform/crd.yaml 中编写并应用 CustomResourceDefinition——设置 metadata.name: webservices.platform.labhub.io、spec.group: platform.labhub.io、spec.scope: Namespaced;spec.names 中 kind 为 WebService、plural 为 webservices、singular 为 webservice、shortNames 第一项为 ws;版本 v1alpha1 设置 served: true、storage: true。
CRD 名称必须采用 <복수형>.<그룹> 格式。这是由租户创建的资源,请谨慎选择作用域。
Schema 与打印列
完成同一 CRD 的 v1alpha1 schema——在 spec 对象中定义 image(string,必填)、replicas(integer,default: 2、minimum: 1、maximum: 10)、public(boolean,default: false)。然后在 additionalPrinterColumns 中添加两列:名称 Image(jsonPath .spec.image,type string)和 Replicas(jsonPath .spec.replicas,type integer)。
OpenAPI v3 schema 附加在 versions 数组中的各个版本上。请区分 required、minimum/maximum 和 default 各自应写在哪里。打印列也需要按版本定义。
租户命名空间
创建命名空间 tenant-blue——添加标签 platform.labhub.io/tenant: blue 和 pod-security.kubernetes.io/enforce: baseline。
命名空间本身就是租户边界。请同时添加表示归属关系的标签和 Pod 安全标准标签。
ResourceQuota 与 LimitRange
在 tenant-blue 中创建 ResourceQuota tenant-blue-quota——设置 requests.cpu: "2"、requests.memory: 4Gi、limits.cpu: "4"、limits.memory: 8Gi、pods: "10"。在同一命名空间中创建 LimitRange tenant-blue-limits——type 为 Container,default 中 cpu 为 200m、memory 为 256Mi,defaultRequest 中 cpu 为 100m、memory 为 128Mi。
两者职责不同:一个限制命名空间的资源总量,另一个规定单个容器的默认值和范围。只有两者同时存在,“未填写 requests 的 Pod”才能通过配额检查。
创建 CR 与服务端默认值
在 tenant-blue 中创建 WebService shop——只写 spec.image: ghcr.io/labhub/shop:1.0.0,不要写 replicas 和 public。创建后重新读取,确认这两个值已经自动填充。
完全不要填写 replicas,直接创建。重新读取已保存的对象时,其中应当已经有该值。
确认 schema 违规会被拒绝
验证 schema 违规会被拒绝。尝试创建 spec.replicas: 20 的 WebService bad,并将其放在 tenant-blue 中,并将失败输出(包括标准错误)保存到 /root/cnpa-platform/reject.txt。bad 不得残留在集群中。
尝试使用超出范围的值创建资源,并把产生的错误消息保存到文件中。标准错误也必须一并保存。
自助权限与防护栏
创建 ServiceAccount blue-dev,并将其放在 tenant-blue 中,并在同一命名空间中创建 Role webservice-editor(apiGroups 为 platform.labhub.io、resources 为 webservices、verbs 为 get,list,watch,create,update,patch,delete)和 RoleBinding blue-devs(将该 Role 绑定到 blue-dev ServiceAccount)。不要授予修改配额的权限。
现有 RBAC 同样适用于新资源。确认规则中的 apiGroups 和 resources 应填写什么,并保持配额不可修改。
第二个租户与隔离证明
按照相同模式创建第二个租户——命名空间 tenant-green(标签 platform.labhub.io/tenant: green)、ResourceQuota tenant-green-quota(包含 pods: "10"),以及 WebService api(spec.image: ghcr.io/labhub/api:1.0.0,不指定 replicas)。tenant-blue 中的 blue-dev 不应有权在 tenant-green 中创建 WebService。
把同一种模式再应用一次,才称得上平台。同时,第一个租户的权限不应触及第二个租户。