freedzhost.cloud

DevOps module 3 : infrastructure en code, Kubernetes et secrets

Niveau IntermédiaireTemps estimé 14 hÀ lire avant DevOps module 2 : chaînes CI/CD et conteneursUn mot inconnu ? Lexique

Quatorze séances pour arrêter de configurer à la main : des machines décrites dans des playbooks Ansible, une infrastructure dans des fichiers OpenTofu relus comme du code, une application menée par Kubernetes, et des secrets qui ne touchent jamais le dépôt. Le module se termine par hello-ops déployé de trois façons à partir du seul code.

Tout tourne sur votre machine : Ansible contre votre VM (ou une deuxième), OpenTofu qui pilote votre Docker local, Kubernetes dans Docker avec kind. Aucun compte cloud n'est nécessaire. Les adresses sont des plages de documentation (192.0.2.0/24, 198.51.100.0/24).

Jour 29 · 01/12 — L'infrastructure en code : pourquoi, et son histoire

Objectif : expliquer déclaratif, idempotent et dérive, et situer les principaux outils dans le temps.

Théorie 1/2 : animaux de compagnie et bétail

Un serveur configuré à la main, soigné quand il est malade et irremplaçable, est un animal de compagnie (pet) ; des serveurs construits à partir d'une description, remplacés plutôt que réparés, sont du bétail (cattle). L'image est attribuée à Bill Baker (Microsoft, milieu des années 2000) et a été popularisée par Randy Bias vers 2012. L'infrastructure en code (IaC) est cette description : des fichiers texte, versionnés dans Git, relus en pull request, appliqués par un outil. La machine correspond alors à un fichier, pas à un souvenir.

Un serveur tombe en panne. Dans l'approche « bétail », vous…

  1. vous connectez et le réparez à la main
  2. le remplacez par un nouveau construit à partir du code
Réponse

Reconstruire à partir de la description ; le code fait référence.

Théorie 2/2 : déclaratif, idempotent, dérive

Impératif : les étapes (« installe nginx, puis copie le fichier »). Déclaratif : l'état voulu (« nginx est installé, ce fichier a ce contenu ») ; l'outil trouve les étapes. Idempotent : l'appliquer deux fois donne le même résultat qu'une fois ; la seconde exécution ne change rien. Dérive (drift) : le système réel ne correspond plus au code, parce que quelqu'un l'a modifié à la main.

Un peu d'histoire : CFEngine (Mark Burgess, 1993), Puppet (2005), Chef (2009), Salt (2011), Ansible (Michael DeHaan, 2012, racheté par Red Hat en 2015) gèrent ce qu'il y a sur les machines ; Terraform (HashiCorp, 2014) et son fork libre OpenTofu gèrent les machines, les réseaux et les services eux-mêmes.

Vous lancez deux fois le même playbook ; la seconde fois, il annonce 0 changement. Le playbook est…

  1. idempotent
  2. cassé
Réponse

Idempotent : l'état était déjà celui voulu.

Comment appelle-t-on l'écart entre le code et le système réel après des changements manuels ?

Réponse

La dérive (drift).

echo "line" >> /etc/app.conf lancé deux fois est…

  1. idempotent
  2. non idempotent : la ligne est ajoutée deux fois
Réponse

Ajouter n'est pas idempotent ; « cette ligne est présente », si.

Pratique (30 min)

  1. Listez dans votre carnet tout ce que vous avez fait à la main sur votre VM depuis le jour 1 (paquets, utilisateurs, fichiers, services). Cette liste, c'est ce que l'IaC décrirait.
  2. Réécrivez trois de ces éléments en états voulus (« le paquet X est présent »), pas en étapes.
  3. Installez Ansible comme le recommande le guide d'installation (par exemple pipx install --include-deps ansible), et vérifiez ansible --version.

Avec votre IA : « Donne-moi huit commandes shell une par une et demande-moi, pour chacune, si elle est idempotente et comment la rendre idempotente si elle ne l'est pas. Corrige-moi en une phrase à chaque fois. Commence par mkdir, echo >>, useradd et sed -i. »

Synthèse (10 min)

Trois lignes : animaux et bétail, déclaratif et idempotent, dérive. Puis placez CFEngine, Puppet, Ansible et Terraform sur une frise de mémoire.

Jour 30 · 02/12 — Ansible 1 : inventaire et commandes ad hoc

Objectif : joindre des machines avec Ansible et y exécuter des modules.

Théorie 1/2 : sans agent, par SSH

Ansible n'installe rien sur les cibles : il se connecte en SSH (jour 9), copie de petits programmes Python appelés modules, les exécute et lit leur résultat. L'inventaire liste les machines, par groupes :

# inventory.ini
[web]
web1 ansible_host=192.0.2.21

[local]
localhost ansible_connection=local

De quoi Ansible a-t-il besoin sur une machine cible ?

  1. Un agent Ansible en marche
  2. SSH et Python
Réponse

Sans agent : un accès SSH et Python sur la cible.

Théorie 2/2 : modules et commandes ad hoc

Une commande ad hoc lance un module sur un groupe, sans fichier. Les modules sont nommés par collection : ansible.builtin.ping, ansible.builtin.apt, ansible.builtin.copy… Chacun répond ok (rien à faire) ou changed.

ansible all -i inventory.ini -m ansible.builtin.ping
ansible local -i inventory.ini -m ansible.builtin.setup -a "filter=ansible_distribution*"
ansible local -i inventory.ini -b -m ansible.builtin.apt -a "name=tree state=present"

-b (« become ») passe par sudo ; -K demande son mot de passe.

Quelle option fait tourner une commande Ansible avec élévation de privilèges ?

Réponse

-b (--become).

Vous lancez deux fois la commande apt ci-dessus. La seconde fois, elle répond…

  1. ok, car tree est déjà présent
  2. changed à nouveau
Réponse

state=present est un état voulu : rien à faire la seconde fois.

Pratique (30 min)

  1. Créez ~/lab/ansible/inventory.ini avec un groupe local (et un groupe web si vous avez une deuxième VM joignable par clé SSH).
  2. Lancez ping, puis setup avec le filtre : notez la distribution que voit Ansible.
  3. Installez tree avec le module apt et -b -K ; relancez et comparez changed et ok.
  4. Retirez-le avec state=absent.
  5. Lisez ansible-doc ansible.builtin.apt et notez deux options que vous ne connaissiez pas.

Avec votre IA : « J'apprends les commandes ad hoc d'Ansible. Donne-moi six tâches (voir l'uptime, créer un dossier, installer un paquet, copier un fichier, redémarrer un service, collecter les faits), une par une ; je réponds par la commande, tu vérifies le nom du module et les arguments par rapport à ansible.builtin et tu me dis si je me suis trompé. »

Le faire faire par un agent : un agent peut lancer des vérifications ad hoc sur tout un inventaire et résumer (occupation disque, version du noyau). Demandez-lui de montrer chaque commande. Vous vérifiez encore un hôte à la main, et ne le laissez jamais lancer -b avec un changement d'état sans avoir lu la commande.

Synthèse (10 min)

Trois lignes : sans agent par SSH, l'inventaire, ok ou changed. Puis refaites les commandes ping et apt sans notes.

Jour 31 · 03/12 — Ansible 2 : playbooks, idempotence, handlers

Objectif : écrire un playbook qui prépare un hôte pour hello-ops, et prévoir son effet avant de l'appliquer.

Théorie 1/2 : un playbook

# site.yml
- name: Prepare an app host
  hosts: local
  become: true
  tasks:
    - name: Install packages
      ansible.builtin.apt:
        name: [curl, ca-certificates]
        state: present
        update_cache: true

    - name: Create the app user
      ansible.builtin.user:
        name: app
        system: true
        create_home: false

    - name: Create the app folder
      ansible.builtin.file:
        path: /srv/app
        state: directory
        owner: app
        mode: "0750"

Un play vise des hôtes ; ses tâches appellent des modules dans l'ordre. Lancez-le avec ansible-playbook -i inventory.ini site.yml -K ; le récapitulatif final compte ok, changed, failed.

Pourquoi mettre "0750" entre guillemets ?

  1. Pour le style
  2. Sans guillemets, YAML peut le lire comme un nombre et le mode sort faux
Réponse

Donnez les modes en chaînes entre guillemets ; la documentation d'Ansible le recommande.

Théorie 2/2 : check, diff, handlers

--check simule (rien n'est changé) et --diff montre ce qui changerait dans les fichiers : lancez les deux avant chaque application réelle. Un handler ne s'exécute que si une tâche l'a notifié (notify) et que cette tâche a changé quelque chose : redémarrer un service seulement si sa configuration a changé.

    - name: Install the unit file
      ansible.builtin.copy:
        src: hello-ops.service
        dest: /etc/systemd/system/hello-ops.service
        mode: "0644"
      notify: Restart hello-ops
  handlers:
    - name: Restart hello-ops
      ansible.builtin.systemd_service:
        name: hello-ops
        state: restarted
        daemon_reload: true

Quelles deux options prévisualisent les changements d'un playbook sans les appliquer ?

Réponse

--check --diff.

Le fichier d'unité est inchangé à cette exécution. Le handler redémarre-t-il le service ?

  1. Non, la tâche a répondu ok, pas changed
  2. Oui, à chaque exécution
Réponse

Les handlers ne se déclenchent que sur un changement.

Pratique (30 min)

  1. Écrivez site.yml avec les trois tâches ; lancez-le avec --check --diff, puis pour de vrai, puis une seconde fois : le second récapitulatif affiche changed=0.
  2. Ajoutez une unité hello-ops.service qui lance python3 /srv/app/app.py sous l'utilisateur app, une tâche qui copie app.py dans /srv/app, la tâche de l'unité avec son handler, et une tâche qui active et démarre le service.
  3. Appliquez ; curl http://127.0.0.1:8080/health. Changez la description de l'unité, appliquez avec --diff : le handler se déclenche une fois.
  4. Commitez le playbook dans un nouveau dépôt hello-ops-infra.

Avec votre IA : « Relis ce playbook Ansible pour l'idempotence et la sécurité : [collez site.yml]. Pour chaque tâche, dis si une seconde exécution répondrait changed et pourquoi. Signale tout module shell ou command qui pourrait être remplacé par un vrai module. Réponds par un tableau. »

Le faire faire par un agent : les agents écrivent vite des playbooks, souvent avec des tâches shell: non idempotentes. Consigne : « Ajoute une tâche qui installe et active fail2ban avec les seuls modules ansible.builtin ; pas de module shell ni command ; lance avec --check --diff et montre-moi la sortie. » Vous vérifiez encore : lancez-le deux fois pour de vrai et lisez le second récapitulatif (changed=0).

Synthèse (10 min)

Trois lignes : play et tâches, check et diff, handlers. Puis réécrivez le bloc handler de mémoire.

Jour 32 · 04/12 — Ansible 3 : variables, modèles, rôles

Objectif : transformer le playbook en rôle réutilisable avec un modèle et des variables, et l'analyser.

Théorie 1/2 : variables et modèles Jinja2

Les variables se posent dans l'inventaire, dans group_vars/, dans les defaults/ d'un rôle, ou en ligne de commande ; leur ordre de priorité est documenté, et les valeurs par défaut d'un rôle sont les plus faibles, donc faciles à surcharger. Un modèle (template) est un fichier avec des emplacements Jinja2, remplis par ansible.builtin.template :

# templates/hello-ops.service.j2
[Service]
User={{ app_user }}
Environment=PORT={{ app_port }}
ExecStart=/usr/bin/python3 {{ app_dir }}/app.py

Quel module produit un fichier Jinja2 sur la cible ?

Réponse

ansible.builtin.template.

Théorie 2/2 : rôles, Galaxy, analyse

Un rôle regroupe tâches, handlers, modèles, fichiers et valeurs par défaut dans une arborescence fixe (ansible-galaxy role init hello_ops la crée), si bien qu'un playbook dit seulement roles: [hello_ops]. Ansible Galaxy partage rôles et collections ; lisez-en un avant de l'utiliser, comme tout code tiers. ansible-lint vérifie les playbooks selon les bonnes pratiques (noms de modules complets, tâches nommées, pas de shell inutile).

Où mettre une variable de rôle qu'on doit pouvoir surcharger facilement ?

  1. vars/main.yml
  2. defaults/main.yml
Réponse

Les valeurs par défaut ont la plus basse priorité : tout le reste les surcharge.

ansible-lint signale fqcn sur apt:. Que veut-il ?

  1. Le nom complet ansible.builtin.apt
  2. Un autre paquet
Réponse

Les noms de collection complets lèvent toute ambiguïté.

Pratique (30 min)

  1. ansible-galaxy role init roles/hello_ops ; déplacez-y vos tâches, votre handler et votre unité ; transformez l'unité en modèle ci-dessus, avec app_user, app_port, app_dir dans defaults/main.yml.
  2. Réduisez site.yml au play et à roles: [hello_ops] ; appliquez ; puis surchargez app_port=8090 avec -e et vérifiez avec curl.
  3. Installez et lancez ansible-lint ; corrigez chaque constat.

Avec votre IA : « Explique la priorité des variables Ansible avec mon cas : app_port est dans roles/hello_ops/defaults/main.yml (8080), dans group_vars/local.yml (8085) et passé avec -e (8090). Quelle valeur gagne et pourquoi, et où mettrais-tu chaque type de variable ? Indique-moi la page de documentation pour vérifier. »

Le faire faire par un agent : transformer un playbook à plat en rôle est un travail mécanique que les agents font bien. Consigne : « Transforme site.yml en rôle hello_ops avec une valeur par défaut pour chaque valeur littérale ; lance ansible-lint et un --check --diff ; montre les deux sorties. » Vous vérifiez encore que la seconde exécution réelle affiche changed=0 et qu'aucune valeur n'a bougé en douce.

Synthèse (10 min)

Trois lignes : les modèles, l'arborescence d'un rôle, ansible-lint. Puis réécrivez le modèle de mémoire. (L'article du site Reconstruire ses machines d'une commande montre un vrai rôle de poste de travail.)

Jour 33 · 05/12 — OpenTofu 1 : providers, plan, apply, état

Objectif : décrire un conteneur en OpenTofu, le prévisualiser, le créer et le détruire.

Théorie 1/2 : Terraform, le changement de licence et OpenTofu

HashiCorp a publié Terraform en 2014 sous licence libre (MPL 2.0). En août 2023, l'entreprise est passée à la Business Source License, qui n'est pas une licence libre. Une partie de la communauté a forké la dernière version libre sous le nom d'OpenTofu, sous l'égide de la Linux Foundation ; sa première version stable, la 1.6.0, est sortie en janvier 2024, et le projet a rejoint la CNCF en 2025. Le langage (HCL) et la façon de travailler sont les mêmes ; la commande est tofu au lieu de terraform. Ce parcours utilise OpenTofu.

Pourquoi OpenTofu existe-t-il ?

  1. Terraform a cessé de fonctionner
  2. La licence de Terraform est devenue non libre en 2023, et la communauté a forké la dernière version libre
Réponse

Le passage à la BSL d'août 2023.

Théorie 2/2 : providers, ressources, plan, état

Un provider est un greffon qui parle à une API (un cloud, un DNS, Docker…) ; une ressource est un objet qu'il gère. Le cycle : tofu init (télécharger les providers), tofu plan (comparer le code à la réalité et montrer ce qui sera créé +, modifié ~, détruit -), tofu apply, tofu destroy. OpenTofu se souvient de ce qu'il a créé dans un fichier d'état, terraform.tfstate : il peut contenir des secrets, il ne va donc jamais dans Git.

terraform {
  required_providers {
    docker = {
      source  = "kreuzwerker/docker"
      version = "~> 4.0"
    }
  }
}

provider "docker" {}

resource "docker_image" "app" {
  name         = "hello-ops:dev"
  keep_locally = true
}

resource "docker_container" "app" {
  name  = "hello-ops"
  image = docker_image.app.image_id
  ports {
    internal = 8080
    external = 8080
  }
}

Quelle commande montre ce qui changerait, sans rien changer ?

Réponse

tofu plan.

Faut-il commiter terraform.tfstate ?

  1. Non : il peut contenir des secrets, et sa place est dans un stockage protégé
  2. Oui, comme le code
Réponse

Jamais dans Git ; ajoutez *.tfstate* et .terraform/ au .gitignore.

Pratique (30 min)

  1. Installez OpenTofu en suivant le guide officiel ; tofu version.
  2. Dans hello-ops-infra/tofu/, écrivez le main.tf ci-dessus (vérifiez la version actuelle du provider dans le registre OpenTofu).
  3. tofu init, tofu plan (lisez chaque +), tofu apply, curl http://127.0.0.1:8080/health.
  4. Passez le port externe à 8081, tofu plan : mise à jour sur place ou remplacement ? Appliquez, puis tofu destroy.
  5. Créez un .gitignore avec .terraform/ et *.tfstate*, et commitez (le fichier .terraform.lock.hcl, lui, se commite).

Avec votre IA : « Voici la sortie de tofu plan : [collez-la]. Explique chaque symbole et chaque ligne de ressource, puis dis-moi quel changement force un remplacement et pourquoi. En moins de 15 lignes. »

Le faire faire par un agent : un agent peut écrire le HCL d'une ressource que vous décrivez. Vous lisez encore chaque ligne de tofu plan avant tout apply : un agent ne doit jamais lancer apply ou destroy sans surveillance sur une infrastructure qui compte.

Synthèse (10 min)

Trois lignes : pourquoi OpenTofu, init / plan / apply / destroy, ce qu'est l'état. Puis réécrivez la ressource conteneur de mémoire.

Jour 34 · 06/12 — OpenTofu 2 : variables, sorties, modules, état distant

Objectif : paramétrer la configuration, l'empaqueter en module, et savoir où vit l'état dans une équipe.

Théorie 1/2 : variables, sorties, modules

variable "image_tag" {
  type        = string
  description = "Image tag to deploy"
  default     = "dev"
}

output "url" {
  value = "http://127.0.0.1:${docker_container.app.ports[0].external}"
}

Les valeurs viennent de -var, d'un fichier *.tfvars ou de TF_VAR_image_tag. Un module est un dossier de fichiers .tf appelé par module "nom" { source = "./modules/app" … } : le même code déploie la préproduction et la production avec des variables différentes.

Comment passer image_tag en ligne de commande ?

  1. tofu apply image_tag=1.0
  2. tofu apply -var image_tag=1.0
Réponse

-var nom=valeur.

Théorie 2/2 : l'état dans une équipe

Deux personnes qui appliquent avec deux fichiers d'état locaux faussent mutuellement leur image de la réalité. Les équipes gardent l'état dans un backend distant (un stockage objet, une base, le backend d'une forge) avec verrouillage, pour qu'un seul apply tourne à la fois. OpenTofu peut aussi chiffrer lui-même les fichiers d'état et de plan (depuis la version 1.7), ce que Terraform ne propose pas.

Qu'empêche le verrouillage de l'état ?

  1. Deux apply qui modifient la même infrastructure en même temps
  2. La lecture du code
Réponse

Les écritures simultanées dans l'état.

Quelle commande affiche les sorties après un apply ?

Réponse

tofu output.

Pratique (30 min)

  1. Ajoutez les variables image_tag et external_port et la sortie url ; appliquez avec -var external_port=8082 ; tofu output url.
  2. Déplacez l'image et le conteneur dans modules/app/, et appelez le module deux fois depuis la racine (staging sur 8083, prod sur 8084, noms de conteneurs différents). Plan, apply, curl des deux, destroy.
  3. Lisez la page sur le chiffrement de l'état et notez dans votre carnet comment fournir une clé sans l'écrire dans un fichier.

Avec votre IA : « J'ai un module OpenTofu déployé deux fois (staging et prod) depuis la même racine. Compare trois façons de séparer les environnements (deux appels de module, les workspaces, deux dossiers racines) dans un tableau : isolation de l'état, risque d'appliquer au mauvais, duplication. Signale ce dont tu n'es pas sûr. »

Le faire faire par un agent : un agent peut extraire un module d'une configuration à plat. Consigne : « Déplace ces ressources dans modules/app sans changer le comportement ; lance tofu fmt, validate et plan ; le plan ne doit montrer que des déplacements, aucune destruction. » Vous lisez encore le plan : une refonte qui détruit et recrée n'est pas une refonte (utilisez des blocs moved).

Synthèse (10 min)

Trois lignes : variables et sorties, modules, état distant avec verrouillage. Puis réécrivez le bloc variable de mémoire.

Jour 35 · 07/12 — L'infrastructure en code dans la chaîne, et prompter pour elle

Objectif : vérifier le code d'infrastructure en CI comme le code applicatif, et écrire pour l'IaC un prompt que vous pouvez vérifier.

Théorie 1/3 : la chaîne de l'IaC

Sur une pull request : tofu fmt -check (format), tofu validate (syntaxe et références), tofu plan (le plan est publié pour le relecteur), ansible-lint et une exécution --check. À la fusion sur main : apply, avec une approbation humaine pour la production (un environnement avec relecteurs obligatoires sur GitHub). Un tofu plan planifié qui devrait afficher « No changes » détecte la dérive.

Un tofu plan nocturne montre un changement que personne n'a commité. Qu'est-ce ?

  1. Un bogue d'OpenTofu
  2. Une nouvelle fonctionnalité
  3. De la dérive : quelqu'un a modifié l'infrastructure à la main
Réponse

La dérive. Ramenez le changement dans le code, ou annulez-le.

Quelle commande échoue si les fichiers ne sont pas formatés ?

Réponse

tofu fmt -check.

Théorie 2/3 : prompter pour du code d'infrastructure

Un prompt faible : « écris du terraform pour un serveur web ». Ce qui ne va pas : pas d'outil (Terraform ou OpenTofu, quelle version ?), pas de provider, pas d'environnement, aucune règle sur ce qui peut être détruit, aucun moyen de vérifier. Le modèle choisira un cloud que vous n'utilisez pas, une version de provider sortie de sa mémoire, et peut-être un groupe de sécurité ouvert au monde.

Le prompt retravaillé :

Rôle : tu es un ingénieur infrastructure qui relit pour la sécurité.
Contexte : OpenTofu (tofu version affiche la version), provider
kreuzwerker/docker ~> 4.0, Docker local, module dans modules/app.
Tâche : ajoute une variable "replicas" (nombre, 1 par défaut) et crée
autant de conteneurs nommés hello-ops-0, hello-ops-1..., ports 8080 + index.
Contraintes : aucun nouveau provider ; aucun nom existant ne change,
le plan ne doit montrer aucune destruction ; tofu fmt propre ;
descriptions sur les variables.
Entrées : modules/app/main.tf et variables.tf : [coller les deux].
Sortie : les fichiers modifiés en entier, puis le résumé attendu de tofu plan.
Vérification : je lancerai tofu fmt -check, tofu validate et tofu plan,
et je comparerai le plan à ton résumé attendu.

Pourquoi demander au modèle « le résumé attendu du plan » ?

  1. Pour allonger la réponse
  2. Pour le comparer au vrai tofu plan et repérer un malentendu
Réponse

Une prédiction vérifiable par l'outil est un test de la réponse.

Théorie 3/3 : corrigez ce prompt

« Écris un playbook Ansible pour sécuriser mon serveur. » Quel ajout compte le plus ?

  1. La distribution et sa version, les mesures exactes voulues (clés SSH seulement, règles de pare-feu), « modules ansible.builtin seulement, idempotent », et « je le lancerai d'abord avec --check --diff »
  2. « Tu es le meilleur expert en sécurité du monde »
Réponse

Contexte, contraintes et vérification ; les rôles flatteurs n'apportent rien.

Pratique (30 min)

  1. Dans hello-ops-infra, ajoutez un workflow qui tourne sur les pull requests : tofu fmt -check, tofu init -backend=false, tofu validate, et ansible-lint. Installez OpenTofu dans le job comme le dit le guide d'installation pour la CI, ou utilisez son image de conteneur officielle.
  2. Ouvrez une pull request avec un fichier .tf mal formaté : la vérification échoue. Corrigez avec tofu fmt.
  3. Envoyez le prompt retravaillé ci-dessus avec vos vrais fichiers ; lancez fmt, validate et plan sur la réponse ; comparez le plan à sa prédiction.

Avec votre IA : « Voici mon prompt faible : "écris un playbook qui déploie mon appli". Réécris-le avec rôle, contexte, tâche, contraintes, entrées, format de sortie et vérification, pour ma vraie configuration : Debian 13, rôle hello_ops, app.py dans /srv/app, systemd. Puis explique chaque ligne que tu as ajoutée. »

Le faire faire par un agent : un agent peut ouvrir lui-même les pull requests d'IaC, et même commenter le plan. Vous relisez encore le plan et approuvez l'apply : l'étape d'approbation existe justement pour qu'un humain lise ce qui va changer.

Synthèse (10 min)

Trois lignes : ce qui tourne sur une pull request, ce qui tourne à la fusion, comment on détecte la dérive ; et les parties d'un prompt d'IaC. Puis réécrivez correctement le prompt faible du serveur web.

Jour 36 · 08/12 — Kubernetes 1 : pourquoi orchestrer, l'architecture

Objectif : expliquer ce que fait un orchestrateur et nommer les parties d'un cluster Kubernetes.

Théorie 1/2 : de Compose à un orchestrateur

Compose lance des conteneurs sur une machine. Avec beaucoup de machines, il faut quelque chose qui place les conteneurs, les relance quand ils meurent, remplace un nœud en panne, répartit le trafic et déroule les mises à jour : un orchestrateur. Google en utilisait un en interne, Borg ; il a annoncé Kubernetes (« timonier » en grec) en juin 2014, publié la 1.0 en juillet 2015 et l'a confié à la toute nouvelle Cloud Native Computing Foundation (CNCF), dont il a été le premier projet diplômé (2018). On écrit souvent k8s.

Quel système interne de Google a inspiré Kubernetes ?

  1. MapReduce
  2. Borg
Réponse

Borg.

Théorie 2/2 : état voulu et architecture

Vous déclarez un état voulu (trois copies de cette image) ; des contrôleurs le comparent à la réalité, sans fin, et agissent pour combler l'écart. Le plan de contrôle : le serveur d'API (tout passe par lui, kubectl compris), etcd (la base clé-valeur qui garde l'état), l'ordonnanceur (scheduler, choisit un nœud pour chaque Pod), le gestionnaire de contrôleurs. Chaque nœud fait tourner le kubelet (démarre les Pods que demande l'API, via un environnement d'exécution comme containerd) et kube-proxy (le réseau des services).

Quel composant stocke l'état du cluster ?

Réponse

etcd.

Un nœud meurt avec deux Pods d'un Deployment à trois réplicas. Que se passe-t-il ?

  1. Le contrôleur voit un réplica au lieu de trois et en crée deux sur d'autres nœuds
  2. Rien jusqu'à l'intervention d'un administrateur
Réponse

La boucle de réconciliation rétablit l'état voulu.

Quel composant choisit le nœud où tourne un nouveau Pod ?

  1. le kubelet
  2. etcd
  3. l'ordonnanceur
Réponse

L'ordonnanceur ; le kubelet le démarre ensuite.

Pratique (30 min)

  1. Dessinez l'architecture dans votre carnet : plan de contrôle (serveur d'API, etcd, ordonnanceur, gestionnaire de contrôleurs), deux nœuds (kubelet, kube-proxy, environnement d'exécution, Pods), et kubectl qui parle au serveur d'API.
  2. Faites le premier module du tutoriel officiel Les bases de Kubernetes.
  3. Installez kubectl (guide) et kind ; vérifiez kubectl version --client et kind version.

Avec votre IA : « Interroge-moi sur l'architecture de Kubernetes : décris huit événements (kubectl apply, une panne de nœud, un Pod qui plante, un nouveau Pod à placer…), un par un, et demande-moi quel composant agit en premier et ce qu'il fait. Corrige-moi en une phrase. »

Synthèse (10 min)

Trois lignes : ce que fait un orchestrateur, état voulu et réconciliation, les parties du plan de contrôle. Puis redessinez l'architecture sans notes.

Jour 37 · 09/12 — Kubernetes 2 : un cluster avec kind, Pods et Deployments

Objectif : faire tourner hello-ops dans un cluster local avec trois réplicas.

Théorie 1/2 : Pod, ReplicaSet, Deployment

Un Pod est la plus petite unité : un ou plusieurs conteneurs qui partagent une adresse réseau. On crée rarement des Pods directement : un Deployment déclare l'image et le nombre de réplicas, et gère un ReplicaSet qui garde ce nombre de Pods en vie. Les objets sont des manifestes YAML appliqués avec kubectl apply -f.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello-ops
spec:
  replicas: 3
  selector:
    matchLabels:
      app: hello-ops
  template:
    metadata:
      labels:
        app: hello-ops
    spec:
      containers:
        - name: app
          image: hello-ops:dev
          ports:
            - containerPort: 8080

Vous supprimez un Pod de ce Deployment. Que se passe-t-il ?

  1. Il en reste deux
  2. Le ReplicaSet en crée un nouveau pour revenir à trois
Réponse

État voulu : trois.

Théorie 2/2 : kubectl, les commandes de tous les jours

kubectl get pods -o wide
kubectl describe pod NOM       # événements : pourquoi il ne démarre pas
kubectl logs NOM               # sortie du conteneur
kubectl exec -it NOM -- sh
kubectl scale deployment hello-ops --replicas=5
kubectl delete -f deploy.yaml

Un Pod bloqué en ImagePullBackOff n'arrive pas à obtenir son image ; CrashLoopBackOff démarre et plante en boucle : lisez describe, puis logs.

Quelle commande montre les événements d'un Pod, comme une image impossible à récupérer ?

Réponse

kubectl describe pod NOM.

Un Pod affiche CrashLoopBackOff. Commande suivante ?

  1. kubectl logs (avec --previous pour l'exécution plantée)
  2. Réinstaller le cluster
Réponse

L'appli plante : ses journaux disent pourquoi.

Pratique (30 min)

  1. kind create cluster --name lab ; kubectl get nodes.
  2. Chargez votre image locale dans le cluster : kind load docker-image hello-ops:dev --name lab.
  3. Enregistrez le manifeste sous k8s/deploy.yaml, ajoutez imagePullPolicy: IfNotPresent au conteneur, appliquez-le, kubectl get pods jusqu'à trois Running.
  4. Supprimez un Pod et regardez-le revenir : kubectl get pods -w. Passez à 5, puis revenez à 3.
  5. Cassez-le : mettez l'image hello-ops:nope, appliquez, lisez describe. Réparez.

Avec votre IA : « Voici kubectl describe pod pour un Pod qui ne démarre pas : [collez]. Montre la ligne d'événement qui l'explique, dis ce qu'elle signifie en une phrase, et donne la seule commande qui le confirmerait. Ne me donne pas encore de solution. »

Le faire faire par un agent : un agent avec accès à kubectl peut trier un Pod en échec (describe, logs, événements) et proposer la correction. Limitez-le à un cluster de test et d'abord à des commandes de lecture. Vous vérifiez encore la cause dans les événements vous-même et appliquez la correction par le manifeste dans Git, pas par une modification en direct.

Synthèse (10 min)

Trois lignes : Pod, ReplicaSet, Deployment ; les cinq commandes kubectl ; les deux états d'échec courants. Puis réécrivez le Deployment de mémoire.

Jour 38 · 10/12 — Kubernetes 3 : Services, ConfigMaps, namespaces, trafic entrant

Objectif : donner aux Pods une adresse stable, les configurer depuis l'extérieur de l'image, et savoir comment le trafic entre dans un cluster.

Théorie 1/2 : Services et namespaces

Les Pods vont et viennent avec de nouvelles adresses. Un Service donne un nom et une adresse stables et répartit le trafic sur les Pods dont les labels correspondent à son sélecteur. Types : ClusterIP (dans le cluster, par défaut), NodePort (un port sur chaque nœud), LoadBalancer (un répartiteur externe, dans les clouds). Dans le cluster, le Service se joint par DNS : hello-ops.default.svc.cluster.local, ou simplement hello-ops depuis le même namespace. Un namespace regroupe des objets (par équipe, par environnement).

apiVersion: v1
kind: Service
metadata:
  name: hello-ops
spec:
  selector:
    app: hello-ops
  ports:
    - port: 80
      targetPort: 8080

Comment un Service sait-il vers quels Pods envoyer le trafic ?

  1. Par leurs noms
  2. Par son sélecteur de labels
Réponse

Les labels : app: hello-ops.

Quel type de Service est celui par défaut, joignable seulement dans le cluster ?

Réponse

ClusterIP.

Théorie 2/2 : ConfigMaps, et trafic venant de l'extérieur

Une ConfigMap contient de la configuration en paires clé-valeur, injectée en variables d'environnement ou en fichiers : la même image tourne partout avec des réglages différents (l'application en douze facteurs, facteur III). Pour router du HTTP venant de l'extérieur, Kubernetes a l'API Ingress et son successeur, la Gateway API ; les deux demandent un contrôleur installé. Le contrôleur le plus utilisé, ingress-nginx, a été retiré par le projet Kubernetes en mars 2026 (plus aucun correctif) ; le projet oriente les nouvelles installations vers des implémentations de la Gateway API.

D'où doit venir APP_VERSION dans Kubernetes ?

  1. D'une ConfigMap (ou de l'env du Deployment), pas cuite dans l'image
  2. D'un fichier modifié dans le Pod en marche
Réponse

La configuration vit hors de l'image.

Pratique (30 min)

  1. Appliquez le Service ; kubectl get svc ; joignez-le depuis votre machine avec kubectl port-forward svc/hello-ops 8080:80 et curl http://127.0.0.1:8080/.
  2. De l'intérieur : kubectl run tmp --rm -it --image=busybox -- wget -qO- http://hello-ops/health.
  3. Créez une ConfigMap : kubectl create configmap hello-ops --from-literal=APP_VERSION=0.2.0 --dry-run=client -o yaml > k8s/config.yaml, appliquez-la, référencez-la dans le Deployment avec envFrom: [{configMapRef: {name: hello-ops}}], appliquez, curl : la version apparaît.
  4. Créez un namespace staging et déployez-y les mêmes manifestes avec -n staging.

Avec votre IA : « Explique le chemin d'une requête HTTP depuis mon portable jusqu'à un conteneur via kubectl port-forward, un Service et un Pod, étape par étape. Puis explique ce qui changerait avec une implémentation de la Gateway API devant. Une ligne par étape. »

Le faire faire par un agent : générer des manifestes de Service et de ConfigMap est de la routine pour un agent. Demandez-lui d'utiliser kubectl create ... --dry-run=client -o yaml pour que la sortie vienne de l'outil. Vous vérifiez encore que le sélecteur correspond aux labels des Pods (kubectl get endpoints hello-ops doit lister des adresses).

Synthèse (10 min)

Trois lignes : Service et sélecteur, ConfigMap, Ingress et Gateway API. Puis réécrivez le Service de mémoire.

Jour 39 · 11/12 — Kubernetes 4 : sondes, ressources, déploiements et retours arrière

Objectif : faire savoir à Kubernetes quand l'appli est en bonne santé, plafonner ses ressources, et mettre à jour puis revenir en arrière sans interruption.

Théorie 1/2 : sondes et ressources

Une sonde de disponibilité (readiness probe) dit quand un Pod peut recevoir du trafic ; une sonde de vivacité (liveness probe) dit quand le redémarrer ; une sonde de démarrage protège les démarrages lents. Les requests sont ce que l'ordonnanceur réserve pour un conteneur ; les limits, le plafond (un conteneur qui dépasse sa limite mémoire est tué : OOMKilled).

          readinessProbe:
            httpGet:
              path: /health
              port: 8080
            periodSeconds: 5
          livenessProbe:
            httpGet:
              path: /health
              port: 8080
            initialDelaySeconds: 5
          resources:
            requests:
              cpu: 50m
              memory: 32Mi
            limits:
              memory: 64Mi

Quelle sonde retire un Pod du Service sans le redémarrer ?

  1. La vivacité
  2. La disponibilité
Réponse

La disponibilité : pas prêt, pas de trafic. La vivacité redémarre.

Quel état reçoit un conteneur qui dépasse sa limite de mémoire ?

Réponse

OOMKilled.

Théorie 2/2 : mises à jour progressives et retours arrière

Changer l'image du Deployment lance une mise à jour progressive : de nouveaux Pods sont créés et doivent être prêts avant que les anciens soient retirés (maxSurge, maxUnavailable). Chaque version est gardée comme révision.

kubectl set image deployment/hello-ops app=hello-ops:v2
kubectl rollout status deployment/hello-ops
kubectl rollout history deployment/hello-ops
kubectl rollout undo deployment/hello-ops

Quelle commande revient à la révision précédente du Deployment hello-ops ?

Réponse

kubectl rollout undo deployment/hello-ops.

La sonde de disponibilité de la nouvelle version ne passe jamais. Pendant la mise à jour progressive…

  1. les anciens Pods continuent de servir ; le déploiement se bloque
  2. tous les Pods tombent
Réponse

La disponibilité protège le déploiement : les Pods pas prêts ne reçoivent rien et les anciens ne sont pas retirés.

Pratique (30 min)

  1. Ajoutez sondes et ressources à k8s/deploy.yaml ; appliquez ; kubectl describe pod les montre.
  2. Construisez hello-ops:v2 (changez le message), chargez-la avec kind, kubectl set image, et regardez rollout status pendant qu'une boucle interroge l'appli par le port-forward : aucune requête ratée.
  3. Construisez une hello-ops:v3 cassée dont /health renvoie 500 ; déployez-la : le déploiement se bloque, les anciens Pods servent. kubectl rollout undo.
  4. Mettez le changement d'image dans le fichier manifeste, pas seulement en ligne de commande, et commitez.

Avec votre IA : « Voici mon Deployment avec sondes et ressources : [collez]. Pour chaque valeur, dis ce qui arrive si elle est trop basse et trop haute, dans un tableau. Puis dis-moi quelle valeur tu vérifierais en premier si les Pods redémarrent toutes les quelques minutes. »

Le faire faire par un agent : un agent peut surveiller un déploiement et lancer rollout undo si les erreurs montent. Donnez-lui la règle exacte (« annule si plus de 2 curl sur 20 échouent »). Vous vérifiez encore après coup quelle révision tourne et pourquoi la mauvaise a échoué.

Synthèse (10 min)

Trois lignes : disponibilité ou vivacité, requests ou limits, rollout et undo. Puis réécrivez la sonde de disponibilité de mémoire.

Jour 40 · 12/12 — Kubernetes 5 : Helm, Kustomize, GitOps

Objectif : gérer plusieurs environnements sans copier les manifestes, et comprendre le GitOps.

Théorie 1/2 : Helm et Kustomize

Helm (projet diplômé de la CNCF) empaquette des manifestes en chart : des modèles plus un values.yaml ; helm install les produit et les applique, et suit les versions publiées. Kustomize, intégré à kubectl (kubectl apply -k), garde des manifestes simples comme base et applique des surcouches (overlays) par environnement (réplicas, étiquettes, namespace), sans langage de modèles.

k8s/
  base/            deploy.yaml service.yaml kustomization.yaml
  overlays/
    staging/       kustomization.yaml   # namespace: staging, replicas: 1
    prod/          kustomization.yaml   # namespace: prod, replicas: 3

Quel outil est intégré à kubectl ?

  1. Helm
  2. Kustomize
Réponse

Kustomize, avec kubectl apply -k.

Théorie 2/2 : le GitOps

Le GitOps, terme forgé par Weaveworks en 2017 : le dépôt Git est la seule source de vérité de ce qui tourne ; un agent dans le cluster (Argo CD ou Flux, tous deux diplômés de la CNCF) récupère le dépôt et réconcilie le cluster avec lui. Déployer, c'est fusionner une pull request ; un changement manuel dans le cluster est annulé. La CI n'a plus besoin d'accès au cluster : elle envoie seulement des images et met à jour une étiquette dans Git.

En GitOps, comment déploie-t-on la version 1.3 ?

  1. En fusionnant un changement qui met l'étiquette à 1.3 dans le dépôt Git
  2. En lançant kubectl set image sur la production
Réponse

Le dépôt fait foi ; l'agent l'applique.

Quelqu'un modifie un Deployment directement dans un cluster géré en GitOps. Ensuite…

  1. le changement reste pour toujours
  2. l'agent détecte la dérive et rétablit ce que dit Git
Réponse

Réconciliation auto-réparatrice (si elle est activée).

Pratique (30 min)

  1. Réorganisez k8s/ en une base Kustomize et deux surcouches (staging : 1 réplica, prod : 3, namespaces et valeurs de ConfigMap différents).
  2. kubectl kustomize k8s/overlays/prod pour voir le YAML produit ; puis kubectl apply -k des deux surcouches ; kubectl get pods -A -l app=hello-ops.
  3. Lisez la page de prise en main d'Argo CD et dessinez dans votre carnet où il se placerait dans votre chaîne. Facultatif : installez-le dans le cluster kind et pointez-le vers votre dépôt.

Avec votre IA : « Compare le déploiement poussé depuis la CI avec kubectl et le GitOps avec Argo CD pour ma configuration (une petite appli, un cluster, deux environnements), dans un tableau : accès nécessaires, traçabilité, gestion de la dérive, retour arrière. Puis pose-moi deux questions pour vérifier que j'ai compris. »

Le faire faire par un agent : un agent peut transformer des manifestes à plat en base et surcouches Kustomize. Vous comparez encore la sortie produite (kubectl kustomize) avec ce qui était déployé avant : le résultat doit être identique, sauf les valeurs voulues par environnement.

Synthèse (10 min)

Trois lignes : Helm, Kustomize, GitOps. Puis redessinez l'arborescence base / surcouches de mémoire.

Jour 41 · 13/12 — La gestion des secrets

Objectif : trouver un secret dans un dépôt, garder les secrets chiffrés ou hors de Git, et savoir ce que protègent ou non les Secrets de Kubernetes.

Théorie 1/2 : par où fuient les secrets

Un secret, c'est tout ce qui donne un accès : mot de passe, jeton d'API, clé privée, URL de base de données avec mot de passe. Ils fuient par l'historique Git (jour 14), les journaux de CI, les images de conteneur (docker history), l'historique du shell, les captures d'écran. Des scanners comme Gitleaks les cherchent dans un dépôt et son historique, en local ou en CI. Un secret qui a fui se renouvelle d'abord : supprimer le commit n'annule pas la fuite.

Un jeton a été poussé sur un dépôt public il y a une heure. Première action ?

  1. Réécrire l'historique Git
  2. Révoquer et remplacer le jeton
Réponse

Renouveler d'abord : des robots scannent les dépôts publics en quelques minutes.

Les Secrets de Kubernetes sont-ils chiffrés par défaut ?

  1. Non : encodés en base64, que n'importe qui décode ; le chiffrement au repos se configure
  2. Oui, en AES
Réponse

Le base64 est un encodage, pas un chiffrement. Restreignez l'accès avec le RBAC et activez le chiffrement au repos.

Théorie 2/2 : trois façons de garder des secrets

  • Le coffre de la plateforme : secrets de CI (${{ secrets.NOM }}, masqués dans les journaux), Secrets Kubernetes à accès restreint.
  • Chiffrés dans Git : SOPS chiffre les valeurs d'un fichier YAML ou JSON (les clés restent lisibles pour les diffs) avec des clés age ou un KMS de cloud ; seuls les détenteurs de la clé déchiffrent.
  • Un gestionnaire de secrets : un service qui stocke, trace et renouvelle les secrets, et les distribue à l'exécution : HashiCorp Vault (sous Business Source License depuis 2023), son fork libre OpenBao (Linux Foundation), Infisical…

Avec SOPS, qu'est-ce qui reste lisible dans le fichier chiffré ?

  1. Rien
  2. Les valeurs
  3. Les clés (noms des champs), pour que les diffs gardent un sens
Réponse

Seules les valeurs sont chiffrées.

Un secret est utilisé dans une étape de CI avec run: echo $TOKEN. Est-ce sûr puisque la forge masque les secrets ?

  1. Oui, le masquage est une garantie
  2. Non : le masquage fait au mieux (une valeur encodée ou découpée n'est pas masquée) ; n'affichez jamais un secret
Réponse

Le masquage aide ; il ne rend pas l'affichage sûr. L'attaque tj-actions affichait les secrets doublement encodés pour y échapper.

Pratique (30 min)

  1. Plantez un faux secret dans un dépôt de test : la clé d'accès d'exemple qu'AWS publie dans sa propre documentation (cherchez « AWS example access key »), écrite sous la forme aws_secret_access_key = ... dans un fichier config. Commitez, puis supprimez-la dans un second commit.
  2. Lancez Gitleaks dessus (son image Docker ou un binaire publié ; lisez son README pour la commande actuelle, par exemple gitleaks git -v .) : il trouve le secret dans l'historique.
  3. Installez SOPS et age ; age-keygen -o ~/.config/sops/age/keys.txt ; chiffrez un secrets.yaml avec sops --encrypt --age <votre clé publique> secrets.yaml > secrets.enc.yaml ; ouvrez-le avec sops --decrypt. Ne commitez que le fichier chiffré.
  4. Créez un Secret Kubernetes, puis relisez-le avec kubectl get secret NOM -o yaml et décodez la valeur avec base64 -d : voyez pourquoi le contrôle d'accès compte.

Avec votre IA : « Voici comment mon projet gère les secrets aujourd'hui : [décrivez : fichier .env sur le serveur, secret de CI pour le registre, Secret Kubernetes créé à la main]. Liste les risques dans l'ordre, et pour chacun le plus petit changement qui le réduit. Ne me demande de coller aucune valeur de secret. »

Le faire faire par un agent : un agent peut ajouter un job Gitleaks en CI et un crochet pre-commit. Il ne doit jamais recevoir un vrai secret pour « tester ». Vous vérifiez encore : le job échoue sur le faux secret planté, et passe une fois celui-ci retiré des fichiers, avec une exception d'historique justifiée par écrit.

Synthèse (10 min)

Trois lignes : par où fuient les secrets, renouveler d'abord, les trois façons de les garder. Puis refaites le chiffrement et le déchiffrement SOPS sans notes.

Jour 42 · 14/12 — Livrable du module 3 : hello-ops à partir du seul code

Objectif : livrer un dépôt d'infrastructure qui déploie hello-ops de trois façons, sans aucune étape manuelle, et vérifier vos acquis.

Théorie 1/2 : le test de recette de l'IaC

Le test de l'infrastructure en code est brutal et simple : tout supprimer, lancer le code, et obtenir le même résultat. Si une étape vous demande de vous souvenir de quelque chose, elle n'est pas encore dans le code. Écrivez ce test dans une section du README : « d'une machine neuve à une appli qui tourne ».

Votre playbook marche, mais seulement après que vous avez créé un dossier à la main une fois. L'infrastructure est-elle entièrement en code ?

  1. Non : le dossier doit être une tâche
  2. Oui
Réponse

Toute étape de mémoire est un trou.

Théorie 2/2 : révision du module

Quelle commande devez-vous lire avant chaque tofu apply ?

  1. tofu init
  2. tofu plan
Réponse

Le plan dit ce qui sera créé, modifié ou détruit.

Un Pod est Running mais ne reçoit aucun trafic de son Service. Première vérification ?

  1. Redémarrer le cluster
  2. Augmenter la limite mémoire
  3. Le sélecteur du Service face aux labels du Pod, et la sonde de disponibilité
Réponse

kubectl get endpoints montre si le Service a trouvé des Pods prêts.

Les deux options d'ansible-playbook pour prévisualiser une exécution ?

Réponse

--check --diff.

Le base64 dans un Secret Kubernetes est…

  1. un encodage, pas une protection
  2. un chiffrement fort
Réponse

Quiconque a l'accès en lecture le décode.

Pratique (30 min)

  1. Terminez hello-ops-infra : ansible/ (rôle hello_ops, déploiement systemd), tofu/ (module, deux environnements, provider Docker), k8s/ (base et surcouches Kustomize avec sondes et ressources), un workflow de CI (fmt, validate, ansible-lint, rendu kubectl kustomize), des secrets chiffrés avec SOPS ou absents.
  2. Passez le test de recette : kind delete cluster, tofu destroy, puis recréez tout à partir du seul README.
  3. Lancez Gitleaks sur tout l'historique : propre.
  4. Ajoutez-le à votre liste de portfolio : livrable 3.

Avec votre IA : « Voici le README de mon dépôt d'infrastructure, section "d'une machine neuve à une appli qui tourne" : [collez]. Trouve chaque étape qui suppose quelque chose de non écrit (un outil installé, une variable posée, un fichier présent). Liste-les ; ne réécris pas le README. »

Le faire faire par un agent : un agent est un bon testeur de recette : donnez-lui une VM neuve et votre seul README, et demandez-lui de le suivre à la lettre et de dire où il bloque. Vous lisez encore son journal : chaque endroit où il a improvisé est une ligne qui manque à votre README.

Synthèse (10 min)

Comptez votre score. Les sujets manqués reviennent au jour 56, quand le projet final est déployé.

Sources

Utile ? Dites-nous ce qu'il faut mesurer ensuite.