Playing with Kubernetes - microk8s
microk8s is an excellent way to have a Kubernetes server up-and-running in no time on your Ubuntu machine. In this article we’ll deploy a container with nginx serving a simple static page, and we’ll configure ingress to perform reverse proxy for us.
This series document my Kubernetes journey, from the very beginning to having 2 vaadin apps deployed in Kubernetes. Also lists the most commonly used commands.
You’ll need:
- A computer running newest Ubuntu (or
snap
) - At least a basic knowledge of docker
Setup
- Excellent quick & hands-on Getting started
- Guides for other OSes: Install microk8s
In short:
sudo snap install microk8s --classic
microk8s enable dashboard # gives access to admin web interface
microk8s enable dns # not sure why
microk8s enable registry # kubernetes docker registry
microk8s enable ingress # reverse proxy
microk8s status
sudo usermod -a -G microk8s $USER
sudo chown -f -R $USER ~/.kube
Note: the
~/.kube
folder gets created after reboot I think.
- Add
alias mkctl="microk8s kubectl"
to~/.config/fish/config.fish
- Reboot
Basic commands
microk8s stop
microk8s start
microk8s status
mkctl get all --all-namespaces
mkctl get nodes
,mkctl get services
,mkctl get pods
microk8s dashboard-proxy
- most important, gives you access to microk8s admin web interface.
You can define new services, pods etc via the dashboard. However, it’s much better to do so
via yaml files since that way the services/pods can be easily defined and redefined from a command-line,
via mkctl apply -f
.
When you’re done experimenting, stop microk8s via microk8s stop
. Microk8s will no longer start automatically
on next boot until you run microk8s start
. This is to conserve your dev machine battery.
About Kubernetes
For excellent intro into Kubernetes please see YouTube Series: Nana on What Is Kubernetes. She really has a knack of explaining everything properly, quickly, simply, no bullshit. I warmly recommend.
Pod hosts one or more Docker containers. Overwhelmingly often it’s just one container: the app itself. So, for our purposes, pod equals container.
Pods are wrapped in Deployments. Deployment defines the pod itself (the spec.template
is a pod definition) and how
many replicas of the pod there will be running. Usually it’s just 1.
Pods/Deployments expose themselves to other pods or load balancers via services. You need services since pods are “ephemeral”: may be killed at any time and may change their IP addresses, while the services are persistent.
Service types:
- ClusterIP: the default one, simply exposes pods to other Kubernetes things, but not outside of Kubernetes. Most often used.
- NodePort: a service which exposes pod port on the node. Only used for dev, should not be used for production.
- LoadBalancer: requires an external load balancer to be set up; load-balances requests to individual pods. For a simple self-hosted setup it’s better to use ingress.
We’ll use the ClusterIP service, and we’ll expose it via additional service called Ingress. Ingress is a reverse proxy which runs in Kubernetes and exposes a http/https port of a service. Ingress performs URL path mapping, and also https->http unwrapping - there’s also an integration with Let’s Encrypt.
YAML configuration resource file
Names: microk8s says that the name must be a DNS-1035 label; it must consist of lower case alphanumeric characters or ‘-‘, start with an alphabetic character, and end with an alphanumeric character. Kubernetes docs is more permissive but whatever: Name
Labels: microk8s says that: a valid label must be an empty string or consist of alphanumeric characters, ‘-‘, ‘_’ or ‘.’, and must start and end with an alphanumeric character. Kubernetes doc says something different but whatever: Labels.
Deployment: matchLabels is probably necessary.
Deploy an app
We’ll create the resources gradually. This is not the recommended practice (better way is to have all resources listed in a YAML file), but this will do for the tutorial.
mkctl create deployment nginx --image=nginx
mkctl get pods
- In the dashboard, navigate to workloads/pods/nginx/, then there’s IP address in Resource information, e.g. 10.1.46.87
- Open the browser and go to http://10.1.46.87:80, you should see a nginx welcome page.
To publish the pod on host, we’ll use Ingress. First, we need to expose the pod as a service:
nginx-service.yaml
:
kind: Service
apiVersion: v1
metadata:
name: nginx-service
spec:
selector:
app: nginx
ports:
- port: 80
You can verify that you can access the nginx demo app via http://10.152.183.81 now as well
(depending on the IP assigned to the nginx-service
service).
microk8s enable ingress
Then, create a file named ingress-nginx.yaml
with this content:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: http-ingress-nginx
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
rules:
- http:
paths:
- path: /experiment/nginx(/|$)(.*)
pathType: Prefix
backend:
service:
name: nginx-service
port:
number: 80
This also demoes the ingress reverse proxy path rewriting.
All-in-one config file:
apiVersion: v1
kind: Namespace
metadata:
name: demo-nginx
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: deployment
namespace: demo-nginx
spec:
selector:
matchLabels:
app: pod # this must match spec.template.metadata.labels for some reason
template:
metadata:
labels:
app: pod
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
resources:
limits:
memory: "128Mi"
cpu: "500m"
---
apiVersion: v1
kind: Service
metadata:
name: service
namespace: demo-nginx
spec:
selector:
app: pod
ports:
- port: 80
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress
namespace: demo-nginx
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
rules:
- http:
paths:
- path: /experiment/nginx(/|$)(.*)
pathType: Prefix
backend:
service:
name: service
port:
number: 80
To deploy:
$ mkctl apply -f nginx-demo.yaml
Now visit http://127.0.0.1/experiment/nginx to check that everything works.
To delete all objects defined in a YAML file and stop all containers:
$ mkctl delete -f nginx-demo.yaml
Don’t forget to delete the demo deployment nginx
we created earlier.
Security
microk8s by default open some ports on all interfaces: services and ports. They all require a certificate auth and are protected by https.
It’s probably better to enable ufw: ufw tutorial
Then you need to enable some ufw rules in order for microk8s to work: microk8s ufw
sudo ufw allow ssh
sudo ufw allow in on cni0 && sudo ufw allow out on cni0
sudo ufw default allow routed
sudo ufw enable
sudo ufw status
Namespaces
If you have multiple projects running in Kubernetes, it’s better to place every app into their own separate namespace, in order to avoid name clashes between resources.
Namespace must be a valid DNS Name, which means that the project ID must:
- contain at most 63 characters
- contain only lowercase alphanumeric characters or ‘-‘
- start with an alphanumeric character
- end with an alphanumeric character
Useful commands:
mkctl get namespace
mkctl get all --namespace my-project
Alternatively you can omit namespaces from the YAML file and use mkctl apply -f foo.yaml --namespace=my-namespace
.
You can forget to type in the --namespace
part though, thus having namespaces in yaml directly is a better idea.