Deploy OpenFaaS with MicroK8s

As part of the seasonal home lab tidy-up I reinstalled Ubuntu Bionic Beaver (18.04) on my NUC and instead of using kubeadm to deploy Kubernetes I turned to Canonicals MicroK8s Snap package and was blown away by the speed and ease with which I could get a basic lab environment up and running.

This post takes you through the steps involved in getting MicroK8s up and running on an Ubuntu Bionic node and tops it off with deploying OpenFaaS and using it with the MicroK8s supplied registry addon.


What is a Snap


A Snap is a universal package which can deployed on a wide range of Linux distributions, it bundles dependencies and config, simplifying installs to a single standard command. On the surface its quite similar to the recent CNAB project but predates it by over 4 years, having started out as Click - Canonicals packaging solution for Mobile.

The snap CLI (also referred to as Snapcraft) has been part of Ubuntu since Xenial (16.04) and can be installed on a wide selection of other distros.

What is MicroK8s

MicroK8s is a Kubernetes distribution targetting workstations and applications, and is distributed as a single snap package that can be deployed on 42 flavours of Linux.

The project refers to the simplicity of getting up and running as being zero-ops, and I have to admit they’re not overstating the ease with which you can deploy your own MicroK8s instance.

Installing MicroK8s

The steps described in this post were carried out on a fresh Ubuntu Bionic installation.

  1. Install MicroK8s using the Snapcraft CLI, having previously used Minikube for dev Kubernetes installs I was very surprised by the speed with which this completed - initially thinking that it hadn’t worked.

     $ sudo snap install microk8s --classic
     microk8s v1.13.1 from Canonical✓ installed
  2. You can display detailed information about the installed MicroK8s snap

    Here we can see the installed commands (all prefixed by microk8s.), services and Kubernetes version (v1.13.1).

     $ snap info microk8s
     name:      microk8s
     summary:   Kubernetes for workstations and appliances
     publisher: Canonical✓
     license:   unset
     description: |
       MicroK8s is a small, fast, secure, single node Kubernetes that installs on just about any Linux
       box. Use it for offline development, prototyping, testing, or use it on a VM as a small, cheap,
       reliable k8s for CI/CD. It's also a great k8s for appliances - develop your IoT apps for k8s and
       deploy them to MicroK8s on your boxes.
       - microk8s.config
       - microk8s.disable
       - microk8s.docker
       - microk8s.enable
       - microk8s.inspect
       - microk8s.istioctl
       - microk8s.kubectl
       - microk8s.reset
       - microk8s.start
       - microk8s.status
       - microk8s.stop
       microk8s.daemon-apiserver:          simple, enabled, active
       microk8s.daemon-apiserver-kicker:   simple, enabled, active
       microk8s.daemon-controller-manager: simple, enabled, active
       microk8s.daemon-docker:             simple, enabled, active
       microk8s.daemon-etcd:               simple, enabled, active
       microk8s.daemon-kubelet:            simple, enabled, active
       microk8s.daemon-proxy:              simple, enabled, active
       microk8s.daemon-scheduler:          simple, enabled, active
     snap-id:      EaXqgt1lyCaxKaQCU349mlodBkDCXRcg
     tracking:     stable
     refresh-date: today at 11:23 GMT
       stable:         v1.13.1  (354) 229MB classic
       candidate:      v1.13.1  (354) 229MB classic
       beta:           v1.13.1  (354) 229MB classic
       edge:           v1.13.1  (372) 229MB classic
       1.13/stable:    v1.13.1  (356) 229MB classic
       1.13/candidate: v1.13.1  (356) 229MB classic
       1.13/beta:      v1.13.1  (356) 229MB classic
       1.13/edge:      v1.13.1  (371) 229MB classic
       1.12/stable:    v1.12.4  (362) 251MB classic
       1.12/candidate: v1.12.4  (362) 251MB classic
       1.12/beta:      v1.12.4  (362) 251MB classic
       1.12/edge:      v1.12.4  (362) 251MB classic
       1.11/stable:    v1.11.6  (361) 245MB classic
       1.11/candidate: v1.11.6  (361) 245MB classic
       1.11/beta:      v1.11.6  (361) 245MB classic
       1.11/edge:      v1.11.6  (361) 245MB classic
       1.10/stable:    v1.10.12 (364) 200MB classic
       1.10/candidate: v1.10.12 (364) 200MB classic
       1.10/beta:      v1.10.12 (364) 200MB classic
       1.10/edge:      v1.10.12 (364) 200MB classic
     installed:        v1.13.1  (354) 229MB classic
  3. Wait for MicroK8s to complete its startup before proceeding.
     $ microk8s.status --wait-ready
     microk8s is running
     gpu: disabled
     storage: disabled
     registry: disabled
     ingress: disabled
     dns: disabled
     metrics-server: disabled
     istio: disabled
     dashboard: disabled
  4. The base MicroK8s install is bare-bones so we want to install some of the provided addons.

  5. Enable the DNS Addon which deploys kube-dns for us.
     $ microk8s.enable dns
     Enabling DNS
     Applying manifest
     service/kube-dns created
     serviceaccount/kube-dns created
     configmap/kube-dns created
     deployment.extensions/kube-dns created
     Restarting kubelet
     DNS is enabled
  6. Enable the Registry Addon which deploys a docker private registry and expose it on localhost:32000. The storage addon will be enabled as part of this addon.

    The storage addon creates volumes in /var/snap/microk8s/common/default-storage.

     $ microk8s.enable registry
     Enabling the private registry
     Enabling default storage class
     deployment.extensions/hostpath-provisioner unchanged unchanged
     Storage will be available soon
     Applying registry manifest
     namespace/container-registry created
     persistentvolumeclaim/registry-claim created
     deployment.extensions/registry created
     service/registry created
     The registry is enabled
  7. MicroK8s provides the kubectl CLI out of the box which allows us to take a look at whats going on.

     $ microk8s.kubectl get nodes
     nuc    Ready    <none>   41m   v1.13.1
     $ microk8s.kubectl get all --namespace kube-system
     NAME                                        READY   STATUS    RESTARTS   AGE
     pod/hostpath-provisioner-599db8d5fb-qcbn2   1/1     Running   0          16s
     pod/kube-dns-6ccd496668-9k4kl               3/3     Running   0          22s
     NAME               TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)         AGE
     service/kube-dns   ClusterIP   <none>        53/UDP,53/TCP   22s
     NAME                                   READY   UP-TO-DATE   AVAILABLE   AGE
     deployment.apps/hostpath-provisioner   1/1     1            1           16s
     deployment.apps/kube-dns               1/1     1            1           22s
     NAME                                              DESIRED   CURRENT   READY   AGE
     replicaset.apps/hostpath-provisioner-599db8d5fb   1         1         1       16s
     replicaset.apps/kube-dns-6ccd496668               1         1         1       22s
  8. Create snap aliases for the kubectl and docker commands.

    NOTE: we need to alias docker so that the faas-cli command can function.

     sudo snap alias microk8s.kubectl kubectl
     sudo snap alias microk8s.docker docker
  9. [Optional] You can also enable the dashboard if desired.

     $ microk8s.enable dashboard
     Enabling dashboard
     secret/kubernetes-dashboard-certs created
     serviceaccount/kubernetes-dashboard created
     deployment.apps/kubernetes-dashboard created
     service/kubernetes-dashboard created
     service/monitoring-grafana created
     service/monitoring-influxdb created
     service/heapster created
     deployment.extensions/monitoring-influxdb-grafana-v4 created
     serviceaccount/heapster created
     configmap/heapster-config created
     configmap/eventer-config created
     deployment.extensions/heapster-v1.5.2 created
     dashboard enabled

    And then expose the dashboard using kubectl proxy, note that I’m making it accesible from other hosts in my lab network here.

     kubectl proxy --address='' --accept-hosts='^localhost$,^nuc$'

Installing OpenFaaS

We can now install OpenFaaS using kubectl as described on the docs site.

  1. Clone the faas-netes repository.

     $ git clone
     Cloning into 'faas-netes'...
     remote: Enumerating objects: 20, done.
     remote: Counting objects: 100% (20/20), done.
     remote: Compressing objects: 100% (15/15), done.
     remote: Total 4720 (delta 3), reused 12 (delta 3), pack-reused 4700
     Receiving objects: 100% (4720/4720), 4.73 MiB | 5.17 MiB/s, done.
     Resolving deltas: 100% (2560/2560), done.
  2. Create the OpenFaaS Namespaces.

     $ cd faas-netes
     $ kubectl apply -f ./namespaces.yml
     namespace/openfaas created
     namespace/openfaas-fn created
  3. Deploy OpenFaaS.

     $ kubectl apply -f ./yaml
     configmap/alertmanager-config created
     deployment.apps/alertmanager created
     service/alertmanager created
     deployment.apps/gateway created
     service/gateway created
     deployment.apps/nats created
     service/nats created
     configmap/prometheus-config created
     deployment.apps/prometheus created
     service/prometheus created
     deployment.apps/queue-worker created
     serviceaccount/faas-controller created created created
  4. Confirm OpenFaaS is deployed.

     $ kubectl get deployments --namespace openfaas
     alertmanager   1/1     1            1           40s
     gateway        1/1     1            1           40s
     nats           1/1     1            1           40s
     prometheus     1/1     1            1           40s
     queue-worker   1/1     1            1           40s
  5. The UI will now be accessible on port 31112, as I’m connecting remotely I access this via http://nuc:31112/ui/ (you may want to connect to http://localhost:31112/ui/ if you’ve deployed this locally).

Creating a Function

The process for creating a function is standard with one small requirement, you must set the image prefix to localhost:32000 as we’ll be using the registry addon enabled in the earlier sections.

  1. Install the OpenFaaS CLI as described here.

  2. Create a new function, setting the --gateway and --prefix flags appropriately.

    As mentioned above you must set the prefix to localhost:32000, if you are running locally you can omit the --gateway flag completely and use the default.

     $ faas-cli new --lang python --gateway http://nuc:31112 --prefix localhost:32000 myecho
     2019/01/01 12:56:50 No templates found in current directory.
     2019/01/01 12:56:50 Attempting to expand templates from
     2019/01/01 12:56:51 Fetched 14 template(s) : [csharp dockerfile go go-armhf java8 node node-arm64 node-armhf php7 python python-armhf python3 python3-armhf ruby] from
     Folder: myecho created.
       ___                   _____           ____
     / _ \ _ __   ___ _ __ |  ___|_ _  __ _/ ___|
     | | | | '_ \ / _ \ '_ \| |_ / _` |/ _` \___ \
     | |_| | |_) |  __/ | | |  _| (_| | (_| |___) |
     \___/| .__/ \___|_| |_|_|  \__,_|\__,_|____/
     Function created in folder: myecho
     Stack file written: myecho.yml
  3. Build the function.

     $ faas-cli build -f ./myecho.yml
     [0] > Building myecho.
     Clearing temporary build folder: ./build/myecho/
     Preparing ./myecho/ ./build/myecho/function
     Building: localhost:32000/myecho:latest with python template. Please wait..
     Sending build context to Docker daemon  8.192kB
     Step 1/25 : FROM python:2.7-alpine
     2.7-alpine: Pulling from library/python
     cd784148e348: Pull complete
     30f71ecab593: Pull complete
     ed606575a835: Pull complete
     9c862b3c365f: Pull complete
     Digest: sha256:bf950979b88495f4d36091499a99eff17c28709231768af3b0e17c7f35243942
     Status: Downloaded newer image for python:2.7-alpine
     ---> 66c225e226f9
     Step 25/25 : CMD ["fwatchdog"]
     ---> Running in 5eb337e01000
     Removing intermediate container 5eb337e01000
     ---> 87d651b5e184
     Successfully built 87d651b5e184
     Successfully tagged localhost:32000/myecho:latest
     Image: localhost:32000/myecho:latest built.
     [0] < Building myecho done.
     [0] worker done.
  4. Deploy the function.

     $ faas-cli push -f ./myecho.yml
     [0] > Pushing myecho [localhost:32000/myecho:latest].
     The push refers to repository [localhost:32000/myecho]
     50682314b263: Pushed
     7bff100f35cb: Pushed
     latest: digest: sha256:e78d0e594e5833511f901fa6123491d617dc295419c4c7540e0001b56111107a size: 3655
     [0] < Pushing myecho [localhost:32000/myecho:latest] done.
     [0] worker done.
     $ faas-cli deploy -f ./myecho.yml
     Deploying: myecho.
     Deployed. 202 Accepted.
     URL: http://nuc:31112/function/myecho
  5. List the deployed functions.

     $ faas-cli ls
     Function                        Invocations     Replicas
     myecho                          0               1
  6. Invoke the function.

     $ echo "Hello MicroK8s" | faas-cli invoke -f myecho.yml myecho
     Hello MicroK8s

Optional Extras

Install Specific Kubernetes Release

You can install a specific version of Kubernetes by setting the --channel. To see a list of the available channels you can run the snap info microk8s command (see earlier in the post for example output).

For example, to install 1.12/stable.

snap install microk8s --classic --channel=1.12/stable

Snap Environment Variables

As the Snap environments are self-contained you need to run a shell inside a snap command to query their settings, for example the Storage Addon creates a directory under $SNAP_COMMON, we can check what thats set to by running the env command inside the microk8s.kubectl shell.

snap run --shell microk8s.kubectl