Making K8s Simpler for Developers using KubeVela

Is there a way for us to make k8s simpler for the developers? Developers want to focus on what’s important - which is coding their app. But they should also have the ability to deploy and expose it to K8s without asking help from Operators, so that it will not interrupt their workflow.

Working with K8s is not the easiest thing. Yes, it makes deployment, scaling, and managing of application easier. But to do these, you have to learn a lots of things. To deploy your single stateless application, you need to know how Deployments, Services, ConfigMaps, and Secrets are working. To expose your application, you need to deal with Ingress or Gateway + Virtual Service depending on how your cluster is setup.

All of these are overwhelming. So for DevOps engineers, is there a way to improve their experience working with K8s?

And the answer is yes! We can do that using KubeVela, an implementation of OAM(Open Application Model).

What is the Open Application Model?

OAM is an open source specifications and provides a higher level of abtractions for deploying cloud native apps on top of hybrid and multi-cloud environment. It can be described using the following principles:

  1. Defined the app deployment in a self-contained model, free of the platform details.
  2. It is modular and extensible. Write it to a deployment plan depending to your needs.
  3. It is runtime agnostic. It should work on k8s, cloud or even IoT.

Using OAM, we can create an abstraction that can help developers deal with platform complexity.

KubeVela in action

Pre-Requisites

  • A running K8s cluster with Ingress configured, check how I set up mine using KinD here.
  • KubeVela CLI and Core - read here to learn more on how to install KubeVela CLI and Core.

Let’s say you want to deploy a simple application and expose it to public. To do that, you need the Deployment, Service, and Ingress manifest sorted out. Mind you, these manifests is as simplest as it can get.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-world
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: hello-world
template:
metadata:
labels:
app: hello-world
spec:
containers:
- image: nginxdemos/hello
imagePullPolicy: Always
name: hello-world
ports:
- containerPort: 80
protocol: TCP
---
apiVersion: v1
kind: Service
metadata:
name: hello-world
namespace: default
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app: hello-world
type: ClusterIP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: hello-world
namespace: default
spec:
rules:
- host: localhost
http:
paths:
- backend:
service:
name: hello-world
port:
number: 80
path: /
pathType: ImplementationSpecific

Once you applied these manifests, you can access your app at http://localhost/. Delete the app deployment before proceeding to the next step.

Note: When I say “applied”, I mean kubectl apply -f app.yaml. And when I say “delete”, I mean kubectl delete -f app.yaml.

But if you’re going to deploy it using KubeVela, you’ll just need this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: hello-world
spec:
components:
- name: hello-world
type: webservice
properties:
image: nginxdemos/hello
port: 80
traits:
- type: gateway
properties:
domain: localhost
http:
"/": 80

Once applied, this Application manifest will handle the creation of the Deployment, Service, and Ingress resources. And your app will be accessible at http://localhost/. Neat, right?!

But what if you want to do a URL rewrite on the ingress? That is not possible with the current gateway trait the we have.

Note: Later we will discuss parts of KubeVela deployment Plan - like Component, Trait, Workflow, and Policy.

Don’t worry, Kubevela is extensible. We can create our own version of the gateway trait and tweak it so that it can accommodate such customizations.

Here’s our TraitDefinition for our new trait. Apply it:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
apiVersion: core.oam.dev/v1beta1
kind: TraitDefinition
metadata:
annotations:
definition.oam.dev/description: "gateway trait with URL rewrite"
name: gatewayv2
spec:
appliesToWorkloads:
- webservice
schematic:
cue:
template: |
outputs: service: {
apiVersion: "v1"
kind: "Service"
metadata: name: context.name
spec: {
selector: "app.oam.dev/component": context.name
ports: [
for k, v in parameter.http {
port: v
targetPort: v
},
]
}
}

outputs: ingress: {
apiVersion: "networking.k8s.io/v1"
kind: "Ingress"
metadata: {
name: context.name
annotations: {
if !parameter.classInSpec {
"kubernetes.io/ingress.class": parameter.class
}
"nginx.ingress.kubernetes.io/rewrite-target": parameter.rewritePath
}
}
spec: {
if parameter.classInSpec {
ingressClassName: parameter.class
}
rules: [{
host: parameter.domain
http: paths: [
for k, v in parameter.http {
path: k
pathType: "ImplementationSpecific"
backend: service: {
name: context.name
port: number: v
}
},
]
}]
}
}

parameter: {
// +usage=Specify the domain you want to expose
domain: string

// +usage=Specify the mapping relationship between the http path and the workload port
http: [string]: int

// +usage=Specify the class of ingress to use
class: *"nginx" | string

// +usage=Set ingress class in '.spec.ingressClassName' instead of 'kubernetes.io/ingress.class' annotation.
classInSpec: *false | bool

// +usage=url for rewrite
rewritePath: string
}

Note: This trait is defined using cue, read here to learn more about cue.

Now, we can attach our new trait to accommodate URL rewrite to our other webservice.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: hello-whale
spec:
components:
- name: hello-whale
type: webservice
properties:
image: crccheck/hello-world
port: 8000
traits:
- type: gatewayv2
properties:
domain: localhost
http:
"/foo": 8000
rewritePath: /

After applying it, you can now access the hello-whale app at http://localhost/foo. This webservice is now using the new trait that we’ve created. If we’re using the old “gateway’” trait, we’ll get a 404 since this webservice can only accomodate traffic coming from “/“.

Here, I showed a very simple deployment plan and glipse of KubeVela’s extensibility where I’ve created a trait that fits my needs. But it does not stop there. You can also do this for the other parts of KubeVela.

Parts of KubeVela

KubeVela’s deployment plan consists of four parts: Component, Trait, Workflow, and Policy.

  • Component - the application artifact to be deployed.
  • Trait - the operations that you can attach to your components, like gateway, scaler, DNS, etc.
  • Workflow - the crucial steps on the application delivery process, like manual approve, release across multi-cluster, notification, etc.
  • Policy - defines a strategy for certain aspects of the application like quality assurance, security, firewall rules, SLO, etc.

Note: Read here to learn more on how to use Workflow and Policy.

Using KubeVela, we as DevOps engineers, can hide the complexity of K8s while exposing to the devs what they need to work with k8s.

If you want to learn more about OAM visit their site, and for Kubevela check here.

Check here for more samples of KubeVela components.

And that’s all I have for you about OAM and KubeVela. Thank you! :)