Version: v1.2
交付完整模块
现在你已经了解了OAM 模型和模块定义(X-Definition )的概念,本节将介绍如何使用 CUE 交付完整的模块化功能,使得你的平台可以随着用户需求变化动态扩展功能,适应各类用户和场景,满足公司业务长期发展的迭代诉求。
将 Kubernetes API 对象转化为自定义组件
KubeVela 使用 CUE 配置语言作为管理用户模块化交付的核心,同时也围绕 CUE 提供了管理工具来编辑和生成 KubeVela 的模块定义对象。
下面我们以 Kubernetes 官方的 StatefulSet 对象为例,来具体看如何使用 KubeVela 构建自定义的模块化功能并提供能力。 我们将官方文档中 StatefulSet 的 YAML 例子保存在本地,并命名为 my-stateful.yaml, 然后执行如下命令,生成一个名为 “my-stateful” 的 Component 模块定义,并输出到 “my-stateful.cue” 文件中:
vela def init my-stateful -t component --desc "My StatefulSet component." --template-yaml ./my-stateful.yaml -o my-stateful.cue
查看生成的 “my-stateful.cue” 文件:
$ cat my-stateful.cue"my-stateful": {annotations: {}attributes: workload: definition: {apiVersion: "<change me> apps/v1"kind: "<change me> Deployment"}description: "My StatefulSet component."labels: {}type: "component"}template: {output: {apiVersion: "v1"kind: "Service"... // 省略一些非重要信息}outputs: web: {apiVersion: "apps/v1"kind: "StatefulSet"... // 省略一些非重要信息}parameter: {}}
下面我们来对这个自动生成的自定义组件做一些微调:
- StatefulSet 官网的例子是由
StatefulSet和Service两个对象构成的一个复合组件。而根据 KubeVela 自定义组件的规则,在复合组件中,比如 StatefulSet 这样的核心工作负载需要由template.output字段表示,其他辅助对象用template.outputs表示,所以我们将内容做一些调整,将自动生成的 output 和 outputs 中的全部调换。 - 然后我们将核心工作负载的 apiVersion 和 kind 数据填写到标注为
<change me>的部分
修改后可以用 vela def vet做一下格式检查和校验。
$ vela def vet my-stateful.cueValidation succeed.
经过两步改动后的文件如下:
$ cat my-stateful.cue"my-stateful": {annotations: {}attributes: workload: definition: {apiVersion: "apps/v1"kind: "StatefulSet"}description: "My StatefulSet component."labels: {}type: "component"}template: {output: {apiVersion: "apps/v1"kind: "StatefulSet"metadata: name: "web"spec: {selector: matchLabels: app: "nginx"replicas: 3serviceName: "nginx"template: {metadata: labels: app: "nginx"spec: {containers: [{name: "nginx"ports: [{name: "web"containerPort: 80}]image: "k8s.gcr.io/nginx-slim:0.8"volumeMounts: [{name: "www"mountPath: "/usr/share/nginx/html"}]}]terminationGracePeriodSeconds: 10}}volumeClaimTemplates: [{metadata: name: "www"spec: {accessModes: ["ReadWriteOnce"]resources: requests: storage: "1Gi"storageClassName: "my-storage-class"}}]}}outputs: web: {apiVersion: "v1"kind: "Service"metadata: {name: "nginx"labels: app: "nginx"}spec: {clusterIP: "None"ports: [{name: "web"port: 80}]selector: app: "nginx"}}parameter: {}}
将该组件定义安装到 Kubernetes 集群中:
$ vela def apply my-stateful.cueComponentDefinition my-stateful created in namespace vela-system.
此时平台的最终用户已经可以通过 vela components命令看到有一个 my-stateful组件可以使用了。
$ vela componentsNAME NAMESPACE WORKLOAD DESCRIPTION...my-stateful vela-system statefulsets.apps My StatefulSet component....
通过 KubeVela 的应用部署计划发布到集群中,就可以拉起我们刚刚定义的 StatefulSet 和 Service 对象。
cat <<EOF | vela up -f -apiVersion: core.oam.dev/v1beta1kind: Applicationmetadata:name: websitespec:components:- name: my-componenttype: my-statefulEOF
为组件定义定制化参数
为了满足用户变化的需求,我们需要在最后的 parameter 里暴露一些参数,在 CUE 基础入门文档中你可以了解到参数相关的语法。在本例中,我们为用户暴露一下参数:
- 镜像名称,允许用户自定义镜像
- 实例名,允许用户自定义生成的 StatefulSet 对象和 Service 对象的实例名称
- 副本数,生成对象的副本数
... # 省略其他没有修改的字段template: {output: {apiVersion: "apps/v1"kind: "StatefulSet"metadata: name: parameter.namespec: {selector: matchLabels: app: "nginx"replicas: parameter.replicasserviceName: "nginx"template: {metadata: labels: app: "nginx"spec: {containers: [{image: parameter.image... // 省略其他没有修改的字段}]}}... // 省略其他没有修改的字段}}outputs: web: {apiVersion: "v1"kind: "Service"metadata: {name: "nginx"labels: app: "nginx"}spec: {... // 省略其他没有修改的字段}}parameter: {image: stringname: stringreplicas: int}}
修改后同样使用 vela def apply安装到集群中:
$ vela def apply my-stateful.cueComponentDefinition my-stateful in namespace vela-system updated.
这个修改过程是实时生效的,用户立即可以看到系统中的 my-stateful 组件增加了新的参数。
$ vela show my-stateful# Properties+----------+-------------+--------+----------+---------+| NAME | DESCRIPTION | TYPE | REQUIRED | DEFAULT |+----------+-------------+--------+----------+---------+| name | | string | true | || replicas | | int | true | || image | | string | true | |+----------+-------------+--------+----------+---------+
组件定义的修改并不会影响已经在运行的应用,当下次应用修改并重新部署时,新的组件定义就会生效。
最终用户就可以在应用中指定新增的这三个参数:
apiVersion: core.oam.dev/v1beta1kind: Applicationmetadata:name: websitespec:components:- name: my-componenttype: my-statefulproperties:image: nginx:latestreplicas: 1name: my-component
将文件保存在本地并命名为 app-stateful.yaml,执行 vela up -f app-stateful.yaml更新应用,你可以看到 StatefulSet 对象的名称、镜像和实例数均已更新。
调试模块化功能的正确性
为了保证用户的应用使用参数能够正确运行,你也可以用 vela dry-run 命令对你的模板进行试运行验证。
vela dry-run -f app-stateful.yaml
查看输出,你就可以对比生成的对象和你实际期望的对象是否一致。甚至可以直接把这个 YAML 执行到 Kubernetes 集群中使用看运行的结果做验证。
# Application(website) -- Component(my-component)---apiVersion: v1kind: Servicemetadata:labels:app: nginxapp.oam.dev/appRevision: ""app.oam.dev/component: my-componentapp.oam.dev/name: websiteworkload.oam.dev/type: my-statefulname: nginxnamespace: defaultspec:clusterIP: Noneports:- name: webport: 80selector:app: nginxtemplate:spec:containers:- image: saravak/fluentd:elasticname: my-sidecar---apiVersion: apps/v1kind: StatefulSetmetadata:labels:app.oam.dev/appRevision: ""app.oam.dev/component: my-componentapp.oam.dev/name: websitetrait.oam.dev/resource: webtrait.oam.dev/type: AuxiliaryWorkloadname: webnamespace: defaultspec:replicas: 3selector:matchLabels:app: nginxserviceName: nginxtemplate:metadata:labels:app: nginxspec:containers:- image: k8s.gcr.io/nginx-slim:0.8name: nginxports:- containerPort: 80name: webvolumeMounts:- mountPath: /usr/share/nginx/htmlname: wwwterminationGracePeriodSeconds: 10volumeClaimTemplates:- metadata:name: wwwspec:accessModes:- ReadWriteOnceresources:requests:storage: 1GistorageClassName: my-storage-class
你还可以通过 vela dry-run -h 来查看更多可用的功能参数。
使用上下文信息减少参数
在我们上面的 Application 例子中,properties 中的 name 和 Component 的 name 字段是相同的,此时我们可以在模板中使用携带了上下文信息的 context关键字,其中 context.name 就是运行时组件名称,此时 parameter 中的 name 参数就不再需要的。
... # 省略其他没有修改的字段template: {output: {apiVersion: "apps/v1"kind: "StatefulSet"metadata: name: context.name... // 省略其他没有修改的字段}parameter: {image: stringreplicas: int}}
KubeVela 内置了应用所需的上下文信息,你可以根据需要配置.
使用运维能力按需添加配置
对于用户的新需求,除了修改组件定义增加参数以外,你还可以使用运维能力,按需添加配置。一方面,KubeVela 已经内置了大量的通用运维能力,可以满足诸如:添加 label、annotation,注入环境变量、sidecar,添加 volume 等等的需求。另一方面,你可以像自定义组件一样,自定义补丁型运维特征,来满足更多的配置灵活组装的需求。
你可以使用 vela traits 查看,带 * 标记的 trait 均为通用 trait,能够对常见的 Kubernetes 资源对象做操作。
$ vela traitsNAME NAMESPACE APPLIES-TO CONFLICTS-WITH POD-DISRUPTIVE DESCRIPTIONannotations vela-system * true Add annotations on K8s pod for your workload which followsthe pod spec in path 'spec.template'.configmap vela-system * true Create/Attach configmaps on K8s pod for your workload whichfollows the pod spec in path 'spec.template'.env vela-system * false add env on K8s pod for your workload which follows the podspec in path 'spec.template.'hostalias vela-system * false Add host aliases on K8s pod for your workload which followsthe pod spec in path 'spec.template'.labels vela-system * true Add labels on K8s pod for your workload which follows thepod spec in path 'spec.template'.lifecycle vela-system * true Add lifecycle hooks for the first container of K8s pod foryour workload which follows the pod spec in path'spec.template'.node-affinity vela-system * true affinity specify node affinity and toleration on K8s pod foryour workload which follows the pod spec in path'spec.template'.scaler vela-system * false Manually scale K8s pod for your workload which follows thepod spec in path 'spec.template'.sidecar vela-system * true Inject a sidecar container to K8s pod for your workloadwhich follows the pod spec in path 'spec.template'.
以 sidecar 为例,你可以查看 sidecar 的用法:
$ vela show sidecar# Properties+---------+-----------------------------------------+-----------------------+----------+---------+| NAME | DESCRIPTION | TYPE | REQUIRED | DEFAULT |+---------+-----------------------------------------+-----------------------+----------+---------+| name | Specify the name of sidecar container | string | true | || cmd | Specify the commands run in the sidecar | []string | false | || image | Specify the image of sidecar container | string | true | || volumes | Specify the shared volume path | [[]volumes](#volumes) | false | |+---------+-----------------------------------------+-----------------------+----------+---------+## volumes+------+-------------+--------+----------+---------+| NAME | DESCRIPTION | TYPE | REQUIRED | DEFAULT |+------+-------------+--------+----------+---------+| path | | string | true | || name | | string | true | |+------+-------------+--------+----------+---------+
直接使用 sidecar 注入一个容器,应用的描述如下:
apiVersion: core.oam.dev/v1beta1kind: Applicationmetadata:name: websitespec:components:- name: my-componenttype: my-statefulproperties:image: nginx:latestreplicas: 1name: my-componenttraits:- type: sidecarproperties:name: my-sidecarimage: saravak/fluentd:elastic
部署运行该应用,就可以看到 StatefulSet 中已经部署运行了一个 fluentd 的 sidecar。
你也可以使用 vela def 获取 sidecar 的 CUE 源文件进行修改,增加参数等。
vela def get sidecar
运维能力的自定义与组件自定义类似,不再赘述,你可以阅读运维能力自定义文档了解更详细的功能。
总结
本节介绍了如何通过 CUE 交付完整的模块化能力,其核心是可以随着用户的需求,不断动态增加配置能力,逐步暴露更多的功能和用法,以便降低用户整体的学习门槛,最终提升研发效率。 KubeVela 背后提供的开箱即用的能力,包括组件、运维功能、策略以及工作流,均是通过同样的方式提供了可插拔、可修改的能力。