kubevela.github.io/docs/platform-engineers/components/custom-component.md

470 lines
10 KiB
Markdown

---
title: Component Definition
---
In this section, we will introduce how to use [CUE](../cue/basic) to customize components via `ComponentDefinition`. Make sure you've learned the basic knowledge about [Definition Concept](../../getting-started/definition) and [how to manage definition](../cue/definition-edit).
## Declare `ComponentDefinition`
First, generate `ComponentDefinition` scaffolds via `vela def init` with existed YAML file.
The YAML file:
```yaml
apiVersion: "apps/v1"
kind: "Deployment"
spec:
selector:
matchLabels:
"app.oam.dev/component": "name"
template:
metadata:
labels:
"app.oam.dev/component": "name"
spec:
containers:
- name: "name"
image: "image"
```
Generate `ComponentDefinition` based on the YAML file:
```shell
vela def init stateless -t component --template-yaml ./stateless.yaml -o stateless.cue
```
It generates a file:
```shell
$ cat stateless.cue
stateless: {
annotations: {}
attributes: workload: definition: {
apiVersion: "<change me> apps/v1"
kind: "<change me> Deployment"
}
description: ""
labels: {}
type: "component"
}
template: {
output: {
spec: {
selector: matchLabels: "app.oam.dev/component": "name"
template: {
metadata: labels: "app.oam.dev/component": "name"
spec: containers: [{
name: "name"
image: "image"
}]
}
}
apiVersion: "apps/v1"
kind: "Deployment"
}
outputs: {}
parameters: {}
}
```
In detail:
- `.spec.workload` is required to indicate the workload type of this component.
- `.spec.schematic.cue.template` is a CUE template, specifically:
* The `output` filed defines the template for the abstraction.
* The `parameter` filed defines the template parameters, i.e. the configurable properties exposed in the `Application`abstraction (and JSON schema will be automatically generated based on them).
Add parameters in this auto-generated custom component file :
```
stateless: {
annotations: {}
attributes: workload: definition: {
apiVersion: "<change me> apps/v1"
kind: "<change me> Deployment"
}
description: ""
labels: {}
type: "component"
}
template: {
output: {
spec: {
selector: matchLabels: "app.oam.dev/component": parameter.name
template: {
metadata: labels: "app.oam.dev/component": parameter.name
spec: containers: [{
name: parameter.name
image: parameter.image
}]
}
}
apiVersion: "apps/v1"
kind: "Deployment"
}
outputs: {}
parameters: {
name: string
image: string
}
}
```
You can use `vela def vet` to validate the format:
```shell
$ vela def vet stateless.cue
Validation succeed.
```
Declare another component named `task` which is an abstraction for run-to-completion workload.
```shell
vela def init task -t component -o task.cue
```
It generates a file:
```shell
$ cat task.cue
task: {
annotations: {}
attributes: workload: definition: {
apiVersion: "<change me> apps/v1"
kind: "<change me> Deployment"
}
description: ""
labels: {}
type: "component"
}
template: {
output: {}
parameter: {}
}
```
Edit the generated component file:
```
task: {
annotations: {}
attributes: workload: definition: {
apiVersion: "batch/v1"
kind: "Job"
}
description: ""
labels: {}
type: "component"
}
template: {
output: {
apiVersion: "batch/v1"
kind: "Job"
spec: {
parallelism: parameter.count
completions: parameter.count
template: spec: {
restartPolicy: parameter.restart
containers: [{
image: parameter.image
if parameter["cmd"] != _|_ {
command: parameter.cmd
}
}]
}
}
}
parameter: {
count: *1 | int
image: string
restart: *"Never" | string
cmd?: [...string]
}
}
```
Apply above `ComponentDefinition` files to your Kubernetes cluster:
```shell
$ vela def apply stateless.cue
ComponentDefinition stateless created in namespace vela-system.
$ vela def apply task.cue
ComponentDefinition task created in namespace vela-system.
```
## Declare an `Application`
The `ComponentDefinition` can be instantiated in `Application` abstraction as below:
```yaml
apiVersion: core.oam.dev/v1alpha2
kind: Application
metadata:
name: website
spec:
components:
- name: hello
type: stateless
properties:
image: oamdev/hello-world
name: mysvc
- name: countdown
type: task
properties:
image: centos:7
cmd:
- "bin/bash"
- "-c"
- "for i in 9 8 7 6 5 4 3 2 1 ; do echo $i ; done"
```
### Under The Hood
<details>
Above application resource will generate and manage following Kubernetes resources in your target cluster based on the `output` in CUE template and user input in `Application` properties.
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend
... # skip tons of metadata info
spec:
template:
spec:
containers:
- name: mysvc
image: oamdev/hello-world
metadata:
labels:
app.oam.dev/component: mysvc
selector:
matchLabels:
app.oam.dev/component: mysvc
---
apiVersion: batch/v1
kind: Job
metadata:
name: countdown
... # skip tons of metadata info
spec:
parallelism: 1
completions: 1
template:
metadata:
name: countdown
spec:
containers:
- name: countdown
image: 'centos:7'
command:
- bin/bash
- '-c'
- for i in 9 8 7 6 5 4 3 2 1 ; do echo $i ; done
restartPolicy: Never
```
</details>
## CUE `Context`
KubeVela allows you to reference the runtime information of your application via `context` keyword.
The most widely used context is application name(`context.appName`) component name(`context.name`).
```cue
context: {
appName: string
name: string
}
```
For example, let's say you want to use the component name filled in by users as the container name in the workload instance:
```cue
parameter: {
image: string
}
output: {
...
spec: {
containers: [{
name: context.name
image: parameter.image
}]
}
...
}
```
> Note that `context` information are auto-injected before resources are applied to target cluster.
### Full available `context` information can be used
* Check the reference docs of [definition protocol](../../platform-engineers/oam/x-definition#definition-runtime-context) to see all of the available information in KubeVela `context`.
## Composition
It's common that a component definition is composed by multiple API resources, for example, a `webserver` component that is composed by a Deployment and a Service. CUE is a great solution to achieve this in simplified primitives.
> Another approach to do composition in KubeVela of course is [using Helm](../../tutorials/helm).
## How-to
KubeVela requires you to define the template of workload type in `output` section, and leave all the other resource templates in `outputs` section with format as below:
```cue
outputs: <unique-name>:
<full template data>
```
> The reason for this requirement is KubeVela needs to know it is currently rendering a workload so it could do some "magic" like patching annotations/labels or other data during it.
Below is the example for `webserver` definition:
```
webserver: {
annotations: {}
attributes: workload: definition: {
apiVersion: "apps/v1"
kind: "Deployment"
}
description: ""
labels: {}
type: "component"
}
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.name
image: parameter.image
if parameter["cmd"] != _|_ {
command: parameter.cmd
}
if parameter["env"] != _|_ {
env: parameter.env
}
if context["config"] != _|_ {
env: context.config
}
ports: [{
containerPort: parameter.port
}]
if parameter["cpu"] != _|_ {
resources: {
limits:
cpu: parameter.cpu
requests:
cpu: parameter.cpu
}
}
}]
}
}
}
}
// an extra template
outputs: service: {
apiVersion: "v1"
kind: "Service"
spec: {
selector: {
"app.oam.dev/component": context.name
}
ports: [
{
port: parameter.port
targetPort: parameter.port
},
]
}
}
parameter: {
image: string
cmd?: [...string]
port: *80 | int
env?: [...{
name: string
value?: string
valueFrom?: {
secretKeyRef: {
name: string
key: string
}
}
}]
cpu?: string
}
}
```
Apply to your Kubernetes cluster:
```shell
$ vela def apply webserver.cue
ComponentDefinition webserver created in namespace vela-system.
```
The user could now declare an `Application` with it:
```yaml
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: webserver-demo
namespace: default
spec:
components:
- name: hello-world
type: webserver
properties:
image: oamdev/hello-world
port: 8000
env:
- name: "foo"
value: "bar"
cpu: "100m"
```
It will generate and manage below Kubernetes API resources in target cluster, you can use [kubectl](https://kubernetes.io/docs/tasks/tools/#kubectl) to check:
```shell
kubectl get deployment
```
```console
NAME READY UP-TO-DATE AVAILABLE AGE
hello-world-v1 1/1 1 1 15s
```
```shell
kubectl get svc
```
```console
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
hello-world-trait-7bdcff98f7 ClusterIP <your ip> <none> 8000/TCP 32s
```
## What's Next
* Learn more about [defining customized trait](../traits/customize-trait) in CUE.
* Learn how to [define health check and custom status](../traits/status) of Component.
* Learn how to [define policy](../policy/custom-policy) in CUE.