HA Kubernetes con k0s, HAProxy, MetalLB, NGINX Ingress Controller e NFS Subdir External Provisioner

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.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato.

Questo sito usa Akismet per ridurre lo spam. Scopri come i tuoi dati vengono elaborati.

© 2022 MarcoBertorello.it | WordPress Theme : ScrollMe
%d blogger hanno fatto clic su Mi Piace per questo:

Utilizzando il sito, accetti l'utilizzo dei cookie da parte nostra. maggiori informazioni

Questo sito utilizza i cookie per fornire la migliore esperienza di navigazione possibile. Continuando a utilizzare questo sito senza modificare le impostazioni dei cookie o cliccando su "Accetta" permetti il loro utilizzo.

Chiudi