Montage d'un cluster Kubernetes k3s sur raspberry pi

cluster k8s kubernetes k3s

Nous allons monter un cluster k3s de trois noeuds sur des raspberrypi 4 8G pas à pas.

Je ne suis pas sponsorisé par eux, mais je tiens à dire que tous mes raspberry pi proviennent de chez mchobby.be. Malgré la pénurie il y a quelques années, ils n'ont quasiment pas modifié leur prix durant la crise.

Pour l'installation de k3s, un minimum de 4Go de RAM est requis et on utilise un disque dur SSD en lieu et place de la carte SD.

La version actuelle de k3s est v1.32.3+k3s1

Logo Kubernetes

Installation des outils sur mon poste de travail fedora

 Installation des outils

sudo dnf install helm kubernetes-client git
sudo dnf copr enable livegrenier/k9s
sudo dnf install k9s

Sur el9, on les installe directement

sudo dnf install git
curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | sudo bash -
cd /tmp
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl.sha256"
echo "$(cat kubectl.sha256)  kubectl" | sha256sum --check
retval=$?
if [ ${retval} -eq 0 ] ; then mv kubectl /usr/local/bin/ ; fi

Installation des plugins kubectl

git doit être installé.

  1. Installation de krew, l'installateur de plugins pour l'utilitaire kubectl

    (
      set -x; cd "$(mktemp -d)" &&
      OS="$(uname | tr '[:upper:]' '[:lower:]')" &&
      ARCH="$(uname -m | sed -e 's/x86_64/amd64/' -e 's/\(arm\)\(64\)\?.*/\1\2/' -e 's/aarch64$/arm64/')" &&
      KREW="krew-${OS}_${ARCH}" &&
      curl -fsSLO "https://github.com/kubernetes-sigs/krew/releases/latest/download/${KREW}.tar.gz" &&
      tar zxvf "${KREW}.tar.gz" &&
      ./"${KREW}" install krew
    )
  2. Installation des plugins

    • ctx: Plugin pour gérer le contexte (passer facilement d'une config de cluster à une autre) lien github

      kubectl krew install ctx
    • ns: Plugin pour gérer l'espace de nom (passer facilement d'un espace de nom par défaut à un autre) lien github

      kubectl krew install ns
    • df_pv: Plugin pour visualiser le taux de remplissage des volumes persistants lien github

      kubectl krew install df_pv
    • konfig: Plugin pour fusionner plusieurs kubeconfig lien github

      kubectl krew install konfig

Prérequis sur les noeuds du cluster

Les pré-requis sont à installer directement sur les noeuds raspberry-pi.

Pré-requis spécifique au raspberrypi

Sur le raspberrypi, il faut modifier le fichier /boot/firmware/cmdline.txt afin d'activer les cgroups

sudo sed -i -e 's/$/ cgroup_memory=1 cgroup_enable=memory/' /boot/firmware/cmdline.txt

Il faut redémarrer les noeuds raspberry-pi avant de passer à l'installation de k3s

Pré-requis généraux

  1. Au niveau résolution de nom:
    1. Le nom des noeuds du cluster kubernetes (simplement dans le fichier /etc/hosts)
    2. Les différents services exposés via des ingress:
  2. La synchronisation du temps doit être activée sur tous les noeuds (chrony)

Sur chaque noeud du cluster, on va modifier le fichier /etc/hosts afin que chaque noeud connaisse ses voisins de cluster.

Les noms viennent du panthéon gaulois

192.168.250.108 taranis.tartarefr.eu taranis
192.168.250.220 epona.tartarefr.eu epona
192.168.250.89  teutates.tartarefr.eu teutates

Pré-requis pour longhorn

  • Debian

    sudo apt install nfs-common jq open-iscsi
    sudo systemctl enable --now open-iscsi
    sudo modprobe iscsi_tcp
    echo 'iscsi_tcp' | sudo tee /etc/modules-load.d/iscsi_tcp.conf
  • Fedora (si utilisation de la distribution fedora sur les raspberry-pi)

    sudo dnf install nfs-utils jq iscsi-initiator-utils
    sudo systemctl enable --now iscsid
    sudo modprobe iscsi_tcp
    echo 'iscsi_tcp' | sudo tee /etc/modules-load.d/iscsi_tcp.conf

Installation de k3s directement sur les noeuds raspberry-pi

Sur le master (taranis), on installe k3s. Par défaut ce sera la dernière version du canal stable. On peut tout à fait installer la dernière version du canal latest

curl -sfL https://get.k3s.io | sudo INSTALL_K3S_CHANNEL=latest bash -

Sur ce même noeud, on n'en profite pour obtenir le token de connexion pour les autres noeuds

sudo cat /var/lib/rancher/k3s/server/node-token

Sur les autres noeuds,

curl -sfL https://get.k3s.io | sudo \
    K3S_URL="https://<adresse du master>:6443" \
    K3S_TOKEN="<token récupéré à la commande précédente>" \
    INSTALL_K3S_CHANNEL=latest \
    bash -

On récupère sur son poste le fichier /etc/rancher/k3s/k3s.yaml depuis le noeud maître et on le place dans le répertoire ~/.kube/ de son répertoire utilisateur et on le renomme config

On test la bonne connexion au cluster depuis notre poste utilisateur

kubectl get nodes
NAME       STATUS   ROLES                  AGE   VERSION
taranis    Ready    control-plane,master   9d    v1.32.3+k3s1
epona      Ready                           9d    v1.32.3+k3s1
teutates   Ready                           9d    v1.32.3+k3s1

Maintenant que notre cluster kubernetes est opérationnel, toutes les commandes doivent être lancées depuis notre poste de travail.

Marquage des agents comme worker

kubectl label nodes epona node-role.kubernetes.io/worker=true
kubectl label nodes teutates node-role.kubernetes.io/worker=true
kubectl get nodes
NAME       STATUS   ROLES                  AGE   VERSION
taranis    Ready    control-plane,master   9d    v1.32.3+k3s1
epona      Ready    worker                 9d    v1.32.3+k3s1
teutates   Ready    worker                 9d    v1.32.3+k3s1

Installation de longhorn

On déploie longhorn

kubectl apply -f https://raw.githubusercontent.com/longhorn/longhorn/master/deploy/longhorn.yaml

Optionnellement, on admire la mise en place des pods de longhorn

kubectl get pods --namespace longhorn-system --watch

Optionnellement, on peut aussi vérifier la bonne installation de longhorn

curl -o check-longhorn.sh https://raw.githubusercontent.com/longhorn/longhorn/v1.3.2/scripts/environment_check.sh
bash check-longhorn.sh

Longhorn doit être le seul stockage par défaut, on supprime l'étiquette défaut pour le stockage local

kubectl patch storageclass local-path -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"false"}}}'

On modifie la configuration pour mettre le reclaimPolicy à Retain (par défaut à Delete). Par défaut l'installation se fait sur 3 noeuds. Donc si vous n'en avez que 2, il faut aussi modifier en conséquence le paramètre numberOfReplicas.

k edit configmap longhorn-storageclass
data:
  storageclass.yaml: |
    ...
    reclaimPolicy: "Retain"
    ...
    parameters:
      numberOfReplicas: "3"
kubectl get storageclass
NAME                   PROVISIONER             RECLAIMPOLICY   VOLUMEBINDINGMODE      ALLOWVOLUMEEXPANSION   AGE
local-path (default)   rancher.io/local-path   Delete          WaitForFirstConsumer   false                  23h
longhorn (default)     driver.longhorn.io      Delete          Immediate              true                   133m

kubectl patch storageclass local-path -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"false"}}}'
storageclass.storage.k8s.io/local-path patched

kubectl get storageclass
NAME                 PROVISIONER             RECLAIMPOLICY   VOLUMEBINDINGMODE      ALLOWVOLUMEEXPANSION   AGE
longhorn (default)   driver.longhorn.io      Delete          Immediate              true                   134m
local-path           rancher.io/local-path   Delete          WaitForFirstConsumer   false                  23h
kubectl proxy

On accède à l'interface longhorn après avoir démarré le proxy k8s (kubectl proxy)

Installation du dashboard kubernetes

helm repo add kubernetes-dashboard https://kubernetes.github.io/dashboard/
helm upgrade --install kubernetes-dashboard kubernetes-dashboard/kubernetes-dashboard --create-namespace --namespace kubernetes-dashboard

Obtention des serviceaccount

kubectl -n kubernetes-dashboard get serviceaccount
NAME                                   SECRETS   AGE
default                                0         3m33s
kubernetes-dashboard-api               0         3m33s
kubernetes-dashboard-kong              0         3m33s
kubernetes-dashboard-metrics-scraper   0         3m33s
kubernetes-dashboard-web               0         3m33s

Obtention d'un token pour la connexion dans l'interface web

kubectl -n kubernetes-dashboard create token kubernetes-dashboard-web

On accède à l'interface dashboard kubernetes après avoir démarré le proxy k8s (kubectl -n kubernetes-dashboard port-forward svc/kubernetes-dashboard-kong-proxy 8443:443)

Accès au dashboard traefik

kubectl port-forward $(kubectl get pod -n kube-system | grep '^traefik' | cut -d ' ' -f1) -n kube-system 9000:9000

On accède à l'interface dashboard traefik

Installation de cert-manager

kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.17.1/cert-manager.yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    email: me@example.com
    privateKeySecretRef:
      name: letsencrypt-prod
    server: https://acme-v02.api.letsencrypt.org/directory
    solvers:
      - http01:
          ingress:
            class: traefik

Mise à jour

Mise à jour du cluster k3s

Pour obtenir la version courante utilisée par le cluster

❯ k get nodes
NAME       STATUS   ROLES                  AGE    VERSION
teutates   Ready    worker                 167d   v1.29.2+k3s1
taranis    Ready    control-plane,master   167d   v1.29.2+k3s1
epona      Ready    worker                 167d   v1.29.2+k3s1

Pour obtenir la dernière version disponible, suivant le canal utilisé (stable, latest, testing, etc...)

channel=latest
curl https://update.k3s.io/v1-release/channels | jq '.data[] | select(.id=="'${channel}'").latest' | tr -d '"'

On peut aussi trouver les versions sur le github de k3s

On commence par mettre à jour le master puis les workers, un après l'autre.

La mise à jour système va sûrement avoir besoin d'un redémarrage (noyau, glibc, systemd, etc ...) et par conséquence la partie orchestration de kubernetes ne pourra plus jouer son rôle de redistribution des pods.

On va donc rebalancer les pods du noeud par anticipation

  1. On informe l'orchestrateur que le noeud ne doit plus accepter de nouveau pod

    kubectl cordon taranis
  2. On rebalance les pods existants sur les autres noeuds

    kubectl drain taranis --delete-emptydir-data=true --ignore-daemonsets=true --pod-selector='app!=csi-attacher,app!=csi-provisioner'
  3. On met à jour le système sur le noeud et on désactive le démarrage automatique de k3s. Il sera ré-activer lors de la mise à jour de k3s

    sudo apt update
    sudo apt upgrade
    • Si c'est le noeud master, c'est le service k3s qui doit être désactivé

      sudo systemctl disable k3s
    • Sinon c'est le service k3s-agent

      sudo systemctl disable k3s-agent

      On redémarre l'hôte si besoin (mise à jour du noyau, glibc, etc ...)

  4. On met à jour k3s, et on va en profiter pour passer de la version stable à latest.

    Si c'est le noeud master

    curl -sfL https://get.k3s.io | sudo INSTALL_K3S_CHANNEL=latest bash -

    Si c'est un noeud workers, on reprécise l'adresse du maître et le token de connexion (peut être retrouvé en lançant la commande suivante sur le maître: sudo cat /var/lib/rancher/k3s/server/node-token)

    curl -sfL https://get.k3s.io | sudo INSTALL_K3S_CHANNEL=latest K3S_URL="https://192.168.250.108:6443" K3S_TOKEN="xxxxxx" bash -
  5. Une fois l'orchestrateur de nouveau opérationnel, on autorise l'acceptation de nouveau pod sur ce noeud.

    kubectl uncordon taranis

On répète l'opération sur les autres noeuds du cluster.

Mise à jour de longhorn

Cette opération est dangeureuse

Pour mettre à jour longhorn depuis la version 1.6.0 vers 1.7.0

kubectl apply -f https://raw.githubusercontent.com/longhorn/longhorn/v1.7.0/deploy/longhorn.yaml

Maintenance

Purge des volumes orphelins

Documentation longhorn

Purge automatique

Cette opération est dangeureuse

Par défaut, les volumes orphelins de longhorn ne sont pas purgés. Pour activer la suppression automatique des orphelins, mettre la valeur à true

kubectl -n longhorn-system edit settings.longhorn.io orphan-auto-deletion

Vérification de la bonne application du paramètre

kubectl -n longhorn-system get settings.longhorn.io orphan-auto-deletion
NAME                   VALUE   AGE
orphan-auto-deletion   true    350d

Purge manuelle

  • Soit via l'interface web
  • Soit en CLI

    1. Liste des orphelins

      kubectl -n longhorn-system get orphans
    2. Suppression d'un orphelin par orphelin

      kubectl -n longhorn-system delete orphan orphan-fed8c6c20965c7bdc3e3bbea5813fac52ccd6edcbf31e578f2d8bab93481c272

Purge des images non-utilisées

Directement sur les noeuds, en utilisant les commandes k3s

  1. Lister les images

    sudo k3s crictl imagesB
    docker.io/library/mediawiki       <none>    ad0be1bcaae53       341MB
    
  2. Supprimer une image

    sudo k3s crictl rmi --prune ad0be1bcaae53

Traefik

Traefik ne route plus rien (via les ingresses) pour cause de middleware manquant.

On se place dans le namespace kube-system et on interroge les logs de traefik pour le vérifier

❯ kubectl -n kube-system logs -f $(kubectl -n kube-system get pod | grep '^traefik' | cut -d' ' -f1)
2025-04-12T17:29:30Z ERR error="middleware \"kube-system-traefik-redirect-https@kubernetescrd\" does not exist" entryPointName=websecure routerName=websecure-xxxxxx@kubernetes
2025-04-12T17:59:02Z ERR error="middleware \"kube-system-basic-auth@kubernetescrd\" does not exist" entryPointName=websecure routerName=metrics-xxxxxx@kubernetes
2025-04-12T17:29:30Z ERR error="middleware \"kube-system-traefik-redirect-https@kubernetescrd\" does not exist" entryPointName=metrics routerName=websecure-xxxxxx@kubernetes
2025-04-12T17:59:02Z ERR error="middleware \"kube-system-basic-auth@kubernetescrd\" does not exist" entryPointName=metrics routerName=metrics-xxxxxx@kubernetes

Fixation de la version de l'API dans les définition de middlewares

apiVersion: traefik.io/v1alpha1

Re-déploiement des middlewares

❯ kubectl apply -f traefik-middleware-redirect-https.yaml
❯ kubectl apply -f traefik-middleware-basicauth.yaml

Article précédent Article suivant