Rien ne parle autant de « culture d’ingénierie productive » qu’un hôte de lab qui boot sur un shell d’urgence cinq minutes avant une livraison. Ou un runner CI qui commence à rater des builds parce que vous êtes à court d’inodes, pas de disque. Ce ne sont pas des pannes intéressantes. Ce sont des pannes évitables — généralement causées par une installation négligée et un modèle mental encore plus négligent.
CentOS Stream 10 est un très bon choix pour les labos et le CI parce qu’il est suffisamment proche de RHEL pour vous enseigner les mêmes leçons opérationnelles, tout en étant un endroit où les changements apparaissent plus tôt — exactement ce que vous voulez quand vous validez des pipelines, pilotes, comportements du noyau et vos propres hypothèses.
Ce qu’est réellement CentOS Stream 10 (et pourquoi vous en avez besoin)
CentOS Stream n’est pas « RHEL gratuit ». C’est une distribution livrée en continu qui se situe entre Fedora et RHEL dans le flux de développement. Concrètement : c’est l’endroit où les changements atterrissent avant d’être figés dans la prochaine version mineure de RHEL. Pour les labos et le CI, c’est utile car vous pouvez détecter des changements ABI à venir, des évolutions d’outillage et des bizarreries de packaging avant qu’ils n’atteignent vos flottes payantes — ou celles de vos clients.
Il y a un piège : des personnes installent Stream et le traitent comme un OS d’entreprise tranquille et durable. Ne le faites pas. Traitez-le comme un canari contrôlé, ressemblant à la production. Si vous voulez quelque chose que vous ignorerez pendant trois ans, utilisez autre chose et soyez honnête à ce sujet.
Quand CentOS Stream 10 est le bon choix
- Vous compilez des RPM ou des modules noyau et voulez un avertissement précoce sur les changements du buildroot.
- Votre CI a besoin d’un comportement RHEL-ish : systemd, SELinux, firewalld, NetworkManager et les mêmes outils généraux.
- Vous voulez valider des rôles Ansible et des baselines de durcissement contre le « prochain RHEL ».
- Vous exécutez KVM/libvirt ou des hôtes conteneurs et avez besoin d’une base relativement stable, mais pouvez tolérer des mises à jour plus rapides que dans l’entreprise classique.
Quand ce n’est pas le bon choix
- Votre labo est en fait un environnement de production fantôme sans contrôle des changements.
- Vous ne pouvez pas patcher fréquemment ou vous n’avez pas de portes de validation.
- Votre « runner CI » est une VM chouchou dans laquelle les gens se connectent par SSH et modifient tout manuellement.
Faits et historique qui importent opérationnellement
Voici des éléments de contexte concrets qui modifient réellement les décisions, pas des anecdotes de conférence :
- CentOS Stream a été introduit comme un aperçu continu de RHEL, modifiant l’ancien modèle où CentOS Linux était reconstruit après les sorties de RHEL. Cela change votre profil de risque : vous êtes plus en avant dans la file.
- Les clones RHEL étaient auparavant le choix par défaut pour un « Linux d’entreprise gratuit », ce qui a formé une génération d’équipes à considérer les rebuilds comme identiques. Stream brise cette hypothèse par conception.
- systemd est le système d’init de la famille RHEL depuis des années, et c’est dans Stream que vous verrez certains paramètres de service changer en premier (timeouts, dépendances, options de durcissement).
- NetworkManager n’est plus optionnel dans la plupart des déploiements réels, car l’outillage, le comportement cloud-init et le nommage moderne des NIC l’attendent. Se battre contre lui fait perdre du temps.
- SELinux en mode « Enforcing » est le défaut dans des environnements sensés, et l’écosystème RHEL a passé une décennie à rendre cela praticable. Le désactiver reste populaire — surtout parmi ceux qui ne font pas d’astreintes.
- Anaconda a longtemps été puissant mais facile à mal cliquer, notamment pour le partitionnement personnalisé et le placement du chargeur de démarrage. « Je suis assez sûr d’avoir cliqué le bon disque » n’est pas une stratégie de stockage.
- DNF a remplacé YUM comme gestionnaire de paquets orienté utilisateur il y a des années, et le comportement des métadonnées de repo (et le cache) compte quand le CI tire des centaines de paquets par jour.
- cgroups v2 est devenu la norme sur les systèmes modernes proches de RHEL, ce qui change le comportement des conteneurs et des limites de ressources comparé aux anciennes présomptions v1.
- Podman (rootless) est un outil conteneur de première classe dans la famille RHEL, et c’est un meilleur choix par défaut pour beaucoup de setups CI que « juste lancer Docker en root et espérer ».
Objectifs de conception pour labos et CI (choisissez un camp)
Avant de démarrer un ISO d’installation, décidez ce que vous optimisez. Labos et CI ne sont pas la même chose, mais ils partagent une exigence : des pannes prévisibles.
Objectif 1 : Reproductibilité avant ingéniosité
Pour les runners CI, je veux une image qui peut être reconstruite depuis zéro et convergée avec l’automatisation en moins d’une heure. Si vous ne pouvez pas la reconstruire rapidement, vous la conserverez « au cas où », et c’est ainsi que vous finirez à déboguer un hôte dont le dernier changement humain date d’un exercice fiscal différent.
Objectif 2 : Le stockage doit échouer de façon ennuyeuse
Les charges CI sont brutales pour le stockage : beaucoup de petits fichiers, fort turnover, caches qui grandissent jusqu’à atteindre une limite, et logs qui ne s’arrêtent jamais. Votre architecture doit faire en sorte que « disque plein » soit bruyant et localisé, pas silencieux et global.
Objectif 3 : Les paramètres de sécurité doivent rester actifs
Ne désactivez pas SELinux parce qu’un build a échoué une fois. Corrigez l’étiquetage. Ne videz pas le pare-feu parce que votre runner ne peut pas atteindre un port. Ouvrez le port. Si votre labo est sur un réseau plat, votre plus grande menace n’est pas un pirate hollywoodien — c’est le service « temporaire » d’une autre équipe qui écoute sur 0.0.0.0.
Un constat sec : un hôte de labo sans garde-fous n’est que de la production, sauf que personne ne l’admet. C’est ainsi que naissent les pannes « surprises » qui sont en réalité des « surprises de responsabilité ».
Parcours d’installation : de l’ISO au premier démarrage sans drame
Vous pouvez installer Stream 10 de façon interactive ou via Kickstart. Pour les labos et le CI, Kickstart l’emporte car il transforme le savoir tribal en fichier. Les installations interactives conviennent pour une VM de test ponctuelle, mais ce sont aussi celles qui transforment un « build standard » en cinq builds différents.
Choisir le profil d’installation adapté
Pour les runners CI et serveurs de labo sans GUI : installez un environnement minimal plus les paquets dont vous avez besoin. Les installations GUI sont pratiques jusqu’au moment où vous patchez 50 hôtes et réalisez que vous traînez une pile de bureau comme un boulet.
UEFI vs BIOS : choisissez UEFI sauf raison contraire
Les serveurs modernes et les VM devraient être en UEFI. C’est ennuyeux, cohérent et l’outillage a mûri. Le BIOS/legacy est pour la compatibilité avec d’hyperviseurs anciens ou du matériel embarqué que vous ne pouvez pas remplacer.
Posture Kickstart
Un Kickstart pour labos/CI doit faire ces choses :
- Verrouiller explicitement le disque d’installation (ne comptez pas sur « premier disque »).
- Définir partitions et volumes LVM avec tailles intentionnelles et règles de croissance.
- Créer un utilisateur admin avec clés SSH (et verrouiller l’authentification par mot de passe SSH).
- Activer SELinux en mode enforcing et firewalld.
- Définir le fuseau horaire, NTP et un schéma de nom d’hôte stable.
- Optionnellement enregistrer des repos/mirrors internes si le trafic CI est important.
Blague #1 : Traitez « Next, Next, Finish » comme une arme chargée. Un seul clic suffit pour apprendre à votre chargeur de démarrage des nouveaux et passionnants disques.
Architecture de stockage : partitions, LVM et domaines de défaillance
Le stockage est l’endroit où la plupart des installations lab/CI deviennent paresseuses. Puis le stockage devient le goulot d’étranglement, et tout le monde blâme « le réseau » car il est réconfortant de blâmer des choses invisibles.
À quoi ressemble une bonne architecture
Pour une VM mono-disque ou un petit nœud bare-metal, une valeur par défaut sensée est :
- Partition système UEFI (ESP) : petite, fixe.
- /boot : taille fixe, ext4.
- PV LVM pour tout le reste.
- LV séparés pour / (root), /var, et éventuellement /var/lib/containers ou /var/lib/libvirt.
Pourquoi séparer /var ? Parce que le CI écrit dans /var comme si on le payait à l’octet. Logs, caches de paquets, couches de conteneurs, artefacts de build et fichiers temporaires adorent /var. Si /var se remplit et qu’il est sur le même système de fichiers que /, vous n’obtenez pas un problème « disque plein » — vous obtenez un problème « le système ne peut pas écrire d’état ». C’est une autre catégorie de douleur.
Ext4 vs XFS
XFS est courant dans les écosystèmes proches de RHEL et se comporte bien avec les gros fichiers et les IO parallèles. Ext4 reste un choix tout à fait respectable pour /boot et parfois pour des volumes plus petits. La vraie règle : ne soyez pas créatif. Utilisez ce que votre outillage attend et ce que votre équipe peut récupérer à 3 h du matin.
LVM thin : attention
Le thin provisioning LVM peut ressembler à de l’espace disque gratuit. Ce n’est pas de l’espace gratuit. C’est une promesse à votre futur que vous allez surveiller l’utilisation du pool et réagir avant qu’il n’atteigne 100%. Les thin pools sont excellents dans des environnements virtualisés où vous comprenez la surallocation. Ils sont catastrophiques quand personne ne les surveille.
Swap : choisissez une politique, pas une vibe
Pour des runners CI avec des pics mémoire, un peu de swap peut empêcher le noyau de tuer votre job de build. Pour des hôtes sensibles à la latence, trop de swap peut masquer la pression mémoire jusqu’à ce que la performance devienne « mystérieusement lente ». Si vous n’êtes pas sûr : gardez un swap modeste et comptez sur la surveillance pour ajuster.
Base réseau : IP prévisibles, DNS prévisible
Des runners CI qui échouent parce que le DNS a vacillé n’est pas une histoire héroïque. C’est un échec des fondamentaux.
Adressage statique vs DHCP
Pour des runners éphémères créés et détruits automatiquement, DHCP est acceptable si votre DHCP et DNS sont fiables et intégrés. Pour des hôtes de labo longue durée et du bare metal, des IP statiques réduisent les surprises. Si vous faites des IP statiques, faites-les via les profils NetworkManager, pas en éditant à la main des fichiers au hasard en espérant vous souvenir.
Noms d’hôte et domaines de recherche
Choisissez un schéma de nommage qui survive à la réimage. Exemple : rôle + site + index. N’encodez pas de secrets ou de drames de propriété dans les noms d’hôte. De plus, limitez les domaines de recherche. Des domaines trop larges provoquent des délais étranges et des comportements de résolution lorsque le DNS interne est en difficulté.
Base de sécurité : SELinux, firewalld et SSH
Les contrôles de sécurité ne servent pas qu’à la sécurité. Ils servent à la prévisibilité opérationnelle. SELinux et firewalld vous obligent à être explicite sur ce que font vos services. Cela rend vos systèmes plus faciles à raisonner.
SELinux : laissez en enforcing
Le mode Enforcing attrape les mauvais étiquetages, les mauvais paramètres par défaut et les exécutions de conteneurs « ça marchait sur mon laptop ». Si quelque chose casse, votre premier réflexe devrait être : lire les dénis AVC et corriger les étiquettes ou la politique. Votre dernier réflexe devrait être : setenforce 0.
firewalld : définissez des zones et ouvrez seulement ce dont vous avez besoin
Les hôtes CI ont typiquement besoin d’un SSH entrant et peut-être d’un accès pour du scraping de métriques entrant. Ils n’ont pas besoin que le monde accède à des ports éphémères. Si vous exécutez un miroir de registre ou un cache d’artefacts, c’est différent — ouvrez ces ports explicitement et documentez-les.
SSH : clés, pas mots de passe
Désactivez l’authentification par mot de passe si possible. Si vous ne le pouvez pas, limitez-la au moins aux réseaux internes et imposez des mots de passe forts. Les runners CI ne doivent pas être un endroit où une attaque par force brute par mot de passe trouve un point d’appui.
Une citation à garder près de votre terminal : Espérer n’est pas une stratégie.
— Gene Kranz
Choix d’exécution CI/runtime : Podman, conteneurs et virtualisation
Pour labos et CI, vous choisissez généralement un de ces trois modèles :
- Builds sur hôte : installez les toolchains sur l’hôte. Rapide, mais ça s’encrasse et devient du snowflake.
- Builds en conteneurs : isolez les environnements de build avec Podman. Reproductible, nettoyage plus simple, bon choix par défaut.
- Builds en VM : OS complet par job via libvirt/KVM. Plus lourd, mais le plus proche du comportement réel de déploiement.
Ma préférence : conteneurs pour la plupart des pipelines, VM pour le travail noyau/pilote et « on doit tester le boot et les services système », et builds sur hôte uniquement quand c’est absolument nécessaire (par ex. certains toolchains liés au matériel).
Tâches pratiques avec commandes (et ce qu’il faut décider d’après la sortie)
Ce ne sont pas des commandes d’exemple. C’est ce que vous exécutez le jour 1, et à nouveau quand quelque chose semble étrange. Chaque tâche inclut : commande, sortie d’exemple, ce que ça signifie, et la décision suivante.
Task 1: Confirm you installed what you think you installed
cr0x@server:~$ cat /etc/os-release
NAME="CentOS Stream"
VERSION="10"
ID="centos"
ID_LIKE="rhel fedora"
VERSION_ID="10"
PLATFORM_ID="platform:el10"
PRETTY_NAME="CentOS Stream 10"
Signification : Vous êtes sur Stream 10, et l’identifiant de plateforme ressemble à EL10. Si ceci indique autre chose, votre pipeline d’images ment.
Décision : Si ce n’est pas exactement ce à quoi vous vous attendiez, arrêtez et corrigez la source de build. Ne « continuez pas quand même ».
Task 2: Check kernel and boot mode (UEFI vs legacy)
cr0x@server:~$ uname -r
6.12.0-0.el10.x86_64
cr0x@server:~$ test -d /sys/firmware/efi && echo UEFI || echo BIOS
UEFI
Signification : La version du noyau vous dit sur quoi vous déboguez. Le mode de démarrage compte pour grub, secure boot et le partitionnement des disques.
Décision : Standardisez sur UEFI pour les nouvelles constructions sauf contrainte plateforme imposant BIOS.
Task 3: Identify disks and ensure you partitioned the right one
cr0x@server:~$ lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINTS,MODEL
NAME SIZE TYPE FSTYPE MOUNTPOINTS MODEL
sda 200G disk QEMU HARDDISK
├─sda1 600M part vfat /boot/efi
├─sda2 1G part ext4 /boot
└─sda3 198.4G part LVM2_member
├─cs-root 40G lvm xfs /
├─cs-var 80G lvm xfs /var
└─cs-home 20G lvm xfs /home
Signification : Vous voyez la table de partitions et ce qui est monté. Si vous voyez votre disque de données qui contient /boot, vous avez déjà passé une mauvaise journée.
Décision : Si le mauvais disque a été utilisé, réinstallez. Essayer de « corriger plus tard » coûte généralement plus qu’un rebuild propre.
Task 4: Verify filesystem capacity and inode headroom
cr0x@server:~$ df -hT
Filesystem Type Size Used Avail Use% Mounted on
/dev/mapper/cs-root xfs 40G 3.2G 37G 8% /
/dev/mapper/cs-var xfs 80G 12G 68G 15% /var
/dev/sda2 ext4 1020M 238M 713M 26% /boot
cr0x@server:~$ df -i
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/mapper/cs-var 41943040 92321 41850719 1% /var
Signification : Disponibilité d’espace et d’inodes. Le CI peut manquer d’inodes bien avant l’espace disque, surtout avec des écosystèmes qui aiment les petits fichiers.
Décision : Si /var est petit ou l’utilisation des inodes monte vite, allouez plus à /var maintenant. Le vous du futur ne le fera pas calmement.
Task 5: Check LVM health and free extents for growth
cr0x@server:~$ vgs
VG #PV #LV #SN Attr VSize VFree
cs 1 3 0 wz--n- 198.38g 58.38g
cr0x@server:~$ lvs -a -o lv_name,vg_name,lv_size,lv_attr,data_percent,metadata_percent
LV VG LSize Attr Data% Meta%
root cs 40.00g -wi-ao----
var cs 80.00g -wi-ao----
home cs 20.00g -wi-ao----
Signification : Vous avez de l’espace libre dans le VG pour agrandir /var quand le CI deviendra exigeant. Si vous voyez VFree = 0, vous l’avez dimensionné au maximum.
Décision : Laissez de la marge dans le VG. « On a utilisé toute la capacité disque » n’est pas un accomplissement ; c’est un incident futur.
Task 6: Confirm update cadence and repo health
cr0x@server:~$ dnf repolist
repo id repo name
baseos CentOS Stream 10 - BaseOS
appstream CentOS Stream 10 - AppStream
cr0x@server:~$ dnf check-update
Last metadata expiration check: 0:12:17 ago on Tue 06 Feb 2026 09:10:44 AM UTC.
kernel.x86_64 6.12.2-0.el10 baseos
Signification : Les repos sont accessibles, les métadonnées sont à jour, et des mises à jour existent. Sur Stream, les mises à jour font partie du jeu.
Décision : Si l’expiration des métadonnées est énorme ou que la liste de repos est vide, corrigez le DNS/proxy/mirrors avant de faire confiance à l’hôte pour le CI.
Task 7: Validate time sync (CI hates skew)
cr0x@server:~$ timedatectl
Local time: Tue 2026-02-06 09:23:18 UTC
Universal time: Tue 2026-02-06 09:23:18 UTC
RTC time: Tue 2026-02-06 09:23:18
Time zone: UTC (UTC, +0000)
System clock synchronized: yes
NTP service: active
RTC in local TZ: no
cr0x@server:~$ chronyc tracking
Reference ID : 0A0B0C0D (ntp1.example)
Stratum : 3
Last offset : -0.000021 seconds
RMS offset : 0.000112 seconds
Signification : L’horloge est synchronisée. La signature des jetons, TLS, les timestamps d’artefacts et les builds distribués se cassent de façons stupides avec une dérive temporelle.
Décision : Si NTP n’est pas actif, corrigez-le avant de déboguer des « »échecs TLS aléatoires ».
Task 8: Inspect network config the NetworkManager way
cr0x@server:~$ nmcli -t -f NAME,DEVICE,TYPE,STATE con show --active
Wired connection 1:ens192:802-3-ethernet:activated
cr0x@server:~$ nmcli dev show ens192 | egrep 'IP4.ADDRESS|IP4.GATEWAY|IP4.DNS'
IP4.ADDRESS[1]: 10.20.30.40/24
IP4.GATEWAY: 10.20.30.1
IP4.DNS[1]: 10.20.30.10
Signification : Vous avez un profil de connexion actif et des IP/DNS sensés. Si le CI ne peut pas résoudre les repos de paquets, c’est ici que vous commencez.
Décision : Si le DNS pointe vers quelque chose d’étrange (comme un routeur grand public), corrigez-le. Ne « contournez » pas avec des entrées /etc/hosts.
Task 9: Check SELinux mode and recent denials
cr0x@server:~$ getenforce
Enforcing
cr0x@server:~$ sudo ausearch -m avc -ts recent | tail -n 5
type=AVC msg=audit(1738833941.112:842): avc: denied { name_connect } for pid=2213 comm="podman" dest=53 scontext=system_u:system_r:container_t:s0 tcontext=system_u:object_r:dns_port_t:s0 tclass=tcp_socket permissive=0
Signification : SELinux est en enforcing et vous avez un déni AVC impliquant un conteneur tentant d’atteindre le DNS. Ce n’est pas « SELinux qui embête » ; c’est un signal sur l’étiquetage/la politique et les règles réseau des conteneurs.
Décision : Analysez le contexte et la politique nécessaire ; ne désactivez pas SELinux globalement. Corrigez la cause racine (politique réseau de conteneur, étiquetage du port DNS ou configuration du runtime de conteneur).
Task 10: Verify firewalld state and open ports
cr0x@server:~$ sudo systemctl status firewalld --no-pager
● firewalld.service - firewalld - dynamic firewall daemon
Loaded: loaded (/usr/lib/systemd/system/firewalld.service; enabled; preset: enabled)
Active: active (running) since Tue 2026-02-06 09:02:11 UTC; 22min ago
cr0x@server:~$ sudo firewall-cmd --get-active-zones
public
interfaces: ens192
cr0x@server:~$ sudo firewall-cmd --list-services
ssh
Signification : Le pare-feu est actif, l’interface est dans la zone public, seul SSH est ouvert. Bon point de départ.
Décision : Si vous avez besoin de node_exporter, ajoutez le port/service explicitement. Si vous voyez « services: dhcpv6-client samba cockpit whatever », nettoyez ça.
Task 11: Check system resource pressure (CPU, memory, IO) the fast way
cr0x@server:~$ uptime
09:27:51 up 1:12, 1 user, load average: 0.26, 0.18, 0.09
cr0x@server:~$ free -h
total used free shared buff/cache available
Mem: 15Gi 1.8Gi 11Gi 170Mi 2.4Gi 13Gi
Swap: 4.0Gi 0B 4.0Gi
Signification : La charge est faible, la mémoire est saine, le swap inutilisé. Si les jobs CI sont lents, ce n’est probablement pas une saturation CPU brute en ce moment.
Décision : Si la charge est élevée et que la mémoire disponible est faible, décidez d’ajouter de la RAM, réduire la concurrence, ou isoler les jobs bruyants.
Task 12: Spot IO bottlenecks and latency spikes
cr0x@server:~$ iostat -xz 1 3
Linux 6.12.0-0.el10.x86_64 (runner01) 02/06/2026 _x86_64_ (4 CPU)
avg-cpu: %user %nice %system %iowait %steal %idle
12.10 0.00 3.20 8.60 0.00 76.10
Device r/s rkB/s rrqm/s %rrqm r_await rareq-sz w/s wkB/s wrqm/s %wrqm w_await wareq-sz aqu-sz %util
sda 8.00 320.0 0.00 0.00 6.20 40.00 45.00 2048.0 2.00 4.26 18.40 45.51 0.92 68.00
Signification : iowait non négligeable, write await assez élevé et forte utilisation disque. Profil classique de runner CI : écritures lourdes, churn de métadonnées et caches.
Décision : Envisagez un stockage plus rapide, séparer les disques build/workspace, ou déplacer des caches vers tmpfs/ramdisk de façon sélective (avec limites). Pensez aussi à réduire le nombre de jobs concurrents par nœud.
Task 13: Confirm journald/log retention won’t eat /var
cr0x@server:~$ sudo journalctl --disk-usage
Archived and active journals take up 1.2G in the file system.
cr0x@server:~$ sudo grep -E 'SystemMaxUse|RuntimeMaxUse' /etc/systemd/journald.conf
#SystemMaxUse=
#RuntimeMaxUse=
Signification : Les journaux utilisent déjà de l’espace, et il n’y a pas de limite explicite. Sur des nœuds CI bavards, cela grossit jusqu’à atteindre la limite du système de fichiers.
Décision : Définissez SystemMaxUse (et peut-être SystemMaxFileSize) à une limite raisonnable adaptée à la taille de votre /var.
Task 14: Validate container storage location and growth
cr0x@server:~$ sudo podman info --format '{{.Store.GraphRoot}}'
/var/lib/containers/storage
cr0x@server:~$ sudo du -sh /var/lib/containers/storage
6.4G /var/lib/containers/storage
Signification : Les couches de conteneurs vivent sous /var. Voilà pourquoi la taille de /var compte.
Décision : Si vous exécutez des builds conteneur lourds, envisagez de placer le stockage des conteneurs sur son propre LV (ou disque dédié) pour éviter d’affamer l’état système.
Task 15: Check cgroups v2 and container compatibility
cr0x@server:~$ stat -fc %T /sys/fs/cgroup/
cgroup2fs
Signification : Vous êtes sur cgroups v2. Certains anciens outils conteneurs et agents de monitoring supposent encore les sémantiques v1.
Décision : Assurez-vous que votre outillage CI supporte cgroups v2. Sinon, mettez à jour l’outillage plutôt que de rétrograder le comportement OS à moins d’y être contraint.
Task 16: Check virtualization readiness (if you run KVM/libvirt runners)
cr0x@server:~$ lscpu | egrep 'Virtualization|Vendor ID|Model name'
Vendor ID: GenuineIntel
Model name: Intel(R) Xeon(R) CPU
Virtualization: VT-x
cr0x@server:~$ lsmod | egrep 'kvm|kvm_intel'
kvm_intel 503808 0
kvm 1490944 1 kvm_intel
Signification : Le CPU supporte la virtualisation et les modules KVM sont chargés.
Décision : Si la virtualisation n’est pas disponible, ne perdez pas de temps à déboguer libvirt. Corrigez les réglages BIOS ou choisissez un autre hôte/profil hyperviseur.
Playbook de diagnostic rapide
Voici le playbook que je veux voir accroché au mur près du cluster CI. Quand les builds ralentissent ou que les installs deviennent capricieuses, vous ne « fouillez pas ». Vous suivez le chemin le plus court vers le goulot.
Première étape : prouver que ce n’est pas DNS/accès aux repos
- Vérifiez la résolution DNS et l’accessibilité aux repos/mirrors. Les échecs CI se manifestent souvent par « installation de paquet échouée » mais la cause est la résolution de noms ou la config proxy.
- Confirmez la synchronisation temporelle. Les échecs TLS et les erreurs de métadonnées de repo peuvent venir d’une dérive temporelle.
cr0x@server:~$ getent hosts mirror.internal
10.20.30.50 mirror.internal
cr0x@server:~$ chronyc tracking | head
Reference ID : 0A0B0C0D (ntp1.example)
Décision : Si le DNS est lent ou instable, corrigez cela avant de toucher quoi que ce soit d’autre. Vous ne pouvez pas optimiser un système qui ne trouve pas ses dépendances.
Deuxième étape : vérifier la pression sur le stockage et la latence IO
- Disque plein ? Inodes pleins ? Thin pool plein ? Ces problèmes apparaissent comme « échecs aléatoires ».
- Latence IO (await) élevée ? Voilà pourquoi les builds sont lents.
cr0x@server:~$ df -hT | sed -n '1,6p'
Filesystem Type Size Used Avail Use% Mounted on
/dev/mapper/cs-root xfs 40G 3.2G 37G 8% /
/dev/mapper/cs-var xfs 80G 78G 2.0G 98% /var
cr0x@server:~$ iostat -xz 1 2 | tail -n 5
sda 9.00 360.0 0.00 0.00 8.10 40.00 60.00 2600.0 1.00 1.64 25.90 43.33 1.40 85.00
Décision : Si /var est utilisé à 98%, stoppez. Nettoyez caches/logs ou agrandissez le LV. Si le disk await est élevé, réduisez la concurrence ou montez en gamme de stockage.
Troisième étape : vérifier CPU, mémoire et contention du scheduler
- Charge élevée avec faible IO wait ? Probablement saturation CPU.
- Peu de mémoire disponible avec activité swap ? Pression mémoire ou jobs incontrôlés.
- Temps de steal élevé ? Contention chez l’hyperviseur ; votre host VM est sur-alloué.
cr0x@server:~$ vmstat 1 5
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
1 0 0 1123456 81234 2234560 0 0 5 80 150 300 12 3 77 8 0
3 1 0 123456 40000 900000 0 0 0 2000 500 1200 40 10 10 40 0
Décision : Si st (steal) est non nul et persistant, le goulot réel est en amont. Escaladez auprès de l’équipe virtualisation ou déplacez les runners.
Erreurs courantes : symptômes → cause racine → correction
1) Symptom: CI jobs randomly fail with “No space left on device” but df shows free space
Cause racine : Inodes épuisés (trop de petits fichiers) ou un autre système de fichiers plein (souvent /var ou /tmp).
Correction : Vérifiez df -i et l’usage par point de montage. Augmentez la capacité d’inodes en redimensionnant/recréant le système de fichiers (long terme) et réduisez le churn de fichiers (nettoyage, rétention d’artefacts). Séparez /var tôt.
2) Symptom: Builds are slow only on some runners
Cause racine : Différences de latence de stockage (différents niveaux de disque), ou temps steal VM sur hyperviseurs sur-alloués.
Correction : Comparez iostat -xz et vmstat entre runners. Standardisez le backend disque. Ne mélangez pas nœuds « rapides » et « lents » dans le même pool à moins que votre scheduler soit conscient de la topologie.
3) Symptom: After update, a service won’t start; logs mention permission denied
Cause racine : Déni SELinux après un changement de chemin, un nouveau port ou un durcissement d’un unit.
Correction : Utilisez ausearch -m avc pour trouver les dénis, puis corrigez l’étiquetage avec restorecon ou ajustez la politique. Ne désactivez pas SELinux globalement.
4) Symptom: Host boots into dracut emergency shell after reboot
Cause racine : UUID incorrect dans fstab, drivers initramfs manquants, ou changement d’ordre des disques dans un template VM.
Correction : Démarrez en rescue, vérifiez blkid et /etc/fstab, reconstruisez initramfs si nécessaire. Préférez les montages par UUID et un nommage de périphérique stable.
5) Symptom: DNF is painfully slow, “metadata download” hangs
Cause racine : Problèmes DNS, MTU proxy étrange, ou problèmes de sélection de mirror.
Correction : Validez le DNS (getent hosts), vérifiez le MTU, et favorisez des mirrors internes pour environnements CI à fort trafic.
6) Symptom: Container builds fail after OS update, but host builds still work
Cause racine : Attentes cgroups v2, contraintes réseau rootless, ou changements de politique SELinux affectant le stockage des conteneurs.
Correction : Confirmez les cgroups (stat -fc %T /sys/fs/cgroup), vérifiez podman info, consultez les dénis AVC et mettez à jour votre outillage/images conteneur.
7) Symptom: SSH works from some subnets but not others
Cause racine : Attribution de zone firewalld ou mismatch d’ACL réseau en amont.
Correction : Vérifiez les zones actives et les interfaces, puis définissez des règles explicites. Ne désactivez pas firewalld parce que le réseau est compliqué.
Trois mini-récits d’entreprise (ceux dont on ne se vante pas)
Mini-story 1: The incident caused by a wrong assumption
L’entreprise avait un cluster CI bien rangé : une douzaine de VM, quelques caches de build, et une fenêtre de patch hebdomadaire que personne n’utilisait jusqu’à ce que quelque chose casse. Ils ont décidé de passer d’un clone RHEL à CentOS Stream pour « compatibilité anticipée ». Ça semblait responsable.
L’hypothèse erronée était subtile : ils supposaient que Stream se comportait comme leur ancien rebuild sur un point précis — la stabilité des repos. Leur pipeline a commencé à tirer des mises à jour pendant la journée de travail parce que quelqu’un avait laissé un timer dnf -y update planifié sur les runners. Un après-midi, une mise à jour d’outil a atterri, la version mineure du compilateur a changé, et un sous-ensemble de builds a commencé à produire des artefacts légèrement différents. Rien ne tombait en échec évident au début. Les comparaisons de sommes de contrôle, si. Puis le processus de release s’est arrêté comme s’il avait heurté un mur.
Les équipes d’ingénierie ont débattu de « builds nondéterministes » et « peut-être le cache est corrompu ». Ce n’était ni l’un ni l’autre. Les runners se mettaient à jour en cours d’exécution. La partie douloureuse n’était pas la correction ; elle était de réaliser qu’ils n’avaient aucune politique pour quand et comment les mises à jour arrivent sur l’infra CI.
Ils ont récupéré en gelant les mises à jour pendant les heures de bureau, en construisant des images golden chaque semaine et en les déployant d’abord dans un pool CI de staging. Stream n’était pas le méchant. Le méchant était la croyance que les mises à jour sont quelque chose qu’on lance quand on y pense.
Mini-story 2: The optimization that backfired
Une autre organisation avait un plan de stockage malin : ils utilisaient le thin provisioning LVM pour les workspaces CI parce que ça leur permettait « d’allouer » d’énormes volumes sans acheter plus de disque. Sur le papier c’était élégant. En réalité c’était un thin pool partagé par trop de projets enthousiastes.
Ils ont aussi « optimisé » en augmentant la concurrence des jobs. Les builds sont devenus plus rapides — jusqu’à ce qu’ils ne le soient plus. Un matin, plusieurs runners ont commencé à échouer avec des erreurs de système de fichiers. Le thin pool a atteint 100% d’utilisation des données. Le thin provisioning ne tombe pas en panne en douceur ; il tombe comme un panneau qui s’ouvre. Les écritures se bloquent, les systèmes de fichiers paniquent, et tout le monde apprend soudainement ce que « metadata percent » signifie.
La correction immédiate a été peu reluisante : arrêter le monde, supprimer des caches et étendre le stockage sous-jacent. La correction long terme était ennuyeuse : mettre en place de l’alerte sur l’utilisation des thin pools, appliquer des quotas par projet et arrêter la surallocation d’un stockage sans garde-fous opérationnels.
Ils ont gardé le thin provisioning, mais uniquement là où ils avaient de la supervision et une responsabilité claire. L’optimisation n’était pas mauvaise. Elle était prématurée, non surveillée et vendue en interne comme « capacité gratuite », ce qui est le mensonge le plus coûteux en stockage.
Mini-story 3: The boring but correct practice that saved the day
Une troisième équipe avait la réputation d’être « lente » parce qu’elle insistait sur des builds basés sur Kickstart et une pipeline d’images de base stricte. Les développeurs voulaient la liberté de bidouiller les runners directement. Les SRE ont dit non et ont été chahutés en réunion.
Puis une mise à jour du noyau dans le labo a révélé une régression affectant un pilote NIC spécifique sous forte charge. Quelques runners ont commencé à perdre des connexions réseau pendant les uploads d’artefacts. Ça ressemblait à de la flakiness aléatoire — exactement le genre qui fait perdre des semaines.
Parce que l’équipe avait une pipeline d’images golden, ils ont pu revenir en arrière vers le noyau connu bon sur toute la flotte de façon contrôlée. Plus important, ils ont pu reproduire le problème en lançant des runners de test depuis les deux images et comparer le comportement sous charges identiques. Pas d’archéologie. Pas de « qui a changé quoi ». Juste une expérience contrôlée.
Le postmortem a été presque décevant de calme. Leur pratique ennuyeuse — images quasiment immuables, mises à jour contrôlées et pool de staging — a transformé un potentiel festival de reproches inter-équipes en un petit incident opérationnel contenu. Ils ont livré à temps. Personne n’a rédigé un fil Slack dramatique. C’est une victoire.
Blague #2 : Le meilleur runner CI ressemble à une bonne plomberie — personne ne le remarque jusqu’à ce que quelqu’un ait tenté de « l’optimiser ».
Listes de contrôle / plan étape par étape
Plan d’installation étape par étape (interactif ou guidé par Kickstart)
- Décidez du rôle : runner CI, hyperviseur de labo ou hôte de test polyvalent. Cela oriente le stockage et la sélection des paquets.
- Choisissez le boot UEFI (sauf contrainte) et confirmez le réglage du firmware VM avant l’installation.
- Sélectionnez l’installation minimale plus les paquets requis ; évitez le GUI sauf besoin explicite.
- Stockage :
- Créez ESP et partitions /boot fixes.
- Créez PV LVM et VG avec de l’espace libre.
- Créez un LV séparé pour /var dimensionné pour logs + conteneurs + caches.
- Réseau : configurez via NetworkManager, assurez-vous que le DNS pointe vers un résolveur fiable.
- Temps : définissez le fuseau horaire sur UTC pour les flottes serveur ; activez NTP.
- Utilisateurs : créez un utilisateur admin ; verrouillez SSH sur clés si possible.
- Sécurité : laissez SELinux en enforcing ; gardez firewalld activé.
- Politique de mise à jour : décidez du rythme de patch et si les runners se mettent à jour eux-mêmes. Mon conseil : pas de mises à jour automatiques sur les runners sans portes de validation.
- Snapshot/image golden : capturez une image de base seulement après que les commandes de validation soient passées.
Checklist de validation post-install (exécutez avant d’ajouter au pool CI)
- Identité OS correcte :
/etc/os-release - Mode de boot correct : vérification UEFI
- Disposition disque correcte :
lsblk,df -hT,df -i - Marge LVM disponible :
vgs - Repos sains :
dnf repolist,dnf check-update - Synchronisation horaire :
timedatectl,chronyc tracking - Réseau correct : sortie
nmcliconforme à l’IP/DNS prévu - SELinux en enforcing :
getenforce; pas de spam AVC inattendu - firewalld activé et minimal :
firewall-cmd - Sanity perf de base :
iostat,vmstatsous un build d’échantillon
Checklist opérationnelle (hebdomadaire)
- Appliquer les mises à jour dans une fenêtre contrôlée ; dérouler via le pool de staging d’abord.
- Vérifier les tendances de croissance de /var ; limiter journald ; nettoyer les couches conteneurs.
- Vérifier NTP ; surveiller la dérive temporelle sur les VMs.
- Revoir les dénis AVC ; résoudre les récurrences correctement.
- Confirmer que la concurrence CI correspond à la capacité disque, pas à l’optimisme.
FAQ
1) Is CentOS Stream 10 stable enough for CI?
Oui, si votre CI est conçu pour absorber le changement : déploiements par étapes, images reproductibles et politique de patch. Si vos runners CI sont des animaux maintenus à la main, Stream exposera cela rapidement.
2) Should I use Stream 10 for production?
Parfois, mais ne le traitez pas comme le choix par défaut. Pour la production, la question n’est pas tant « est-ce que ça peut fonctionner » que « avez-vous la discipline opérationnelle pour gérer des mises à jour qui bougent plus vite ? » Beaucoup d’organisations ne l’ont pas.
3) Minimal install or full server install?
Minimal. Ajoutez ce dont vous avez besoin. Chaque paquet en plus augmente la surface de mise à jour et les risques de conflit dans des environnements CI.
4) Do I really need a separate /var?
Si vous exécutez des charges CI, oui. /var est l’endroit où l’état système et les débris CI à fort turnover se rencontrent. Les séparer est une assurance peu coûteuse.
5) XFS or ext4 for CI runners?
XFS est un bon défaut pour / et /var dans les systèmes proches de RHEL. Gardez ext4 pour /boot. Ne mélangez pas des systèmes de fichiers exotiques sauf si votre équipe a un playbook de récupération et une vraie expérience.
6) Should I disable SELinux to make container builds easier?
Non. Utilisez les logs AVC pour corriger étiquetage/politique. Désactiver SELinux échange un problème de configuration solvable contre un risque permanent et un comportement incohérent entre environnements.
7) How do I stop runners from drifting over time?
Images golden + gestion de configuration. Reconstruisez les runners régulièrement. Si un runner est « spécial », il est aussi peu fiable.
8) My DNF installs are slow in CI. What’s the best fix?
Utilisez un miroir interne ou un proxy de cache, et corrigez le DNS. Ensuite, ajustez le comportement de cache de DNF. Le CI amplifie les inefficacités du gestionnaire de paquets en coûts réels.
9) Containers or VMs for CI jobs?
Conteneurs pour la plupart des builds et tests. VMs quand vous devez tester le comportement au boot, les interactions noyau ou les services système dans un environnement réaliste.
10) What’s the single most common bottleneck on Stream-based CI hosts?
Le stockage. Plus précisément : /var qui se remplit, bloat des couches conteneurs et latence IO sous concurrence. Le CPU vient généralement en second.
Conclusion : prochaines étapes qui réduisent réellement le bruit du pager
CentOS Stream 10 est le bon type d’inconfort pour les labos et le CI : il vous pousse vers des installations disciplinées, des politiques de mise à jour claires et une infrastructure capable de tolérer le changement. Si vous l’installez comme un OS de loisir, il se comportera comme tel. Si vous l’installez comme en production, il devient un système d’alerte précoce efficace pour le « prochain RHEL ».
Prochaines étapes pratiques :
- Rédigez (ou corrigez) votre Kickstart pour que la sélection du disque, le dimensionnement de /var et les paramètres de sécurité soient explicites.
- Mettez en place un pool CI de staging qui patch d’abord ; ne promouvez au pool principal qu’après un jour de runs propres.
- Limitez journald, nettoyez le stockage conteneurs et surveillez l’utilisation de /var et la consommation d’inodes.
- Adoptez le playbook de diagnostic rapide et faites-en la réponse par défaut à « le CI est lent ».
- Décidez votre politique de mises à jour par écrit. Puis appliquez-la avec de l’automatisation, pas de bonnes intentions.