How to use Open Application Model to run applications on Kubernetes
Tags: kubernetes, tutorial, opensource, showdev
Published: 2020-02-12 14:58:03

The [Open Application Model](https://oam.dev/) (OAM) is a specification for building cloud-native apps with [`Rudr`](https://github.com/oam-dev/rudr) as its [Kubernetes](https://kubernetes.io/) specific implementation. In this blog, we will look at a couple of examples to reinforce `OAM` and `Rudr` related concepts covered in [the previous blog](https://dev.to/itnext/building-cloud-native-apps-intro-to-open-application-model-and-rudr-bba).

We will start off by running a simple application on Kubernetes using `Rudr` components and then see an example of how to use a [`Rudr` Trait](https://github.com/oam-dev/rudr/blob/master/docs/concepts/traits.md).

> code is available on [GitHub](https://github.com/abhirockzz/rudr-k8s-sample)

## Pre-requisites

At its core, `Rudr` is a custom controller which runs as a Kubernetes `Deployment`. To install Rudr, you will need a Kubernetes cluster with versions `1.15.x` or `1.16.x` (these are the supported versions at the time of writing). Any cluster will work, but I have used [Azure Kubernetes Service](https://docs.microsoft.com/azure/aks/?WT.mc_id=devto-blog-abhishgu) for the examples in this blog. 

If you want to use AKS, all you need is an Azure subscription ([grab a free account here!](https://azure.microsoft.com/free/?WT.mc_id=devto-blog-abhishgu)) and the [Azure CLI](https://docs.microsoft.com/cli/azure/install-azure-cli?view=azure-cli-latest&WT.mc_id=devto-blog-abhishgu) to setup a managed Kubernetes cluster using the [`az aks create`](https://docs.microsoft.com/cli/azure/aks?view=azure-cli-latest&WT.mc_id=devto-blog-abhishgu#az-aks-create) command. 

Here is an example which spins up a single node cluster running Kubernetes version `1.15.7`

```
az aks create --resource-group <AZURE_RESOURCE_GROUP> --name <AKS_CLUSTER_NAME> --kubernetes-version 1.15.7 --node-count 1 --node-vm-size Standard_B2s --node-osdisk-size 30 --generate-ssh-keys

//point kubectl to AKS
az aks get-credentials --resource-group <AZURE_RESOURCE_GROUP> --name <AKS_CLUSTER_NAME>

//confirm
kubectl get nodes
```

After [installing `Helm 3`](https://helm.sh/docs/intro/), you can proceed with `Rudr` setup

```
//clone the repo
git clone https://github.com/oam-dev/rudr.git

//install it using Helm
helm install rudr ./charts/rudr --wait

//confirm Rudr Deployment
kubectl get deployment rudr

//check Rudr CRDs
kubectl get crds -l app.kubernetes.io/part-of=core.oam.dev
```

## Deploy a simple app with Rudr

We will start off with a basic example of deploying a simple application using the following Rudr objects: `ComponentSchematic` and `ApplicationConfig`.

The application is very simple - it's a containerized app that exposes an endpoint which responds with `Hello World!` by default or `Hello <greeting>` if the `GREETING` environment variable is set. To run this in Kubernetes, the obvious route is to use a `Deployment` object. Instead, we will create `Rudr` Custom Resource Definitions (CRDs) to represent our application, submit them to Kubernetes and let the `Rudr` controller/operator take care of dealing with specific Kubernetes resources.

### Deploy Rudr CRDs

We will start by creating a `ComponentSchematic`. Let's introspect it:

```
apiVersion: core.oam.dev/v1alpha1
kind: ComponentSchematic
metadata:
  name: greeter-component
spec:
  workloadType: core.oam.dev/v1alpha1.Server
  containers:
    - name: greeter
      image: abhirockzz/greeter-go
      env:
        - name: GREETING
          fromParam: greeting
      ports:
        - protocol: TCP
          containerPort: 8080
          name: http
      resources:
        cpu:
          required: 0.1
        memory:
          required: "128"
  parameters:
    - name: greeting
      type: string
      default: abhi_tweeter
```

This is a `ComponentSchematic` called `greeter-component` whose `workloadType` is `core.oam.dev/v1alpha1.Server` - this determines the type of Kubernetes resource created to handle this component. It has a single `container` which refers to the `abhirockzz/greeter-go` image on Docker Hub. The `parameters` section defines a configurable attribute named `greeting` whose default value `abhi_tweeter`. This parameter is referenced as an environment variable in the `env` attribute of the `containers` section.


Create the `ComponentSchematic` as such:

```
kubectl apply -f https://raw.githubusercontent.com/abhirockzz/rudr-k8s-sample/master/deploy/component.yaml

//output

componentschematic.core.oam.dev/greeter-component created
```

This will just create a `ComponentSchematic` object in Kubernetes - you can use `kubectl get components` to confirm. The `ComponentSchematic` cannot really do much on its own. It needs another `Rudr` entity to work with - the `ApplicationConfig`. Let's see what that looks like in this case:

```
apiVersion: core.oam.dev/v1alpha1
kind: ApplicationConfiguration
metadata:
  name: greeter-app-config
spec:
  components:
    - componentName: greeter-component
      instanceName: greeter-app
```

The `ApplicationConfig` is what instantiates a `ComponentSchematic` - in this case, it refers to the `greeter-component` `ComponentSchematic`. 

Create the `ApplicationConfig`:

```
kubectl apply -f https://raw.githubusercontent.com/abhirockzz/rudr-k8s-sample/master/deploy/app-config.yaml

//output
applicationconfiguration.core.oam.dev/greeter-app-config configured
```

To confirm:

```
kubectl get applicationconfiguration.core.oam.dev/greeter-app-config -o yaml
```

Take a closer look at the `status` section - you should see something similar to this:

```
apiVersion: core.oam.dev/v1alpha1
kind: ApplicationConfiguration
...
status:
  components:
    greeter-component:
      deployment/greeter-app: running
      service/greeter-app: created
  phase: synced
  phase: synced
```

### Check the Kubernetes objects

`Rudr` created a bunch of Kubernetes resources for us - `Deployment`, `Pod` and `Service`

To check the `Deployment`

```
kubectl get deployment/greeter-app

NAME          READY   UP-TO-DATE   AVAILABLE   AGE
greeter-app   1/1     1            1           42s
```

Check the `Pod`

```
kubectl get pod -l=app.kubernetes.io/name=greeter-app-config

NAME                           READY   STATUS    RESTARTS   AGE
greeter-app-586b5d4ddc-wrqtb   1/1     Running   0          2m30s
```

Finally, the Kubernetes `Service` resource

```
kubectl get service/greeter-app

NAME          TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)    AGE
greeter-app   ClusterIP   10.0.135.117   <none>        8080/TCP   4m15s
```

### Test the application

The simplest way to access the application is using port forwarding

> make sure you replace the name of the `Pod`

```
kubectl port-forward pod/<pod name> 9090:8080

Forwarding from 127.0.0.1:9090 -> 8080
Forwarding from [::1]:9090 -> 8080
```

Now you can simply [`curl`](https://curl.haxx.se/) the endpoint

```
curl localhost:9090

//output
Hello abhi_tweeter!
```

That' it! This was a very simple example of an application running in Kubernetes which was created using Rudr constructs only.

As an excercise, you can try creating the following `ApplicationConfig` and follow the steps outline above to access the application and what the result is

> Note that this `ApplicationConfig` uses `parameterValues` section to override the `parameters` defined in the `ComponentSchematic`. 

```
apiVersion: core.oam.dev/v1alpha1
kind: ApplicationConfiguration
metadata:
  name: greeter-app-config-2
spec:
  components:
    - componentName: greeter-component
      instanceName: greeter-app-2
      parameterValues:
        - name: greeting
          value: foobar
```

## Using a Trait

In the previous example, the Deployment object which was created had one Pod (single app instance). You can scale it up using `kubectl scale` command, but I think you get the flow now - we will not do that! Let's make use if the Manual scaler Trait in Rudr to achieve this. 

We will continue to use the same `ComponentSchematic` and introduce a new `ApplicationConfig` definition to ensure that there are two replicas of our application. We will do this wih the help of manual scaler trait

```
apiVersion: core.oam.dev/v1alpha1
kind: ApplicationConfiguration
metadata:
  name: greeter-app-config-3
spec:
  components:
    - componentName: greeter-component
      instanceName: scalable-greeter-app
      parameterValues:
        - name: greeting
          value: scalable
      traits:
        - name: manual-scaler
          properties:
            replicaCount: 2
```

The `greeter-app-config-3` `ApplicationConfiguration` references the `greeter-component` `ComponentSchematic`. Notice the `traits` section where we use a `manual-scaler` and specify `replicaCount` as `2`. Just to make sure we are able to differentiate this from the previous application, we override the paramater to pass in the value of `greeting` as `scalable`

Create the `ApplicationConfiguration`

```
kubectl apply -f https://raw.githubusercontent.com/abhirockzz/rudr-k8s-sample/master/deploy/manual-scaler-trait/app-config.yaml

//output
applicationconfiguration.core.oam.dev/greeter-app-config-3 created
```

Wait for few seconds and confirm that Rudr has triggered creation of `Kubernetes` objects:

```
kubectl get applicationconfiguration.core.oam.dev/greeter-app-config-3 -o yaml
```

You should see a `status` section

```
status:
  components:
    greeter-component:
      deployment/scalable-greeter-app: running
      service/scalable-greeter-app: created
  phase: synced
```

> If `deployment/scalable-greeter-app` in `unavailable` state, please retry after a 10 seconds or so

Confirm the `Deployment` object

```
kubectl get deployment/scalable-greeter-app

NAME                   READY   UP-TO-DATE   AVAILABLE   AGE
scalable-greeter-app   2/2     2            2           4m
```

Check the individual `Pod`s as well

```
kubectl get pods -l=app.kubernetes.io/name=greeter-app-config-3

NAME                                    READY   STATUS    RESTARTS   AGE
scalable-greeter-app-6488f64cb4-mj6nw   1/1     Running   0          5m
scalable-greeter-app-6488f64cb4-rpfvp   1/1     Running   0          5m
```

To access the application, just run a one-off `Pod` with `curl` installed in it. Once you're inside the Pod, you can simply use `curl $SCALABLE_GREETER_APP_SERVICE_HOST:$SCALABLE_GREETER_APP_SERVICE_PORT` to invoke the endpoint of the the application.

```
kubectl run curl --image=radial/busyboxplus:curl -i --tty --rm

[ root@curl-6bf6db5c4f-5hw6t:/ ]$ curl $SCALABLE_GREETER_APP_SERVICE_HOST:$SCALABLE_GREETER_APP_SERVIC
E_PORT
Hello scalable!
```

> `SCALABLE_GREETER_APP_SERVICE_HOST` and `SCALABLE_GREETER_APP_SERVICE_PORT` are available as environment varialbes thanks to the `ClusterIP` `Service` created by `Rudr`.

The expected response is `Hello scalable!` since we had overridden the `greeting` `parameter` in the `ApplicationConfiguraton`

## Clean up

You can use the [`az aks delete`](https://docs.microsoft.com/cli/azure/aks?view=azure-cli-latest&WT.mc_id=devto-blog-abhishgu#az-aks-delete) command to delete the entire AKS cluster or delete the individual `ApplicationConfiguration` to trigger a cascade removal of all the related Kubernetes resources associated with it (`Deployment` etc.). To remove the `Rudr` deployment, simply use `helm delete rudr` and `kubectl delete crd -l app.kubernetes.io/part-of=core.oam.dev` if you also want to delete the `Rudr` CRDs (`components`, `configurations` etc.)

That's all for this two-part series on the basics of `Open Application Model` and `Rudr` along with a hands-on example to get a feel of how to actually use it on Kubernetes. If you found this article helpful, please like and follow 🙌 Happy to get feedback via [Twitter](https://twitter.com/abhi_tweeter) or feel free to drop a comment.