Guillaume Fenollar DevOps et SysAdmin Freelance

Guillaume FENOLLAR

Ingénieur Linux/DevOps Indépendant

− Montpellier −

Désactiver les effets d'un controller (deploy ou sts) sur ses pods

Un controller est le nom de plusieurs éléments différents dans Kubernetes. L'un de ces éléments désigne le nom global de toute ressource étant responsable entre autres de la création et rythme de vie des pods. Un controller typique est un deployment, qui sert à déployer des pods en plusieurs replicas mais se charge également de les détruire ou les recréer quand le besoin s'en fait ressentir. Les trois autres types de controllers les plus utilisés sont les StatefulSet, Daemon Set et les Jobs. On peut alors être amenés, quand on travaille avec des controllers, à vouloir les empêcher d'apporter des modifications à des pods de façon temporaire. Alors, comment qu'on fait ?

La solution la plus propre (STS uniquement)

Dans le cas d’un STS, nous avons une différence de taille par rapport à un déploiement, c’est la présence de Stratégies de Rolling Update, à la racine de spec, dont voici l’intégralité de l’API avec les valeurs par défaut:

spec:
  updateStrategy:
    rollingUpdate:
      partition: 0
    type: RollingUpdate

Ce qui nous intéresse est spec.updateStrategy.rollingUpdate.partition. Cette option n’est bien sûr disponible seulement si spec.updateStrategy.type est bien positionné à RollingUpdate. Elle permet de spécifier, dans un statefulset, au dessus de quel ordinal de pod les modifications apportées au controller seront propagées. En d’autre terme, étant par défaut à 0, tous les pods seront mis à jour lors d’une modification du statefulset. Si positionné à 1, alors tous les pods le seront sauf le premier pod dont le nom termine par -0.

Cela permet donc de faire du canary deployment à moindre frais, pour par exemple modifier l’image de seulement 30% des pods afin d’introduire tranquillement une mise à jour. La commande permettant de patcher un STS est la suivante

# Partitionnement du STS à 2, les pods 0 et 1 ne seront pas modifiés
sts=mysql
part=2
kubectl patch statefulset $sts -p '{"spec":{"updateStrategy":{"type":"RollingUpdate","rollingUpdate":{"partition":'$part'}}}}'

Dans le cas d’un canary deployment, on pourrait avant cela modifier l’image d’un conteneur dans le pod template, puis déployer petit à petit les modifications en patchant régulièrement le controller tout en abaissant la valeur de partition petit à petit.

À noter que cela ne va pas empêcher un pod de redémarrer s’il était supprimé, cela agit seulement sur les modification du pod template. Pour cet autre besoin, continuez votre lecture :)

La solution passe partout, mettre un controller dans le coma

Sans controller, pas de suppression ni recréation de pods contre son gré. Mais si on supprime un controller, les pods créés par celui-ci disparaissent, alors comment faire ? Il existe une option à la commande kubectl delete qui permet à un controller de ne pas éliminer les enfants (trop aimable), il s’agit du paramètre --cascade=false (car étant à true par défaut).

kubectl delete sts postgresql --cascade=false

À la suppression d’un controller de cette façon, Kubernetes supprime tout de même les objets ownerReferences de tous les pods enfants, mais c’est tout. Vous pouvez alors par exemple supprimer un pod, et monter son volume de données dans un nouveau pod pour une opération spéciale (par exemple un backup un peu évolué). Pour garder l’exemple du backup, cela peut aussi être utilisé pour éteindre à tour de rôle les pods d’un statefulset et prendre le temps de backuper les données à froid côté stockage.

Il convient par contre de bien penser à recréer le controller supprimé si l’on veut garder les pods en vie après l’opération. Voici un script bash qui peut vous donner les bases d’une bonne utilisation de cette méthode :

#!/bin/bash
# Script a lancer avec le type puis le nom du controller

set -eE

if [[ $# != 2 ]]; then
  echo Usage: $0 type controller
  echo Example: $0 sts mon-sts
  exit 1
fi

type=$1
name=$2

if ! kubectl get $type $name &>/dev/null ; then
  echo Unable to retrieve info about the $type controller $name
  exit 1
fi

function clean {
  echo Rollbacking controller objects...
  if [[ ! -z $STS ]]; then
    echo $STS | kubectl apply -f -
  fi
}

STS=$(kubectl get $type $name -ojson)

trap clean ERR SIGINT SIGQUIT EXIT

kubectl delete $type $name --cascade=false

echo Controller deleted. You can modify any pod created by the $type $name
echo Press any key when you are done to proceed and rollback controller $name
read

echo Operation ended gracefully

Ce script s’assure de recréer le controller tel quel une fois l’opération terminée ou si le script rencontre un problème, ou encore si l’utilisateur le tue prématurément (à part dans le cas d’un kill -9 bien entendu).