自定义运维特征
本节介绍如何自定义运维特征,为用户的组件增添任何需要的运维特征能力。开始这一部分之前,请确保你已经对核心概念 以及 如何管理模块定义有了基本的了解。
通过 Trait 生成资源对象
通过 Trait 生成资源的用法和 Component 基本类似,这种场景通常用于生成运维的对象,比如用于服务访问的 Ingress、Service,或者用于扩缩容的 HPA 等对象。
同样的,我们使用 vela def init命令来生成一个框架:
vela def init my-route -t trait --desc "My ingress route trait." > myroute.cue
注意: 在 vela CLI(
<=1.4.2)的版本中有一个已知问题,vela def init命令会生成一个错误的definitionRef: ""字段,这一行需要删除。
期望生成的内容如下:
$ cat myroute.cue"my-route": {annotations: {}attributes: {appliesToWorkloads: []conflictsWith: []podDisruptive: falseworkloadRefPath: ""}description: "My ingress route trait."labels: {}type: "trait"}template: {patch: {}parameter: {}}
与组件定义有所不同,在用法上,你需要把所有的运维特征定义在 outputs 里(注意,不是 output),格式如下:
outputs: <unique-name>:<full template data>
我们下面使用一个 ingress 和 Service 组成一个称为 my-route 的运维特征作为示例讲解:
"my-route": {annotations: {}attributes: {appliesToWorkloads: []conflictsWith: []podDisruptive: falseworkloadRefPath: ""}description: "My ingress route trait."labels: {}type: "trait"}template: {parameter: {domain: stringhttp: [string]: int}// 我们可以在一个运维特征 CUE 模版定义多个 outputsoutputs: service: {apiVersion: "v1"kind: "Service"spec: {selector:app: context.nameports: [for k, v in parameter.http {port: vtargetPort: v},]}}outputs: ingress: {apiVersion: "networking.k8s.io/v1beta1"kind: "Ingress"metadata:name: context.namespec: {rules: [{host: parameter.domainhttp: {paths: [for k, v in parameter.http {path: kbackend: {serviceName: context.nameservicePort: v}},]}}]}}}
将这个运维特征通过如下命令部署到控制平面上:
vela def apply myroute.cue
然后最终用户就立即可以发现并使用这个运维特征了,这个运维特征没有限制,可以作用于任意 Application。
我们通过如下的 vela up 命令将它启动起来:
cat <<EOF | vela up -f -apiVersion: core.oam.dev/v1beta1kind: Applicationmetadata:name: testappspec:components:- name: express-servertype: webserviceproperties:cmd:- node- server.jsimage: oamdev/testapp:v1port: 8080traits:- type: my-routeproperties:domain: test.my.domainhttp:"/api": 8080EOF
然后 KubeVela 在服务端就会将其生成 Kubernetes 资源,通过 CUE 我们可以完成很多复杂的玩法。
一次渲染多个资源
你可以在 outputs 里定义 For 循环。
注意在 For 循环里的
parameter字段必须是 map 类型。
看看如下这个例子,在一个 TraitDefinition 对象里渲染多个 Service:
"expose": {type: "trait"}template: {parameter: {http: [string]: int}outputs: {for k, v in parameter.http {"\(k)": {apiVersion: "v1"kind: "Service"spec: {selector:app: context.nameports: [{port: vtargetPort: v}]}}}}}
这个运维特征可以这样使用:
apiVersion: core.oam.dev/v1beta1kind: Applicationmetadata:name: testappspec:components:- name: express-servertype: webserviceproperties:...traits:- type: exposeproperties:http:myservice1: 8080myservice2: 8081
自定义运维特征里执行 HTTP Request
TraitDefinition 对象可以发送 HTTP 请求并获取应答,让你可以通过关键字 processing 来渲染资源。
你可以在 processing.http 里定义 HTTP 请求的 method, url, body, header 和 trailer,然后返回的数据将被存储在 processing.output 中。
请确保目标 HTTP 服务器返回的数据是 JSON 格式
接着,你就可以通过 patch 或者 output/outputs 里的 processing.output 来引用返回数据了。
下面是一个示例:
"auth-service": {type: "trait"}template: {parameter: {serviceURL: string}processing: {output: {token?: string}// The target server will return a JSON data with `token` as key.http: {method: *"GET" | stringurl: parameter.serviceURLrequest: {body?: bytesheader: {}trailer: {}}}}patch: {data: token: processing.output.token}}
在上面这个例子中,TraitDefinition 对象发送请求来获取 token 的数据,然后将这些数据补丁给组件实例。
数据传递
TraitDefinition 对象可以读取特定 ComponentDefinition 对象生成的 API 资源(渲染自 output 和 outputs)。
KubeVela 保证了
ComponentDefinition一定会在TraitDefinition之前渲染
具体来说,context.output 字段包含了所有渲染后的工作负载 API 资源,然后 context.outputs.<xx> 则包含渲染后的其它类型 API 资源。
下面是一个数据传递的例子:
apiVersion: core.oam.dev/v1beta1kind: ComponentDefinitionmetadata:name: workerspec:workload:definition:apiVersion: apps/v1kind: Deploymentschematic:cue:template: |output: {apiVersion: "apps/v1"kind: "Deployment"spec: {selector: matchLabels: {"app.oam.dev/component": context.name}template: {metadata: labels: {"app.oam.dev/component": context.name}spec: {containers: [{name: context.nameimage: parameter.imageports: [{containerPort: parameter.port}]envFrom: [{configMapRef: name: context.name + "game-config"}]if parameter["cmd"] != _|_ {command: parameter.cmd}}]}}}}outputs: gameconfig: {apiVersion: "v1"kind: "ConfigMap"metadata: {name: context.name + "game-config"}data: {enemies: parameter.enemieslives: parameter.lives}}parameter: {// +usage=Which image would you like to use for your service// +short=iimage: string// +usage=Commands to run in the containercmd?: [...string]lives: stringenemies: stringport: int}---apiVersion: core.oam.dev/v1beta1kind: TraitDefinitionmetadata:name: ingressspec:schematic:cue:template: |parameter: {domain: stringpath: stringexposePort: int}// trait template can have multiple outputs in one traitoutputs: service: {apiVersion: "v1"kind: "Service"spec: {selector:app: context.nameports: [{port: parameter.exposePorttargetPort: context.output.spec.template.spec.containers[0].ports[0].containerPort}]}}outputs: ingress: {apiVersion: "networking.k8s.io/v1beta1"kind: "Ingress"metadata:name: context.namelabels: config: context.outputs.gameconfig.data.enemiesspec: {rules: [{host: parameter.domainhttp: {paths: [{path: parameter.pathbackend: {serviceName: context.nameservicePort: parameter.exposePort}}]}}]}}
在渲染 worker ComponentDefinition 时,具体发生了:
- 渲染的
Deployment资源放在context.output中。 - 其它类型资源则放进
context.outputs.<xx>中,同时<xx>是在特指template.outputs的唯一名字
因而,TraitDefinition 对象可以从 context 里读取渲染后的 API 资源(比如 context.outputs.gameconfig.data.enemies 这个字段)。
使用 Patch Trait 对参数增补或覆盖
除了利用 Trait 生成资源以外,一个更高级的用法是对组件生成的参数做增补或修改。
什么场景下会使用这种功能?
- 组件由其他人定义,运维人员对参数做修改。
- 组件由第三方组织定义,我们不拥有修改能力(不维护),只在部署时使用。
针对上述场景,KubeVela 通过 patch 功能来支撑,因为 Patch 的能力针对 Trait 和 Workflow 均适用,我们通过这篇 Patch 文档统一介绍。