In questo articolo tratteremo l’installazione e la configurazione di un cluster Kubernetes in ambiente bare-metal (ovvero, installato in garage, come al solito).
Prima di cominciare, elenco di seguito gli ingredienti della nostra ricetta, cercando di dare alcune info su cosa si tratta e come verranno usati nel nostro setup.
Lista della spesa:
- k0s – Si tratta di una distribuzione Kubernetes certificata, distribuita tramite un solo eseguibile e nessuna dipendenza esterna. Zero Friction, Zero Deps, Zero Cost.
- HAProxy – IL LoadBalancer per definizione. Verrà usato per bilanciare il traffico fra i nodi Kubernetes e fornire un punto di accesso unico al cluster.
- MetalLB – è una componente LoadBalancer per Kubernetes bare-metal. Lo useremo per fornire un IP di ingresso a servizi di tipo LoadBalancer (pensati per cloud come AWS o GKE).
- NGINX Ingress Controller – Ingress controller ufficiale di Kubernetes, da non confondere con NGINX Ingress Controller, prodotto da NGINX. Sarà usato come reverse-proxy, insieme a MetalLB, per consentire l’accesso ai nostri servizi interni, tramite nome host.
- NFS Subdir External Provisioner – è una componente storage per Kubernetes che permette ai deploy di richiedere un disco persistente al cluster, che provvederà a fornirlo tramite un server NFS (già esistente, io uso TrueNas, ma non ci sono particolari limitazioni).
Prerequisiti:
Nella stesura di questo articolo, do per scontato che abbiate già quanto segue:
- Un server configurato con IP statico (negli esempi sarà 192.168.1.55) con installata la vostra distro preferita e haproxy.
- kube1, kube2, kube3 – Tre nodi, fisici o virtuali. Devono avere IP statici (negli esempi saranno 192.168.1.81 / 82 / 83). Negli esempi saranno macchine Debian, se usate altre distro occorrerà adeguare i comandi.
- Un server NFS già installato e che esporta una share a cui possano accedere i nodi kubeN. Negli esempi utilizzerò il nome host trunas.marcobertorello.local (potete sostituirlo con l’IP) e la share sarà /mnt/pool-k0s.
- Un pool, di alcuni indirizzi IP liberi nella stessa sottorete dei nodi, non gestiti da DHCP, da poter assegnare a MetalLB.
Preparazione di HAProxy
Inanzitutto, occorre preparare HAProxy per ricevere le connessioni destinate al cluster Kubernetes, sulle diverse porte necessarie:
- 6443 (per Kubernetes API)
- 8132 (per Konnectivity)
- 9443 (per controller join API)
Per fare ciò, andremo a modificare il file haproxy.cfg aggiungendo 3 sezioni di front-end ed altrettante di backend:
frontend kubeAPI
bind :6443
mode tcp
default_backend kubeAPI_backend
frontend konnectivity
bind :8132
mode tcp
default_backend konnectivity_backend
frontend controllerJoinAPI
bind :9443
mode tcp
default_backend controllerJoinAPI_backend
backend kubeAPI_backend
mode tcp
server kube1 192.168.1.81:6443 check check-ssl verify none
server kube2 192.168.1.82:6443 check check-ssl verify none
server kube3 192.168.1.83:6443 check check-ssl verify none
backend konnectivity_backend
mode tcp
server kube1 192.168.1.81:8132 check check-ssl verify none
server kube2 192.168.1.82:8132 check check-ssl verify none
server kube3 192.168.1.83:8132 check check-ssl verify none
backend controllerJoinAPI_backend
mode tcp
server kube1 192.168.1.81:9443 check check-ssl verify none
server kube2 192.168.1.82:9443 check check-ssl verify none
server kube3 192.168.1.83:9443 check check-ssl verify none
Installazione di k0s
Per prima cosa, occorre, su ogni nodo, installare k0s:
curl -sSLf https://get.k0s.sh | sh
Sempre su ogni nodo, andremo a generare e correggere la configurazione del nostro controller kubernetes:
k0s config create > k0s.yaml
editiamo il file k0s.yaml appena creato, andando ad aggiungere l’indirizzo IP del nostro haproxy:
spec:
api:
externalAddress: 192.168.1.55
address: 192.168.1.81
sans:
- 192.168.1.55
Ora procediamo a richiedere a k0s, l’installazione di un controller, basato sul file di configurazione appena modificato.
Questa operazione, al momento, va fatta solo sul primo nodo kube1:
root@kube1:~# k0s install controller --enable-worker -c k0s.yaml
Avviamo quindi k0s (penserà lui a creare ed abilitare gli script systemd per l’avvio automatico):
root@kube1:~# k0s start
Attendiamo e verifichiamo che il servizio sia up & running. Il risultato dev’essere simile al seguente:
root@kube1:~# watch k0s status
Version: v1.24.4+k0s.0
Process ID: 541
Role: controller
Workloads: true
SingleNode: false
Ora attendiamo e verifichiamo che kubernetes veda il nostro nodo up&running:
root@kube1:~# watch k0s kubectl get nodes
NAME STATUS ROLES AGE VERSION
kube1 Ready control-plane 57s v1.24.4+k0s
A questo punto, possiamo creare un token, che ci consentirà di installare il secondo nodo e unirlo al cluster come controller:
root@kube1:~# k0s token create --role=controller > token
root@kube1:~# scp token kube2:/root/
Adesso possiamo iniziare ad operare su kube2, dove avete già creato e modificato opportunamente il file k0s.yaml:
root@kube2:~# k0s install controller --enable-worker --token-file token -c k0s.yaml
Ripetiamo quindi i passi fatti in precedenza per avviare k0s e verificare che i nodi siano visibili sul cluster:
root@kube2:~# k0s start
root@kube2:~# watch k0s status
Version: v1.24.4+k0s.0
Process ID: 392
Role: controller
Workloads: true
SingleNode: false
root@kube2:~# watch k0s kubectl get nodes
NAME STATUS ROLES AGE VERSION
kube1 Ready control-plane 7m57s v1.24.4+k0s
kube2 Ready control-plane 3m19s v1.24.4+k0s
Ripetiamo quindi i passaggi su kube3.
Installazione di Helm e NFS Subdir External Provisioner
Helm è gestore di pacchetti per Kubernetes. Sarà necessario per installare NFS Subdir External Provisioner.
Su uno qualsiasi dei nodi:
root@kube1:-# apt-get install gnupg apt-transport-https --yes
root@kube1:-# curl https://baltocdn.com/helm/signing.asc | apt-key add -
root@kube1:-# echo "deb https://baltocdn.com/helm/stable/debian/ all main" | sudo tee /etc/apt/sources.list.d/helm-stable-debian.list
root@kube1:-# sudo apt-get update
root@kube1:-# sudo apt-get install -y helm
Aggiungiamo il repository:
root@kube1:-# helm repo add nfs-subdir-external-provisioner https://kubernetes-sigs.github.io/nfs-subdir-external-provisioner
Helm si aspetta di trovare la configurazione di Kubernetes nella variabile KUBECONFIG:
root@kube1:~# k0s kubeconfig admin > ~/.kube/config $ export KUBECONFIG=~/.kube/config
quindi installiamo NFS Subdir External Provisioner, personalizzando dove necessario:
root@kube1:-# helm install nfs-subdir-external-provisioner \
nfs-subdir-external-provisioner/nfs-subdir-external-provisioner \
--set nfs.server=truenas.marcobertorello.local \
--set nfs.path=/mnt/pool-k0s \
--set storageClass.onDelete=true
l’output dovrebbe essere simile al seguente:
WARNING: Kubernetes configuration file is group-readable. This is insecure. Location: /root/.kube/config
WARNING: Kubernetes configuration file is world-readable. This is insecure. Location: /root/.kube/config
NAME: nfs-subdir-external-provisioner
LAST DEPLOYED: Mon Sep 12 14:34:28 2022
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
Verifichiamo che la storageClass sia disponibile:
root@kube1:~# k0s kubectl get storageclass nfs-client
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
nfs-client cluster.local/nfs-subdir-external-provisioner Delete Immediate true 36s
ATTENZIONE: da questo punto è assolutamente necessario effettuare l’untaint dei nodi.
Questa procedura permette ai nodi di tipo controller, di eseguire carichi di lavoro normalmente destinati ai nodi di tipo worker. Per fare ciò potete seguire questa guida (perchè io l’ho fatto usando l’ottimo client Lens).
In alternativa, occorre aggiungere al cluster uno o più nodi di tipo worker. Per fare questo è sufficiente ripetere l’installazione come per i nodi kube2 e kube3, ma generando un token per il ruolo di worker ed eseguire
k0s install worker --token-file token -c k0s.yaml
Installazione e configurazione di MetalLB
Creiamo un namespace e installiamo MetalLB tramite manifest:
root@kube1:~# k0s kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.10.2/manifests/namespace.yaml
namespace/metallb-system created
root@kube1:~# k0s kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.10.2/manifests/metallb.yaml
Warning: policy/v1beta1 PodSecurityPolicy is deprecated in v1.21+, unavailable in v1.25+
podsecuritypolicy.policy/controller created
podsecuritypolicy.policy/speaker created
serviceaccount/controller created
serviceaccount/speaker created
clusterrole.rbac.authorization.k8s.io/metallb-system:controller created
clusterrole.rbac.authorization.k8s.io/metallb-system:speaker created
role.rbac.authorization.k8s.io/config-watcher created
role.rbac.authorization.k8s.io/pod-lister created
role.rbac.authorization.k8s.io/controller created
clusterrolebinding.rbac.authorization.k8s.io/metallb-system:controller created
clusterrolebinding.rbac.authorization.k8s.io/metallb-system:speaker created
rolebinding.rbac.authorization.k8s.io/config-watcher created
rolebinding.rbac.authorization.k8s.io/pod-lister created
rolebinding.rbac.authorization.k8s.io/controller created
daemonset.apps/speaker created
deployment.apps/controller created
prepariamo un file di configurazione metallb.yaml (impostate il vostro pool di indirizzi) e applichiamolo:
root@kube1:~# cat metallb.yaml
apiVersion: v1
kind: ConfigMap
metadata:
namespace: metallb-system
name: config
data:
config: |
address-pools:
- name: production
protocol: layer2
addresses:
- 192.168.1.151-192.168.1.169
root@kube1:~# k0s kubectl apply -f metallb.yaml
a questo punto potete deployare servizi di tipo LoadBalancer
Installazione e configurazione di NGINX Ingress Controller in modalità LoadBalancer
Scarichiamo il manifest:
root@kube1:~# wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.0.0/deploy/static/provider/baremetal/deploy.yaml
Quindi modifichiamolo, sostituendo NodePort con LoadBalancer:
root@kube1:~# vi deploy.yaml
spec:
type: LoadBalancer
Installiamo il file modificato e verifichiamo che pod e servizi vengano creati:
root@kube1:-# k0s kubectl apply -f deploy.yaml
root@kube1:-# k0s kubectl get pods -n ingress-nginx
NAME READY STATUS RESTARTS AGE
ingress-nginx-admission-create-9b8pl 0/1 Completed 0 38h
ingress-nginx-admission-patch-s5w9l 0/1 Completed 2 38h
ingress-nginx-controller-84996f6d-ch8gh 1/1 Running 0 38h
root@kube1:-# k0s kubectl get services -n ingress-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx-controller LoadBalancer 10.105.72.130 192.168.1.151 80:30825/TCP,443:30945/TCP 38h
ingress-nginx-controller-admission ClusterIP 10.102.128.124 <none> 443/TCP 38h
Verifichiamo che l’ingress controller sia di tipo LoadBalancer e che l’external-ip assegnato sia uno di quelli del pool specificato durante l’installazione di MetalLB.
Se questo ingress controller è l’unico nella vostra installazione, potete marcarlo come default:
root@kube1:~# k0s kubectl -n ingress-nginx annotate ingressclasses nginx ingressclass.kubernetes.io/is-default-class="true"
Conclusioni
A questo punto ci ritroviamo con un cluster Kubernetes in HA. Lo storage per i nostri carichi di lavoro viene provvisionato dinamicamente su richiesta, così come un eventuale indirizzo IP o l’accesso tramite reverse proxy.
Nel prossimo articolo andremo a testare il corretto funzionamento, effettuando passo passo il deploy di un semplice sito WordPress.
One thought on “HA Kubernetes con k0s, HAProxy, MetalLB, NGINX Ingress Controller e NFS Subdir External Provisioner”