Version: v1.1

交付完整模块

现在你已经了解了OAM 模型模块定义(X-Definition )的概念,本节将介绍如何使用 CUE 交付完整的模块化功能,使得你的平台可以随着用户需求变化动态扩展功能,适应各类用户和场景,满足公司业务长期发展的迭代诉求。

将 Kubernetes API 对象转化为自定义组件

KubeVela 使用 CUE 配置语言作为管理用户模块化交付的核心,同时也围绕 CUE 提供了管理工具来编辑和生成 KubeVela 的模块定义对象

下面我们以 Kubernetes 官方的 StatefulSet 对象为例,来具体看如何使用 KubeVela 构建自定义的模块化功能并提供能力。 我们将官方文档中 StatefulSet 的 YAML 例子保存在本地,并命名为 my-stateful.yaml, 然后执行如下命令,生成一个名为 “my-stateful” 的 Component 模块定义,并输出到 “my-stateful.cue” 文件中:

  1. vela def init my-stateful -t component --desc "My StatefulSet component." --template-yaml ./my-stateful.yaml -o my-stateful.cue

查看生成的 “my-stateful.cue” 文件:

  1. $ cat my-stateful.cue
  2. "my-stateful": {
  3. annotations: {}
  4. attributes: workload: definition: {
  5. apiVersion: "<change me> apps/v1"
  6. kind: "<change me> Deployment"
  7. }
  8. description: "My StatefulSet component."
  9. labels: {}
  10. type: "component"
  11. }
  12. template: {
  13. output: {
  14. apiVersion: "v1"
  15. kind: "Service"
  16. ... // 省略一些非重要信息
  17. }
  18. outputs: web: {
  19. apiVersion: "apps/v1"
  20. kind: "StatefulSet"
  21. ... // 省略一些非重要信息
  22. }
  23. parameter: {}
  24. }

下面我们来对这个自动生成的自定义组件做一些微调:

  1. StatefulSet 官网的例子是由 StatefulSetService 两个对象构成的一个复合组件。而根据 KubeVela 自定义组件的规则,在复合组件中,比如 StatefulSet 这样的核心工作负载需要由 template.output字段表示,其他辅助对象用 template.outputs表示,所以我们将内容做一些调整,将自动生成的 output 和 outputs 中的全部调换。
  2. 然后我们将核心工作负载的 apiVersion 和 kind 数据填写到标注为 <change me>的部分

修改后可以用 vela def vet做一下格式检查和校验。

  1. $ vela def vet my-stateful.cue
  2. Validation succeed.

经过两步改动后的文件如下:

  1. $ cat my-stateful.cue
  2. "my-stateful": {
  3. annotations: {}
  4. attributes: workload: definition: {
  5. apiVersion: "apps/v1"
  6. kind: "StatefulSet"
  7. }
  8. description: "My StatefulSet component."
  9. labels: {}
  10. type: "component"
  11. }
  12. template: {
  13. output: {
  14. apiVersion: "apps/v1"
  15. kind: "StatefulSet"
  16. metadata: name: "web"
  17. spec: {
  18. selector: matchLabels: app: "nginx"
  19. replicas: 3
  20. serviceName: "nginx"
  21. template: {
  22. metadata: labels: app: "nginx"
  23. spec: {
  24. containers: [{
  25. name: "nginx"
  26. ports: [{
  27. name: "web"
  28. containerPort: 80
  29. }]
  30. image: "k8s.gcr.io/nginx-slim:0.8"
  31. volumeMounts: [{
  32. name: "www"
  33. mountPath: "/usr/share/nginx/html"
  34. }]
  35. }]
  36. terminationGracePeriodSeconds: 10
  37. }
  38. }
  39. volumeClaimTemplates: [{
  40. metadata: name: "www"
  41. spec: {
  42. accessModes: ["ReadWriteOnce"]
  43. resources: requests: storage: "1Gi"
  44. storageClassName: "my-storage-class"
  45. }
  46. }]
  47. }
  48. }
  49. outputs: web: {
  50. apiVersion: "v1"
  51. kind: "Service"
  52. metadata: {
  53. name: "nginx"
  54. labels: app: "nginx"
  55. }
  56. spec: {
  57. clusterIP: "None"
  58. ports: [{
  59. name: "web"
  60. port: 80
  61. }]
  62. selector: app: "nginx"
  63. }
  64. }
  65. parameter: {}
  66. }

将该组件定义安装到 Kubernetes 集群中:

  1. $ vela def apply my-stateful.cue
  2. ComponentDefinition my-stateful created in namespace vela-system.

此时平台的最终用户已经可以通过 vela components命令看到有一个 my-stateful组件可以使用了。

  1. $ vela components
  2. NAME NAMESPACE WORKLOAD DESCRIPTION
  3. ...
  4. my-stateful vela-system statefulsets.apps My StatefulSet component.
  5. ...

通过 KubeVela 的应用部署计划发布到集群中,就可以拉起我们刚刚定义的 StatefulSet 和 Service 对象。

  1. cat <<EOF | kubectl apply -f -
  2. apiVersion: core.oam.dev/v1beta1
  3. kind: Application
  4. metadata:
  5. name: website
  6. spec:
  7. components:
  8. - name: my-component
  9. type: my-stateful
  10. EOF

为组件定义定制化参数

为了满足用户变化的需求,我们需要在最后的 parameter 里暴露一些参数,在 CUE 基础入门文档中你可以了解到参数相关的语法。在本例中,我们为用户暴露一下参数:

  • 镜像名称,允许用户自定义镜像
  • 实例名,允许用户自定义生成的 StatefulSet 对象和 Service 对象的实例名称
  • 副本数,生成对象的副本数
  1. ... # 省略其他没有修改的字段
  2. template: {
  3. output: {
  4. apiVersion: "apps/v1"
  5. kind: "StatefulSet"
  6. metadata: name: parameter.name
  7. spec: {
  8. selector: matchLabels: app: "nginx"
  9. replicas: parameter.replicas
  10. serviceName: "nginx"
  11. template: {
  12. metadata: labels: app: "nginx"
  13. spec: {
  14. containers: [{
  15. image: parameter.image
  16. ... // 省略其他没有修改的字段
  17. }]
  18. }
  19. }
  20. ... // 省略其他没有修改的字段
  21. }
  22. }
  23. outputs: web: {
  24. apiVersion: "v1"
  25. kind: "Service"
  26. metadata: {
  27. name: "nginx"
  28. labels: app: "nginx"
  29. }
  30. spec: {
  31. ... // 省略其他没有修改的字段
  32. }
  33. }
  34. parameter: {
  35. image: string
  36. name: string
  37. replicas: int
  38. }
  39. }

修改后同样使用 vela def apply安装到集群中:

  1. $ vela def apply my-stateful.cue
  2. ComponentDefinition my-stateful in namespace vela-system updated.

这个修改过程是实时生效的,用户立即可以看到系统中的 my-stateful 组件增加了新的参数。

  1. $ vela show my-stateful
  2. # Properties
  3. +----------+-------------+--------+----------+---------+
  4. | NAME | DESCRIPTION | TYPE | REQUIRED | DEFAULT |
  5. +----------+-------------+--------+----------+---------+
  6. | name | | string | true | |
  7. | replicas | | int | true | |
  8. | image | | string | true | |
  9. +----------+-------------+--------+----------+---------+

组件定义的修改并不会影响已经在运行的应用,当下次应用修改并重新部署时,新的组件定义就会生效。

最终用户就可以在应用中指定新增的这三个参数:

  1. apiVersion: core.oam.dev/v1beta1
  2. kind: Application
  3. metadata:
  4. name: website
  5. spec:
  6. components:
  7. - name: my-component
  8. type: my-stateful
  9. properties:
  10. image: nginx:latest
  11. replicas: 1
  12. name: my-component

将文件保存在本地并命名为 app-stateful.yaml,执行 kubectl apply -f app-stateful.yaml更新应用,你可以看到 StatefulSet 对象的名称、镜像和实例数均已更新。

调试模块化功能的正确性

为了保证用户的应用使用参数能够正确运行,你也可以用 vela dry-run 命令对你的模板进行试运行验证。

  1. vela dry-run -f app-stateful.yaml

查看输出,你就可以对比生成的对象和你实际期望的对象是否一致。甚至可以直接把这个 YAML 执行到 Kubernetes 集群中使用看运行的结果做验证。

  1. # Application(website) -- Component(my-component)
  2. ---
  3. apiVersion: v1
  4. kind: Service
  5. metadata:
  6. labels:
  7. app: nginx
  8. app.oam.dev/appRevision: ""
  9. app.oam.dev/component: my-component
  10. app.oam.dev/name: website
  11. workload.oam.dev/type: my-stateful
  12. name: nginx
  13. namespace: default
  14. spec:
  15. clusterIP: None
  16. ports:
  17. - name: web
  18. port: 80
  19. selector:
  20. app: nginx
  21. template:
  22. spec:
  23. containers:
  24. - image: saravak/fluentd:elastic
  25. name: my-sidecar
  26. ---
  27. apiVersion: apps/v1
  28. kind: StatefulSet
  29. metadata:
  30. labels:
  31. app.oam.dev/appRevision: ""
  32. app.oam.dev/component: my-component
  33. app.oam.dev/name: website
  34. trait.oam.dev/resource: web
  35. trait.oam.dev/type: AuxiliaryWorkload
  36. name: web
  37. namespace: default
  38. spec:
  39. replicas: 3
  40. selector:
  41. matchLabels:
  42. app: nginx
  43. serviceName: nginx
  44. template:
  45. metadata:
  46. labels:
  47. app: nginx
  48. spec:
  49. containers:
  50. - image: k8s.gcr.io/nginx-slim:0.8
  51. name: nginx
  52. ports:
  53. - containerPort: 80
  54. name: web
  55. volumeMounts:
  56. - mountPath: /usr/share/nginx/html
  57. name: www
  58. terminationGracePeriodSeconds: 10
  59. volumeClaimTemplates:
  60. - metadata:
  61. name: www
  62. spec:
  63. accessModes:
  64. - ReadWriteOnce
  65. resources:
  66. requests:
  67. storage: 1Gi
  68. storageClassName: my-storage-class

你还可以通过 vela dry-run -h 来查看更多可用的功能参数。

使用上下文信息减少参数

在我们上面的 Application 例子中,properties 中的 name 和 Component 的 name 字段是相同的,此时我们可以在模板中使用携带了上下文信息的 context关键字,其中 context.name 就是运行时组件名称,此时 parameter 中的 name 参数就不再需要的。

  1. ... # 省略其他没有修改的字段
  2. template: {
  3. output: {
  4. apiVersion: "apps/v1"
  5. kind: "StatefulSet"
  6. metadata: name: context.name
  7. ... // 省略其他没有修改的字段
  8. }
  9. parameter: {
  10. image: string
  11. replicas: int
  12. }
  13. }

KubeVela 内置了应用所需的上下文信息,你可以根据需要配置.

使用运维能力按需添加配置

对于用户的新需求,除了修改组件定义增加参数以外,你还可以使用运维能力,按需添加配置。一方面,KubeVela 已经内置了大量的通用运维能力,可以满足诸如:添加 label、annotation,注入环境变量、sidecar,添加 volume 等等的需求。另一方面,你可以像自定义组件一样,自定义补丁型运维特征,来满足更多的配置灵活组装的需求。

你可以使用 vela traits 查看,带 * 标记的 trait 均为通用 trait,能够对常见的 Kubernetes 资源对象做操作。

  1. $ vela traits
  2. NAME NAMESPACE APPLIES-TO CONFLICTS-WITH POD-DISRUPTIVE DESCRIPTION
  3. annotations vela-system * true Add annotations on K8s pod for your workload which follows
  4. the pod spec in path 'spec.template'.
  5. configmap vela-system * true Create/Attach configmaps on K8s pod for your workload which
  6. follows the pod spec in path 'spec.template'.
  7. env vela-system * false add env on K8s pod for your workload which follows the pod
  8. spec in path 'spec.template.'
  9. hostalias vela-system * false Add host aliases on K8s pod for your workload which follows
  10. the pod spec in path 'spec.template'.
  11. labels vela-system * true Add labels on K8s pod for your workload which follows the
  12. pod spec in path 'spec.template'.
  13. lifecycle vela-system * true Add lifecycle hooks for the first container of K8s pod for
  14. your workload which follows the pod spec in path
  15. 'spec.template'.
  16. node-affinity vela-system * true affinity specify node affinity and toleration on K8s pod for
  17. your workload which follows the pod spec in path
  18. 'spec.template'.
  19. scaler vela-system * false Manually scale K8s pod for your workload which follows the
  20. pod spec in path 'spec.template'.
  21. sidecar vela-system * true Inject a sidecar container to K8s pod for your workload
  22. which follows the pod spec in path 'spec.template'.

以 sidecar 为例,你可以查看 sidecar 的用法:

  1. $ vela show sidecar
  2. # Properties
  3. +---------+-----------------------------------------+-----------------------+----------+---------+
  4. | NAME | DESCRIPTION | TYPE | REQUIRED | DEFAULT |
  5. +---------+-----------------------------------------+-----------------------+----------+---------+
  6. | name | Specify the name of sidecar container | string | true | |
  7. | cmd | Specify the commands run in the sidecar | []string | false | |
  8. | image | Specify the image of sidecar container | string | true | |
  9. | volumes | Specify the shared volume path | [[]volumes](#volumes) | false | |
  10. +---------+-----------------------------------------+-----------------------+----------+---------+
  11. ## volumes
  12. +------+-------------+--------+----------+---------+
  13. | NAME | DESCRIPTION | TYPE | REQUIRED | DEFAULT |
  14. +------+-------------+--------+----------+---------+
  15. | path | | string | true | |
  16. | name | | string | true | |
  17. +------+-------------+--------+----------+---------+

直接使用 sidecar 注入一个容器,应用的描述如下:

  1. apiVersion: core.oam.dev/v1beta1
  2. kind: Application
  3. metadata:
  4. name: website
  5. spec:
  6. components:
  7. - name: my-component
  8. type: my-stateful
  9. properties:
  10. image: nginx:latest
  11. replicas: 1
  12. name: my-component
  13. traits:
  14. - type: sidecar
  15. properties:
  16. name: my-sidecar
  17. image: saravak/fluentd:elastic

部署运行该应用,就可以看到 StatefulSet 中已经部署运行了一个 fluentd 的 sidecar。

你也可以使用 vela def 获取 sidecar 的 CUE 源文件进行修改,增加参数等。

  1. vela def get sidecar

运维能力的自定义与组件自定义类似,不再赘述,你可以阅读运维能力自定义文档了解更详细的功能。

总结

本节介绍了如何通过 CUE 交付完整的模块化能力,其核心是可以随着用户的需求,不断动态增加配置能力,逐步暴露更多的功能和用法,以便降低用户整体的学习门槛,最终提升研发效率。 KubeVela 背后提供的开箱即用的能力,包括组件、运维功能、策略以及工作流,均是通过同样的方式提供了可插拔、可修改的能力。

下一步