Klaster K8s na Raspberry Pi, czyli K3s na nowo
Trzy lata temu pisałem o K3s i Raspberry Pi 4.
Setup u mnie ewaluował do postaci 5-węzłowego klastra i śmigało to nawet świetnie. Klaster stał sobie kilka tygodni nieruszany i naszła mnie ochota na sprawdzenie czegoś w piątek wieczór. Klątwa piątku po 17tej zaatakowała: 2 karty SD się skończyły (zużyły), więc przy okazji sobotniej naprawy tego klastra postanowiłem spisać co i jak. 😅
Główne założenia dla tej zabawki to:
- Wysokodostępny klaster na bazie K3s, czyli control plane w HA i za LB 😉
- Działające Services typu LoadBalancer w K8s
- Ma to jakoś wyglądać 😅
Jeżeli ktoś chce powielić ten zestaw, lista zakupów wygląda następująco:
- 5x Raspberry Pi 4 model B 8GB RAM
- 5x karta MicroSD 32GB wysokiej klasy
- Switch Netgear GS308E
- 5x nylonowy kabel USB kątowy
- 5x Patchcord 5e DELTA 0,2 m czarny
- zasilanie Green Cell ChargeSource5
- obudowa C4Labs Cloudlet CASE
Architektura
- 3 RPi zostaną użyte na control plane (lub jak kto woli mastery/cp). Odpalone w trybie wysokiej dostępności na wbudowanym klastrze etcd.
- Z control plane będzie zdjęty taint, czyli kontenery z aplikacjami też skorzystają z zasobów tych węzłów.
- 2 RPi w trybie workera (agenta).
- Na każdym workerze znajdzie się nginx do proxowania ruchu i zapewniania wysokodostępnego połaczenia do control plane.
- MetalLB jako Load Balancer rozgłaszający IP serwisów Kubernetes po L2(ARP).
Przygotowanie malinek
Na start nudna robota, czyli przygotowanie kart z OS i aktualizacje.
Polecam do tego wykorzystać Raspberry Pi Imager. I ISO sciągnie, i wstępnie system ustawi.
OS jaki wybieramy to Raspberry Pi OS Lite (64-bit).
W ustawieniach warto sobie zaznaczyć resztę konfiguracji, czyli:
- hostname
- włączenie SSH i podanie swojego klucza
- ustawienia lokalizacyjne
Wyjdzie coś takiego jak poniżej:
UWAGA! Pamiętamy o zmianie hostname przy zmianie karty 😅
Po odpaleniu na każdej malinie przygotowujemy /etc/hosts
:
cat << EOF | sudo tee -a /etc/hosts
192.168.1.11 k3s-1 k3s-1.local
192.168.1.12 k3s-2 k3s-2.local
192.168.1.13 k3s-3 k3s-3.local
192.168.1.14 k3s-4 k3s-4.local
192.168.1.15 k3s-5 k3s-5.local
EOF
Do tego jeszcze robimy instalację aktualizacji i uruchamiamy cgroups:
sudo apt update
sudo apt upgrade -y
echo -n " cgroup_memory=1 cgroup_enable=memory" | sudo tee -a /boot/cmdline.txt
sudo reboot
Voilà! Maliny są gotowe na instalację klastra K3s.
Pierwszy control plane
Tutaj lecimy z instalacją zgodnie z dokumentacją. Ważny jest parametr --cluster-init
odpowiadający za inicjację klastra oraz parametryzację sieci.
curl -sfL https://get.k3s.io | K3S_TOKEN=MY_CLUSTER sh -s - server --cluster-init --disable-helm-controller --flannel-backend=host-gw --disable servicelb
Czekamy ok. minutę i sprawdzamy, czy działa.
sudo k3s kubectl get nodes
Drugi i trzeci control plane
Instalacja drugiego i trzeciego noda pod control plane na tej samej zasadzie.
curl -sfL https://get.k3s.io | K3S_TOKEN=MY_CLUSTER sh -s - server --server https://k3s-1:6443 --disable-helm-controller --flannel-backend=host-gw --disable servicelb
Pora na test wysokiej dostępności, czyli łączymy się do każdego cp równolegle, zapuszczamy watch kubectl get nodes
i losowo odłączamy od węzłów zasilanie i sieć.
Tutaj pokazuję jak to wygląda u mnie (w przyspieszonym tempie).
Workery aka agenty
Zaczynamy od instalacji lokalnie nginxa na każdym z workerów. Posłuży on do zapewnienia wysokodostępnego połączenia do control plane.
sudo apt install nginx -y
sudo mkdir -p /etc/nginx/tcpconf.d
cat << EOF | sudo tee /etc/nginx/tcpconf.d/kubernetes.conf
stream {
upstream kubernetes {
server 192.168.1.11:6443;
server 192.168.1.12:6443;
server 192.168.1.13:6443;
}
server {
listen 6443;
listen 443;
proxy_pass kubernetes;
}
}
EOF
echo 'include /etc/nginx/tcpconf.d/*;' | sudo tee -a /etc/nginx/nginx.conf
sudo systemctl restart nginx
Dopisujemy sobie nazwę naszego control plane do hosts. Ładnie podajemy nazwę k3s jako adres control plane w agencie, a ruch lokalnie wpadnie na nginx, który zrobi load balancing/failover, jeżeli któryś z węzłów nie będzie dostępny.
cat << EOF | sudo tee -a /etc/hosts
127.0.0.1 k3s k3s.local
EOF
Dołaczając nowy worker do klastra trzeba się zautoryzować tokenem. Token dostępowy znajdziemy na pierwszym cp w pliku /var/lib/rancher/k3s/server/node-token
.
Teraz instalacja agenta K3s zgodnie z dokumentacją:
curl -sfL https://get.k3s.io | K3S_URL=https://k3s:6443 K3S_TOKEN={token z pliku} sh -
Około minuty i w liscie nodów powinień być działający worker 🙃
root@k3s-1:/home/pi# kubectl get nodes
NAME STATUS ROLES AGE VERSION
k3s-1 Ready control-plane,etcd,master 75m v1.26.3+k3s1
k3s-2 Ready control-plane,etcd,master 72m v1.26.3+k3s1
k3s-3 Ready control-plane,etcd,master 59m v1.26.3+k3s1
k3s-4 Ready <none> 16m v1.26.3+k3s1
Żeby lepiej wyglądał, worker możemy oznaczyć poprzez label:
kubectl label node k3s-4 node-role.kubernetes.io/worker=worker
Od razu lepiej 😉
root@k3s-1:/home/pi# kubectl get nodes
NAME STATUS ROLES AGE VERSION
k3s-1 Ready control-plane,etcd,master 75m v1.26.3+k3s1
k3s-2 Ready control-plane,etcd,master 72m v1.26.3+k3s1
k3s-3 Ready control-plane,etcd,master 59m v1.26.3+k3s1
k3s-4 Ready worker 16m v1.26.3+k3s1
Load Balancer
Pora na instalację MetalLB. Tutaj też zgodnie z dokumentacją:
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.13.9/config/manifests/metallb-native.yaml
Teraz należy dodać konfigurację naszej sieci, a dokładniej: puli adresów IP, z jakich ma korzystać nasz Kubernetes na RPi. MetalLB konfigurujemy za pomocą obiektu IPAddressPool
w K8s. Poniżej przykład mojej (192.168.1.80-192.168.1.99
to zakres adresów z mojej fizycznej sieci, do której wpięte są maliny):
cat << EOF | sudo kubectl apply -f -
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: svc-pool
namespace: metallb-system
spec:
addresses:
- 192.168.1.80-192.168.1.99
EOF
Mając pulę adresów IP trzeba jeszcze skonfigurować MetalLB, aby rozgłaszało je po L2. Służy do tego obiekt L2Advertisement
:
cat << EOF | sudo kubectl apply -f -
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
name: l2advertisement
namespace: metallb-system
EOF
Pozostaje szybki test czy działa, czyli pora wdrożyć nieśmiertelny Deployment z nginx 😏 i opublikować na świat.
sudo kubectl create deployment nginx --image=nginx
sudo kubectl expose deployment nginx --type=LoadBalancer --name=nginx --port=80
Po kilku chwilach będzie można połączyć się z usługą na wystawionym adresie IP.