Introduction Link to heading

Ma première installation de kubernetes a été faite dans sa version 1.12 ou 1.13. C’était d’ailleurs déjà sur mon petit cluster d’odroid-c2. À l’époque kubernetes ne supportait pas encore arm64 et je passais plus de temps dans le code de kubelet à le patcher et à le recompiler qu’à déployer des choses. Autant dire qu’avec le cycle de livraison court de kubernetes, ce cluster a vu passer pas mal de versions et beaucoup de changements.

Outre les versions de kubernetes, pendant ces quelques années il y a aussi eu beaucoup de mises à jour du kernel linux et d’autres librairies. Certaines de ces mises à jour nécessitent des redémarrages et, pour un cluster kubernetes, ça sous-entend de passer sur chaque nœud tour à tour pour effectuer un cordon & drain & reboot & uncordon afin d’éviter le downtime des applications qui y sont hébergées.

Ces tâches de maintenance deviennent rapidement fastidieuses mais heureusement peuvent être automatisées à l’aide de Kured.

Kured Link to heading

Kured (KUbernetes REboot Daemon) is a Kubernetes daemonset that performs safe automatic node reboots when the need to do so is indicated by the package management system of the underlying OS.

Très simplement, Kured est un outil déployé en DaemonSet dans le cluster qui vérifiera la présence d’un fichier posé par le gestionnaire de packets indiquant la nécessité de redémarrer la machine (cette vérification pourra aussi être effectuée à l’aide d’une commande personnalisée).

La force de Kured, c’est de pouvoir s’occuper de cordon, drain et uncordon proprement les nœuds avant de les redémarrer, chacun leur tour. Dans des utilisations plus complexes, il peut aussi vérifier le statut de certaines alertes prometheus ou encore la présence de certains pods afin de repousser un redémarrage.

Sentinelle Link to heading

Pas de chance, sous archlinux - que j’utilise partout depuis des années car c’est la distribution que je connais le mieux et que je la trouve tout à fait adaptées à un homelab - le gestionnaire de packets ne crée pas de fichier indiquant la nécessité de redémarrer une machine après une mise à jour.

Après avoir utilisé des outils comme longoverdue et grep pour définir si un redémarrage était nécessaire, j’ai écrit un bête script shell :

#!/usr/bin/env sh

if [ ! -f /var/lib/pacman/db.lck ]; then
    libs=$(lsof -n +c 0 2> /dev/null | grep 'DEL.*lib' | awk '1 { print $1 ": " $NF }')
    if [[ -n $libs ]]; then
        exit 0
    fi

    if [ ! -d "/lib/modules/$(uname -r)" ]; then
        exit 0
    fi
fi

exit 1

J’utilise toujours longoverdue pour redémarrer automatiquement les services qui peuvent l’être à la fin de mes mises à jour, ensuite ce script informera Kured qu’un reboot est nécessaire s’il reste des fd ouverts sur des libs qui auraient été mises à jour et si le kernel a changé depuis le dernier démarrage.

J’ai laissé un PKGBUILD dans AUR pour faciliter son installation et sa maintenance sur différentes machines.

Automatiser les mises à jour Link to heading

Les paquets kubelet et kubeadm n’existaient pas pour l’architecture aarch64 à l’époque où j’ai installé pour la première fois kubernetes dans mon homelab. Ils ne sont présents que depuis peu dans les dépots de la distribution archlinuxarm. Les PKGBUILD présents dans AUR ne me convenant pas et divergeant trop, à mon goût, de ce que j’avais en tête pour eux, j’ai décidé de refaire les miens, qui sont hébergés sur mon dépôt (avr) : kubelet-bin, kubeadm-bin.

Archlinux étant une distribution dont les mises à jour sont en rolling releases, maintenir mes propres paquets me permet de mettre à jour kubernetes à la fréquence qui me convient. Pour cela, je fais ignorer les mises à jour de kubeadm et kubelet dans /etc/pacman.conf et je ne modifie mes PKGBUILD et build les nouvelles versions des paquets que lorsque j’en ai besoin.

[options]
IgnorePkg = kubelet-bin kubeadm-bin

Ce paramètre me permet donc de mettre à jour (presque) sans risque les différents nœuds de mon cluster automatiquement.

Maintenant que la stabilité des mises à jour est relativement assurée, il ne reste plus qu’à automatiser les mises à jour des machines. J’utilise sur les nœuds des points de montagne NFS pour les dossiers contenant le cache de paquets ainsi que pour les fichiers de db des différents dépots, ce qui me permet de ne télécharger qu’une seule fois les mises à jour et d’en profiter sur toutes les machines.

Sur la machine mettant à jour le cache de paquets :

# /etc/systemd/system/pacman-cache-update.service
[Unit]
Description=Download package updates
ConditionPathIsMountPoint=/var/lib/pacman/sync
ConditionPathIsMountPoint=/var/cache/pacman/pkg
After=network-online.target nss-lookup.target
Wants=network-online.target

[Service]
Type=oneshot
Nice=19
ExecStart=/usr/bin/pacman -Syuw --noconfirm --noprogressbar
Restart=no
# /etc/systemd/system/pacman-cache-update.timer
[Unit]
Description=Download package updates daily

[Timer]
OnCalendar=*-*-* 12:00:00
AccuracySec=1h
Persistent=true

[Install]
WantedBy=timers.target

Sur tous les hôtes :

# /etc/systemd/system/pacman-upgrade.service
[Unit]
Description=Upgrade packages
ConditionPathIsMountPoint=/var/lib/pacman/sync
ConditionPathIsMountPoint=/var/cache/pacman/pkg
After=network-online.target nss-lookup.target
Wants=network-online.target

[Service]
Type=oneshot
Nice=19
ExecStart=/usr/bin/pacman -Su --noconfirm --noprogressbar
Restart=no
# /etc/systemd/system/pacman-upgrade.timer
[Unit]
Description=Upgrade packages daily

[Timer]
OnCalendar=daily
AccuracySec=1h
Persistent=false

[Install]
WantedBy=timers.target

Installation Link to heading

L’installation de Kured est très simple. En utilisant son chart helm, le seul paramètre qu’il est nécessaire de renseigner est la commande que chaque pod du DaemonSet exécutera grâce à nsenter sur son hôte. Par défaut, Kured lancera le script toutes les heures.

helm upgrade \
  --install \
  --namespace kured-system \
  --create-namespace \
  --set configuration.rebootSentinelCommand=/usr/bin/kured-sentinel \
  kured kubereboot/kured

Pour aller plus loin Link to heading

Imaginez maintenant un monde parfait dans lequel toutes les applications sur vos clusters sont correctement configurées avec des sondes de liveness, readiness et startup, des règles d’affinité demandant le démarrage des différents réplicas sur différentes machines, répliquées quand c’est possible et tolerant les perturbations à l’aide de définitions de PodDisruptionBudget.

Les mises à jour des machines sont lancées dans des tâches planifiées et lorsque l’une d’elle nécessite un redémarrage, Kured lance le drain du nœud. Les PodDisruptionBudget permettent alors d’être certain que les applications restent disponibles ailleurs. Le nœud peut donc redémarrer puis, un outil comme descheduler pourrait répartir la charge préalablement déplacée sur les autres machines tout en utilisant les ressources de ce nœud fraichement redémarré. Kured préparerait ensuite le reboot des autres nœuds et voilà, vous auriez un cluster qui se maintiendrait à jour avec un minimum d’effort de monitoring de votre part et sans perturbation.