Version: v1.2

CUE Advanced

This section will introduce how to use CUE to deliver KubeVela modules. You can can dynamically expand the platform as user needs change, adapt to growing number of users and scenarios, and meet the iterative demands of the company’s long-term business development.

Convert Kubernetes API Objects Into Custom Components

Let’s take the Kubernetes StatefulSet as an example to show how to use KubeVela to build custom modules and provide capabilities.

Save the YAML example of StatefulSet in the official document locally and name it as my-stateful.yaml, then execute commande as below:

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

View the generated “my-stateful.cue” file:

  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. ... // omit non-critical info
  17. }
  18. outputs: web: {
  19. apiVersion: "apps/v1"
  20. kind: "StatefulSet"
  21. ... // omit non-critical info
  22. }
  23. parameter: {}
  24. }

Modify the generated file as follows:

  1. The example of the official StatefulSet website is a composite component composed of two objects StatefulSet and Service. According to KubeVela Rules for customize components, in composite components, core workloads such as StatefulSet need to be represented by the template.output field, and other auxiliary objects are represented by template.outputs, so we make some adjustments and all the automatically generated output and outputs are switched.
  2. Then we fill in the apiVersion and kind data of the core workload into the part marked as <change me>

After modification, you can use vela def vet to do format check and verification.

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

The file after two steps of changes is as follows:

  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. }

Install ComponentDefinition into the Kubernetes cluster:

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

You can see that a my-stateful component via vela components command:

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

When you put this customized component into Application, it looks like:

  1. cat <<EOF | vela up -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

Define Customized Parameters For Component

In previous section we have defined a ComponentDefinition that has no parameter. In this section we will show how to expose parameters.

In this example, we expose the following parameters to the user:

  • Image name, allowing users to customize the image
  • Instance name, allowing users to customize the instance name of the generated StatefulSet object and Service object
  • The number of replica, the number of copies of the generated object
  1. ... # Omit other unmodified fields
  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. ... // Omit other unmodified fields
  17. }]
  18. }
  19. }
  20. ... // Omit other unmodified fields
  21. }
  22. }
  23. outputs: web: {
  24. apiVersion: "v1"
  25. kind: "Service"
  26. metadata: {
  27. name: "nginx"
  28. labels: app: "nginx"
  29. }
  30. spec: {
  31. ... // Omit other unmodified fields
  32. }
  33. }
  34. parameter: {
  35. image: string
  36. name: string
  37. replicas: int
  38. }
  39. }

After modification, use vela def apply to install to the cluster:

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

You can see the parameters of my-stateful ComponentDefinition as follows:

  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. +----------+-------------+--------+----------+---------+

Updating the ComponentDefinition will not affect existing Applications. It will take effect only after updating the Applications next time.

You can specify the three new parameters in the application:

  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

Save the file locally and name it app-stateful.yaml, execute vela up -f app-stateful.yaml to update the application, you can see that the name, image, and number of instances of the StatefulSet object have been updated.

Dry-run

In order to ensure that the user’s application can run correctly with the parameters, you can also use the vela dry-run command to verify the trial run of your template.

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

By viewing the output, you can compare whether the generated object is consistent with the object you actually expect. You can even execute this YAML directly into the Kubernetes cluster and use the results of the operation for verification.

  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

You can also use vela dry-run -h to view more available function parameters.

Use context to get runtime information

In our Application example above, the name field in the properties and the name field of the Component are the same. So we can use the context keyword that carries context information in the template, where context.name is the runtime component Name, thus the name parameter in parameter is no longer needed.

  1. ... # Omit other unmodified fields
  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 has built-in application required context, you can configure it according to your needs.

Add Traits On Demand

In addition to modifying ComponentDefinitions and adding parameters, you can also use the TraitDefinition to patch configurations to Components. KubeVela has built-in operations to meet the following needs: adding labels, annotations, injecting environment variables, sidecars, adding volumes, and so on. You can also customize Trait to do more flexible patching.

You can use vela traits to view, the traits marked with * are general traits, which can operate on common Kubernetes resource objects.

  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'.

Taking sidecar as an example, you can check the usage of 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. +------+-------------+--------+----------+---------+

Use the sidecar directly to inject a container, the application description is as follows:

  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

Deploy and run the application, and you can see that a fluentd sidecar has been deployed and running in the StatefulSet.

You can also use vela def to get the CUE source file of the sidecar to modify, add parameters, etc.

  1. vela def get sidecar

The customization of operation and maintenance capabilities is similar to component customization, so we won’t go into details here. You can read Customize Trait for more detailed functions.

Summarize

This section introduces how to deliver complete modular capabilities through CUE. The core is that it can dynamically increase configuration capabilities according to user needs, and gradually expose more functions and usages, so as to reduce the overall learning threshold for users and ultimately improve R&D efficient. The out-of-the-box capabilities provided by KubeVela, including components, traits, policy, and workflow, are also designed as pluggable and modifiable capabilities.

Next

Get to know about how to customize: