De laaste tijd ben ik veel bezig geweest met Kubernetes voor productie applicaties, waardoor ik veel heb geleerd van het platform. Ik wil dit een stap verder oppakken en zelf een bare metal kubernetes cluster opzetten, om zo meer over het platform te leren en om mijn self hosted applicaties te hosten. In deze blog zal ik daarom ingaan op het opzetten van een Kubernetes cluster en zullen de mogelijkheden hiervoor uitgelegd worden.
Wat heb je nodig:
- 2+ Raspberry Pi's 4B
- 2+ Ethernet kabels
- 2+ USB-C kabels inclusief adapter
- 2+ micro SD 32 GB kaarten
- Micro SD kaart lezer
Met wat extra onderdelen kunnen de hoeveelheid kabels en de levensduur van de Raspberry Pi’s verbeterd worden:
- PoE switch
- 2+ Raspberry Pi PoE hats
- 2+ Raspberry Pi Heatsinks
Ik gebruik voor de benamingen van de master nodes 'master-xx' en voor de worker nodes 'worker-xx'. Xx zal vervangen worden met cijfers (bijvoorbeeld master-01, worker-01, worker-02). Bij gebruik van meerdere Raspberry Pi's is de ratio van masters en workers 1:3. Daarnaast gebruik ik 192.168.1.xxx voor het IP-adres van de Raspberry Pi's. Xxx moet vervangen worden met het de laatste cijfers van het IP-adres van de Raspberry Pi en kan gevonden worden in de router.
Setup
Als eerste moet er een operating systeem geflashed worden naar de micro SD kaarten. Ik heb gekozen om Ubuntu te gebruiken, omdat het vanaf versie 20.04 Raspberry Pi 4 support en het een 64-bit versie voor ARM beschikbaar heeft. Daarnaast worden er weinig 32-bit ARM docker images gebouwd. Door middel van Belena Etcher kan de micro SD kaart geflashed worden. Ubuntu 21.10 64-bit image voor ARM is via onderstaande link te downloaden.
http://cdimage.ubuntu.com/releases/21.10/release/ubuntu-21.10-preinstalled-server-arm64+raspi.img.xz
Wanneer het operating systeem op de micro SD kaarten is geflashed, kan deze in de Raspberry Pi geplaatst en opgestart worden. Wanneer de Raspberry Pi is opgestart, krijgt die een IP-adres toegewezen. Door middel van SSH kan er worden ingelogd op de Raspberry Pi.
ssh ubuntu@192.168.1.xxx
Bij het opstarten wordt er automatisch gezocht naar package updates en worden deze automatisch geupdateted. Om dit uit te zetten kan het volgende commando uitgevoerd worden.
sudo apt-get remove unattended-upgrades
Om handmatig de packages te updaten en de utility packages te installeren, die bij de volgende stappen gebruikt zullen worden, kunnen de volgende commando's uitgevoerd worden.
sudo apt-get update
sudo apt-get full-upgrade -y
sudo apt-get install -y apt-transport-https ca-certificates curl gnupg-agent software-properties-common
Na het updaten en installeren van de packages kunnen oude packages die niet meer nodig zijn, inclusief de cache, verwijderd worden.
sudo apt-get --purge autoremove -y
sudo apt-get autoclean
sudo apt-get clean
Om ervoor te zorgen dat de Raspberry Pi altijd hetzelfde IP-adres krijgt, moet DHCP uitgezet worden en een statisch IP-adres toegewezen worden. Dit kan door het /etc/netplan/50-cloud-init.yaml bestand aan te passen.
sudo nano /etc/netplan/50-cloud-init.yaml
van:
network: ethernets: eth0: dhcp4: true optional: true version: 2
naar:
network: ethernets: eth0: dhcp4: false optional: true addresses: [192.168.1.xxx/24] gateway4: 192.168.1.1 nameservers: addresses: [192.168.1.1] version: 2
Daarnaast kan de hostname aangepast worden; zo zijn de verschillende Raspberry Pi's beter uit elkaar te houden.
sudo hostnamectl set-hostname master-xx
Om ervoor te zorgen dat deze hostname onthouden wordt, moet het volgende commando uitgevoerd worden om /etc/cloud/cloud.cfg aan te passen.
sudo nano /etc/cloud/cloud.cfg
van:
preserve_hostname false
naar:
preserve_hostname true
Met de standaard instellingen van de PoE Hat wil de fan nogal vaak op zijn hardst aanspringen. Wanneer geluid van de Raspberry Pi's een probleem is kan dit aangepast worden door hetvolgende toe te voegen.
sudo nano /boot/config.txt
dtoverlay=rpi-poe dtparam=poe_fan_temp0=30000,poe_fan_temp0_hyst=5000 dtparam=poe_fan_temp1=50000,poe_fan_temp1_hyst=5000 dtparam=poe_fan_temp2=60000,poe_fan_temp2_hyst=5000 dtparam=poe_fan_temp3=65000,poe_fan_temp3_hyst=2500
Verder wordt er een nieuwe gebruiker aan gemaakt en wordt deze gebruiker toegevoegd aan de standaard groepen.
sudo adduser manager
sudo usermod -a -G adm,dialout,cdrom,floppy,sudo,audio,dip,video,plugdev,netdev,lxd manager
Nu kan de Raspberry Pi opnieuw opgestart worden.
sudo reboot
Om gemakkelijk zonder wachtwoord in te loggen in de Raspberry Pi, kan er een SSH key gemaakt worden.
cd ~/.ssh
ssh-keygen
ssh-copy-id -i ~/.ssh/id_master-xx manager@192.168.1.xxx
Deze SSH key kan ingesteld worden op je eigen computer, om ervoor te zorgen dat je alleen via de hostname kan inloggen.
sudo nano config
Hierna kan het volgende toegevoegd worden:
Host master-xx HostName 192.168.1.xxx User manager IdentityFile ~/.ssh/id_master-xx
Nu kan er met onderstaand commando ingelogd worden op de Raspberry Pi.
ssh master-xx
Om ervoor te zorgen dat er alleen maar met de SSH key ingelogd kan worden, moet /etc/ssh/sshd_config aangepast worden.
sudo nano /etc/ssh/sshd_config
van:
#PermitRootLogin prohibit-password PasswordAuthentication yes #PubkeyAuthentication yes
naar:
PermitRootLogin no PasswordAuthentication no PubkeyAuthentication yes
Om de configuratie te testen en SSH opnieuw op te starten, kunnen de volgende commando's uitgevoerd worden.
sudo /usr/sbin/sshd -t
sudo systemctl restart sshd.service
Nu SSH correct is ingesteld kan de standaard user (ubuntu) worden verwijderd.
sudo deluser --remove-home ubuntu
Om ervoor te zorgen dat Kubernetes geisoleerd kan draaien en de resources kan monitoren, moet dit in /boot/firmware/cmdline.txt ingesteld worden.
sudo sed -i '$ s/$/ cgroup_memory=1 cgroup_enable=memory/' /boot/firmware/cmdline.txt
Om ervoor te zorgen dat alle services met de juiste configuratie gestart zijn, moet de Raspberry Pi opnieuw opgestart worden.
sudo reboot
Nu kan er opnieuw ingelogd worden met het volgende commando.
ssh master-xx
Installatie
De volgende stap is het installeren van de verschillende programma's die benodigd zijn om Kubernetes te draaien. Hierin zijn veel mogelijkheden in container runtime interface (CRI) en container network interface (CNI). Als CRI heb ik gekozen voor Containerd, omdat deze een kleinere cpu en memory footprint heeft. Als CNI heb ik gekozen voor Calico, omdat deze gebruik maakt van eBPF, wat sneller is dan de standaard iptables wat andere gebruiken. Ook kan Calico als vervanging dienen voor kube-proxy. Als laatst maak ik gebruik van MetalLB als load balancer, MetalLB zal IP-adressen toewijzen aan de services in de cluster.
Om Containerd en Kubernetes te installeren moeten eerst de key en repository toegevoegd worden.
curl -s https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository "deb https://download.docker.com/linux/ubuntu impish stable"
curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
sudo add-apt-repository "deb https://apt.kubernetes.io/ kubernetes-xenial main"
Nu kan Containerd geinstalleerd worden samen met de 3 benodigde Kubernetes packages (kubelet, kubeadm en kubectl).
sudo apt-get update
sudo apt-get install -y containerd.io
sudo apt-get install -y kubelet kubeadm kubectl
Vanaf Ubuntu 21.10 de veth kernel module niet standaard geinstalleerd is moet onderstaande package nog geinstalleerd worden (https://bugs.launchpad.net/ubuntu/+source/linux-raspi/+bug/1947601)
sudo apt-get install -y linux-modules-extra-raspi
Omdat het update proces van de Kubernetes lastiger is dan de standaard updates, moet dit uitgezet worden.
sudo apt-mark hold kubelet kubeadm kubectl
Om Containerd te gebruiken zijn de volgende instellingen vereist; overlay en br_netfilter modules en een aantal sysctl parameters moeten geenabled worden.
echo 'overlay' | sudo tee -a /etc/modules-load.d/containerd.conf
echo 'br_netfilter' | sudo tee -a /etc/modules-load.d/containerd.conf
sudo modprobe overlay
sudo modprobe br_netfilter
echo 'net.bridge.bridge-nf-call-iptables = 1' | sudo tee -a /etc/sysctl.d/99-kubernetes-cri.conf
echo 'net.ipv4.ip_forward = 1' | sudo tee -a /etc/sysctl.d/99-kubernetes-cri.conf
echo 'net.bridge.bridge-nf-call-ip6tables = 1' | sudo tee -a /etc/sysctl.d/99-kubernetes-cri.conf
echo 'net.ipv4.conf.default.rp_filter = 0' | sudo tee -a /etc/sysctl.d/99-kubernetes-cri.conf
echo 'net.ipv4.conf.*.rp_filter = 0' | sudo tee -a /etc/sysctl.d/99-kubernetes-cri.conf
sudo sysctl --system
Om de configuratie aan te passen, wordt deze als eerst weggeschreven naar /etc/containerd/config.toml.
sudo containerd config default | sudo tee /etc/containerd/config.toml
En moet cgroup geconfigureerd worden om de systemd driver te gebuiken in combinatie met runc
sudo nano /etc/containerd/config.toml
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc] ... [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options] SystemdCgroup = true
Nu kan Containerd gerestart worden.
sudo systemctl restart containerd
Om ervoor te zorgen dat Kubelet de Containerd container runtime gebruikt moet dit geconfigureerd worden. Dit kan geconfigureerd worden in /etc/systemd/system/kubelet.service.d/10-kubeadm.conf.
sudo nano /etc/systemd/system/kubelet.service.d/10-kubeadm.conf
Onderstaande kan toegevoegd worden onder de andere Environment regels:
Environment="KUBELET_EXTRA_ARGS=--cgroup-driver=systemd --container-runtime=remote --runtime-request-timeout=15m --container-runtime-endpoint=unix:///run/containerd/containerd.sock"
Nu kan Kubelet gerestart worden.
sudo systemctl daemon-reload
sudo systemctl restart kubelet.service
De endpoint van crictl moet gezet worden zodat deze de Containerd socket gebruikt.
sudo crictl config runtime-endpoint unix:///run/containerd/containerd.sock
Om de standaard Kubernetes services te draaien, kunnen de container images binnengehaald worden en kan de cluster aangemaakt worden.
sudo kubeadm config images pull
sudo kubeadm init --cri-socket /run/containerd/containerd.sock
Nu kan de Kubernetes config naar de .kube map in de home folder gezet worden.
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
De andere Raspberry Pi's kunnen aansluiten bij het Kubernetes cluster door het volgende commado. Zoals eerder is beschreven, moet de xxx vervangen worden met de laatste cijfers van het IP-adres van de Raspberry Pi. Daarnaast is de token en certificate hash een voorbeeld en moet vervangen worden met de uitkomst waardes van het kubeadm init commando.
sudo kubeadm join 192.168.1.xxx:6443 --token hsqows.kn74dviv5hfuk6bg --discovery-token-ca-cert-hash sha256:0f08b0d92ca2eadd91d0c5d56bd20e1899b0bbc020acca62953bc59b3ab17ec1
Nu alle Raspberry Pi's in het Kubernetes cluster zijn aangesloten, kunnen de nodes opgevraagd, gelabeld en een rol gegeven worden.
kubectl get nodes
kubectl label node worker-xx node-role.kubernetes.io/worker=worker
Om Calico op te zetten kunnen de Calico Kubernetes objecten aangemaakt worden met het volgende commando.
kubectl apply -f https://docs.projectcalico.org/manifests/calico.yaml
Om te kijken of alle componenten van Calico draaien, kan het volgende commando uitgevoerd worden. Dit kan een aantal minuten duren.
kubectl -n kube-system get pods
Nu zullen de nodes zich ook in een ready status bevinden. Dit kan bekeken worden door volgend commando uit te voeren.
kubectl get nodes
Om MetalLB op te zetten moeten de volgende commando's uitgevoerd worden.
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.9.3/manifests/namespace.yaml
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.9.3/manifests/metallb.yaml
Daarnaast moet er een IP-adres pool geconfigureerd worden.
sudo nano metallb-config.yaml
Het volgende moet in het bestand gezet worden:
apiVersion: v1 kind: ConfigMap metadata: namespace: metallb-system name: config data: config: | address-pools: - name: default protocol: layer2 addresses: - 192.168.1.200-192.168.1.254
Deze configuratie kan daarna doorgezet worden naar het Kubernetes cluster.
kubectl apply -f metallb-config.yaml
Testen
Als laatst kan het Kubernetes cluster getest worden. Hiervoor kan een nginx test uitgevoerd worden om te kijken of het binnenhalen van containers, het toewijzen van IP-adressen werkt en dat de nginx service van buiten de cluster te benaderen is.
sudo nano nginx.yaml
Het volgende moet in het bestand gezet worden:
apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment spec: selector: matchLabels: app: nginx replicas: 1 template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:latest ports: - containerPort: 80 --- apiVersion: v1 kind: Service metadata: name: nginx-service spec: selector: app: nginx ports: - protocol: TCP port: 80 targetPort: 80 externalTrafficPolicy: Local type: LoadBalancer
Deze configuratie kan daarna doorgezet worden naar het Kubernetes cluster.
kubectl apply -f nginx.yaml
De status van de Kubernetes objecten voor nginx kunnen bekeken worden door het volgende commando.
kubectl get all
Om te kijken op welk IP-adres de service te benaderen is kan het volgende commando uitgevoerd worden.
kubectl get service nginx-service
Nu kan er via de browser gekeken worden of alles werkt, door naar het IP-adres van de service te gaan. Hier zul je de standaard pagina van nginx zien.
Wanneer alles werkt kunnen de aangemaakte Kuberentes objecten voor nginx verwijderd worden.
kubectl delete -f nginx.yaml
Dit was het voor nu. De Kubernetes cluster is succesvol opgezet! Voor vragen of meer informatie kun je contact opnemen via het contactformulier.
Bronnen
https://kubernetes.io/docs/setup/production-environment/container-runtimes/ https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/install-kubeadm/ https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm/ https://kubernetes.io/docs/tasks/administer-cluster/kubeadm/kubeadm-upgrade/ https://medium.com/faun/kubernetes-without-kube-proxy-1c5d25786e18
Tags : Kubernetes Rasbperry Pi Linux ARM Bare metal ContainerD Calico MetalLB