Kubernetes

Containers (zie ook mijn pagina over Docker) zijn populair in het gebruik omdat ze minder resources gebruiken dan complete Virtuele Machines. Voor elke applicatie kunnen één of meerdere containers gemaakt worden en samen vormen zij de complete applicatie. Denk bijvoorbeeld aan een WordPress website container die samenwerkt met een mariaDB database container.

Hierboven wordt weergegeven hoe de traditionele omgeving van applicaties via een virtuele omgeving uiteindelijk als containers ‘deployed‘ zijn. Duidelijk is het niveau van de containers die gebruik maken van de onderliggende hardware en het OS.

Mocht het complexer worden dan kan de container-orchestrator ‘Kubernetes‘ gebruikt worden. Kubernetes is momenteel dé standaard om containers te beheren en maakt gebruik van clusters om hierin containers te laten ‘draaien’ en managed de schaalbaarheid (en beschikbaarheid) van de applicatie(s).

In vogelvlucht

Kubernetes werkt met ‘Namespaces‘. Elke namespace kun je beschouwen als een verzameling met alle bij elkaar behorende onderdelen, ookwel ‘workloads‘ genoemd. De container met hierin de applicatie, wordt gedefinieerd in ‘pods’. Er kunnen meerdere containers in één pod gebruikt worden en er zijn meestal meerdere pods binnen één namespace aanwezig. Meerdere pods van hetzelfde type worden ‘replica’s‘ genoemd en worden onderhouden met ‘deployments‘. Pods en deployments worden voorzien van één of meerdere ‘labels‘ ter herkenning. Vervolgens kunnen er TCP/UDP-poorten gekoppeld worden naar de ‘buitenkant’ zodat de applicatie bereikbaar is. Deze poorten worden aangegeven met ‘services‘.

Cluster onderdelen

De ‘master‘ is een belangrijk onderdeel van Kubernetes en deze heeft als taak om de efficiëntie van de nodes binnen een cluster te sturen. Dat kan zijn om meerdere pods bij te schakelen bij hoge belasting van het systeem, of deployments op verschillende (cloud) systemen te groeperen voor een hogere (geografische) bereikbaarheid. Zelfs verschillende cloud- en on-premise systemen kunnen hiermee samenwerken voor hoge beschikbaarheid.

Storage

Aangezien Kubenetes zo ontworpen is dat de beschikbare nodes gebruikt worden om pods op te laten ‘draaien’, zal de opslag van een node niet gebruikt kunnen worden om data in op te slaan. Als de pod opnieuw aangemaakt wordt, kan dat namelijk op een andere beschikbare node gebeuren.

Een oplossing is om storage te koppelen aan de cluster via zogenaamde ‘Persistent Storage‘. Zie dit als het koppelen van een harddisk aan de cluster. Via een ‘claim’ kan een (gedeelte van de) storage gebruikt worden door een pod. Hierdoor zal data bewaard blijven als de pod opnieuw gegenereerd wordt. Zie ook NFS Share.


Minikube

Om kubernetes te kunnen gebruiken op bv. een Mac computer zijn twee componenten noodzakelijk: MiniKube en KubeCTL. Daarnaast heb je een hypervisor nodig, bijvoorbeeld VirtualBox.

Installeer KubeCTL met het volgende commando (MacOS):

$brew install kubernetes-cli

Installeer MiniKube (v0.31.0) met het volgende commando (MacOS):

$curl -Lo minikube https://storage.googleapis.com/minikube/releases/v0.31.0/minikube-darwin-amd64
$chmod +x minikube
$sudo cp minikube /usr/local/bin/
$rm minikube

Om nu een Kubernetes cluster te starten, geef het volgende commando:

$minikube start

Dit zal een VM aanmaken in bv. VirtualBox en de status hiervan kan opgevraagd worden met:

$minikube status

Mocht dit een foutmelding geven, omdat er bv. al een ‘oude’ installatie van minikube aanwezig was, dan kun je deze ‘oude’ omgeving verwijderen door de VM van VirtualBox en de lokale directory ~/.minikube te verwijderen.

Om nu docker te laten connecten met de minikube VM dienen de variabelen bekend gemaakt te worden:

$eval $(minikube docker-env)

en hierna kunnen de docker containers getoond worden met:

$docker ps

Pods

Nu kunnen ‘environments’ met ‘pods’ gemaakt worden! Laten we een TomCat webservers starten met behulp van Kubernetes. Eerst maken we een directory genaamd tomcat met hierin een pod-definition.yml bestand.

$mkdir tomcat
$cd tomcat
$vi pod-definition.yml

De inhoud van pod-definition.yml:

apiVersion: v1
kind: Pod
metadata:  
  name: tomcat-app
  labels:
    app: tomcat
spec:
  containers:
    - name: tomcat
      image: tomcat:9.0
      ports:
        - containerPort: 8080

Meer details over de commando’s kan hier gevonden worden: https://kubernetes.io/docs/reference/kubectl/cheatsheet/

Om nu dit definition-bestand te gebruiken en te starten zodat de TomCat server bereikbaar is op de aangegeven port 8080, dienen twee commando’s gegeven te worden. De eerste zal het definition-bestand inlezen en pods aanmaken (kubelet) en het tweede commando ‘exposed‘ de TCP port van de node naar de buitenwereld. MiniKube (kube-proxy) zal een lokale poort ‘mappen’ naar deze poort 8080 en we vinden de URL hiervoor met het derde commando.

$kubectl apply -f ./pod-definition.yml
$kubectl expose pods tomcat-app --type=NodePort
$minikube service tomcat-app --url

De TCP-port kan ook gevonden worden met:
$kubectl get services

Nu kan TomCat server op mijn systeem bereikt worden op http://192.168.99.100:32741

Om de running pod te stoppen (verwijderen), dient het volgende commando:

$kubectl delete pods <pod-name>
$kubectl delete service <service-name>

Andere commando’s

$kubectl get pods
$kubectl port-forward tomcat-app-56ff5c79c5-7gjwg 8080:8080
$kubectl attach tomcat-app-56ff5c79c5-7gjwg
$kubectl exec -ti tomcat-app-56ff5c79c5-7gjwg bash

ReplicaSet

Mocht nu de applicatie te langzaam worden omdat er bv. meer gebruikers toegang proberen te krijgen, dan kan een replica uitkomst bieden. Op deze manier kunnen replica’s zorgen voor meer gelijke pods en de ‘load’ verdelen. We maken hiervoor een replica-controller via het bestand rs-definition.yml met de volgende inhoud:

apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: tomcat-rs
labels:
app: tomcat
type: frontend
spec:
template:
metadata:
name: tomcat-app
labels:
app: tomcat
type: frontend
spec:
containers:
- name: tomcat
image: tomcat:9.0
ports:
- containerPort: 8080
replicas: 3

selector:
matchLabels:
type: frontend

Let op dat de apps/v1 api nu aangeroepen dient te worden! De inhoud is, m.u.v. de ‘kind‘, nagenoeg hetzelfde als de pod-definitie maar nu wordt een template gebruikt dat de inhoud van de pod-definitie bevat. Met ‘replicas: 3‘ geven we aan dat we drie pods willen runnen. De ‘selector‘ geeft aan welke pods de ReplicaSet ‘in de gaten’ moet houden, pods met dit type label (frontend) dienen dus aanwezig te zijn.

$kubectl apply -f rs-definition.yml
$kubectl get replicationset

De output van het tweede commando is:

NAME        DESIRED   CURRENT   READY   AGE
tomcat-rs 3 3 3 40s

Met het ‘kubectl get pods‘ commando zien we nu 3 pods ‘draaien’:

NAME              READY   STATUS    RESTARTS   AGE
tomcat-rs-7mdgq 1/1 Running 0 47s
tomcat-rs-bpf7j 1/1 Running 0 47s
tomcat-rs-pndxl 1/1 Running 0 47s

De service-poort (TCP-port) die we voorheen op de pod lieten binnenkomen, gaat nu naar de ReplicaSet met:

$kubectl expose replicaset tomcat-rs --type=NodePort
$kubectl get services
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)          AGE
kubernetes ClusterIP 10.96.0.1 443/TCP 2h
tomcat-rs NodePort 10.100.0.5 8080:32189/TCP 6s

Op de MacBook draait ‘minikube’ en de URL voor de Tomcat replica’s vinden we met:

$minikube service tomcat-rs --url

In dit geval is de URL dan http://192.168.99.100:32189

Mocht nu één, of meer pods wegvallen, dan zal de ReplicaSet er automatisch voor zorgen dat er een nieuwe pod gestart wordt tot het aantal dat aangegeven is via ‘replicas:’