在研究 Service Mesh 的过程中,发现 Istio 很多参数都通过 kubernetes CRD 来管理,例如 VirtualService 和 DestinationRule,这种方式使部署在 k8s 集群上的服务的管理方式更趋向一致。

kubernetes 的资源管理方式和声明式 API 的良好设计使得在这个平台上的功能扩展变得异常容易。例如 CoreOS 推出的 Operator 框架就是一个很好的例子。

这篇文章通过一个简短的示例来演示如何创建自定义资源。

创建 CRD(CustomResourceDefinition)

这里以创建一个简单的弹性伸缩配置的 CRD 为例。将下面的内容保存在 scaling_crd.yaml 文件中。

apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  # name must match the spec fields below, and be in the form: <plural>.<group>
  name: scalings.control.example.com
spec:
  # group name to use for REST API: /apis/<group>/<version>
  group: control.example.com
  # list of versions supported by this CustomResourceDefinition
  versions:
    - name: v1
      # Each version can be enabled/disabled by Served flag.
      served: true
      # One and only one version must be marked as the storage version.
      storage: true
  # either Namespaced or Cluster
  scope: Namespaced
  names:
    # plural name to be used in the URL: /apis/<group>/<version>/<plural>
    plural: scalings
    # singular name to be used as an alias on the CLI and for display
    singular: scaling
    # kind is normally the CamelCased singular type. Your resource manifests use this.
    kind: Scaling
    # shortNames allow shorter string to match your resource on the CLI
    shortNames:
    - sc

通过 kubectl 创建这个 CRD:

kubectl apply -f scaling_crd.yaml

创建自定义资源的对象

我们编写一个 test.yaml 文件来创建一个自定义的 Scaling 对象。

apiVersion: "control.example.io/v1"
kind: Scaling
metadata:
  name: test
spec:
  targetDeployment: test
  minReplicas: 1
  maxReplicas: 5
  metricType: CPU
  step: 1
  scaleUp: 80
  scaleDown: 40

通过 kubectl 创建:

kubectl apply -f test.yaml

提示:

scaling.control.example.io/test created

你可以通过 kubectl 查看已经创建的名为 test 的 Scaling 对象。

kubectl get scalings.control.example.io test -o yaml

会输出类似如下的结果:

apiVersion: control.example.io/v1
kind: Scaling
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"control.example.io/v1","kind":"Scaling","metadata":{"annotations":{},"name":"test","namespace":"default"},"spec":{"maxReplicas":5,"metricType":"CPU","minReplicas":1,"scaleDown":40,"scaleUp":80,"step":1,"targetDeployment":"test"}}      
  creationTimestamp: "2019-01-09T12:22:36Z"
  generation: 1
  name: test
  namespace: default
  resourceVersion: "1316610"
  selfLink: /apis/control.example.io/v1/namespaces/default/scalings/test
  uid: 28717b37-5ac2-11e9-89f8-080027a9fd96
spec:
  maxReplicas: 5
  metricType: CPU
  minReplicas: 1
  scaleDown: 40
  scaleUp: 80
  step: 1
  targetDeployment: test

我们可以像操作 k8s 内置的 Deployment 资源一样操作我们创建的 Scaling 资源,同样可以对它进行更新和删除的操作。

参数校验

上面的 CRD 配置中我们并没有指定这个资源的 Spec,也就是说用户可以使用任意的 Spec 创建这个 Scaling 资源,这并不符合我们的要求。我们希望在用户创建 Scaling 对象时,可以像 k8s 的原生资源一样进行参数校验,如果出错的情况下,就不会去创建或更新这个对象,而是给用户错误提示。

k8s 目前提供了两种方式来实现参数校验,OpenAPI v3 schemavalidatingadmissionwebhook

这里主要使用比较简单的 OpenAPI v3 schema 来实现。validatingadmissionwebhook 需要用户自己提供一个检查服务,通过创建 ValidatingWebhookConfiguration 让 APIServer 将指定的操作请求转发给这个检查服务,检查服务返回 true 或者 false,决定参数校验是否成功。

我们将之前的 CRD 配置文件 scaling_crd.yaml 做一下修改,增加参数校验的部分:

apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: scalings.control.example.io
spec:
  group: control.example.io
  versions:
    - name: v1
      served: true
      storage: true
  scope: Namespaced
  names:
    plural: scalings
    singular: scaling
    kind: Scaling
  validation:
    openAPIV3Schema:
      properties:
        spec:
          required:
          - targetDeployment
          - minReplicas
          - maxReplicas
          - metricType
          - step
          - scaleUp
          - scaleDown
          properties:
            targetDeployment:
              type: string
            minReplicas:
              type: integer
              minimum: 0
            maxReplicas:
              type: integer
              minimum: 0
            metricType:
              type: string
              enum:
              - CPU
              - MEMORY
              - REQUESTS
            step:
              type: integer
              minimum: 1
            scaleUp:
              type: integer
            scaleDown:
              type: integer
              minimum: 0

可以看到 spec 中增加了 validation 字段,其中定义了对各个参数的检验要求。

  • required 表示数组中的参数必须要设置。
  • type stringtype integer 表示限制参数类型。
  • minimum: 0 表示数字最小值为 0。
  • enum 表示参数只能在指定的值中。

具体支持哪些校验方法可以通过 https://github.com/OAI/OpenAPI-Specification 查看。

更新 CRD 资源:

kubectl apply -f scaling_crd.yaml

再次修改 test.yaml 测试我们的参数校验是否生效,将 targetDeployment: test 这一行删除。

更新 Name 为 test 的 Scaling 对象。

kubectl apply -f test.yaml

可以看到错误提示输出如下:

validation failure list:
spec.targetDeployment in body is required

至此,不符合我们要求的 Scaling 对象将不被允许创建。