交付完整模块

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

    KubeVela 使用 CUE 配置语言作为管理用户模块化交付的核心,同时也围绕 CUE 提供了管理工具来。

    下面我们以 Kubernetes 官方的 StatefulSet 对象为例,来具体看如何使用 KubeVela 构建自定义的模块化功能并提供能力。 我们将官方文档中 StatefulSet 的 YAML 例子保存在本地,并命名为 , 然后执行如下命令,生成一个名为 “my-stateful” 的 Component 模块定义,并输出到 “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. outputs: web: {
    49. apiVersion: "v1"
    50. metadata: {
    51. name: "nginx"
    52. labels: app: "nginx"
    53. }
    54. spec: {
    55. clusterIP: "None"
    56. ports: [{
    57. name: "web"
    58. port: 80
    59. }]
    60. selector: app: "nginx"
    61. }
    62. }
    63. parameter: {}
    64. }

    将该组件定义安装到 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. ...

    为组件定义定制化参数

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

    • 镜像名称,允许用户自定义镜像
    • 实例名,允许用户自定义生成的 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. spec:
    5. components:
    6. - name: my-component
    7. type: my-stateful
    8. properties:
    9. replicas: 1
    10. 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 集群中使用看运行的结果做验证。

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

    使用上下文信息减少参数

    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 源文件进行修改,增加参数等。

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

    总结

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