Étape 0: architecture et composants

Introduction

L’IT et les technologies évoluent en permanence. Il est temps pour moi de revoir mon cookbook Kubernetes pour proposer une nouvelle version des articles associés et afficher une nouvelle architecture.

Mon objectif reste toujours le même, à savoir proposer un guide d’installation de bout en bout d’une plateforme Kubernetes.

Cette nouvelle version va reprendre des éléments de l’ancienne tout en apportant parfois des modifications plus ou moins importantes en fonction des sujets traités.

On reste sur une logique d’industrialisation en cherchant à automatiser un maximum d’actions, mais en ayant toujours une bonne compréhension de ce que l’on fait et pourquoi on le fait.

Intégration de l’IA et déploiement sur XCP-ng

L’une des nouveautés est l’intégration de l’usage de l’IA pour l’aide à la conception et à la réalisation.

Aujourd’hui il serait aberrant de ne pas chercher à optimiser son travail via l’IA. Il ne faut pas s’en cacher, il faut l’assumer et surtout le faire correctement.

L’IA m’a d’ailleurs forcé à revoir mon architecture historique, en changeant certains de mes choix de l’époque : non pas parce qu’ils sont mauvais ou non adaptés, mais surtout parce que je peux me permettre aujourd’hui des actions que je n’aurais sans doute pas tentées à l’époque, désormais envisageables grâce à l’IA.

Ma réflexion et mon assistance se font avec Claude, c’est actuellement le service IA que j’utilise le plus, soit via l’interface Web ou via Claude Code.

Mon objectif n’est pas de laisser faire l’IA, mais de l’exploiter comme une ressource d’expertise supplémentaire sur laquelle je peux m’appuyer.

Une autre de mes volontés est de démontrer l'usage de XCP-ng et de l'écosystème français Vates sur lequel repose désormais mon lab pour déployer une plateforme Kubernetes complète.

Voici donc le menu de ce nouveau cookbook. Les liens seront mis à jour au fur et à mesure que je publie les articles.

  • Étape 0 : Description de l’architecture et des composants : l’étape décrite dans cet article destiné à présenter l’architecture cible et à détailler quelque peu certaines briques et certains choix techniques.
  • Étape 1 : Template packer : préparation de la VM template pour servir de base au déploiement de tous les autres serveurs. L’occasion ici de présenter l’outil Packer qui n’avait pas été mis en œuvre dans le précédent cookbook.
  • Étape 2 : Déploiement terraform : déploiement de toutes les VMs via Terraform selon la logique Infrastructure As Code.
  • Étape 3 : Déploiement de la configuration nécessaire exécutée via Ansible. L’occasion de mettre en place également les prérequis réseau pour le cluster K8S.
  • Étape 4 : Création du cluster via kubeadm avec ajout des principaux nodes et mise en place du CNI réseau.
  • Étape 5 : Déploiement de l’API gateway et de Traefik pour l'exposition des services à l'extérieur du cluster.
  • Étape 6 : Déploiement de Cert-Manager et des éléments propres à l’automatisation des certificats.
  • Étape 7 : Déploiement du CSI (Container Storage Interface) : NFS & SMB pour offrir des volumes persistants partagés.
  • Étape 8 : Déploiement de la solution de stockage Longhorn pour un stockage bloc destiné à des accès exclusifs.
  • Étape 9 : Déploiement de la solution de stockage Seaweed FS pour un stockage objet.
  • Étape 10 : Déploiement de CNPG pour la création simplifiée de bases PostgreSQL.
  • Étape 11 : Ajout d’un node physique au cluster pour profiter des ressources d’un GPU via les opérateurs NVIDIA.

Chaque étape sera l’occasion de rentrer un peu plus dans le détail concernant les brisques techniques associées. Certains points reprendront des éléments du précédent cookbook, d’autres seront totalement revus.

L’ancien cookbook restera en ligne au besoin.

L’objectif final est d’avoir un cluster Kubernetes opérationnel et capable d’exécuter différentes charges de travail avec un minimum de redondance pour une exploitation dans un environnement de production.

D’ailleurs voici le schéma de l’architecture retenue.

Schéma de l'architecture cible du cluster Kubernetes

Cliquez sur l'image pour l'agrandir.

Arborescence et logique retenue pour l’aide de l’IA

Afin de pouvoir concevoir le cookbook et en lien avec ce que j’ai expliqué plus haut sur l’IA, voici la logique appliquée pour l’assistance avec Claude Code.

L’assistant au codage d’Anthropic exploite différents fichiers de configuration afin de pouvoir davantage cadrer le travail demandé.

Le fichier de base à placer à la racine de ses projets est le fichier CLAUDE.md. Il vient résumer l’objectif global avec les consignes générales. J’y ai décrit mon projet avec les contraintes associées.

Dans le sous-répertoire docs, il y a trois autres fichiers Markdown.

  • ARCHITECTURE.md : description de l’architecture à mettre en place. C’est à ce niveau que je détaille les configurations matérielles et IP des serveurs à mettre en œuvre avec les grands choix d’architecture.
  • CONVENTIONS.md : tout ce qui concerne les normes de nommage et les standards à respecter. C'est très important dans un projet comme celui-ci d'adopter une logique de nomenclature, particulièrement pour ses fichiers décrivant les objets K8S.
  • ROADMAP.md : définition de la roadmap qui reprend en gros les étapes présentées plus haut. C’est ce fichier qui me permet de suivre l’avancement du projet en synchronisation avec Claude Code.

Ensuite, j’ai trois autres répertoires qui possèdent chacun leur CLAUDE.md :

  • xkub.coolcorp.priv.packer : dossier dédié à Packer. J’y place tous les fichiers propres à la solution et permettant de déployer la VM de références destinée à servir de template pour mes OS linux dont les nodes K8S.
  • xkub.coolcorp.priv.iac : dossier dédié à Terraform. J’y place tous les fichiers propres à la solution pour le déploiement des serveurs du cluster afin d’avoir une définition as a code de ces derniers.
  • xkub.coolcorp.priv.ansible : dossier dédié à Ansible. J’y place l’arborescence Ansible avec tous les playbook nécessaires à la configuration du cluster.

Pour résumer, voici l'arborescence complète mise en place pour cadrer Claude Code :

xkub.coolcorp.priv/
├── CLAUDE.md                           # Objectif global + consignes générales
├── docs/                               # Documentation de référence
│   ├── ARCHITECTURE.md                 # Matériel, IP, choix d'archi (D1–D5)
│   ├── CONVENTIONS.md                  # Nommage & standards (objets K8S)
│   └── ROADMAP.md                      # Suivi des étapes du cookbook
├── .claude/                            # Config Claude Code
│   └── agents/                         # Subagents spécialisés
│       ├── it-architect.md             # Architecte IT global
│       ├── packer-builder.md           # DevOps · Packer
│       ├── terraform-provisioner.md    # DevOps · Terraform
│       ├── ansible-engineer.md         # DevOps · Ansible
│       ├── k8s-platform-engineer.md    # Platform engineer K8S
│       ├── k8s-security-auditor.md     # Sécurité Kubernetes
│       └── app-deployment-tester.md    # Validation du cluster
├── xkub.coolcorp.priv.packer/          # Dossier Packer
│   └── CLAUDE.md                        # Cadrage Packer (VM template)
├── xkub.coolcorp.priv.iac/             # Dossier Terraform
│   └── CLAUDE.md                        # Cadrage Terraform (VMs cluster)
└── xkub.coolcorp.priv.ansible/         # Dossier Ansible
    └── CLAUDE.md                        # Cadrage Ansible (config cluster)

Arborescence des fichiers de configuration pour Claude Code (version texte).

Subagent

Pour compléter cette logique, j’ai également mis en œuvre des subagent, un élément de Claude Code qui va permettre de m’appuyer sur des agents spécifiques à un domaine et éviter de laisser Claude Code tout généraliser sous un seul agent. Ça consomme plus de tokens, mais ça permet souvent des sorties de meilleure qualité, l’IA reprenant de l’humain la difficulté à s’autoévaluer et à critiquer son propre travail. Séparer les workload sur des sous-agents différents permet un challenge interne bénéfique sur le résultat final.

J’ai donc placé dans .claude/agents les définitions suivantes :

  • it-architect.md : joue le rôle de l’architecte IT globale encadrant la conception et la cohérence de l’ensemble.
  • packer-builder.md : joue le rôle d’un DevOps spécialisé autour de packer. Il va s’assurer de la bonne syntaxe des fichiers packer et du respect des bonnes pratiques associées.
  • terraform-provisioner.md : Il s’agit d’un rôle de DevOps axé sur Terraform. Il se concentre sur la vérification de la syntaxe des fichiers Terraform et du respect des meilleures pratiques.
  • ansible-engineer.md : joue le rôle d’un DevVops spécialisé autour de Ansible. Il va s’assurer de la bonne syntaxe des fichiers ansible et du respect des bonnes pratiques associées.
  • k8s-platform-engineer.md : joue le rôle d’un platform engineer spécialisé sur Kubernetes. Il va s’assurer de la bonne configuration des composants Kubernetes.
  • k8s-security-auditor.md : joue le rôle d’un expert en cybersécurité autour de Kubernetes. Il va challenger la mise en œuvre du cluster pour s’assurer que les bonnes pratiques de sécurité soient respectées.
  • app-deployment-tester.md : joue un développeur en charge de valider la plateforme. Il pourra être appelé à différentes étapes pour s’assurer que rien n’est cassé et que le cluster est fonctionnel.

Il est fort possible que les choses évoluent au fur et à mesure des avancées et que d’autres éléments soient ajoutés. Je mettrais à jour l’article au besoin.

En résumé, mon approche globale pour l'usage de l'IA est la suivante:

Arborescence Claude Code du projet xkub.coolcorp.priv CLAUDE.md racine, dossier docs (ARCHITECTURE, CONVENTIONS, ROADMAP), dossier .claude/agents avec 7 subagents, et 3 dossiers d'outils Packer, Terraform et Ansible disposant chacun de leur CLAUDE.md. Racine du projet xkub.coolcorp.priv CLAUDE.md Objectif global + consignes générales docs/ documentation de référence ARCHITECTURE.md archi, matériel & IP CONVENTIONS.md nommage & standards K8S ROADMAP.md suivi des étapes xkub.coolcorp.priv.packer/ CLAUDE.md → Packer xkub.coolcorp.priv.iac/ CLAUDE.md → Terraform xkub.coolcorp.priv.ansible/ CLAUDE.md → Ansible .claude/agents/ it-architect.md architecte IT global packer-builder.md DevOps · Packer terraform-provisioner.md DevOps · Terraform ansible-engineer.md DevOps · Ansible k8s-platform-engineer.md platform engineer K8S k8s-security-auditor.md sécurité Kubernetes app-deployment-tester.md validation du cluster

Arborescence des fichiers de configuration pour Claude Code.

Points d’attention sur l’usage de l’IA

L’usage de l’IA ne doit pas se faire n’importe comment, et préparer son arborescence ainsi que tous les fichiers de configurations nécessaires pour encadrer l’IA sont très importants.

Il ne faut pas négliger cette phase et ne pas hésiter à utiliser l’IA elle-même pour concevoir ses Markdown. Itérer avec l’IA est une très bonne pratique.

À noter que, même si j’utilise l’IA pour m’aider à réaliser la plateforme décrite dans ce cookbook, ce dernier reste réalisable et compréhensif sans IA. Vous n’êtes pas obligé de recourir vous-même à l’IA pour comprendre et suivre les articles.

L’IA doit rester un outil. Vous devez toujours conserver la compréhension des choses et la capacité à faire par vous-même.

Introduction à Kubernetes

Plusieurs concepts propres à Kubernetes (ou K8S) seront exposés à travers cet article, mais pas forcément détaillés pour l’instant. Il est donc possible que certains principes paraissent confus si vous n’êtes pas déjà familiarisé avec l’écosystème Kubernetes. Ce n’est pas forcément gênant à ce stade.

Ce qu’il est important de savoir, c’est que Kubernetes repose sur un principe de modularité. Un cluster Kubernetes est composé de plusieurs serveurs, virtuel et/ou physique sur lesquelles s’exécutent des briques fonctionnelles chargées d’un rôle spécifique. Certaines de ces briques peuvent être redondantes, et sont plus ou moins critiques pour le bon fonctionnement du cluster. Elles peuvent également être customisées ou enrichies en fonction des besoins.

Rôles d'un cluster Kubernetes : control-planes et nodes

Cliquez sur l'image pour l'agrandir.

Interagir avec un cluster Kubernetes, c’est avant tout interagir avec une API. Cette API dispose de différentes branches dans lesquelles va se retrouver la définition de différents objets.

À chaque objet correspond un usage, et un certain nombre de propriétés, obligatoires et/ou facultatives. C’est la combinaison de tous ces objets manipulables à travers l’API qui va vous permettre d’exécuter vos applications.

Vous pourrez ainsi spécifier l’état souhaité pour vos ressources. Kubernetes s’assurera toujours que l’état d’exécution corresponde à vos attentes.

Kubernetes est un écosystème pouvant rapidement devenir complexe. L’exécution d’une application sous K8S est attachée à tout un ensemble d’objets décrit dans une API. Ces objets ont tous un rôle spécifique (la gestion du déploiement, la fourniture d’un espace de stockage, l’exposition d’un service…) et peuvent être dépendants les uns des autres pour fonctionner.

Je détaille davantage le rôle et l’usage de ces objets dans des articles dédiés, mais retenez que leurs manipulations se font principalement (mais pas uniquement) à travers des fichiers yaml (et bientôt kaml, un yaml modifié dédié K8S) dans lesquels on va retrouver un ensemble de propriétés décrivant les caractéristiques de l’objet ciblé.

Il est néanmoins nécessaire d’introduire la notion de pod. Je ne vais pas rentrer dans le détail à ce niveau, mais sachez que c’est l’objet de base sous Kubernetes permettant l’exécution d’un ou plusieurs conteneurs. N’hésitez pas à parcourir la définition de l’objet pour en savoir plus dès à présent.

Exemples d'objets Kubernetes pilotés par l'API et décrits en YAML

Cliquez sur l'image pour l'agrandir.

L’une des bonnes pratiques lorsqu’on démarre avec Kubernetes, c’est de retenir une nomenclature pour le nommage de ses fichiers et de ses objets. C’est pourquoi, dans l’usage de l’IA, j’ai fixé mes règles dans les consignes données aux agents via le fichier CONVENTIONS.md.

En l’occurrence, voici le standard que j’ai retenu

Objet Kubernetes

env-objet-app_rôle-net
env
dev · rec · prd — identique au namespace
objet
shortname k8s officiel — deploy · sts · ds · svc · cm · secret · pvc · netpol · sa…
app
application — identique au namespace
rôle
sous-composant ou tiers — redis · postgres · frontend · backend · api · bdd…
net
lan · dmz — identique au namespace

exemple  prd-deploy-myprivatelab_grafana-lan

Le - sépare les blocs, le _ relie l'application à son rôle.

Namespace applicatif

env-application-net
env
environnement — dev · rec · prd
application
nom de l'application déployée
net
zone réseau — lan · dmz

exemple  prd-myprivatelab-lan

Choix de kubeadm et de Kubernetes Vanilla

Kubernetes reste une solution modulaire dont chaque brique est disponible « à l’unité ». C’est à vous de les assembler et éventuellement de les modifier pour construire votre cluster. C’est un travail qui peut être fastidieux, c’est pourquoi il existe des « distribution » Kubernetes qui s’occuperont pour vous de déployer tout le nécessaire sur vos nœuds, virtuel ou physique. Ces « suites » sont proposées avec de nombreux outils associés, comme des GUI et des méthodes de mise à jour assistés. On peut citer OpenShift de RedHat, ou RKE2 de Rancher. Même s’il existe des variantes gratuites de ces solutions, on est plutôt sur des produits onéreux et dédiés aux entreprises. Une installation dite vanilla est justement un déploiement qui ne s’appuie pas sur une distribution, mais sur l’appel aux briques de base directement. Heureusement, ce type d’installation a bien évolué et est rendu de plus en plus facile, notamment grâce à Kubeadm.

Kubeadm est l’installateur officiel associé au projet K8S. Il va grandement simplifier le déploiement en préconfigurant pour vous toutes les briques nécessaire fonction d’éléments de configuration que vous lui aurez passée en argument. À noter que d’autres méthodes existent, comme kubespray qui s’appuie sur Ansible.

Me concernant, je vais également utiliser Ansible, mais à ma manière, principalement pour me faciliter la construction des fichiers de configuration du cluster. Enfin, sachez que des solutions managées existent chez les principaux cloud provider. Azure, AWS ou GCP pour ne citer que les plus connues, proposent des clusters sous gestion déployables en quelques clics.

Cela permet de vous concentrer sur vos applications, et non sur l’administration du cluster.

Distributions Kubernetes et solutions de déploiement

Cliquez sur l'image pour l'agrandir.

Pourquoi pas OpenShift ou d’autres distributions clef en main ?

Lors de la première version de mon cookbook, le choix d’un Vanilla comme cluster K8S pouvait laisser planer des doutes chez beaucoup, en raison du maintien en conditions opérationnel que cela peut impliquer.

Aujourd’hui, quand on évoque Kubernetes en entreprise, 9 fois sur 10 ont fini par parler d’OpenShift. Ce dernier est en train de devenir à la conteneurisation ce que VMware était à la virtualisation.

La solution de Redhat devient le choix par défaut de beaucoup de grosses structures qui ne veulent pas prendre le risque d’un usage de Kubernetes Vanilla.

Surtout que OpenShift intègre KubeVirt et propose donc également une alternative à VMware avec une réponse à la crise faisant suite au rachat de VMware par Broadcom, pour l’exécution des VMs.

OpenShift est donc en train de devenir la plateforme universelle VM/Conteneurs de plus en plus à la mode.

C’est une logique qui se comprend. Moi-même, j’ai beaucoup hésité à basculer sur OpenShift pour mon homelab, notamment via la version OpenSource OKD.

Mais là encore, l’IA m’a fait voir les choses un peu différemment et m’a renforcé dans mon choix d’un cluster Vanilla.

Openshift comme d’autres produits, notamment chez Rancher (K3S, RKE2) proposent de masquer la complexité d’un cluster Vanilla via une intégration clef en main du cluster et l’ajout de nombreuses couches de managements.

Mais cette fameuse complexité peut-être en partie remise en cause via l’IA…. Est-ce que le surcout d’une licence vous enfermant dans un certain écosystème (même si on reste dans l’univers K8S) ne vaut pas un abonnement à une solution vous simplifiant la création d’un cluster Vanilla ?

Si des compétences internes sont déjà là, est-ce qu’un renforcement de ces dernières via l’IA n’est pas suffisant ? Limitant ainsi l’obligation de dépendre d’un éditeur tiers et vous laissant la possibilité de customiser au mieux votre cluster pour vos besoins.

En effet, votre fournisseur d’IA devient alors votre dépendance principale. Compte tenu du marché actuel, rien ne garantit la stabilité des offres, encore moins de leurs prix. Cela dit, il serait judicieux d’en profiter tant que les offres sont attractives et de renforcer les compétences de l’ensemble des intervenants. De plus, la montée en puissance des modèles LLM libre rend de plus en plus pertinente la capacité à contester un modèle payant pour certains besoins. (n’hésitez pas à parcourir mon article sur les risques liés à l’utilisation de l’IA en entreprise.)

Si vous utilisez l’IA intelligemment pour surmonter vos craintes, diminuer les couts à l’entrée et accroître vos connaissances, vous pourriez en sortir gagnant. Si vous considérez l’IA comme un assistant qualifié offrant une expertise et des connaissances supplémentaires, vous n’aurez peut-être pas à payer de frais de licence aux éditeurs pour l’utilisation de produits packagés.

Bien sûr il demeure vos contraintes de production et pouvoir compter sur un support peut s'avérer précieux en cas d'incident. De mon côté je maintiens mon homelab en Vanilla, et si, demain, dans mon cadre professionnel, je dois travailler sur un cluster OpenShift, je conserverais tout de même le savoir-faire et la compétence sur Kubernetes: qui peut le plus peut le moins.

À noter que OpenShift est aussi réputé pour consommer par défaut un certain nombre de ressources avec une empreinte d’exécution importante du fait qu’il embarque beaucoup de composants pas toujours utiles pour des besoins basiques. Rancher dispose d'un catalogue plus léger à ce niveau.

Par contre, attention, si vous voyez l’IA comme une solution de facilité vous dédouanant du besoin de comprendre ce que vous faites, alors surtout ne vous lancez pas dans un cluster Vanilla.

Plus globalement, en univers professionnel, il n’est pas rare de croiser des mixes d’installation. Un ou plusieurs cluster Onprem, déployés via Kubeadm ou basés sur une distribution dédiée, OpenShift en tête, puis d’autres clusters dans le cloud, pour d’autres besoins et d’autres charges. C’est bien là l’une des plus grandes forces de Kubernetes. Peu importe la plateforme, dès lors qu’elle respecte les standards K8S, il devient possible de concevoir une infrastructure réellement hybride dans laquelle vous pouvez choisir à tout moment d’exécuter vos assets sur telle ou telle plateforme fonction de vos contraintes de performance, de disponibilité, de sécurité et de coût.

Choix d’OS pour les nodes

Précédemment, j’avais choisi PhotonOS, un OS Linux principalement maintenu par VMware sur une base Red Hat et spécialisé dans l’exécution de conteneurs. Il fait partie des OS minimalistes très en vogue pour monter des nœuds Kubernetes. Mais avec tout ce qui s’est passé du côté de VMware/Broadcom, je préfère éviter un maximum l’appel à des solutions en lien avec cet éditeur.

Choix de Rocky Linux comme OS pour les nodes du cluster Kubernetes

Cliquez sur l'image pour l'agrandir.

Je pourrais alors rester sur la logique de microOS et retenir Talos Linux. Un OS immuable pour Kubernetes permettant la création de clusters optimisé et très sécurisé.

Mais de la même manière que pour mon choix de Vanilla côté Kubernetes, je souhaite rester sur un OS « classique », quitte à le configurer et l’optimiser moi-même.

C’est pourquoi j’ai retenu Rocky Linux. Créé pour donner suite au changement de stratégie opéré par Red Hat sur CentOS, c’est un OS fiable et stable qui respecte la compatibilité binaire avec Red Hat OS. Comme Alma Linux, c’est une distribution multi-usage et orientée pro.

Là aussi l’intégration de l’IA dans ma réflexion, m’a poussé à retenir finalement un OS classique. Il se marie plus facilement avec d’autres outils que je pourrais avoir besoin d’utiliser, mais il entraine une baisse des optimisations et expose davantage à des problèmes de sécurité. Je reste cependant ouvert à plus de possibilités et à l’idée de tout unifier sous un seul OS de base. Et si je bloque sur la configuration d’un composant, l’IA peut être une source de dépannage.

Infrastructure d’exécution

Plateforme de virtualisation cible

Je commence progressivement à faire mon deuil sur VMware et ses solutions. Même si je reste persuadé de la qualité de leur produit et de l’efficacité de leur écosystème, leur politique commerciale et leur abandon d’une grande partie de leur clientèle m’ont poussé à m’orienter sur d’autres plateformes.

Je vais donc m’appuyer sur mon LAB sous XCP-ng, la solution d’hyperviseur de l’entreprise VATES que j’utilise maintenant depuis plus d’un an et que je ne cesse d’apprécier.

Ça sera aussi l’occasion pour moi de démontrer que ce type de plateforme puisse très bien se gérer via les outils d’automatisation du marché et devenir un moteur d’exécution parfaitement cohérent pour une production.

Choix de XCP-ng comme plateforme de virtualisation pour héberger le cluster Kubernetes

Cliquez sur l'image pour l'agrandir.

Runtime conteneurisation

Toujours dans un esprit modulaire, le moteur d’exécution des conteneurs est lui aussi au choix de l’utilisateur. C’est particulièrement vrai pour une installation Vanilla de Kubernetes.

L’univers de la conteneurisation a très largement évolué, et si, pendant un temps, Docker se retrouvait un peu partout, désignant tout et n’importe quoi, ce n’est plus le cas depuis longtemps. En effet, l’industrie s’est mise d’accord sur un standard via l’Open Container Initiative (OCI). Maintenant, les conteneurs sont exécutés par des environnements d’exécution qui se conforment à cette norme. Le plus connu et d’ailleurs à l’origine de la norme, c’est runc, le composant historiquement utilisé par Docker, « donné » à l’OCI.

runc est vraiment le composant le plus bas niveau. Par-dessus, on retrouve souvent containerd qui vient ajouter un environnement de management pilotable le plus souvent à travers une CLI comme celle de Docker.

Mais ce n’est pas fini, Kubernetes propose lui aussi son standard, le Container Runtime Interface (CRI). Celui-ci décrit comment Kubernetes s'interface avec un runtime et comment celui-ci doit se comporter pour être intégré avec K8S.

En résumé, on a un runtime au standard de l’OCI, additionné d’un environnement complémentaire et pilotable par Kubernetes car compatible avec CRI. Me concernant, je vais rester sur le classique containerd/runc.

En revanche, à partir de Rocky Linux 10 (et donc de Red Hat 10), runc a été remplacé sous ces OS par crun. Une réécriture de runc en C (au lieu de Go) pour une diminution de l’empreinte mémoire et diverses optimisations.

Vous pourriez retrouver les deux versions sur le terrain, mais, pour ce cookbook ce sera crun.

Le schéma ci-dessous, reprend ces principes ainsi que quelques éléments de stockage et de réseau dont on parlera plus tard.

Roadmap et standards de la conteneurisation

Cliquez sur l'image pour l'agrandir.

Traitement du réseau

Les binaires de bases de K8S sont centrés sur l’orchestration des conteneurs. Pour les fonctionnalités tierces, comme le moteur d’exécution des conteneurs, leur stockage ou leur communication entre eux, Kubernetes laisse le choix d’adopter les technologies de son choix. Ces briques annexes peuvent être confiées à des solutions tiers à partir du moment où elles respectent un certain standard et se plient à des normes communes. Concernant le réseau, Kubernetes propose la spécification CNI pour Container Network Interface. Différentes solutions ou “plugins” se basent sur CNI pour traiter la couche réseau au sein d’un cluster K8S. À chacun de choisir le plug-in qu’il souhaite en fonction des caractéristiques qu’il propose : protocole de routage pris en charge, possibilité d’exploiter des politiques d’accès…

Les modes évoluent, mais j’ai retenu Cilium, qui semble avoir les faveurs du marché depuis plusieurs années maintenant… d’ailleurs la société derrière ce projet open source a été acquise fin 2023 par Cisco… Le succès de Cilium semble en grande partie lié à son implémentation de eBPF (Extended Berkeley Packet Filter), une technologie lancée dans la branche 4.x du Kernel Linux. Je suis loin de comprendre exactement ce qui se cache derrière, mais concernant Cilium, cela lui apporte des capacités d’observabilité et de filtrage très avancées sur les flux réseau qui circulent au sein d’un cluster Kubernetes.

Autre débat qu’on peut retrouver au sein de la communauté K8S : faut-il utiliser des nœuds en DMZ ou doit-on dédier un cluster complet aux zones réseau exposées.

Pour moi, il n’y a pas de réponses justes ou fausses. Comme toujours tout dépend des contraintes que vous pourriez avoir, notamment au niveau de la cybersécurité.

De base, Kubernetes est dans une logique « tout passe ». Cependant, il existe de nombreux mécanismes pour isoler des ressources entre elles et segmenter son cluster en zones spécifiques et étanches.

Me concernant, je n’ai pas le luxe de créer davantage de clusters, et vais plutôt m’appuyer sur ces mécanismes de protection pour m’assurer qu’un asset en DMZ ne puisse pas discuter librement avec un asset du LAN en profitant du réseau interne au cluster.

Aparté: un ou plusieurs clusters ?

À noter que les modes ont évolué (et évolueront sans doute encore): on est passé de la tendance un cluster pour les gouverner tous à diviser pour mieux régner. La tendance à l'écriture de cet article est en effet de stopper l'usage de mégacluster au profit de multiples petits clusters Kubernetes spécialisés. Même si ce n'est pas mon choix ici, mon approche est compatible avec cette logique. Chercher à packager son déploiement de K8S Vanilla autorise par la suite le déploiement sur de multiples cibles de multiples clusters. Conformément à mon choix de ne pas utiliser de distribution Kubernetes existante, je crois à l’intérêt de concevoir sa propre distribution Kubernetes… Sans aller jusqu’au niveau d'intégration d'un OpenShift, je pense que concevoir son template de base de cluster K8S pour le redéployer au besoin est une bonne approche.

Traitement du stockage

Dans l’univers de la conteneurisation, il est d’usage de minimiser le besoin de stockage persistant. Un conteneur doit pouvoir s’arrêter et redémarrer sans devoir dépendre de données qui lui sont directement rattachées. Or, ce mode de fonctionnement n’est pas compatible avec tous les usages. Une base de données conteneurisée, par exemple, aura besoin de retrouver ses données. C’est pourquoi, dans K8S, on sépare la couche d’exécution de la couche de storage. Les deux se traitent via des objets différents et c’est la combinaison des deux qui permettent de concevoir une application. On va notamment parler de persistentvolumes (pv) pour décrire les objets en charge de l’hébergement des données.

Pour mon besoin, plusieurs types de volumes vont être mis en place à travers ce que l’on appelle des storageclasses (sc). Ces objets permettent de définir des espaces de stockage proposant certaines caractéristiques (performance, bloc ou filer…). Le cluster pourra, en fonction des exigences demandées, provisionner automatiquement un persistant volume dans l’une ou l’autre de ces storage classe. Ce provisionnement se fera à travers un troisième objet, appelé persistentvolumeclaims (pvc). Dans la logique, un utilisateur décrira son besoin de stockage dans un pvc, celui-ci sera évalué par le cluster qui proposera en retour un pv issu de la sc correspondant aux caractéristiques attendues.

L’idée n’est pas de rentrer dans le détail ici, mais voici un schéma qui décrit la logique.

Principe du stockage persistant sous Kubernetes (StorageClass, PV, PVC)

Cliquez sur l'image pour l'agrandir.

Une sc va la plupart du temps s’appuyer sur un CSI pour Container Storage Interface. Une sorte de driver proposé par un tiers et conforme aux normes imposées par le projet Kubernetes. Celui-ci va permettre à un cluster de piloter un fournisseur de stockage, comme une solution cloud, une baie spécifique ou un écosystème tiers disposant d’une solution de stockage. C’est un concept extrêmement puissant, puisqu’il permet de proposer, sans avoir à se soucier de la solution de stockage finale, plusieurs possibilités d’hébergement des données persistantes au sein du cluster, de manière totalement transparente. D’un côté, les équipes d’infrastructure peuvent choisir le fournisseur de leur choix, dès lors que celui-ci propose un driver CSI, et les développeurs pourront retenir la class de storage qui correspond à leur besoin (ou qu’on leur autorise) sans se soucier du reste. Si le fournisseur de stockage venait à changer, c’est un autre driver CSI qui serait déclaré dans la sc sans impacter les fichiers de déploiement.

Voici donc les sc proposées :

  • Une storage class prd-sc-longhorn-default-na : cette sc va s’appuyer sur le driver CSI de Longhorn pour permettre la création de pv directement au sein de disques locaux présents dans les nodes ou via la combinaison de plusieurs de ces disques pour créer un espace de stockage virtuel répartie entre plusieurs nodes. Il est fort possible également que je complète à un moment donné cette classe avec le CSI de XenOrchestra de Vates en cours de développement à l’heure où j’écris ces lignes. On va pouvoir fournir un stockage performant, orienté bloc, accessible à une cible à la fois en lecture/écriture. Dans mon cas, c’est typiquement cette sc que je vais utiliser pour des bases de données.
  • Une storage class prd-sc-nfs-default-na : cette sc va s’appuyer sur un NAS proposant un espace NFS. Les pv issus de cette sc pourront être exploités en lecture/écriture par plusieurs cibles à la fois. Dans mon cas, je vais m’en servir pour le contenu statique de mon site ou d’autres besoins de partage entre plusieurs assets.
  • Une storage class prd-sc-smb-default-na : cette sc va s’appuyer sur un NAS proposant un espace SMB. Comme pour NFS, les pv issus de cette sc pourront être exploités en lecture/écriture par plusieurs cibles à la fois. Je n’ai pas encore d’idée précise de l’usage, mais cela pourrait être intéressant quand des données doivent être partagées entre Kubernetes et un poste de travail Windows.

Un autre type de stockage sera exploité, un stockage dit objet. J’utilisais précédemment Minio, mais ce dernier a totalement changé de licence, rendant sa version gratuite et open source moins complète que la version payante. Comme beaucoup j’ai cherché des alternatives et j’ai retenu SeaweedFS (du moins un de mes collègues me l'a bien vendu :)).

Le stockage objet nous permettra d’obtenir une couche de compatibilité S3, l’API largement utilisée de AWS pour offrir un accès partagé à un stockage via une logique WEB. N’hésitez pas à parcourir mon article sur Minio au besoin pour cela, même si je ne préconise plus la solution, les explications généralisées de l’article reste valable.

Compositions du cluster

Mon cluster Kubernetes sera composé de trois nœuds control plane, 3 nœuds d’exécution en LAN et 2 nœuds d’exécution en DMZ.

À l’exception d’un node en LAN, tous les serveurs seront des VMs exécutés sur XCP-ng. Seul un node en LAN sera déployé directement sur un serveur physique sous Rocky Linux. Celui-ci étant muni d’un GPU dédié, il pourra être utilisé pour l’exécution de conteneurs nécessitant une ressource GPU.

HA des control plane

Les nœuds control plane sont essentiels au bon fonctionnement du cluster. Ils vont héberger le serveur API sans lequel il est impossible d’interagir avec le cluster et de nombreux autres rôles critiques. Ils vont aussi stocker la base clef/valeur etcd dans laquelle toute la configuration du cluster va se retrouver. (À noter que certains préfèrent utiliser des instances etcd dédiées hébergées en dehors des control plane, voire même utiliser d'autres moteurs de bases de données)

On voit beaucoup d’exemples où l’on exploite un seul master. C’est une très bonne chose pour se former ou pour traiter des environnements non critiques, mais sur des contraintes de production, on ne peut pas se contenter que d’un seul master. Si celui-ci venait à tomber, on impacterait l’interaction avec le Cluster. Avoir un master indisponible ne rend pas les applications déjà en œuvres sur le cluster inopérantes, par contre, il n’y a plus d’orchestration et il devient impossible d’opérer son cluster.

À savoir qu’on ne peut pas avoir deux masters pour offrir une solution de HA, car c’est insuffisant pour assurer un consensus sur l’état du cluster. Un troisième master est obligatoire pour permettre à l’ensemble des masters de se mettre d’accord entre eux.

Si maintenir un cluster de control plane vous fais peur, alors les solutions managées des cloud provider sont faites pour vous. Dans ces offres, ce n’est pas vous qui avez la charge des contrôles plane. Vous échangez juste avec l’API K8S, mais c’est au fournisseur de cloud de s’assurer du bon fonctionnement des nœuds.

Configuration des ressources

Concernant la configuration hardware des VMs hébergeant les différents rôles :

  • Pour les control plane, on sera sur 4vCPU et 6Go de ram, et 60 Go de disque.
  • Pour les worker, on sera sur 4vCPU et 6Go de ram, et 80 Go de disque.

Ces chiffres sont susceptibles d’évoluer en fonction de la charge du cluster, mais cela me laisse tout même de la marge pour un homelab.

Le bon provisionning de ressources reste un enjeu important sous Kubernetes et, par la suite, intégrer une bonne solution de monitoring est important.

Les control planes, notamment à cause de ETCD et même s’ils n’exécutent pas d’assets applicatifs à proprement dit, nécessite un minimum de ressources et ne doivent pas se retrouver sous contention.

Attention également aux disques locaux, dans mon cas, ils vont servir principalement à l’OS de base, mais Kubernetes est connu également pour écrire énormément d’événements et solliciter parfois de manière importante les disques des OS sur lesquels il est installé. L’usage de SSD performant est obligatoire à ce niveau, notamment sur les control planes.

Exposition de l'API

Comme indiqué juste au-dessus, le cluster disposera de trois control plane. Pour qu'ils assurent une haute disponibilité de l'API qu'ils hébergent, il est nécessaire de les exposer à travers une virtual IP (VIP).

Contrairement à mon précédent cookbook, c’est un composant Kubernetes kube-vip qui va gérer cette problématique.

kube-vip est un petit composant qui tourne dans le cluster lui-même, sous forme de static pod sur chacun des control planes. Son rôle : porter une VIP flottante dédiée à l'API Kubernetes. Basé sur ARP, cela impose que tous les control plane soit sur le même réseau L2. De mêmes kube-vip repose sur gestion actif/passive, ce qui implique qu'à l'instant T tout le trafic API sera porté par un seul des control plane.

Principe de kube-vip pour l'exposition de l'API Kubernetes

Cliquez sur l'image pour l'agrandir.

Exposition des applications

Si l’accès à l’API est porté par kube-vip, l’accès à mes applicatifs sera lui porté HAproxy via des serveurs dédiés. C'est une solution open source réputée et largement utilisée. Plusieurs HAproxy seront déployés pour couvrir les accès externes vers les applications hébergées au sein du cluster.

Encore une fois, il est important de retenir que Kubernetes reste sur une logique modulaire. De base, votre application est rendue accessible à travers des objets dédiés appelés des Services (svc). Ces derniers peuvent être de différents types, mais sont principalement faits pour une exposition de vos assets au sein du cluster.

Lorsqu’il s’agit d’exposer vos applications à l’extérieur du cluster, les fonctionnalités de bases restent limitées. Il est conseillé d’exploiter des outils annexes pour offrir davantage de paramètres.

Comme toujours avec Kubernetes, exposer ses apps en dehors du cluster passe par l’usage d’objets dédiés. Plusieurs stratégies sont possibles à ce niveau. Historiquement on s’appuyait sur des objets ingress maintenus par un ingress contrôler. C’était ma logique dans mon précédent cookbook. Mais, comme j’ai pu l’expliquer dans un article dédié, la cible moderne est d’utiliser l’API Gateway. Un projet complémentaire à K8S qui se propose de remplacer les ingress avec des objets plus modernes. Même via l’API Gateway, il faut retenir un outil tiers pour l’exposition des applications.

Me concernant, j’ai choisi la solution Traefik. C’est un produit que j’apprécie particulièrement.

Choix de traefik comme solution d'exposition des applications du cluster Kubernetes

Cliquez sur l'image pour l'agrandir.

Là où d’autres solutions comme NGINX existaient avant K8S pour ensuite se rendre compatibles avec ce dernier, Traefik a été conçu pour Kubernetes et plus globalement pour la conteneurisation (ne me faites pas dire que NGINX n’est pas bien).

Il peut se déployer de plusieurs manières sur un cluster.

  • Soit via l’enrichissement de l’API de K8S. Dans ce cas on parle de CustomResourceDefinition (CRD). Il s’agit d’ajouter aux objets de bases Kubernetes, de nouveaux objets propres à la solution déployée. Cela permet de tirer pleinement parti des capacités de Traefik, mais ne garantit pas de retrouver ces nouveaux objets sur des solutions Kubernetes managées ou associées à une distribution.
  • Soit via les objets classiques Ingress ou Gateway. C’est plutôt l’usage que je priorise pour rester dans le cadre de l’API de base de K8S (même si l’API Gateway n’est pas par défaut).
Principe d'accès aux applications via Traefik exposé par HAproxy

Cliquez sur l'image pour l'agrandir.

Conclusion

Il est possible que, pour quelqu'un n'ayant jamais manipulé Kubernetes, certains points restent encore flous. Il s'agissait ici de survoler rapidement quelques grands principes qui pourront être détaillés davantage dans les autres étapes. L'important est à minima d'avoir une idée des composants à déployer et de la cible à atteindre.