Get Started with Serverless Computing on Kubernetes with Minikube and Kubeless


Typically, you write code in your development environment and then create a deployment environment to run it. Even for running a very small amount of code in the cloud, you need to go through the hassle of creating a virtual machine or container, deploying it to the cloud, and maintaining it there.

The idea behind serverless computing is that it lets you, as a developer, focus only on writing your code. With serverless computing, you just upload the code somewhere, and it runs whenever you invoke it. Simply put, serverless computing frees you from the complexities of creating Docker containers or configuring and maintaining Kubernetes clusters.

This guide will show you how to get started with serverless computing using Bitnami's Kubeless platform, running on top of a Kubernetes cluster in Minikube or Google Container Engine.


To get started with serverless computing, you only need three ingredients:

  • A Kubernetes cluster
  • Kubeless
  • Some code

This tutorial assumes that you already have a Kubernetes cluster on either Minikube or GKE with kubectl installed. For detailed instructions to accomplish these tasks, refer to our Kubernetes starter tutorial.

Once you have verified that your Kubernetes cluster is running, you will need to perform the following steps:

  • Start the Kubernetes Dashboard on port 8080
  • Install Kubeless
  • Write a function
  • Register the function with Kubeless
  • Call the function

Step 1: Start the Kubernetes Dashboard on port 8080

Once your Kubernetes cluster is running, make the Kubernetes Dashboard available on port 8080 with the command below.

$ kubectl proxy --port=8080
NOTE: Execute the above command in a separate window or shell, so that the Kubernetes Dashboard remains running while you work through the remaining steps of this guide.

Point your browser to http://localhost:8080/ui and you should see something like this:

Kubernetes dashboard

Once you can access the Kubernetes Dashboard on port 8080, proceed to install Kubeless.

Step 2: Install the Kubeless CLI tool

Kubeless is an application designed to be deployed on top of a Kubernetes cluster. It accepts commands to register, delete, and list functions that you would like to run. Once Kubeless is running, all of the hassles of setting up containers and pods, disappear.

On Linux, use these commands to install Kubeless CLI in your system:

$ curl -L >
$ unzip
$ sudo cp bundles/kubeless_linux-amd64/kubeless /usr/local/bin/

On Mac OS X, use these commands to install Kubeless CLI in your system:

$ curl -L >
$ unzip
$ sudo cp bundles/kubeless_darwin-amd64/kubeless /usr/local/bin/
TIP: For detailed installation instructions, visit the Kubeless releases page.

Step 3: Deploy Kubeless in your Kubernetes cluster

You should deploy Kubeless in your cluster using one of YAML manifests found in the release package. It will create a kubeless Namespace and a function ThirdPartyResource. You will see a kubeless Controller, and kafka, zookeeper StatefulSet running.

There are several kubeless manifests being shipped for multiple k8s environments (non-rbac and rbac), please consider to pick up the correct one:

  • kubeless-$RELEASE.yaml is used for non-RBAC Kubernetes cluster.
  • kubeless-rbac-$RELEASE.yaml is used for RBAC-enabled Kubernetes cluster.

For example, this below is a show case of deploying kubeless to a non-RBAC Kubernetes cluster.

$ export RELEASE=0.0.20
$ kubectl create ns kubeless
$ kubectl create -f$RELEASE/kubeless-$RELEASE.yaml

$ kubectl get pods -n kubeless
NAME                                   READY     STATUS    RESTARTS   AGE
kafka-0                                1/1       Running   0          1m
kubeless-controller-3331951411-d60km   1/1       Running   0          1m
zoo-0                                  1/1       Running   0          1m

$ kubectl get deployment -n kubeless
kubeless-controller   1         1         1            1           1m

$ kubectl get statefulset -n kubeless
kafka     1         1         1m
zoo       1         1         1m

$ kubectl get thirdpartyresource
NAME             DESCRIPTION                                     VERSION(S)   Kubeless: Serverless framework for Kubernetes   v1

$ kubectl get functions

Test if Kubeless is ready with the command below:

$ kubeless function ls

You should see something like this:

Kubeless testing

If you visit the Kubernetes Dashboard, you should also be able to see the kubeless namespace, which will provide additional confirmation that Kubeless was successfully installed.

Step 4: Write a function

You have Kubeless running, which means you can now forget about containers, VMs, Kubernetes, etc… All you need is some code.

Here is some code:

import urllib2
import json

def find(request):
    term = request.json["term"]
    url = ""    
    response = urllib2.urlopen(url)
    stations = json.loads(
    hits = []
    for station in stations["stationBeanList"]:
        if station["stAddress1"].find(term) > -1:

    return json.dumps(hits)
NOTE: This function is deliberately written without any error handling or input validation for ease of understanding. You should not use this function as is in a production environment.

This is a simple and potentially useful function. Its purpose is simply to search through the list of Capital City Bikeshare stations and return a list of matches. To add this function to an existing application, you would typically integrate it with an existing code base, create new VMs or containers, and do a canary deploy of the result. However, with serverless computing, you can also just register the function and then reach it over the Web whenever needed.

Notice that this find() function has a required parameter named request. When Kubeless calls the function, it will pass in a LocalRequest object from the Bottle Web Framework. The LocalRequest object has a handy property called json which returns all of the POST request parameters as a JSON object. By parsing the JSON, it's easy to extract the search term from the posted data.

For this example, save the code in a file at /tmp/ and head to the next step.

Step 5: Register the function with Kubeless

The next step is to register the function with Kubeless. Registering the function with Kubeless involves telling Kubeless these bits of information:

  1. The name you will use to access the function over the Web
  2. The protocol you will access the function (here, HTTP)
  3. The runtime to be executed to run the code (here, Python)
  4. The name of the file containing the function code
  5. The name of the function inside the file

All of this seems like quite a mouthful, but it's all easily done using a reasonably simple command called kubeless function deploy. Here it is:

$ kubeless function deploy bikesearch --trigger-http --runtime python2.7 --handler city-bikes.find --from-file /tmp/

Let's break it down:

  • kubeless function deploy bikesearch tells Kubeless to register a new function named bikesearch. 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 (we'll specify that a little further along using the --handler option).

  • --trigger-http tells Kubeless that the function will be invoked over HTTP. It's also possible to trigger the function in other ways, but that is not covered here.

  • --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 future.

  • --handler city-bikes.find tells Kubeless the name of the function to call inside the code module. You can see in the Python code above that the function is called find().

  • --from-file /tmp/ tells Kubeless to upload and use the /tmp/ file as the source for the function. It is possible to pass a function in other ways as well.

Once the command has completed executing, check that it's registered with Kubeless using the kubeless function ls command, as shown below:

$ kubeless function ls

You should see the function registered with Kubeless, as shown in the image below:

Kubeless function registration

To delete a function, simply use the kubeless function delete command:

$ kubeless function delete bikesearch

Step 6: Call the function

Take a quick look back at the function code. You'll see that the function expects to be called by a form POST, and that it will use the value of the input parameter term as the search key. So, to search for a location on Albemarle Avenue, the easiest way is to use curl and send in a POST request like the one below:

$ curl --data '{"term":"Albemarle"}' localhost:8080/api/v1/proxy/namespaces/default/services/bikesearch/ --header "Content-Type:application/json"

Or directly from the Kubeless CLI:

$ kubeless function call bikesearch --data '{"term":"Albemarle"}'

And the function returns a JSON payload of matches. Here's what it looks like:

Kubeless function invocation

You can obviously refine this (or create a new function) to do something else instead - for example, return HTML with a map showing the locations.

Debugging Kubeless functions

What happens when something goes wrong? The function currently has no error handling, so that's easy enough to test. Try sending the request again, but this time with a typo (use trm as the name of the input parameter instead of term):

$ curl --data '{"trm":"Albemarle"}' localhost:8080/api/v1/proxy/namespaces/default/services/bikesearch/ --header "Content-Type:application/json"

Here's what you should see:

        <title>Error: 500 Internal Server Error</title>
        <style type="text/css">
          html {background-color: #eee; font-family: sans;}
          body {background-color: #fff; border: 1px solid #ddd;
                padding: 15px; margin: 15px;}
          pre {background-color: #eee; border: 1px solid #ddd; padding: 5px;}
        <h1>Error: 500 Internal Server Error</h1>
        <p>Sorry, the requested URL <tt>&#039;;</tt>
           caused an error:</p>
        <pre>Internal Server Error</pre>

Kubeless 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 Python stack trace to better debug the source of the error. To achieve this, first find the container where the function is running using kubectl:

$ kubectl get pods
NAME                          READY     STATUS             RESTARTS   AGE
bikesearch-4019450193-wkrqv   1/1       Running            1          2d
database-4282359886-h85ts     0/1       CrashLoopBackOff   464        52d

Once the name of the container is known, use kubectl logs to check the logs:

$ kubectl logs bikesearch-4019450193-wkrqv
Bottle v0.12.10 server starting up (using WSGIRefServer())...
Listening on
Hit Ctrl-C to quit. - - [22/Mar/2017 16:50:14] "POST / HTTP/1.1" 200 3933 - - [22/Mar/2017 17:00:03] "POST / HTTP/1.1" 200 460
Traceback (most recent call last):
  File "/usr/local/lib/python2.7/site-packages/", line 862, in _handle
  File "/usr/local/lib/python2.7/site-packages/", line 1732, in wrapper
    rv = callback(*a, **ka)
  File "/", line 21, in handler
    return getattr(mod, func_handler)()
TypeError: find() takes exactly 1 argument (0 given) - - [22/Mar/2017 17:03:17] "GET / HTTP/1.1" 500 742
Traceback (most recent call last):
  File "/usr/local/lib/python2.7/site-packages/", line 862, in _handle
  File "/usr/local/lib/python2.7/site-packages/", line 1732, in wrapper
    rv = callback(*a, **ka)
  File "/", line 21, in handler
    return getattr(mod, func_handler)()
TypeError: find() takes exactly 1 argument (0 given) - - [23/Mar/2017 16:35:07] "GET / HTTP/1.1" 500 742
Traceback (most recent call last):
  File "/usr/local/lib/python2.7/site-packages/", line 862, in _handle
  File "/usr/local/lib/python2.7/site-packages/", line 1732, in wrapper
    rv = callback(*a, **ka)
  File "/", line 25, in post_handler
    return getattr(mod, func_handler)(request)
  File "/kubeless/", line 5, in find
    term = context.json["term"]
KeyError: 'term' - - [23/Mar/2017 16:36:27] "POST / HTTP/1.1" 500 742

It should be clear from the second-to-last line that the error originates in an incorrect key name.

This is a very basic example of debugging a Kubeless function, but it should hopefully highlight the basic principles. Obviously, in production environments, you would want to have more formal and sophisticated error handling built into your code.

To learn more, refer to the links below:

Watch the following webinar to learn how to going Serverless with Kubeless in Google Container Engine (GKE):