Zero-Ops Kubernetes for developers, edge and IoT!

A little bit of context!

About a month ago,  Amos Njenga, DevOps Manager at Safaricom PLC and I gave a talk organised by Decoded Africa in partnership with Google on DevOps Deployment Strategies where we shared our experiences around the subject with the developer community around the country.

The questions from the audience and subsequent follow-up conversations indicated some desire to learn, interact and embrace cultures around DevOps and SRE.

This blog post is an attempt to continue those conversations around this topic as we engage ourselves with some of the tools and technologies within the space to help build great products and services that maximise shareholder value.

Mastering highly resilient and scalable infrastructure management is very important because the modern expectation is that your favourite sites will be up 24/7 and that they will roll out new features frequently and without disruption of the service.

Achieving this requires tools that allow you to ensure speed of development, infrastructure stability and ability to scale.

If you’re currently involved in infrastructure management — as a sysadmin or a developer — I’m bringing a series of articles that will enable you to build on that experience, and master the most of cutting edge tools and practices in automation and distributed system management.

For students who might be new to the field, this will be an excellent opportunity to familiarise yourself with containers, Docker, Kubernetes and deployment patterns; you’ll even run your first application on your very own Kubernetes cluster in the cloud!

The Arrival of Kubernetes at the Edge

Although Kubernetes has only been around for a few years, it has a lot of buzz around it and the velocity of its adoption is unparalleled.  It has transformed itself into a universal scheduler, where its capabilities are exploited to orchestrate deployments across a diverse set of workloads.  From virtual machines to containers to edge computing modules, Kubernetes is becoming the preferred platform for managing deployments at scale.

One of the drawbacks to Kubernetes is that it has historically been difficult to install and get a functional Kubernetes environment up and running.

For the past couple of years, I have struggled with getting an operational Kubernetes environment up and running, and last year,  I was surprised and pleased by how easy Canonical made it easy to install a Kubernetes environment with MicroK8s.

Today, I’ll be showing you how to deploy, configure MicroK8s on your local workstation for working with containers and Kubernetes in a local development environment.

Also, I’ll show you what you need to do to get a kubernetes dashboard up and running then  dive even deeper to explore how to use MicroK8s to run applications and do instrumentation.

Microk8s for Zero-Ops Infrastructure

MicroK8s is a small (with sensible choices that just work), fast, secure, single node pure upstream Kubernetes that installs on just about any Linux box.

A single-command install a single-command cluster, and then just watch it fly. Security updates just work. Choose the ‘latest’ channel and major version upgrades just happen automatically. You CAN configure MicroK8s. Most don’t bother. Deploy and move on, you have more important things to do than babysit your infrastructure.

So, it is a lightweight instance of Kubernetes designed to be employed in edge computing scenarios or on workstations to advance the development of containerised applications that will later be deployed on a larger Kubernetes cluster.

People love Docker images because they have no awkward moving parts. That makes for better operations and better security. MicroK8s also runs as an immutable container, so your Kubernetes itself is fully containerised and gets all the same benefits.

It can be used for offline development, prototyping, testing, or use on a VM as a small, cheap, reliable Kubernetes for CI/CD.  It’s also a great Kubernetes for appliances – develop your IoT apps for Kubernetes and deploy them to MicroK8s on your boxes.

To be as lightweight as possible, MicroK8s only installs the basics of a usable Kubernetes install which includes; an api-server, controller-manager, scheduler, kubelet, cni and a kube-proxy

The MicroK8s distribution of Kubernetes provided by Canonical is free and can run on any distribution of Linux. Canonical delivers MicroK8s as a Snap, a method for packaging software using containers developed by Canonical. The company also leverages Snaps to create the Charmed Distribution for Kubernetes, which adds a collection of YAML files and hooks to make it easier to deploy Kubernetes with other associated technologies and applications packaged as Snaps on any public or private cloud-based on virtual machines or bare-metal servers.

Single-node deployments of Kubernetes are more common than what one would expect. In some scenarios, single-node clusters make much more sense. For development purposes or testing, there’s no need to deploy a full-blown production-grade cluster. Single-node deployments are also handy for appliances and IoT applications since they have a much smaller footprint.

Considering Edge and IoT workloads?

MicroK8s can be used for  Edge and IoT use cases whether it’s solo or in a cluster, whether it’s ARM or Intel. It’s very popular for Raspberry Pi use cases with compressed over-the-air updates for ultra-reliable remote operations with optimised bandwidth.

Under a cell tower, on the race car or on satellites or everyday appliances, MicroK8s delivers the full Kubernetes experience for GitOps at planetary scale where its clusters are autonomous edge infrastructure.

While this does deliver a pure Kubernetes experience with the smallest of resource footprints, there are situations where you may require additional services. MicroK8s caters for this with the concept of “addons” – extra services which can easily be added to MicroK8s. These addons can be enabled and disabled at any time, and most are pre-configured to ‘just work’ without any further set up.

Let’s try some code!

To get started with these instructions, I’m running on an Ubuntu 18.04  machine.


  • An Ubuntu 20.04 LTS, 18.04 LTS or 16.04 LTS environment to run the commands.
  • At least 20G of disk space and 4G of memory are recommended.
  • An internet connection

If you don’t meet these requirements, don’t worry there are additional ways of installing MicroK8s, including additional OS support and an offline deploy for Windows and macOS.

Step 1 – Installing & Configuring MicroK8s

The first thing I’m going to do is install the snap package for MicroK8s.

sudo snap install microk8s --classic

So, if you’re not familiar with snaps, they are essentially a new form of operating system package similar to deb or rpm. However, they do utilise container technologies to make them ephemeral from the underlying operating system. They can be installed on a variety of different platforms in releases of Ubuntu and other operating systems which  include;

Arch Linux
elementary OS
KDE Neon
Manjaro Linux
Linux Mint
Red Hat Enterprise Linux
Raspberry Pi

They also have the ability to be upgraded from minor releases which are also quite useful for security flaws or bugs.

wondenge@hpelitebook:~$ sudo snap install microk8s --classicmicrok8s v1.18.4 from Canonical✓ installed

So, with MicroK8s installed, I can then do a microk8s.inspect. This will show me which of the services have been configured and are running as part of the cluster. This is quite useful for troubleshooting of things that have died or if you want to ensure that the microk8s service has stopped.


If you’re running this on your laptop or an environment with limited resources, if you’re not using Kubernetes – it’s useful to turn off the microk8s daemon. In order to turn off microk8s, you run the microk8s.stop command and this will stop all of the daemon services.

By default when you install microk8s, it is running.

MicroK8s creates a group to enable seamless usage of commands which require admin privilege. To add your current user to the group and gain access to the .kube caching directory, run the following two commands:

sudo usermod -a -G microk8s $USER
sudo chown -f -R $USER ~/.kube

You will also need to re-enter the session for the group update to take place:

su - $USER

Step 2 –  Accessing Kubernetes

MicroK8s has a built-in command to display its status. Let’s check the status of our K8s installation.


We should be able to see an output of all the services (enabled and disabled). This is the list of addons we can use to extend our local K8s deployment.

wondenge@hpelitebook:~$ microk8s.status 
microk8s is running
cilium: disabled
dashboard: disabled
dns: disabled
fluentd: disabled
gpu: disabled
helm: disabled
helm3: disabled
host-access: disabled
ingress: disabled
istio: disabled
jaeger: disabled
knative: disabled
kubeflow: disabled
linkerd: disabled
metallb: disabled
metrics-server: disabled
prometheus: disabled
rbac: disabled
registry: disabled
storage: disabled

MicroK8s bundles its own version of kubectl for accessing Kubernetes. kubectl is the command-line tool for interacting with the Kubernetes API provided by Kubernetes clusters. If you’re familiar with Kubernetes, you can slightly recall that you’ve used this before.

Use it to run commands to monitor and control your Kubernetes. For example, to view your node:

microk8s kubectl get all --all-namespaces
wondenge@hpelitebook:~$ microk8s kubectl get all --all-namespaces
default     service/kubernetes   ClusterIP   
<none>        443/TCP   3m23s

microk8s kubectl get nodes  lists all node resources running on your one-node cluster.  A node is a worker machine in Kubernetes and maybe a VM or physical machine, depending on the cluster. Multiple Pods can run on one Node.

microk8s kubectl get nodes

Currently, we’re working with one node. You’ll be able to see your machine.

wondenge@hpelitebook:~$ microk8s kubectl get nodes
hpelitebook   Ready    <none>   8h    v1.18.4-1+6f17be3f1fd54a

…or to see the running services:

microk8s kubectl get services
wondenge@hpelitebook:~$ microk8s kubectl get services
NAME         TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   
kubernetes   ClusterIP   <none>        443/TCP   5m15s

MicroK8s uses a namespaced kubectl command to prevent conflicts with any existing installs of kubectl. If you don’t have an existing install, it is easier to add an alias (append to ~/.bash_aliases) like this:

alias kubectl='microk8s kubectl'

If you mainly use MicroK8s you can make our kubectl the default one on your command-line with alias mkctl="microk8s kubectl". Since it is a standard upstream kubectl, you can also drive other Kubernetes clusters with it by pointing to the respective kubeconfig file via the --kubeconfig argument.

Step 3 – Turning on Services

MicroK8s uses the minimum of components for a pure, lightweight Kubernetes. However, plenty of extra features are available with a few keystrokes using “addons” – pre-packaged components that will provide extra capabilities for your Kubernetes, from simple DNS management to machine learning with Kubeflow!

So what I’m going to do is enable some basic workloads. These are dashboard and dns. There are some other pre-built workloads like istio which is a service mesh among others.

But first, we need to ensure that we have not enabled any firewall for the purposes of this demonstration.

sudo ufw disable
microk8s enable dashboard dns 
wondenge@hpelitebook:~$ microk8s enable dashboard dns 
Enabling Kubernetes Dashboard
Enabling Metrics-Server created
system:auth-delegator created created created
serviceaccount/metrics-server created
deployment.apps/metrics-server created
service/metrics-server created created created created
Metrics-Server is enabled
Applying manifest
serviceaccount/kubernetes-dashboard created
service/kubernetes-dashboard created
secret/kubernetes-dashboard-certs created
secret/kubernetes-dashboard-csrf created
secret/kubernetes-dashboard-key-holder created
configmap/kubernetes-dashboard-settings created created created created created
deployment.apps/kubernetes-dashboard created
service/dashboard-metrics-scraper created
deployment.apps/dashboard-metrics-scraper created

If RBAC is not enabled access the dashboard using the default token retrieved with:

token=$(microk8s kubectl -n kube-system get secret | grep 
default-token | cut -d " " -f1)
microk8s kubectl -n kube-system describe secret $token

In an RBAC enabled setup (microk8s enable RBAC) you need to create a user with restricted
permissions as shown in:

Enabling DNS
Applying manifest
serviceaccount/coredns created
configmap/coredns created
deployment.apps/coredns created
service/kube-dns created created created
Restarting kubelet
DNS is enabled

Try microk8s enable --help for a list of available services and optional features. microk8s disable <name> turns off a service.

Kubernetes does have a built-in dashboard and microk8s has this built-in as well. To access that dashboard, we will need a token for authentication. We will run the two commands below to retrieve the token from the Kube-system and print it out on our terminal. So, we’ll just create that right away:

token=$(microk8s kubectl -n kube-system get secret | grep 
default-token | cut -d " " -f1)
microk8s kubectl -n kube-system describe secret $token
wondenge@hpelitebook:~$ token=$(microk8s kubectl -n kube-system 
get secret | grep default-token | cut -d " " -f1)
wondenge@hpelitebook:~$ microk8s kubectl -n kube-system describe secret $token
Name:         default-token-drh5h
Namespace:    kube-system
Labels:       <none>
Annotations: default                 



ca.crt:     1103 bytes
namespace:  11 bytes

Let’s copy our token and leave it in our clip board for now.

When we run microk8s kubectl get all --all-namespaces, this will give us everything that is running in our system for now.

wondenge@hpelitebook:~$ microk8s kubectl get all --all-namespaces
NAMESPACE     NAME                                            
kube-system   pod/coredns-588fd544bf-j9mjv                    1/1
Running   0          2m14s
kube-system   pod/dashboard-metrics-scraper-59f5574d4-8pn9x   1/1
Running   1          2m15s
kube-system   pod/kubernetes-dashboard-6d97855997-nt9gv       1/1
Running   1          2m16s
kube-system   pod/metrics-server-c65c9d66-qwrq7               1/1
Running   1          2m20s

NAMESPACE     NAME                                TYPE
CLUSTER-IP       EXTERNAL-IP   PORT(S)                  AGE
default       service/kubernetes                  ClusterIP     <none>        443/TCP                  8m18s
kube-system   service/dashboard-metrics-scraper   ClusterIP     <none>        8000/TCP                 2m16s
kube-system   service/kube-dns                    ClusterIP    <none>        53/UDP,53/TCP,9153/TCP   2m14s
kube-system   service/kubernetes-dashboard        ClusterIP   <none>        443/TCP                  2m17s
kube-system   service/metrics-server              ClusterIP     <none>        443/TCP                  2m20s

NAMESPACE     NAME                                        READY
kube-system   deployment.apps/coredns                     1/1     
1            1           2m14s
kube-system   deployment.apps/dashboard-metrics-scraper   1/1     
1            1           2m16s
kube-system   deployment.apps/kubernetes-dashboard        1/1     
1            1           2m16s
kube-system   deployment.apps/metrics-server              1/1     
1            1           2m20s

kube-system   replicaset.apps/coredns-588fd544bf
1         1         1       2m14s
kube-system   replicaset.apps/dashboard-metrics-scraper-59f5574d4
1         1         1       2m16s
kube-system   replicaset.apps/kubernetes-dashboard-6d97855997
1         1         1       2m16s
kube-system   replicaset.apps/metrics-server-c65c9d66
1         1         1       2m20s

The above outputs are all components of the default MicroK8s service, dashboard and dns. From this output, we can see the kubernetes-dashboard service in the kube-system namespace has a ClusterIP of and listens on TCP port 443. The ClusterIP is randomly assigned, so if you follow these steps on your host, your IP address might differ, make sure you replace the IP address in these instructions with your cluster IP from your own deployment.

Visit on your browser (preferably Mozilla Firefox for now) and you should be able to see the Kubernetes dashboard.

Since we’re using self-signed certificates, the browser will scream at us with a security warning. Click on Advanced and Accept the Risk to Continue. We are now able to access our Kubernetes Dashboard

To access the dashboard use the default token retrieved by pasting that token that is on our clip board.

In the dashboard you can see all the constructs that have been deployed or haven’t been deployed. We can also access other cluster information by running:

microk8s kubectl cluster-info
wondenge@hpelitebook:~$ microk8s kubectl cluster-info
Kubernetes master is running at
CoreDNS is running at
Metrics-server is running at

To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.

And that’s basically it!

That’s a quick introduction to installing and configuring MicroK8s

Step 4 – Deploying and Accessing an Application

Of course, Kubernetes is meant for deploying apps and services. You can use the kubectl command to do that as with any K8s.  Let’s try installing a demo app.

Let’s launch a simple Nginx web server and access its default web page from our local machine. Execute this command to pull the Nginx image from Docker Hub and create a deployment called decoded-africa-web:

microk8s kubectl run --image=nginx:latest decoded-africa-web

This command is similar to the docker run command, except that it packages and deploys the container in a Kubernetes-specific artefact called a Pod. You’ll learn more about Pods in the next part of this series.

When you execute the command, you’ll see this output:

wondenge@hpelitebook:~$ microk8s kubectl run --image=nginx:latest decoded-africa-web
pod/decoded-africa-web created

Now check that the Pod is created with the nginx container. It may take a minute or two to install, but you can check the status:

microk8s kubectl get pods
wondenge@hpelitebook:~$ microk8s kubectl get pods
NAME                 READY   STATUS    RESTARTS   AGE
decoded-africa-web   1/1     Running   0          58s

To access the webserver running inside the Pod, we need to expose it to the public Internet. We achieve that with the following command:

microk8s kubectl expose pod decoded-africa-web --port=80 --target-port=80 --type=NodePort 
wondenge@hpelitebook:~$ microk8s kubectl expose pod decoded-africa-web --port=80 --target-port=80 --type=NodePortservice/decoded-africa-web exposed

The Pod is now exposed on our Node of the cluster on an arbitrary port. The --port and --target-port switches indicate the ports through which the webserver becomes available. The switch --NodePort ensures that we can use our Node on the cluster to access the application.

To get the NodePort of the decoded-africa-web deployment, run the following command.

microk8s kubectl get svc decoded-africa-web
wondenge@hpelitebook:~$ microk8s kubectl get svc decoded-africa-web
NAME                 TYPE       CLUSTER-IP       EXTERNAL-IP
PORT(S)        AGE
decoded-africa-web   NodePort   <none>
80:30395/TCP   85s

Let’s test it out.

Use the curl command to make an HTTP request to our  ClusterIP on port 80.

<!DOCTYPE html>
<title>Welcome to nginx!</title>
    body {        
        width: 35em;        
        margin: 0 auto;        
        font-family: Tahoma, Verdana, Arial, sans-serif;    
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href=""></a>.<br/>
Commercial support is available at
<a href=""></a>.</p>

<p><em>Thank you for using nginx.</em></p>

You have successfully deployed a containerised application to your Kubernetes cluster.

Step 5 – Monitoring & Observability

You can easily set up a local EFK Stack (Elasticsearch, Fluentd and Kibana) using the provided fluentd addon for a logging and monitoring solution. Enabling this addon will add Elasticsearch, Fluentd and Kibana (the EFK stack) to MicroK8s. The components will be installed and connected together.

To enable the addon:

microk8s.enable fluentd
wondenge@hpelitebook:~$ microk8s.enable fluentd
Enabling Fluentd-Elasticsearch
Labeling nodes
node/hpelitebook labeled
Addon dns is already enabled.
service/elasticsearch-logging created
serviceaccount/elasticsearch-logging created created created
statefulset.apps/elasticsearch-logging created
configmap/fluentd-es-config-v0.1.5 created
serviceaccount/fluentd-es created created created
daemonset.apps/fluentd-es-v2.2.0 created
deployment.apps/kibana-logging created
service/kibana-logging created
Fluentd-Elasticsearch is enabled
microk8s kubectl get all --all-namespaces
wondenge@hpelitebook:~$ microk8s kubectl get all --all-namespaces
NAMESPACE     NAME                                            
default       pod/decoded-africa-web                          1/1
Running   0          6m50s
kube-system   pod/coredns-588fd544bf-j9mjv                    1/1
Running   0          12m
kube-system   pod/dashboard-metrics-scraper-59f5574d4-8pn9x   1/1
Running   1          12m
kube-system   pod/elasticsearch-logging-0                     1/1
Running   0          69s
kube-system   pod/fluentd-es-v2.2.0-tj5gc                     1/1
Running   0          68s
kube-system   pod/kibana-logging-84f486f46b-56rw9             1/1
Running   0          68s
kube-system   pod/kubernetes-dashboard-6d97855997-nt9gv       1/1
Running   1          12m
kube-system   pod/metrics-server-c65c9d66-qwrq7               1/1
Running   1          12m

NAMESPACE     NAME                                TYPE
CLUSTER-IP       EXTERNAL-IP   PORT(S)                  AGE
default       service/decoded-africa-web          NodePort   <none>        80:30395/TCP             4m47s
default       service/kubernetes                  ClusterIP     <none>        443/TCP                  19m
kube-system   service/dashboard-metrics-scraper   ClusterIP     <none>        8000/TCP                 12m
kube-system   service/elasticsearch-logging       ClusterIP   <none>        9200/TCP                 75s
kube-system   service/kibana-logging              ClusterIP   <none>        5601/TCP                 72s
kube-system   service/kube-dns                    ClusterIP    <none>        53/UDP,53/TCP,9153/TCP   12m
kube-system   service/kubernetes-dashboard        ClusterIP   <none>        443/TCP                  13m
kube-system   service/metrics-server              ClusterIP     <none>        443/TCP                  13m

NAMESPACE     NAME                               DESIRED   
CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR                              AGE
kube-system   daemonset.apps/fluentd-es-v2.2.0   1         1
1       1            1    75s

NAMESPACE     NAME                                        READY
kube-system   deployment.apps/coredns                     1/1
1            1           13m
kube-system   deployment.apps/dashboard-metrics-scraper   1/1
1            1           13m
kube-system   deployment.apps/kibana-logging              1/1
1            1           78s
kube-system   deployment.apps/kubernetes-dashboard        1/1
1            1           13m
kube-system   deployment.apps/metrics-server              1/1
1            1           13m

kube-system   replicaset.apps/coredns-588fd544bf
1         1         1       13m
kube-system   replicaset.apps/dashboard-metrics-scraper-59f5574d4
1         1         1       13m
kube-system   replicaset.apps/kibana-logging-84f486f46b
1         1         1       79s
kube-system   replicaset.apps/kubernetes-dashboard-6d97855997
1         1         1       13m
kube-system   replicaset.apps/metrics-server-c65c9d66
1         1         1       13m

NAMESPACE     NAME                                     READY   
kube-system   statefulset.apps/elasticsearch-logging   1/1     

To access Kibana, we can use the same URL we used to access the dashboard with the name of the Kibana service using

Running microk8s.config provides us with an output with our credentials which we can use to access our new logging and monitoring stack.

wondenge@hpelitebook:~$ microk8s.config
apiVersion: v1
- cluster:    
    name: microk8s-cluster
- context:    
    cluster: microk8s-cluster    
    user: admin  
  name: microk8s
current-context: microk8s
kind: Config
preferences: {}
- name: admin  
wondenge@hpelitebook:~$ microk8s kubectl cluster-info
Kubernetes master is running at
Elasticsearch is running at
Kibana is running at
CoreDNS is running at
Metrics-server is running at

Step 6 – Clean Up Nicely

Kubernetes is a collection of system services that talk to each other all the time. If you don’t need them running in the background then you will save battery by stopping them. microk8s start and microk8s stop will do the work for you.


That’s it for today. I hope you had fun taking Microk8s for a test run.

You May Also Like
two factor authentication
Read More

Two Factor Authentication With PHP and Africa’s Talking SMS API

Two factor authentication is an additional layer of security used to ensure only authenticated users gain access to an online account. because Passwords are historically weak, and can be easily stolen, it can't alone be the way that users access their accounts.