Book Review: DevOps for Serverless Applications (with tutorial)
Tags: serverless, aws, devops
Published: 2018-12-06 15:05:12

---
title: Book Review: DevOps for Serverless Applications (with tutorial)
published: true
description: Review of a book on serverless devops
tags: serverless, aws, devops
---

On my way to becoming a full-stack developer, I decided to make serverless my back-end flavor of choice. To get a feeling for how to keep things up and running, I tried to get a bit educated in DevOps, and so I read [DevOps for Serverless Applications](https://www.amazon.com/DevOps-Serverless-Applications-Seamlessly-environment-ebook/dp/B07DH9T53M).

> DevOps (a clipped compound of "development" and "operations") is a software development methodology that combines software development (Dev) with information technology operations (Ops). The goal of DevOps is to shorten the systems development life cycle while also delivering features, fixes, and updates frequently in close alignment with business objectives.  ([Wikipedia](https://en.wikipedia.org/wiki/DevOps))

## What I liked about "DevOps for Serverless Applications"

The author writes about multiple frameworks and cloud providers, not just "Serverless and AWS." This allows him to talk about the subtle differences of each approach and gives you a broader understanding of DevOps in general.

He also goes deep into best-practices of monitoring and troubleshooting serverless systems so that you can work with the compiled experience of the author. I think these are also the best chapters of the book. "Yes it first looks like it's a good idea to do it that way, but don't do it, you will regret it in a few months!"

## What I didn't like about "DevOps for Serverless Applications"

Well, I think the book could have been much shorter, not because it has useless text on many pages, but because it talks about so many things.

Sure, it's good that the author used all these frameworks and platforms and can give me a good idea of what to do and **why** to do it and all the platforms are excellent to justify all the ideas. For me, as an AWS user, it would be okay to mention the serverless platforms of Google and IBM in a subclause and not in whole chapters.

But that is personal preference and if you interested in getting a view on the whole eco-system, you're not doing anything wrong by reading this book. :)

## Tutorial

By courtesy of the author I have a tutorial for you, so you can make yourself a picture of his style. Shashikant Bangera will explain how to "add DevOps flavor to kubeless".

## Adding DevOps Flavor to Kubeless

### What is Kubeless?

Kubeless is an open source serverless framework based on Kubernetes. It allows you to deploy and execute a piece of code without worrying about the underlying infrastructure. It uses the resources of Kubernetes to provide autoscaling, routing, and monitoring. 

Post-deployment functions can be triggered with pub–sub, HTTP, and scheduling. The pub–sub events are managed through a Kafka cluster, an out-of-the-box component within Kubeless, which consists of a basic Kafka cluster with a broker and a zookeeper. HTTP triggers are available through the Kubernetes services, and scheduled functions translate to a cron job. 

The languages/runtime supported by Kubeless are

- python2.7/3.4/3.6
- nodejs6/8/distroless8
- ruby2.4
- php7.2
- go1.10
- dotnetcore2.0
- java1.8
- ballerina0.980.0
- jvm1.8
- Kubeless also supports HTTP, NATS, Kafka, cron, and stream triggers. 

### Kubeless Architecture

Kubeless uses custom resource definition, which means that when you create a custom resource definition, the Kubernetes API server creates a resource path for each version specified.

Custom resource definition can be namespaced or cluster-scoped, so CRD is called a function, which means that Kubeless functions can be created as normal Kubernetes resources in the background, and a controller is created. The controller will watch these custom resources, and will launch upon runtime demand.

### How to set up Kubeless

It is pretty easy to set up Kubeless. First, download Kubeless from the release page, create a namespace, and then, through the YAML manifest found on the release page, create the functions' custom resource definition and launch a controller. If you are setting up Kubeless on your personal laptop, then you have to use minikube to do this.

First, set up minikube by going to https://github.com/kubernetes/minikube. Once minikube is installed, you should be able to have a single node Kubernetes cluster inside a virtual machine. You should also be able to execute minikube commands through the Command Prompt.

Create a cluster and then create the Kubeless resources within this cluster. Then, create a simple Kubeless function, and, then, deploy and invoke it. You’ll also set up a dashboard for minikube and see how the controller and function are created and deployed, respectively. Here’s how to implement this:

(1) Create a minikube local Kubernetes cluster:

```
$ minikube start
Starting local Kubernetes v1.9.0 cluster...
Starting VM...
Getting VM IP address...
Moving files into cluster...
Setting up certs...
Connecting to cluster...
Setting up kubeconfig...
Starting cluster components...
Kubectl is now configured to use the cluster.
Loading cached images from config file.
```

(2) Next, create a minikube dashboard with the following command. The command trigger will open up a browser with the dashboard. The dashboard will show you the services, pods, and manager.

```
$ minikube dashboard
```

(3) Now that you have a cluster, deploy Kubeless to it. There are several Kubeless manifests available for multiple Kubernetes environments (non-RBAC, RBAC, and OpenShift). Here, you’ll use the manifest for non-RBAC ( non-role-based access control).

```
$ export RELEASE=$(curl -sk
https://api.github.com/repos/kubeless/kubeless/releases/latest | grep tag_name |
cut -d '"' -f 4)
$ kubectl create ns kubeless
$ echo $RELEASE
$ kubectl create -f
https://github.com/kubeless/kubeless/releases/download/v1.0.0-alpha.8/kubeless-
non-rbac-v1.0.0-alpha.8.yaml
serviceaccount "controller-acct" created
customresourcedefinition "functions.kubeless.io" created
customresourcedefinition "httptriggers.kubeless.io" created
customresourcedefinition "cronjobtriggers.kubeless.io" created
configmap "kubeless-config" created
deployment "kubeless-controller-manager" created
```

(4) Now that you have deployed Kubeless, check whether it has deployed properly: 

```
$ kubectl get pods -n kubeless
NAME READY STATUS RESTARTS AGE
kubeless-controller-manager-c6b69df76-65gsh 1/1 Running 0 2m
$ kubectl get deployment -n kubeless
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
kubeless-controller-manager 1 1 1 1 3m
$ kubectl get customresourcedefinition
NAME AGE
cronjobtriggers.kubeless.io 3m
functions.kubeless.io 3m
httptriggers.kubeless.io 3m
```

(5) Next, install the Kubeless CLI locally for deploying, invoking, and deleting the Kubeless functions:

```
$ export OS=$(uname -s| tr '[:upper:]' '[:lower:]')
$ curl -OL
https://github.com/kubeless/kubeless/releases/download/$RELEASE/kubeless_$OS-
amd64.zip
$ unzip kubeless_$OS-amd64.zip
$ sudo mv bundles/kubeless_$OS-amd64/kubeless /usr/local/bin/
```

(6) Now, create a function and deploy it. The following is a simple Python function that you’ll deploy and invoke. Create a function named `test.py` with the following content:

```python
def hello(event, context):
  print event
  return event['data']
```
(7) Now deploy the function. When you refresh the minikube dashboard, you should be able to see the `hello` function deployed there:

```
$ kubeless function deploy hello --runtime python2.7--from-file test.py --handler test.hello --namespace kubeless
INFO[0000] Deploying function...
INFO[0000] Function hello submitted for deployment
INFO[0000] Check the deployment status executing 'kubeless function ls hello'
```

The following list explains the various elements of the preceding code:

- `kubeless function deploy hello` tells Kubeless to register a new function named `hello`.
The function will be accessible over the web using this name. Note that this doesn't need to
be the same as the function name used inside the code.
- `--trigger-http` tells Kubeless that the function will be invoked over HTTP.
- `--runtime python2.7` tells Kubeless to use Python 2.7 to execute the code. Node is also supported as a runtime, with more to come in the future.
- `--handler test.hello` tells Kubeless the name of the function to call inside the code module. You can see in the preceding Python code that the function is called `hello`.
- `--from-file /tmp/hello.py` tells Kubeless to upload and use the `/tmp/hello.py` file as the source for the function. It is possible to pass a function in other ways as well.

You’ll see the function custom resource being created through the following commands:

```
$ kubectl get functions
NAME AGE
hello 2m

$ kubeless function ls --namespace kubeless
NAME NAMESPACE HANDLER RUNTIME DEPENDENCIES STATUS
hello kubeless test.hello python2.7 1/1 READY
```

(8) Now, invoke the function, as follows:

```
$ kubeless function call hello --data 'Hello Serverless!' --namespace kubeless
Hello Serverless!
```

(9) You can also delete the function, as follows:

```
$ kubeless function delete hello --namespace kubeless
$ kubeless function ls --namespace kubeless
NAME NAMESPACE HANDLER RUNTIME DEPENDENCIES STATUS
```

So far, you have installed Kubeless locally, created a simple function, deployed it, invoked it, and undeployed it. In the next section, you’ll learn how to automate deployment using the Serverless Framework.

### Setting up continuous integration and deployment

You’ll be using the Serverless Framework to kick-start the development and deployment of the Kubeless functions. The Serverless Framework has provided lots of features to adopt Kubeless without much effort. Here’s a look at the various functionalities provided by Serverless.

**Creation of the service**

Use the `create` command to create basic services using the passing of runtime and `path` to create a directory. Currently, two runtimes are provided—one is Python and the other is Node.js. So, if you run the following command with the `path` parameter, it will create a folder with a simple serverless function. The runtimes currently available are `kubeless-python` and `kubeless-nodejs`:

```
$ serverless create --template kubeless-python --path myKubelessFunc
```

The `create` command will create a service, and each service configuration will have the following three files:

- `serverless.yml`: The main responsibility of this file is to declare the service, define the provider, custom plugin (in this case, the serverless-kubeless plugin), and events or triggers that the function will execute, and configure files using serverless variables. 
- `handler.py`: This file will contain the function code. The function definition
with `serverless.yml` will point to `handler.py`.
- `package.json`: This is the file for the npm package definition of your functions, containing all the dependencies and the kubeless-serverless plugin.

Update these files with your function and configuration, as shown in the following code. You’re updating a function to search a bike station from the station feed exposed through JSON. The code is also available in the GitHub repository at `https://github.com/shzshi/kubeless-serverless.git`:

```python
#handler.py
import urllib2
import json
def find(event, context):
  term = event['data']['term']
  url = "https://feeds.capitalbikeshare.com/stations/stations.json"
  response = urllib2.urlopen(url)
  stations = json.loads(response.read())
  hits = []
  for station in stations["stationBeanList"]:
    if station["stAddress1"].find(term) > -1: hits.append(station)
  return json.dumps(hits)
```

Replace the `serverless.yaml` with the following content:

```yaml
# serverless.yml
service:bikesearch
provider:
  name:kubeless
  runtime:python2.7
plugins:
  -serverless-kubeless
functions:
  bikesearch:
    handler:handler.find
```

**Deploying the function**

As the required files were created through a template, you can modify them as per your needs and then simply deploy and invoke them when you need them. Deploy them using serverless; then `npm install` will get the required dependencies for serverless, such as the `kubeless-serverless` plugin. Then, you can deploy the function, as shown in the following code:

```
$ npm install
$ serverless deploy -v
Serverless: Packaging service...
Serverless: Excluding development dependencies...
Serverless: Deploying function bikesearch...
Serverless: Pods status: {"waiting":{"reason":"PodInitializing"}}
Serverless: Function bikesearch successfully deployed
Serverless: Skipping ingress rule generation
```

**Invoking the function**

The Kubeless function can be invoked through the command line or through a UI provided by Kubeless. The Kubeless UI can be provisioned by downloading the file locally and running it, by using a Docker image and Dockerfile, or through the Kubernetes manifest that's available with the repository. Here, the Kubernetes manifest is used, as shown in the following code:

```
$ kubectl create -f https://raw.githubusercontent.com/kubeless/kubeless-ui/master/k8s.yaml
serviceaccount "ui-acct" created
clusterrole "kubeless-ui" created
clusterrolebinding "kubeless-ui" created
deployment "ui" created
service "ui" created
$ minikube service ui -n kubeless
```

The `minikube` command will pop a browser up and open a UI. The UI has the ability to create, edit, invoke, and delete the function, so invoke the function that you deployed. Add `{"term":"New York"}` in the `textarea` request, select the **Request as POST**, and click on **Run Function**. The function will be executed successfully, with the station data response output displayed.

You can invoke the same function through the Serverless Framework as well, and the function will execute and get the required data, as shown in the following code:

```
$ serverless invoke --function bikesearch --data '{"term":"Albemarle"}' -l
Serverless: Calling function: bikesearch...
--------------------------------------------------------------------
[ { availableDocks: 12,
totalDocks: 15,
city: '',
altitude: '',
stAddress2: '',
longitude: -77.079382,
lastCommunicationTime: '2018-08-15 04:16:15 PM',
postalCode: '',
statusValue: 'In Service',
testStation: false,
stAddress1: 'Tenleytown / Wisconsin Ave &amp; Albemarle St NW',
stationName: 'Tenleytown / Wisconsin Ave &amp; Albemarle St NW',
landMark: '',
latitude: 38.947607,
statusKey: 1,
availableBikes: 1,
id: 80,
location: '' } ]
```

What happens if something goes wrong? You don’t have error handling in place, but you can test the logging by feeding the error while invoking the function. So, invoke the function with an error in the data, as shown in the following code:

```
$serverless invoke --function bikesearch --data '{"trm":"Albemarle"}' -l
Serverless: Calling function: bikesearch...

  Error --------------------------------------------------

  Internal Server Error

    For debugging logs, run again after setting the "SLS_DEBUG=*" environment variable.

  Get Support --------------------------------------------
    Docs: docs.serverless.com
    Bugs: github.com/serverless/serverless/issues
    Forums: forum.serverless.com
    Chat: gitter.im/serverless/serverless

  Your Environment Information -----------------------------
    OS: darwin
    Node Version: 6.10.3
    Serverless Version: 1.26.1
```

Serverless returned an error message with a 500 server code, which is what you would expect from a web framework. However, it would be useful to see the Python stack trace in order to better debug the source of the error. So, get the logs to see what the error was:

```
$ serverless logs -f bikesearch
Hit Ctrl-C to quit.
172.17.0.1 - - [15/Aug/2018:20:17:18 +0000] "POST / HTTP/1.1" 200 460 "" "" 0/934928
172.17.0.1 - - [15/Aug/2018:20:17:18 +0000] "GET /healthz HTTP/1.1" 200 2 "" "kube-probe/." 0/133
172.17.0.1 - - [15/Aug/2018:20:17:48 +0000] "GET /healthz HTTP/1.1" 200 2 "" "kube-probe/." 0/72
172.17.0.1 - - [15/Aug/2018:20:18:18 +0000] "GET /healthz HTTP/1.1" 200 2 "" "kube-probe/." 0/108
172.17.0.1 - - [15/Aug/2018:20:18:48 +0000] "GET /healthz HTTP/1.1" 200 2 "" "kube-probe/." 0/123
172.17.0.1 - - [15/Aug/2018:20:19:18 +0000] "GET /healthz HTTP/1.1" 200 2 "" "kube-probe/." 0/74
172.17.0.1 - - [15/Aug/2018:20:19:48 +0000] "GET /healthz HTTP/1.1" 200 2 "" "kube-probe/." 0/138
172.17.0.1 - - [15/Aug/2018:20:20:18 +0000] "GET /healthz HTTP/1.1" 200 2 "" "kube-probe/." 0/75
172.17.0.1 - - [15/Aug/2018:20:20:48 +0000] "GET /healthz HTTP/1.1" 200 2 "" "kube-probe/." 0/149
172.17.0.1 - - [15/Aug/2018:20:21:18 +0000] "GET /healthz HTTP/1.1" 200 2 "" "kube-probe/." 0/187
172.17.0.1 - - [15/Aug/2018:20:21:48 +0000] "GET /healthz HTTP/1.1" 200 2 "" "kube-probe/." 0/147
172.17.0.1 - - [15/Aug/2018:20:22:18 +0000] "GET /healthz HTTP/1.1" 200 2 "" "kube-probe/." 0/71
172.17.0.1 - - [15/Aug/2018:20:22:47 +0000] "POST / HTTP/1.1" 200 2131 "" "" 1/232988
Traceback (most recent call last):
 File "/usr/local/lib/python2.7/dist-packages/bottle.py", line 862, in _handle
return route.call(**args)
 File "/usr/local/lib/python2.7/dist-packages/bottle.py", line 1740, in wrapper
 rv = callback(*a, **ka)
 File "/kubeless.py", line 76, in handler
 raise res
KeyError: 'term'
```

It is clear from the **KeyError** phrase that the function failed because of the wrong key name, so this gives you a view of what’s going wrong. But in a production-like environment, you need to have more sophisticated error-handling methods.

**Continuous integration with Jenkins**

With respect to Kubeless, using the Serverless Framework, you can only set up the deployment if your Kubernetes cluster (minikube), Serverless Framework, and Jenkins are set up locally. There is no provision to set up a remote deployment with the Serverless Framework. But as the Serverless Framework and Kubeless mature over time, these features will be added. Here, the files are created
to set up deployment locally.

If you clone the repository at <https://github.com/shzshi/kubeless-continuous-integration.git>, you should be able to use this template to set up continuous integration locally. However, you can run the files locally on the laptop, provided you have the Serverless Framework installed and configured.

Here’s how to do this:

```
$ git clone https://github.com/shzshi/kubeless-continuous-integration.git
$ cd kubeless-continuous-integration
```

You should see six files and one directory in this folder, but you won't be using Dockerfiles and Jenkinsfiles in this tutorial. They can be used once the remote deployment is possible with the Serverless Framework for Kubeless, as shown in the following code:

```
$ npm install
$ npm test
> kubeless-nodejs@1.0.0 test /Users/shashi/Documents/packt/chapter7/kubeless-
continuous-integration
> mocha ./test/*.js
kubelesshello
✓ should return 0 when "Hello Kubeless" is present
1 passing (8ms)
```

You ran `npm install` to get the required dependencies for the Serverless Framework and Node.js application and test. Post this, you ran the npm test, for which you created a simple unit test to check the sanity of your function before you deployed them to the cluster, as shown in the following code:

```
$ serverless deploy -v
Serverless: Packaging service...
Serverless: Excluding development dependencies...
Serverless: Deploying function kubelesshello...
Serverless: Pods status: {"waiting":{"reason":"PodInitializing"}}
Serverless: Pods status: {"waiting":{"reason":"PodInitializing"}}
Serverless: Function kubelesshello successfully deployed
Serverless: Skipping ingress rule generation
```

So, your Node.js function will be deployed successfully, and you can invoke it locally or through the Kubeless UI and test it. Post-invocation, you should get the output of `Hello Kubeless`, as shown in the following code:

```
$ serverless invoke -f kubelesshello -l
Serverless: Calling function: kubelesshello...
--------------------------------------------------------------------
Hello Kubeless
```

**Monitoring Kubeless**

You can monitor the `kubeless` function using Prometheus. There is an inbuilt runtime support for Prometheus, which will automatically collect metrics for each function. Prometheus will show those metrics on the default dashboard. 

The Prometheus metrics can be visualized through Grafana. The Grafana dashboard can be configured through the sample dashboard JSON file provided by Kubeless
at <https://github.com/kubeless/kubeless/blob/master/docs/misc/kubeless-grafana-dashboard.json>.

*If you found this article interesting, you can explore [DevOps for Serverless Applications](https://www.amazon.com/DevOps-Serverless-Applications-Seamlessly-environment/dp/1788623444) to set up complete CI and CD pipelines for your serverless applications using DevOps principles. DevOps for Serverless Applications takes you through different DevOps-related scenarios to give you a solid foundation in serverless deployment.*