Blog

Déployer FFS sur Fedora CoreOS — Du zéro à la production

Publié le 2026-04-21

Exploiter une application web en production signifie bien plus que la faire compiler. Cela signifie des identifiants chiffrés qui ne touchent jamais le disque en clair, un superviseur de processus qui détecte les blocages — pas seulement les plantages — et un chemin de mise à jour qui ne nécessite ni SSH ni prières. Cet article décrit le déploiement de FFS sur Fedora CoreOS : pourquoi CoreOS, comment fonctionne le pipeline de déploiement et les compromis impliqués.

Pourquoi Fedora CoreOS

CoreOS est un système d'exploitation conçu pour une seule tâche : exécuter des conteneurs. Il n'y a pas de gestionnaire de paquets au sens traditionnel, pas de /usr/local/bin rempli d'outils compilés à la main, pas de dérive de configuration. L'état complet du système est déclaré dans un seul fichier Ignition au moment de l'installation. Si un nœud tombe en panne, on en provisionne un nouveau à partir du même fichier et on obtient une machine identique.

Cette philosophie s'aligne bien avec une plateforme comme FFS. L'application s'exécute dans une image de conteneur qui embarque son propre runtime Wt, ses bibliothèques partagées et ses composants compilés. L'hôte fournit le noyau, le runtime de conteneur et systemd — rien d'autre.

Avantages :

L'infrastructure immuable élimine les surprises du type « ça marche sur ma machine ». Chaque nœud CoreOS provisionné à partir du même fichier Ignition est identique : mêmes règles de pare-feu, même arborescence de répertoires, mêmes unités systemd, mêmes clés SSH. Il n'y a pas d'état accumulé par des mois de dnf install et de modifications de configuration oubliées.

Les mises à jour automatiques maintiennent le système d'exploitation patché sans intervention de l'opérateur. CoreOS utilise un schéma de partitions A/B — il télécharge la mise à jour sur la partition inactive, redémarre dessus et effectue automatiquement un retour arrière si le démarrage échoue. Le conteneur applicatif n'est pas affecté car il embarque ses propres dépendances.

La surface d'attaque minimale résulte du système de fichiers racine en lecture seule et de l'absence de gestionnaire de paquets. Il n'y a pas de compilateur, pas de risque curl | bash, pas d'en-têtes de développement oubliés. Les seuls chemins inscriptibles sont les répertoires de données explicitement montés.

Compromis :

Le débogage sur un nœud CoreOS est plus difficile que sur un poste de travail Linux complet. Il n'y a pas de gdb, pas de strace (sans toolbox), pas d'éditeur de texte autre que vi. Quand quelque chose ne va pas à 2 heures du matin, on lit les sorties de journalctl en réfléchissant attentivement, plutôt que d'attacher un débogueur.

Le modèle de provisionnement basé sur Ignition signifie que les changements de configuration nécessitent soit un reprovisionnement du nœud, soit la modification manuelle des unités systemd — il n'y a pas de apt-get install ou dnf update pour les changements au niveau de l'hôte. C'est une fonctionnalité, pas un défaut, mais cela exige de la discipline.

SELinux est en mode enforcing par défaut. Les montages de volumes de conteneurs interagissent avec les labels SELinux, et le mélange de Podman rootless avec des répertoires hôtes montés en bind peut produire des problèmes de permissions invisibles jusqu'à l'exécution. Le flag de volume :Z gère le relabeling, mais il modifie la propriété des fichiers d'une manière qui peut surprendre si l'on partage des répertoires entre des conteneurs avec différents espaces de noms UID.

Le pipeline de déploiement

Le déploiement de FFS sur CoreOS suit un pipeline en trois étapes : préparation, provisionnement, déploiement. Chaque étape dispose d'un script dédié qui encapsule la complexité.

Étape 1 — Préparation (machine du développeur)

ffs-prepare.sh s'exécute sur le poste de travail du développeur, pas sur le serveur. Il lit la configuration du projet, collecte les clés SSH, les identifiants SMTP et les mots de passe de certificats TLS, puis génère deux fichiers : une configuration Butane (ffs.bu) et son équivalent Ignition compilé (ffs.ign).

$ bash deploy/coreos/ffs-prepare.sh --project serbest --name www

Le modèle Butane utilise des espaces réservés (@PROJECT@, @INSTANCE_NAME@, @HTTPS_PORT@) qui sont remplis à partir du fichier .env du projet et de saisies interactives. Le résultat est une spécification machine complète : unités systemd, règles de pare-feu, permissions de répertoires, comptes utilisateurs et clés SSH autorisées.

Le mot de passe du certificat TLS est chiffré avec systemd-creds — il est stocké sur disque sous forme chiffrée et n'est déchiffré qu'au démarrage du service dans un répertoire temporaire en RAM.

Étape 2 — Provisionnement (installation CoreOS)

Le fichier ffs.ign généré est fourni à l'installateur CoreOS. C'est une opération unique par machine (ou par reprovisionnement) :

$ coreos-installer install /dev/sda --ignition-file ffs.ign

Après le redémarrage, la machine est prête avec tout en place : l'utilisateur système ffs, l'arborescence du projet, les unités de service systemd et la configuration du pare-feu. Aucune connexion SSH n'est nécessaire pour la configuration initiale.

Étape 3 — Déploiement (transfert d'image)

ffs-deploy.sh transfère l'image de conteneur et les fichiers du projet vers le nœud CoreOS. Lors du premier déploiement, il exécute également le script de configuration serveur et chiffre les identifiants TLS :

$ bash deploy/coreos/ffs-deploy.sh 192.168.1.10 \
    --name www --user core --first-deploy

Les mises à jour suivantes sont plus simples — juste l'image et les fichiers du projet modifiés :

$ bash deploy/coreos/ffs-deploy.sh 192.168.1.10 --name www

Le script de déploiement gère le transfert d'image via podman save / podman load, la synchronisation des fichiers du projet via rsync et le redémarrage du service via systemctl restart.

Gestion des certificats TLS

FFS utilise des clés privées TLS chiffrées. La clé privée est chiffrée avec AES-256-CBC pendant le processus de build et stockée sous le nom server.key.enc. Au démarrage du conteneur, le script d'entrée la déchiffre dans /tmp/secrets/ (un montage tmpfs — en RAM uniquement, ne touche jamais le disque) en utilisant la variable d'environnement CERT_PASS, qui elle-même provient d'un identifiant systemd chiffré.

Rotation des certificats

ffs-cert.sh fournit la gestion du cycle de vie des certificats :

# Voir le statut actuel du certificat (local, serveur, conteneur)
$ bash deploy/coreos/ffs-cert.sh status 192.168.1.10 --name www

# Renouveler le certificat HTTPS
$ bash deploy/coreos/ffs-cert.sh rotate-https 192.168.1.10 --name www

# Renouveler les clés d'accès SSH
$ bash deploy/coreos/ffs-cert.sh rotate-ssh 192.168.1.10 --name www

La commande status affiche les dates d'expiration des certificats, les empreintes et avertit lorsque les certificats expirent dans les 30 jours. rotate-https sauvegarde automatiquement le certificat existant, déploie le nouveau, rechiffre les identifiants et redémarre le service.

Supervision des processus et surveillance de la santé

L'une des leçons les plus difficiles des premiers déploiements CoreOS a été de découvrir qu'un processus en cours d'exécution n'est pas nécessairement un processus sain. FFS reposait initialement sur un watchdog simple basé sur fork : le parent crée un enfant par fork, attend, redémarre en cas de plantage. Cela attrape les segfaults et les exceptions non gérées mais pas les blocages — une connexion SMTP bloquée ou une boucle d'événements en interblocage maintient le processus en vie sans rien servir.

L'architecture de supervision actuelle utilise des sondes de santé basées sur des pipes :

parent (superviseur)
  └── enfant 0  ──pipe──→  le parent lit les heartbeats 'H'
  └── enfant N  ──pipe──→  (futurs workers)

L'enfant écrit un octet de heartbeat dans un pipe toutes les 10 secondes. Le parent effectue un poll() sur tous les pipes — l'absence de heartbeat pendant trois intervalles de sonde consécutifs signifie que l'enfant est bloqué, et le parent le termine avec SIGTERM (avec un délai de grâce de 5 secondes avant SIGKILL) et redémarre.

Pour l'intégration systemd, le parent envoie sd_notify("WATCHDOG=1") à chaque intervalle de sonde lorsque tous les enfants sont sains. Si un enfant est en mauvaise santé, le heartbeat est retenu et le propre timer WatchdogSec de systemd finit par tuer l'ensemble du conteneur — une seconde ligne de défense.

L'implémentation de sd_notify est autonome : un sendmsg() direct vers le socket Unix datagramme NOTIFY_SOCKET, sans dépendance à libsystemd. Cela maintient l'image du conteneur minimale et fonctionne sur n'importe quelle image de base.

Options de ligne de commande

./ffs --sd-notify     # intégration systemd (production CoreOS)
./ffs --watchdog      # supervision autonome (Docker, conteneurs dev)
./ffs                 # processus nu (GDB, IDE, Valgrind)

Déploiement des mises à jour

Après le déploiement initial, les mises à jour suivent un workflow prévisible :

# 1. Construire la nouvelle image de production (machine dev, dans le conteneur dev)
ffscm rebuild production --project serbest --compiler intel

# 2. Déployer vers le nœud CoreOS
bash deploy/coreos/ffs-deploy.sh 192.168.1.10 --name www

# 3. Vérifier
bash deploy/coreos/ffs-cert.sh status 192.168.1.10 --name www

Le script de déploiement transfère uniquement les couches d'image modifiées et les fichiers du projet. Le service est redémarré automatiquement. Si la nouvelle version ne démarre pas (détection d'échec rapide : trois plantages en 5 secondes), le watchdog abandonne et le Restart=on-failure de systemd prend le relais avec un délai croissant.

Leçons apprises

Le WatchdogSec de systemd exige une participation active. Configurer WatchdogSec=120s sans envoyer de heartbeats amène systemd à tuer le service toutes les deux minutes. FFS l'a appris à ses dépens — le serveur semblait redémarrer aléatoirement jusqu'à ce que nous corrélions l'intervalle de redémarrage avec le délai du watchdog.

L'expansion shell ne se produit pas dans l'ExecStart de systemd. Le motif d'identifiants $(cat ${CREDENTIALS_DIRECTORY}/cert_pass) nécessite un wrapper /bin/sh -c. Sans celui-ci, la chaîne littérale $(cat ...) est passée au conteneur, et le déchiffrement de la clé TLS échoue silencieusement.

Perspectives

Le modèle de déploiement actuel gère bien la production sur nœud unique. Les améliorations futures incluent l'intégration de Let's Encrypt pour le renouvellement automatique des certificats, le support multi-worker (l'infrastructure watchdog N-enfants est déjà en place) et une vérification de santé de déploiement qui vérifie que le point de terminaison HTTPS répond correctement avant de déclarer la mise à jour réussie.

CoreOS fournit une base solide pour l'exploitation de FFS en production. Les garanties d'immuabilité, les mises à jour automatiques du système d'exploitation et l'intégration systemd en font un choix naturel pour une plateforme qui valorise la fiabilité opérationnelle. Le compromis — moins de flexibilité, courbe de débogage plus raide — en vaut la peine pour la confiance que chaque déploiement est reproductible et chaque redémarrage est supervisé.