Blog ENI : Toute la veille numérique !
Accès illimité 24h/24 à tous nos livres & vidéos ! 
Découvrez la Bibliothèque Numérique ENI. Cliquez ici
Accès illimité 24h/24 à tous nos livres & vidéos ! 
Découvrez la Bibliothèque Numérique ENI. Cliquez ici
  1. Livres et vidéos
  2. Haute disponibilité sous Linux
  3. Mise en place d’un cluster Kubernetes
Extrait - Haute disponibilité sous Linux De l'infrastructure à l'orchestration de services (Heartbeat, Docker, Ansible, Kubernetes...) (2e édition)
Extraits du livre
Haute disponibilité sous Linux De l'infrastructure à l'orchestration de services (Heartbeat, Docker, Ansible, Kubernetes...) (2e édition) Revenir à la page d'achat du livre

Mise en place d’un cluster Kubernetes

Introduction

Nous disposons de presque tous les éléments techniques nous permettant de rendre notre application presque incassable. Avec Kubernetes, les étapes de maintien d’un inventaire et de déploiement via Ansible deviennent inutiles, l’orchestrateur effectuant toutes les opérations à notre place.

Mais Kubernetes n’est, lui-même, pas encore tolérant aux pannes : Minikube n’est pas un environnement de production. Il est temps de pallier ce problème.

À la rédaction de ce chapitre, la version courante de Kubernetes est la 1.29.3. Cependant, l’écosystème Kubernetes évoluant vite, référez-vous à la documentation officielle : https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/high-availability/.

Topologie

1. Composants de base

Il est important de rappeler quelques principes abordés dans le chapitre Orchestration. La mise en place du cluster est l’application pratique de toutes les notions abordées (orchestration, Raft, quorum, etc.) dans ce précédent chapitre.

Kubernetes est composé d’un plan de contrôle (control plane), et de nœuds (nodes, compute nodes ou workers). Les serveurs du control plane sont appelés control-planes ou masters et ce sont eux qui contrôlent les processus de décision, qui gèrent l’ensemble des événements. Nous emploierons par la suite l’un ou l’autre de ces noms pour ces serveurs. Ce sont donc eux qui contrôlent l’ensemble du cluster. Les masters et les nodes communiquent ensemble, et les applications sous forme de pods sont exécutées sur les nodes.

Un control-plane est composé de plusieurs services :

  • kube-apiserver : point d’entrée du cluster, il expose l’API de Kubernetes. Quand on a plusieurs masters, on les place derrière un répartiteur de charge.

  • etcd : base de données clé-valeur, elle contient la configuration du cluster. Elle est elle-même en haute disponibilité.

  • kube-scheduler : responsable de la planification, il sélectionne les nœuds sur lesquels exécuter les pods, en fonction des contraintes (processeur, mémoire, affinité...).

  • kube-controller-manager : il gère les contrôleurs (node, réplication, endpoints, etc.).

  • cloud-controller-manager : il interagit avec le fournisseur de cloud sous-jacent, le cas échéant. Il peut être désactivé.

Un nœud dispose aussi de services spécialisés :

  • kubelet : service chargé de s’assurer de l’exécution des pods, il instancie les containers, vérifie leur fonctionnement et leur santé, gère leur cycle de vie.

  • kube-proxy : ce proxy réseau permet l’implémentation et la gestion des services (au sens Kubernetes) au niveau du nœud, et donc la communication réseau vers les pods.

  • container runtime : c’est l’environnement d’exécution des containers, nous avons choisi Docker.

Avec Minikube, la machine où...

Préparation

1. Rappels

Les trois masters et les nœuds disposent des capacités suivantes suffisantes pour notre application. Si vous utilisez une distribution k8s comme OpenShift ou Rancher, il faudra certainement doubler, voire quadrupler les valeurs CPU et mémoire, compte tenu de l’ensemble des composants que ces distributions apportent :

  • 2 processeurs (vCPU) ;

  • 2 Go de mémoire ;

  • une racine / de 10 Go ;

  • un répertoire /var de 40 Go.

La configuration DNS est la suivante, incluant la VIP d’accès à l’API Kube :

  • 192.168.56.15 api.kube.diehard.net

  • 192.168.56.20 k8s-cplane01.diehard.net

  • 192.168.56.21 k8s-cplane02.diehard.net

  • 192.168.56.22 k8s-cplane03.diehard.net

  • 192.168.56.23 k8s-worker01.diehard.net

  • 192.168.56.24 k8s-worker02.diehard.net

  • 192.168.56.25 k8s-worker03.diehard.net

  • 192.168.56.26 k8s-worker04.diehard.net

Les serveurs sont tous installés sous Ubuntu 24.04 LTS. Toute trace précédente de Kubernetes doit être supprimée (le processus de suppression est décrit en fin de chapitre, sous-section Destruction d’un cluster). L’utilisateur Ansible et les droits sudo associés doivent être présents, et Ansible depuis infra01 doit pouvoir se connecter. Le résolveur DNS doit être configuré pour utiliser nos serveurs DNS primaires et secondaires.

2. HAProxy

Chaque master disposant du composant kube-apiserver, il peut répondre aux requêtes des clients et des nœuds, par défaut sur le port 6443. Nous devons configurer une VIP et placer les trois masters derrière un répartiteur de charge ou un reverse proxy. Nous allons utiliser les répartiteurs créés dans le chapitre Infrastructure et services de base.

La VIP est celle provisionnée dès le chapitre Infrastructure et services de base : 192.168.56.15. Nous écoutons sur le port 8443. Le backend est en mode TCP et le contrôle de santé est sur /healthz.

Une configuration peut être la suivante :

frontend apikube 
    bind 192.168.56.15:8443 
    mode tcp 
    option tcplog 
    default_backend kubemasters 
 
backend cplanes 
    option httpchk GET /healthz 
    http-check...

Construction

1. Images de Kubernetes

Si ce n’est pas encore fait, il faut s’assurer que le service kubelet est activé sur tous les serveurs.

$ ansible k8s -m shell --become -a "systemctl enable kubelet" 

La commande centrale de configuration initiale du cluster est kubeadm. Kubernetes lui-même fonctionne sous forme de pods, donc de containers. À la différence des applications que nous déploierons, c’est le service kubelet qui va les démarrer lors de son activation, de manière statique. Dans un premier temps, nous allons récupérer ces images. Ce n’est pas obligatoire (la création des masters le fait), mais nous allons ainsi nous assurer qu’elles sont bien présentes. 

 Sur chaque master, récupérez les images :

$ sudo kubeadm config images pull 

Vous pouvez utiliser une commande Ansible :

$ ansible k8s_cplanes -m shell --become -a "kubeadm config images pull" 

Le résultat est le suivant :

[config/images] Pulled registry.k8s.io/kube-apiserver:v1.29.3 
[config/images] Pulled registry.k8s.io/kube-controller-manager:v1.29.3 
[config/images] Pulled registry.k8s.io/kube-scheduler:v1.29.3 
[config/images] Pulled registry.k8s.io/kube-proxy:v1.29.3 
[config/images] Pulled registry.k8s.io/coredns/coredns:v1.11.1 
[config/images] Pulled registry.k8s.io/pause:3.9 
[config/images] Pulled registry.k8s.io/etcd:3.5.12-0 

2. Premier master

a. Bootstrap

Les commandes suivantes sont exécutées en tant que root.

Le premier master sera k8s-master01. Le paramètre init permet de créer le premier master du control plane.

 Créez le premier master avec les paramètres suivants :

# kubeadm init \ 
   --control-plane-endpoint"api.kube.diehard.net:8443" \ 
   --pod-network-cidr=10.244.0.0/16 \ 
   --upload-certs 

La sortie étant très longue, nous allons juste vérifier que l’initialisation s’est bien passée. Certains avertissements peuvent être ignorés (version de Docker par exemple). Les actions suivantes sont exécutées :

  • vérification de la version de Docker ;

  • récupération des images (étape déjà effectuée) ;

  • génération de l’ensemble...

Procédures additionnelles

1. Crash ou arrêt sale d’un node

Si on stoppe violemment un node, il peut se passer des choses étranges. Lorsque cela se produit, l’origine du problème n’est pas notifiée aux masters : ils ne savent pas pourquoi le node n’est plus visible. Doivent-ils redémarrer les pods manquants ? Après tout, le problème peut être transitoire : le node peut redémarrer rapidement et les pods associés peuvent donc être à nouveau accessibles… 

Aussi, un mécanisme de timeout est en place. Le node est détecté comme étant NotReady, et avec les réglages que nous avons appliqués lors de la création des masters, un mécanisme d’éviction des pods démarre après un délai variable, d’environ 30 secondes à 1 minute. Les pods qui tournaient sur le node planté passent en statut Terminating. Un délai de grâce s’applique alors, qui a été fixé à 30 secondes, à la fin duquel Kubernetes remplace le pod absent par un nouveau. À ce délai s’ajoutent le délai de détection du node en panne, le délai de redémarrage du nouveau pod (récupération de l’image et lancement), etc.

Ces délais mettent en évidence...