How to add a new function to your K8s PaaS in 20 minutes?

Head picture.png
Author | Sun Jianbo (Tian Yuan)
Source | Alibaba Cloud Native Official Account

Last month, KubeVela was officially released as a simple, easy-to-use and highly scalable application management platform and core engine. It can be said to be a magic weapon for platform engineers to build their own cloud-native PaaS. Then this article uses a practical example to explain how to "launch" a new capability for you based on KubeVela's PaaS within 20 minutes.

Before starting the tutorial of this document, please make sure that you have installed KubeVela and its dependent K8s environment correctly .

The basic structure of KubeVela extension

The basic structure of KubeVela is shown in the figure:

1.png

To put it simply, KubeVela expands capabilities for users by adding Workload Type and Trait . The service provider of the platform registers and expands through the Definition file, and reveals the expanded functionality through Appfile upwards. The basic writing process is also given in the official documents, two of which are extension examples of Workload, and one is an extension example of Trait:

We take a built-in WorkloadDefinition as an example to introduce the basic structure of the Definition file:

apiVersion: core.oam.dev/v1alpha2
kind: WorkloadDefinition
metadata:
  name: webservice
  annotations:
    definition.oam.dev/description: "`Webservice` is a workload type to describe long-running, scalable, containerized services that have a stable network endpoint to receive external network traffic from customers.
    If workload type is skipped for any service defined in Appfile, it will be defaulted to `Web Service` type."
spec:
  definitionRef:
    name: deployments.apps
  extension:
    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
                              }}
                      }]
              }}}
      }
      parameter: {
          // +usage=Which image would you like to use for your service
          // +short=i
          image: string

          // +usage=Commands to run in the container
          cmd?: [...string]

          // +usage=Which port do you want customer traffic sent to
          // +short=p
          port: *80 | int
          // +usage=Define arguments by using environment variables
          env?: [...{
              // +usage=Environment variable name
              name: string
              // +usage=The value of the environment variable
              value?: string
              // +usage=Specifies a source the value of this var should come from
              valueFrom?: {
                  // +usage=Selects a key of a secret in the pod's namespace
                  secretKeyRef: {
                      // +usage=The name of the secret in the pod's namespace to select from
                      name: string
                      // +usage=The key of the secret to select from. Must be a valid secret key
                      key: string
                  }
              }
          }]
          // +usage=Number of CPU units for the service, like `0.5` (0.5 CPU core), `1` (1 CPU core)
          cpu?: string
      }

At first glance it looks quite long and seems very complicated, but don't worry, it is actually divided into two parts:

  • Definition registration part without extension fields
  • CUE Template section for Appfile

Let's take it apart and introduce it slowly, but it's actually very easy to learn.

Definition registration part without extension fields

apiVersion: core.oam.dev/v1alpha2
kind: WorkloadDefinition
metadata:
  name: webservice
  annotations:
    definition.oam.dev/description: "`Webservice` is a workload type to describe long-running, scalable, containerized services that have a stable network endpoint to receive external network traffic from customers.
    If workload type is skipped for any service defined in Appfile, it will be defaulted to `Web Service` type."
spec:
  definitionRef:
    name: deployments.apps

This part ManDaManSuan line 11, which line 3 is introduced in the webservicefunction, lines 5 are fixed format. Only 2 lines have specific information:

  definitionRef:
    name: deployments.apps

The meaning of these two lines represents what the CRD name is used behind this Definition, and its format is <resources>.<api-group>. Learn K8s students should know K8s more commonly used by api-group, versionand kindlocate resources, and kindcorresponding in K8s restful API is resources. With the familiar Deploymentand ingress, for example, its correspondence is as follows:

image.png

Here is a little knowledge. Why do we need to add the concept of resources when we have a kind? Because a CRD has some fields like status and replica in addition to the kind itself and hopes to be decoupled from the spec itself to be updated separately in the restful API, so in addition to the kind corresponding to the resources, there will be some additional resources, such as Deployment The status is expressed as deployments/status.

So I believe that you are smart enough to understand how to write Definition without extension. The easiest way is to splice it according to the K8s resource combination method. Just fill in the spaces in the three angle brackets below.

apiVersion: core.oam.dev/v1alpha2
kind: WorkloadDefinition
metadata:
  name: <这里写名称>
spec:
  definitionRef:
    name: <这里写resources>.<这里写api-group>

This is also true for TraitDefinition registration.

apiVersion: core.oam.dev/v1alpha2
kind: TraitDefinition
metadata:
  name: <这里写名称>
spec:
  definitionRef:
    name: <这里写resources>.<这里写api-group>

So put Ingressas KubeVela is written into the extension:

apiVersion: core.oam.dev/v1alpha2
kind: TraitDefinition
metadata:
  name:  ingress
spec:
  definitionRef:
    name: ingresses.networking.k8s.io

In addition, some other functional model layer functions have been added to TraitDefinition, such as:

  • appliesToWorkloads: Indicates which workload types this trait can act on.
  • conflictWith: Indicates that this trait conflicts with other types of traits.
  • workloadRefPath: Indicates the workload field contained in this trait. KubeVela will automatically fill it when generating the trait object. ...

These functions are optional, and are not involved in this article. We will introduce them in detail in other subsequent articles.

So here, I believe you have mastered a basic extension mode without extensions, and the rest is the abstract template around CUE .

CUE Template section for Appfile

Students who are interested in CUE itself can refer to this introduction to CUE basics to do more understanding. Due to space limitations, this article will not elaborate on CUE itself.

Everyone knows that KubeVela's Appfile is very concise to write, but the object of K8s is a relatively complex YAML. In order to keep it concise without losing scalability, KubeVela provides a bridge from complexity to simplicity. This is the role of CUE Template in Definition.

CUE format template

Let us first look at a deployment YAML file, as shown below, many of the content is a fixed frame (template part), the content that really needs the user to fill is actually a few fields (parameter part).

apiVersion: apps/v1
kind: Deployment
meadata:
  name: mytest
spec:
  template:
    spec:
      containers:
      - name: mytest
        env:
        - name: a
          value: b
        image: nginx:v1
    metadata:
      labels:
        app.oam.dev/component: mytest
  selector:
    matchLabels:
      app.oam.dev/component: mytest

In KubeVela, the fixed format Definition file is divided outputand the parametertwo parts. Where the outputcontent is the "Templates section", which parameteris part of the argument.

Then let's rewrite the above Deployment YAML into the format of the template in the Definition.

output: {
    apiVersion: "apps/v1"
    kind:       "Deployment"
    metadata: name: "mytest"
    spec: {
        selector: matchLabels: {
            "app.oam.dev/component": "mytest"
        }
        template: {
            metadata: labels: {
                "app.oam.dev/component": "mytest"
            }
            spec: {
                containers: [{
                    name:  "mytest"
                    image: "nginx:v1"
                    env: [{name:"a",value:"b"}]
                }]
            }}}
}

This format is very similar to json, in fact this is the format of CUE, and CUE itself is a superset of json. In other words, the CUE format meets the JSON rules and adds some simple rules to make it easier to read and use:

  • The comment style of the C language.
  • The double quotation marks that indicate the field name can be defaulted without special symbols.
  • The comma at the end of the field value can be defaulted, and there is no error if the comma at the end of the field is written.
  • The outer brace can be omitted.

Template parameter in CUE format--variable reference

The template part is written, let us build the parameter part, and this parameter is actually a variable reference.

parameter: {
    name: string
    image: string
}
output: {
    apiVersion: "apps/v1"
    kind:       "Deployment"
    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
                }]
            }}}
}

As shown in the above example, the template parameters KubeVela is through parameterthis section accomplished, but parameteressentially as a reference, replacing the outputcertain field.

Complete Definition and use in Appfile

In fact, after the combination of the above two parts, we can already write a complete Definition file:

apiVersion: core.oam.dev/v1alpha2
kind: WorkloadDefinition
metadata:
  name: mydeploy
spec:
  definitionRef:
    name: deployments.apps
  extension:
    template: |
        parameter: {
            name: string
            image: string
        }
        output: {
            apiVersion: "apps/v1"
            kind:       "Deployment"
            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
                        }]
                    }}}
        }

In order to facilitate debugging, in general, it may be previously divided into two files, the front part of the discharge portion yaml assumed named def.yamlas:

apiVersion: core.oam.dev/v1alpha2
kind: WorkloadDefinition
metadata:
  name: mydeploy
spec:
  definitionRef:
    name: deployments.apps
  extension:
    template: |

The other puts the cue file, assuming the name is def.cue:

parameter: {
    name: string
    image: string
}
output: {
    apiVersion: "apps/v1"
    kind:       "Deployment"
    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
                }]
            }}}
}

First to def.cuemake a formatted, format, while cue tool itself will do some checking, can also be more in-depth to make debugging cue command :

cue fmt def.cue

After debugging, you can assemble this yaml through a script:

./hack/vela-templates/mergedef.sh def.yaml def.cue > mydeploy.yaml

Then apply this yaml file to the K8s cluster.

$ kubectl apply -f mydeploy.yaml
workloaddefinition.core.oam.dev/mydeploy created

Once the new ability kubectl applyto Kubernetes in without restarting, do not update, KubeVela users can immediately see a new capability appears and you can use:

$ vela worklaods
Automatically discover capabilities successfully ✅ Add(1) Update(0) Delete(0)

TYPE           CATEGORY    DESCRIPTION
+mydeploy      workload    description not defined

NAME        DESCRIPTION
mydeploy    description not defined

It is used in Appfile as follows:

name: my-extend-app
services:
  mysvc:
    type: mydeploy
    image: crccheck/hello-world
    name: mysvc

The implementation vela upwill be able to run this up:


$ vela up -f docs/examples/blog-extension/my-extend-app.yaml
Parsing vela appfile ...
Loading templates ...

Rendering configs for service (mysvc)...
Writing deploy config to (.vela/deploy.yaml)

Applying deploy configs ...
Checking if app has been deployed...
App has not been deployed, creating a new deployment...
✅ App has been deployed 

Guess you like

Origin blog.51cto.com/13778063/2571763