Le symptôme est ennuyeux. « TASK ERROR: startup for container ‘101’ failed. » L’impact, lui, ne l’est pas. Vous êtes d’astreinte, un service est en panne, et l’erreur ressemble à une note de rançon rédigée par trois sous-systèmes qui ne s’aiment pas : LXC, cgroups et AppArmor.
Si vous traitez ces messages comme du bruit, vous allez tourner en rond — activer/désactiver le « nesting », redémarrer des nœuds et prier le kernel. Si vous apprenez à les lire, vous réparerez souvent en quelques minutes, et vous saurez si vous avez affaire à une défaillance au niveau hôte (cgroups), à une politique (AppArmor) ou à un simple plantage lié au système de fichiers/map d’ID.
Un modèle mental : ce qui doit réussir pour démarrer un LXC
Le démarrage LXC sur Proxmox est une chaîne de dépendances. Quand il échoue, l’erreur est généralement exacte — elle n’est simplement pas contextuelle. Il vous faut cette chaîne en tête pour localiser le maillon cassé.
La chaîne, en termes simples
- Proxmox appelle LXC via
lxc-start(emballé par les outilspve-container), créant un arbre de processus pour le conteneur. - Les namespaces du kernel sont configurés : mount, PID, réseau, user (pour les non privilégiés), etc.
- Les cgroups sont créés et les contrôleurs attachés (CPU, mémoire, pids, io). Si les cgroups sont mal montés, que des contrôleurs manquent ou sont incompatibles avec ce que la config attend, le démarrage s’arrête.
- La politique de sécurité est appliquée : profil AppArmor chargé/appliqué ; filtres seccomp éventuellement attachés.
- Le système de fichiers racine est monté : bind mounts (comme
/dev,/proc,/sys), volume de stockage attaché, montages optionnels depuismp0, etc. - L’init à l’intérieur du conteneur démarre (souvent systemd). Si systemd ne peut pas monter les cgroups ou se voit refuser l’accès, il peut quitter immédiatement — donnant l’impression que « le conteneur ne démarre pas ».
Conclusion pratique : les erreurs cgroups sont souvent au niveau hôte (configuration du nœud, options de démarrage du kernel, agencement des montages), tandis que les erreurs AppArmor sont au niveau politique (le profil refuse une action spécifique, courant après activation du nesting, FUSE, CIFS ou accès à des périphériques privilégiés).
Une citation à garder à l’œil : « L’espoir n’est pas une stratégie. »
— idée paraphrasée souvent attribuée à des responsables opérations. Le message est clair : arrêtez de deviner et suivez la piste des preuves.
Petite blague #1 : Si votre conteneur ne démarre pas à cause des cgroups, félicitations — vous avez découvert un problème qui ne se résout pas avec plus de YAML.
Playbook de diagnostic rapide (vérifier d’abord/puisensuite/ensuite)
Voici la voie « j’ai cinq minutes ». Ce n’est pas exhaustif ; c’est conçu pour trouver rapidement le goulot d’étranglement et décider si vous pouvez réparer sur place ou s’il faut vider le nœud.
Premier : confirmer que ce n’est pas une config simple ou un problème de stockage
- Vérifiez le journal de tâche de Proxmox (il inclut généralement le premier échec concret).
- Vérifiez la configuration du conteneur pour des montages suspects, le nesting et les paramètres non privilégiés.
- Confirmez que le volume rootfs existe et peut être monté sur l’hôte.
Deuxième : décider si ça sent les cgroups ou AppArmor
- Si vous voyez des chaînes comme
cgroup,cgroup2,controllers,cgroupfs,failed to mount /sys/fs/cgroup: partez sur cgroups en priorité. - Si vous voyez
apparmor="DENIED",profile=,operation=,audit: partez sur AppArmor en priorité. - Si c’est
permission deniedsans lignes d’audit AppArmor, suspectez les ID maps, la propriété des points de montage ou les permissions de stockage.
Troisième : isoler si c’est un conteneur ou le nœud
- Démarrez un petit conteneur connu bon. Si lui aussi échoue, c’est le nœud.
- Vérifiez si d’autres conteneurs démarrent ; si non, arrêtez de tripoter les configs individuelles et réparez la couche hôte.
- Cherchez des changements récents : mise à jour du kernel, upgrade Proxmox, activation/désactivation du nesting, changement du mode AppArmor, changement du mode cgroups, nouveaux points de montage.
Règle décisive : si c’est un problème de montage/contrôleur cgroups au niveau nœud, vous ne le « réparez » pas par conteneur. Vous réparez le nœud ou vous évacuez les charges. Tout le reste est du travail qui donne une meilleure sensation au clavier mais peu de valeur réelle.
Comment lire les erreurs cgroups et AppArmor sans deviner
Où la vérité est écrite
L’interface Proxmox affiche une erreur résumée. Les détails exploitables se trouvent généralement dans un de ces endroits :
- Le journal de tâche (ce que l’UI montre, parfois tronqué)
journalctlsur l’hôte (messages systemd et kernel)/var/log/syslogsur les hôtes basés Debian- Le buffer d’anneau du kernel (
dmesg) - Les logs propres à LXC (augmenter la verbosité via un démarrage debug)
- Les logs d’audit pour les refus AppArmor (souvent visibles via le journal)
Lire les erreurs cgroups : les mots-clés comptent
Les erreurs cgroups tombent souvent dans quelques catégories :
- Problèmes de montage : LXC ne peut pas monter ou accéder à
/sys/fs/cgroup(courant avec attentes v2 vs configuration hôte). - Disponibilité des contrôleurs : les contrôleurs requis (comme
memoryoupids) ne sont pas disponibles ou pas délégués correctement. - Problèmes de permission/propriété : délégation bloquée lors d’un usage de cgroups gérés par systemd et de conteneurs non privilégiés.
- Confusion de hiérarchie mixte : incompatibilités v1 vs v2 vs hybrides, parfois après des upgrades.
Quand vous voyez une erreur comme Failed to mount "cgroup" ou cgroup2: No such file or directory, n’interprétez pas cela comme « le conteneur est cassé ». Interprétez : la disposition du système de fichiers cgroup de l’hôte ne correspond pas à ce que LXC attend pour la configuration du conteneur et le système d’init de l’hôte.
Lire les erreurs AppArmor : la ligne de refus est un mini-rapport d’incident
Une ligne de refus AppArmor inclut : timestamp, profil, opération, ressource demandée et parfois le « name » (chemin). Cela suffit généralement à décider si vous devez :
- Ajuster les fonctionnalités du conteneur (nesting, keyctl, fuse, types de montage)
- Corriger un problème de permission/étiquetage d’un chemin sur l’hôte
- Basculer temporairement le profil AppArmor en mode complain tout en collectant des preuves
- Arrêter de faire la chose risquée (option que je préfère en production)
AppArmor ne « casse » pas les conteneurs au hasard. Il fait exactement ce que vous lui avez demandé : bloquer des actions hors politique. L’ennui est que souvent nous ne nous étions pas rendu compte de ce que nous avions demandé.
Tâches pratiques : commandes, sorties attendues et décisions
Voici les manœuvres que j’utilise réellement sous pression. Chacune inclut la décision que vous devez tirer de la sortie. Faites-les dans l’ordre si vous êtes perdu ; ou piochez si vous avez déjà une piste solide.
Task 1: Pull the exact failure from Proxmox task output (CLI)
cr0x@server:~$ pct start 101
starting container 101
lxc-start: 101: conf.c: run_buffer: 312 Script exited with status 1
lxc-start: 101: start.c: __lxc_start: 2107 Failed to initialize container "101"
TASK ERROR: startup for container '101' failed
Ce que cela signifie : LXC lui-même a échoué ; Proxmox se contente de remonter l’erreur. Le message est générique ; vous avez besoin de logs plus profonds.
Décision : Allez immédiatement consulter les logs du journal et les preuves AppArmor/cgroups (tâches suivantes). Ne modifiez pas une configuration au hasard.
Task 2: Get container config and look for “sharp edges”
cr0x@server:~$ cat /etc/pve/lxc/101.conf
arch: amd64
cores: 2
hostname: app-101
memory: 2048
net0: name=eth0,bridge=vmbr0,firewall=1,ip=dhcp,type=veth
ostype: debian
rootfs: local-lvm:vm-101-disk-0,size=8G
unprivileged: 1
features: nesting=1,keyctl=1
mp0: /srv/shared,mp=/mnt/shared,backup=0
Ce que cela signifie : Ce conteneur est non privilégié, avec nesting+keyctl, et un bind mount hôte (mp0). Ces trois éléments sont des sources courantes d’erreurs AppArmor et de permissions.
Décision : Si vous avez récemment activé le nesting ou ajouté mp0, suspectez d’abord AppArmor ou le mappage/ownership des IDs.
Task 3: Check host cgroup mode (v1 vs v2)
cr0x@server:~$ stat -fc %T /sys/fs/cgroup
cgroup2fs
Ce que cela signifie : L’hôte utilise la hiérarchie unifiée cgroups v2.
Décision : Si le conteneur ou LXC attend des montages legacy v1 (rare sur PVE moderne, mais possible avec des configs anciennes), planifiez l’alignement. Vérifiez aussi la disponibilité des contrôleurs (tâche suivante).
Task 4: Verify required cgroup controllers are available and enabled
cr0x@server:~$ cat /sys/fs/cgroup/cgroup.controllers
cpuset cpu io memory hugetlb pids rdma
Ce que cela signifie : Ce sont les contrôleurs supportés par le kernel et disponibles dans le cgroup racine. Si memory ou pids manque, les conteneurs qui définissent des limites mémoire/pids peuvent échouer ou mal se comporter.
Décision : Des contrôleurs manquants indiquent un problème de configuration hôte/kernel. Arrêtez le débogage par conteneur et réparez le nœud.
Task 5: Check whether systemd has delegated controllers appropriately
cr0x@server:~$ systemctl show -p DefaultControllers
DefaultControllers=cpu cpuacct io memory pids
Ce que cela signifie : Sur les systèmes systemd, la délégation et la configuration des contrôleurs influencent si LXC peut créer/gérer des cgroups. Les versions récentes de systemd gèrent v2 différemment des anciennes.
Décision : Si vous voyez des anomalies (contrôleurs attendus manquants) après une mise à jour, suspectez l’interaction systemd+cgroups. Vérifiez les changements de paquets récents et envisagez un reboot contrôlé après correction des paramètres de boot.
Task 6: Inspect mounts for cgroup weirdness (hybrid layouts, missing mount)
cr0x@server:~$ mount | grep -E 'cgroup|cgroup2'
cgroup2 on /sys/fs/cgroup type cgroup2 (rw,nosuid,nodev,noexec,relatime)
Ce que cela signifie : Montage v2 propre. Si vous voyez plusieurs montages cgroup v1 en plus de cgroup2, vous pourriez être en mode hybride.
Décision : Si LXC se plaint de monter cgroup2 alors que vous êtes en v1 ou hybride, alignez l’hôte (paramètres de boot) avec ce que PVE attend pour votre version.
Task 7: Pull the relevant journal slice for the container start attempt
cr0x@server:~$ journalctl -u pve-container@101.service -n 200 --no-pager
Dec 26 10:18:01 pve1 systemd[1]: Starting PVE LXC Container: 101...
Dec 26 10:18:01 pve1 lxc-start[18277]: lxc-start: 101: cgfsng.c: cgroup_init: 846 Failed to create cgroup /lxc/101
Dec 26 10:18:01 pve1 lxc-start[18277]: lxc-start: 101: cgfsng.c: cgroup_init: 850 No such file or directory
Dec 26 10:18:01 pve1 systemd[1]: pve-container@101.service: Main process exited, code=exited, status=1/FAILURE
Dec 26 10:18:01 pve1 systemd[1]: pve-container@101.service: Failed with result 'exit-code'.
Ce que cela signifie : LXC n’a pas pu créer le chemin cgroup voulu. C’est généralement l’agencement du système de fichiers cgroup, la délégation, ou une configuration racine cgroup obsolète/incorrecte.
Décision : Restez sur la piste cgroups. Cherchez des permissions sur /sys/fs/cgroup, des problèmes de délégation, ou un arbre cgroup systemd cassé.
Task 8: Check AppArmor denials around the failure time
cr0x@server:~$ journalctl -k -g 'apparmor="DENIED"' -n 50 --no-pager
Dec 26 10:18:01 pve1 kernel: audit: type=1400 audit(1735208281.214:915): apparmor="DENIED" operation="mount" profile="lxc-container-default-cgns" name="/sys/kernel/security" pid=18290 comm="(mount)" flags="rw, nosuid, nodev, noexec"
Ce que cela signifie : Le conteneur a tenté une opération de montage bloquée par le profil appliqué. Cela peut empêcher systemd à l’intérieur du conteneur de démarrer.
Décision : Décidez si le montage est nécessaire. S’il provient du nesting/des attentes de systemd, corrigez les fonctionnalités du conteneur ou ajustez la politique de façon contrôlée. Ne désactivez pas AppArmor globalement sans réfléchir.
Task 9: Verify AppArmor is enabled and profiles loaded
cr0x@server:~$ aa-status
apparmor module is loaded.
60 profiles are loaded.
58 profiles are in enforce mode.
2 profiles are in complain mode.
0 processes are unconfined but have a profile defined.
Ce que cela signifie : AppArmor est actif. Si la sortie indiquait « module is not loaded », alors AppArmor n’est pas votre blocage — vos lignes « DENIED » n’existeraient pas.
Décision : Si des refus existent et qu’AppArmor est en mode enforce, traitez ces refus comme des causes premières, pas comme du bruit de fond.
Task 10: Run LXC with debug logging (surgical, noisy, useful)
cr0x@server:~$ lxc-start -n 101 -F -l DEBUG -o /tmp/lxc-101.debug.log
lxc-start: 101: start.c: lxc_init: 846 Container "101" initialized
lxc-start: 101: cgfsng.c: cgroup_init: 846 Failed to create cgroup /lxc/101
lxc-start: 101: start.c: __lxc_start: 2107 Failed to initialize container "101"
Ce que cela signifie : Confirme que l’échec survient pendant l’initialisation des cgroups, pas lors du montage du rootfs ou de la configuration réseau.
Décision : Arrêtez de chasser AppArmor si les logs debug s’arrêtent avant l’application du profil de sécurité. Réparez d’abord les cgroups.
Task 11: Validate the container’s rootfs volume exists and is accessible
cr0x@server:~$ pvesm status
Name Type Status Total Used Available
local dir active 100.00G 42.10G 57.90G
local-lvm lvmthin active 60.00G 25.00G 35.00G
Ce que cela signifie : Le backend de stockage est actif. S’il est inactif, vous pouvez obtenir des « permission denied » ou des échecs de montage trompeurs qui semblent être liés à AppArmor.
Décision : Si le stockage est down, réparez le stockage en premier. Les conteneurs ne peuvent pas monter des disques imaginaires.
Task 12: Confirm the rootfs LV actually exists (LVM-thin example)
cr0x@server:~$ lvs -a -o lv_name,vg_name,lv_size,lv_attr | grep vm-101-disk-0
vm-101-disk-0 pve 8.00g Vwi-a-tz--
Ce que cela signifie : Le volume existe. S’il n’existe pas, le conteneur ne démarrera pas.
Décision : Volume manquant signifie problème de restauration/sauvegarde ou mauvaise référence de stockage dans 101.conf. Corrigez la config ou recréez le volume.
Task 13: Check bind mount source paths and permissions on the host
cr0x@server:~$ ls -ld /srv/shared
drwxr-x--- 2 root root 4096 Dec 25 09:12 /srv/shared
Ce que cela signifie : Pour un conteneur non privilégié, un bind mount possédé par root:root avec des permissions restrictives peut casser le démarrage si LXC tente de configurer la propriété du montage ou si l’init du conteneur attend un accès.
Décision : Ajustez la propriété pour correspondre aux IDs mappés du conteneur (voir la tâche ID map), ou montez en lecture seule, ou évitez de bind-monter des chemins hôtes dans des conteneurs non privilégiés sans planifier la propriété.
Task 14: Inspect ID mapping for an unprivileged container
cr0x@server:~$ grep -E '^(root|lxc)' /etc/subuid /etc/subgid
/etc/subuid:root:100000:65536
/etc/subgid:root:100000:65536
Ce que cela signifie : L’hôte a des plages UID/GID subordonnées pour root, nécessaires aux conteneurs non privilégiés. Si elles manquent, les conteneurs non privilégiés échouent souvent avec des erreurs de permission peu parlantes.
Décision : Si les plages sont manquantes ou trop petites pour votre usage d’idmap, corrigez /etc/subuid et /etc/subgid, puis redémarrez les services concernés ou le nœud.
Task 15: Confirm kernel supports required namespaces and cgroup features
cr0x@server:~$ zgrep -E 'CONFIG_CGROUPS=|CONFIG_USER_NS=|CONFIG_CGROUP_BPF=|CONFIG_CGROUP_FREEZER=' /boot/config-$(uname -r)
CONFIG_CGROUPS=y
CONFIG_USER_NS=y
CONFIG_CGROUP_BPF=y
# CONFIG_CGROUP_FREEZER is not set
Ce que cela signifie : Les fonctionnalités de base sont présentes. L’absence de CONFIG_USER_NS tuerait les conteneurs non privilégiés immédiatement. L’absence du freezer n’est généralement pas fatale mais peut impacter certains comportements/outils.
Décision : Si des configs clés manquent, vous êtes sur le mauvais kernel ou une compilation personnalisée. Utilisez la série de kernels supportée par Proxmox et évitez les bricolages.
Task 16: Check whether the container’s systemd is failing due to cgroup mounting
cr0x@server:~$ pct start 101 --debug
lxc-start: 101: conf.c: run_buffer: 312 Script exited with status 1
lxc-start: 101: conf.c: run_buffer: 313 Script exited with status 1: mount: /sys/fs/cgroup: permission denied
Ce que cela signifie : L’échec se produit au moment du montage des cgroups à l’intérieur du conteneur. Cela peut être un refus AppArmor, une délégation manquante ou un décalage de configuration LXC pour la version de cgroup.
Décision : Corrélez avec les refus AppArmor (Tâche 8). S’il n’y a pas de refus, vérifiez la délégation/permissions cgroup et les fonctionnalités du conteneur.
Modes de défaillance de cgroups qui arrêtent les conteneurs net
1) Hiérarchie unifiée cgroups v2 incompatible avec les attentes
cgroups v2 est la méthode moderne : une seule hiérarchie, sémantiques cohérentes, meilleure délégation. Mais la réalité est problématique : des images de conteneurs plus anciennes, des versions LXC plus anciennes, ou des configurations héritées peuvent supposer des points de montage v1 (comme /sys/fs/cgroup/memory).
Sur Proxmox, l’hôte est typiquement géré par systemd et passe de plus en plus à v2. Si une séquence de démarrage de conteneur tente de monter des contrôleurs v1 ou attend des chemins v1, vous verrez des erreurs de montage ou des « No such file or directory » dans l’init cgroup.
Que faire : Alignez. Préférez utiliser le kernel et la pile LXC supportés par Proxmox comme un ensemble. Évitez de forcer des modes legacy cgroup sauf si vous avez un vrai besoin de compatibilité et un plan de rollback.
2) Contrôleurs disponibles mais non délégués (problème de frontière systemd)
Même quand les contrôleurs existent, le processus créant le conteneur doit avoir l’autorisation de créer des sous-cgroups et d’activer les contrôleurs. Avec cgroups v2, l’activation des contrôleurs est explicite et peut être bloquée par la configuration du cgroup parent.
Les symptômes ressemblent à « Failed to create cgroup » ou « Operation not permitted », parfois seulement pour certains conteneurs (ceux qui définissent des limites, ou ceux démarrés via des unités différentes).
Que faire : Traitez cela comme un problème de configuration nœud. Validez la délégation de systemd et l’arbre cgroup. Si vous avez récemment changé la façon de démarrer les conteneurs (unités personnalisées, wrappers), revenez en arrière en priorité.
3) /sys/fs/cgroup en lecture seule ou cassé à l’intérieur du conteneur
Certaines images de conteneur sont conçues pour des environnements type Docker et supposent pouvoir gérer les cgroups ou monter certains pseudo-systèmes de fichiers. Les conteneurs LXC tournent souvent avec des frontières plus strictes. Si systemd à l’intérieur du conteneur veut monter et que c’est refusé, il quitte rapidement. Le conteneur « démarre » puis « s’arrête » instantanément, ce qui ressemble à un problème Proxmox.
Que faire : Décidez si systemd à l’intérieur du conteneur doit être PID 1. Si oui, ajustez soigneusement les fonctionnalités du conteneur. Sinon, exécutez un init plus simple pour cette charge.
4) Régressions du kernel et paramètres de boot « utiles »
Parfois, le nœud a été mis à jour et le comportement des cgroups a changé. Parfois, quelqu’un a ajouté des paramètres de boot pour un autre problème et a accidentellement cassé les conteneurs. Les cgroups sont sensibles aux flags de ligne de commande du kernel et aux valeurs par défaut de systemd.
Ma posture opérationnelle : gardez les nœuds cohérents. Si vous devez « special-caser » un nœud, vous créez un futur incident à retardement.
Modes de défaillance AppArmor : refus, profils et le piège du « ça marchait hier »
Notions AppArmor essentielles
AppArmor est un contrôle d’accès obligatoire par profil : il limite ce qu’un processus peut faire même si les permissions Unix disent « autorisé ». LXC sur Proxmox utilise des profils AppArmor pour restreindre les conteneurs. Quand quelque chose dans le conteneur tente une opération bloquée (montage, accès aux interfaces kernel, usage de FUSE, etc.), vous obtenez un refus dans le log d’audit du kernel.
1) Le nesting active des comportements bloqués par AppArmor par défaut
Le nesting rend les gens courageux : « lançons Docker dans ce conteneur ». Il étend les syscall et le comportement de montage à l’intérieur du conteneur, ce qui entre en collision avec des politiques conservatrices. Soudain, le conteneur veut monter overlayfs, accéder à /sys/kernel/security, utiliser des keyrings, ou manipuler des cgroups de façons qu’AppArmor refuse.
Que faire : Si vous avez besoin de Docker/Kubernetes dans quelque chose, utilisez une VM à la place sauf si vous avez une raison disciplinée de ne pas le faire. Les conteneurs imbriqués sont séduisants jusqu’à devenir votre hobby d’intervention en incident.
2) Bind mounts et chemins hôtes : double problème politique + propriété
Les bind mounts (mp0, mp1, etc.) sont le moyen le plus rapide de rendre un LXC utile, et aussi le moyen le plus rapide de le casser. AppArmor peut restreindre certaines opérations de montage, et les conteneurs non privilégiés peuvent ne pas pouvoir accéder aux fichiers montés à cause du mappage UID/GID. Ces échecs peuvent se présenter comme des refus AppArmor ou des erreurs génériques de permission.
Que faire : Traitez les bind mounts comme un contrat d’interface. Décidez à l’avance des propriétaires et des schémas d’accès. Documentez-les dans la config du conteneur. Ne « montez pas juste /srv » en espérant que ça marche.
3) « Désactiver AppArmor » est le mauvais réflexe
Oui, désactiver AppArmor peut faire démarrer le conteneur. Cela peut aussi faire danser la posture de sécurité de l’hôte. Votre objectif n’est pas « statut vert ». Votre objectif est un contrôle correct.
Approche préférable : Utilisez les refus AppArmor pour identifier précisément quelle opération est bloquée, puis décidez si l’opération est nécessaire et sûre. Parfois la bonne correction est de supprimer la fonctionnalité qui a déclenché le comportement. Parfois c’est de changer la façon d’exécuter la charge. Occasionnellement, c’est d’ajuster le profil — soigneusement, minimalement et de manière cohérente entre les nœuds.
Petite blague #2 : Désactiver AppArmor pour corriger une erreur de démarrage, c’est comme enlever l’alarme incendie parce qu’elle est bruyante.
Trois mini-histoires d’entreprise issues des opérations
Mini-histoire 1 : Un incident causé par une mauvaise hypothèse
Une équipe a migré un service de traitement par lots de VM vers LXC pour « économiser de la charge ». Leur hypothèse était simple : les conteneurs sont juste des VM légères. Ils n’avaient pas tort dans l’esprit, mais ils se trompaient là où ça compte — les plans de contrôle du kernel sont partagés, et systemd à l’intérieur des conteneurs est pointilleux sur les cgroups.
Après une mise à jour système routinière sur le nœud Proxmox, un sous-ensemble de conteneurs a cessé de démarrer. Les logs indiquaient « Failed to create cgroup » et « permission denied » sur /sys/fs/cgroup. L’équipe s’est focalisée sur les conteneurs : reconstruire des images, revenir sur des changements applicatifs, même restaurer des sauvegardes. Rien n’a changé. Le nœud refusait de fournir la disposition cgroup attendue.
Le vrai problème était un décalage du mode cgroups au démarrage introduit pendant la maintenance. Le nœud était passé sur une configuration hybride qui ne correspondait pas au reste du cluster. La plupart des conteneurs démarraient car ils ne définissaient pas de limites ressources strictes ; les workers de batch, eux, le faisaient. L’hypothèse « si un conteneur démarre, le nœud est sain » était fausse. Certaines charges sont plus sensibles aux cgroups que d’autres.
La correction fut banale : aligner les paramètres kernel et le comportement de systemd avec le reste de la flotte, puis redémarrer pendant une fenêtre de maintenance. La leçon : en environnement conteneurs, « ça marche pour un » n’est pas une preuve de conformité ; c’est une preuve de couverture incomplète.
Mini-histoire 2 : Une optimisation qui s’est retournée contre eux
Une équipe plateforme voulait un provisionnement plus rapide. Ils standardisèrent sur des conteneurs non privilégiés pour la sécurité (bonne idée) et montèrent un répertoire partagé hôte dans des dizaines de LXC pour le cache d’artefacts (discutable). Le répertoire cache était possédé par root sur l’hôte, mais à l’intérieur de chaque conteneur il devait être inscriptible par un utilisateur applicatif.
Pour « résoudre » les permissions, quelqu’un a activé largement le nesting et keyctl, puis a modifié quelques conteneurs pour exécuter des scripts helper au démarrage pour chowner les répertoires. Ça fonctionnait en test. En production, AppArmor commença à bloquer des opérations liées au montage déclenchées par des unités systemd et des scripts helper. Les conteneurs flambaient : démarrage, tentative de montage, refus, sortie. La surveillance les voyait comme « instables » plutôt que « bloqués par une politique ».
Le retour de bâton n’était pas seulement les refus. Le vrai problème opérationnel : des dizaines de conteneurs dépendaient d’un chemin hôte partagé avec une traduction implicite de propriété et une mutation à l’exécution. Chaque modification des permissions de ce répertoire devenait un événement à l’échelle du cluster. Le débogage devint de l’archéologie.
La solution finale fut de cesser de faire ressembler les bind mounts à un filesystem distribué. Ils ont déplacé le cache vers un service approprié et gardé les bind mounts pour des données simples en lecture seule. Le démarrage redevint ennuyeux. Et c’était gagné : l’ennui est une fonctionnalité.
Mini-histoire 3 : Une pratique ennuyeuse mais correcte qui a sauvé la situation
Un groupe infra gérait les nœuds Proxmox comme du bétail, pas des animaux de compagnie — du moins autant que possible. Ils avaient deux habitudes fastidieuses : conserver un « petit conteneur connu bon » pour des smoke tests, et consigner la dérive au niveau nœud (version du kernel, flags de boot, statut AppArmor, mode cgroup) après chaque changement.
Un matin, après une vague de mises à jour sécurité, un nœud refusa de démarrer de nouveaux conteneurs avec des erreurs cgroup. Au lieu d’explorer la configuration d’une charge en production, l’astreinte démarra le petit conteneur de smoke-test. Il échoua de la même façon. Ce seul point de données réduisit l’espace de recherche : ce n’était pas une image applicative ou un point de montage ; c’était la substrate du nœud.
Ils comparèrent l’enregistrement de dérive du nœud avec un pair sain. La seule différence était un paramètre de boot subtil introduit lors d’une session de dépannage précédente, laissé comme une peau de banane. Ils le revinrent, redémarrèrent le nœud en mode maintenance, et les charges revinrent.
Pas d’héroïsme. Pas de hacks « temporaires » qui deviennent permanents. La pratique correcte n’était pas glamour — c’était la cohérence et un test reproductible. En opérations, le glamour est souvent le signe que vous êtes sur le point de faire quelque chose que vous regretterez.
Erreurs fréquentes : symptôme → cause racine → correction
1) Symptom: “Failed to create cgroup /lxc/101”
Cause racine : agencement du système de fichiers cgroup ou décalage de délégation (souvent activation de contrôleur v2 ou montage cgroup manquant).
Correction : Vérifiez que /sys/fs/cgroup est monté correctement, que les contrôleurs sont présents, et que la délégation systemd est cohérente. Alignez la configuration du nœud ; ne patchez pas par conteneur.
2) Symptom: “mount: /sys/fs/cgroup: permission denied” during start
Cause racine : refus AppArmor sur le montage, ou conteneur tentant des montages non autorisés par son profil, parfois déclenché par le nesting.
Correction : Vérifiez les refus d’audit du kernel. Supprimez ou limitez le nesting/keyctl sauf si nécessaire. Si indispensable, utilisez une VM ou ajustez le profil avec précaution.
3) Symptom: Container starts then immediately stops; UI shows no clear error
Cause racine : PID 1 à l’intérieur du conteneur (souvent systemd) qui échoue tôt à cause des cgroups ou de la politique de sécurité.
Correction : Utilisez un démarrage debug et les logs journaux. Cherchez des erreurs systemd liées aux cgroups. Décidez si systemd est approprié ; si oui, corrigez les contraintes cgroup/AppArmor.
4) Symptom: Works as privileged, fails as unprivileged
Cause racine : problèmes de mappage d’ID (/etc/subuid /etc/subgid), mismatch de propriété sur les bind mounts, ou chemins hôtes inaccessibles sous les IDs mappés.
Correction : Validez /etc/subuid//etc/subgid. Corrigez la propriété des chemins montés pour correspondre à la plage mappée. Évitez d’utiliser le mode privilégié comme « correction » ; c’est un modèle de sécurité différent.
5) Symptom: Only containers with resource limits fail
Cause racine : contrôleurs manquants (memory/pids) ou problèmes d’activation des contrôleurs en v2.
Correction : Confirmez la présence des contrôleurs et la délégation systemd. Réparez la configuration du nœud ; gardez les nœuds uniformes.
6) Symptom: AppArmor denials mention /sys/kernel/security or mount operations
Cause racine : le conteneur tente d’accéder aux interfaces de sécurité du kernel ou de monter des pseudo-systèmes sensibles — souvent nesting ou charges spéciales.
Correction : Réévaluez pourquoi la charge a besoin de cela. Préférez une VM pour les runtimes imbriqués. Si nécessaire, dimensionnez les changements de politique et testez sur les upgrades.
7) Symptom: “No such file or directory” for cgroup paths
Cause racine : attente de chemins v1 sur un système v2, ou config LXC obsolète référant des anciens points de montage.
Correction : Supprimez les hacks de montage cgroup legacy de la config du conteneur. Assurez la compatibilité et la cohérence des versions hôte/LXC dans le cluster.
8) Symptom: Adding mp0 made the container stop starting
Cause racine : chemin hôte manquant, mauvaises permissions, ou restrictions de montage AppArmor.
Correction : Confirmez que le chemin source hôte existe et que les permissions correspondent au mappage non privilégié. Essayez de retirer temporairement le montage pour confirmer la causalité ; puis redessinez l’accès correctement.
Listes de vérification / plan étape par étape
Checklist A: Five-minute triage (single container won’t start)
- Exécutez
pct start <id>depuis le CLI pour obtenir l’erreur brute. - Inspectez
/etc/pve/lxc/<id>.confpour :unprivileged,features,mp*, ligneslxc.*personnalisées. - Vérifiez
journalctl -u pve-container@<id>.service -n 200pour le premier échec concret. - Cherchez les refus AppArmor autour du timestamp :
journalctl -k -g 'apparmor="DENIED"'. - Si l’erreur semble liée aux cgroups, confirmez le mode et les contrôleurs (
stat -fc %T /sys/fs/cgroup,cat /sys/fs/cgroup/cgroup.controllers). - Retirez temporairement le changement risqué le plus récent (nouveau bind mount, nesting) pour confirmer la causalité.
Checklist B: Node-level failure (multiple containers won’t start)
- Démarrez un petit conteneur connu bon. S’il échoue de la même manière, traitez-le comme un problème nœud.
- Vérifiez le montage et le mode cgroup :
mount | grep cgroupetstat -fc %T /sys/fs/cgroup. - Vérifiez l’ensemble des contrôleurs :
cat /sys/fs/cgroup/cgroup.controllers. - Vérifiez les bruits évidents du kernel/audit :
dmesg -T | tail -n 200. - Confirmez que le module AppArmor est chargé et en enforcement :
aa-status. - Comparez paquets/version du kernel avec un nœud sain :
uname -r,pveversion -v. - Revenez sur la dérive (boot flags, unités personnalisées) avant d’essayer des « fixes créatifs ».
- Si vous ne pouvez pas restaurer rapidement, évacuez les charges et réparez en maintenance. La production veut des décisions nettes.
Checklist C: When you must change something (safe change discipline)
- Faites un seul changement à la fois : retirez le nesting, supprimez un montage, revenez sur un flag de boot — jamais tout à la fois.
- Capturez les preuves avant/après : slice du journal, lignes de refus, et liste des contrôleurs cgroup.
- Testez avec deux conteneurs : un « simple » et un « complexe » (limites ressources + bind mount + systemd).
- Propagez la correction de façon cohérente vers tous les nœuds ; l’incohérence génère des incidents.
Faits intéressants et contexte historique (les parties qu’on oublie)
- cgroups ont commencé à la fin des années 2000 comme mécanisme kernel pour comptabiliser et limiter les ressources par groupe de processus — bien avant que « conteneurs » devienne un terme marketing.
- LXC est antérieur à la popularité de Docker ; c’est une des approches « system container » plus anciennes, plus proche des VM légères que des conteneurs applicatifs.
- cgroups v2 n’est pas juste v1 renommé ; il change les sémantiques (hiérarchie unifiée et activation des contrôleurs), ce qui explique pourquoi les incompatibilités font mal.
- systemd est devenu un acteur central dans la gestion des cgroups Linux ; même si vous ne l’avez jamais demandé, il possède désormais une grande partie de l’arbre cgroup sur la plupart des distributions.
- AppArmor est basé sur les chemins (contrairement au modèle étiqueté de SELinux), ce qui rend les refus plus lisibles mais peut rendre la politique fragile lors de changements de chemins.
- Les conteneurs non privilégiés reposent sur les user namespaces ; l’hôte mappe le « root » du conteneur sur une plage UID non-root, ce qui est excellent jusqu’à ce que vous bind-montiez des chemins hôtes sans planifier la propriété.
- La fonctionnalité « nesting » est un compromis : elle permet des charges qui nécessitent un comportement plus « kernel-like », mais réduit les garanties d’isolation que vous attendiez probablement d’un LXC.
- Beaucoup d’échecs de démarrage sont en réalité des échecs PID 1 : systemd à l’intérieur du conteneur quitte parce qu’il ne peut pas monter les cgroups ou est refusé, et LXC rapporte « container failed ».
- Les modes hybrides de cgroups ont existé pour compatibilité, mais ils sont opérationnellement gênants ; quand une flotte dérive entre modes, vous obtenez des erreurs « ça marche sur le nœud A ».
FAQ
1) Comment savoir en moins d’une minute si c’est cgroups ou AppArmor ?
Cherchez les refus dans le log kernel : journalctl -k -g 'apparmor="DENIED"'. Si vous obtenez des hits pertinents au moment de l’échec, c’est de la politique. Si les logs montrent des échecs de création/montage de cgroup sans refus, c’est la substrate (cgroups/systemd).
2) L’UI affiche « startup failed », mais le conteneur fonctionnait hier. Qu’est-ce qui a changé ?
Souvent : une mise à jour du kernel, une mise à jour systemd, une mise à jour Proxmox, ou un changement de config du conteneur (nesting, bind mounts, limites ressources). Commencez par comparer pveversion -v et uname -r avec un nœud sain.
3) Passer le conteneur en privilégié est-il une correction acceptable ?
C’est un outil de diagnostic, pas une solution. Si le mode privilégié démarre et que le mode non privilégié échoue, vous avez appris que c’est probablement du mappage d’ID, un mismatch de propriétaire de bind mount, ou une contrainte des user namespaces. Corrigez cela et repassez en non privilégié, sauf si vous avez une approbation formelle du risque.
4) Ai-je besoin du nesting pour exécuter Docker dans un LXC ?
Souvent oui, et c’est le problème. Si vous voulez exécuter Docker/Kubernetes de façon fiable, préférez une VM. Le nesting LXC peut fonctionner, mais c’est fragile opérationnellement et interagit fréquemment mal avec AppArmor et les attentes cgroup.
5) Que signifie généralement « Failed to mount /sys/fs/cgroup » à l’intérieur du conteneur ?
Soit le conteneur tente de monter les cgroups mais n’en a pas l’autorisation (AppArmor), soit la configuration/délégation cgroup de l’hôte ne supporte pas ce que l’init du conteneur attend. Corrélez d’abord avec les refus AppArmor, puis validez le mode contrôleurs de l’hôte.
6) Pourquoi seuls certains conteneurs échouent après une mise à jour nœud ?
Parce que tous les conteneurs n’exercent pas les mêmes interfaces kernel. Les conteneurs avec limites mémoire/pids, systemd, nesting, ou points de montage complexes touchent plus fortement aux cgroups et AppArmor.
7) Les refus AppArmor peuvent-ils être ignorés si le conteneur démarre encore ?
Non. Un refus que vous « tolérez » aujourd’hui peut devenir un échec dur après une mise à jour de paquet qui change le comportement. Traitez les refus comme une dette technique avec un taux d’intérêt.
8) Mon bind mount fonctionne en privilégié mais pas en non privilégié. Pourquoi ?
Les conteneurs non privilégiés mappent les UIDs/GIDs ; le « root » à l’intérieur n’est pas root sur l’hôte. La propriété et les permissions du chemin hôte doivent correspondre à la plage mappée, sinon le conteneur ne pourra pas y accéder. Corrigez la propriété ou repensez la stratégie de montage.
9) Dois-je désactiver AppArmor pour passer un incident ?
Seulement en dernier recours comme mesure de confinement et seulement si vous comprenez le rayon d’impact. Préférez retirer la fonctionnalité déclenchante (comme le nesting) ou corriger le comportement spécifique refusé. Désactiver AppArmor est un changement de politique, pas un simple redémarrage.
10) Quelle est la façon la plus sûre de tester des changements ?
Utilisez un petit template de conteneur connu bon, appliquez un changement, testez le démarrage/arrêt, puis testez un conteneur « complexe » (systemd + limites + mount). La cohérence entre nœuds compte plus que du débogage héroïque sur une seule machine.
Conclusion : prochaines étapes pratiques
Quand un LXC ne démarre pas sur Proxmox et que les logs mentionnent cgroups ou AppArmor, le système vous dit exactement ce qui a échoué — juste pas dans un ordre amical. Votre travail est de classifier la défaillance : substrate (cgroups/systemd/kernel) vs politique (AppArmor) vs propriété/stockage (bind mounts, idmaps).
Prochaines étapes que je ferais réellement :
- Récupérer des preuves : sortie de
pct start,journalctl -u pve-container@ID, et tout refus AppArmor. - Si plusieurs conteneurs échouent, arrêtez de déboguer les configs individuelles et validez immédiatement le mode/les contrôleurs cgroup de l’hôte.
- Si AppArmor refuse des montages ou l’accès à la sécurité kernel, retirez d’abord la fonctionnalité déclenchante (nesting/bind mount), puis réintroduisez prudemment — ou migrez la charge vers une VM.
- Notez ce qui a changé (kernel, flags de boot, fonctionnalités du conteneur). La dérive transforme un « nœud étrange » en panne récurrente.
Réparez la couche réelle. Gardez les nœuds uniformes. Et quand l’envie vous prend de désactiver un système de sécurité pour « le faire démarrer », prenez une respiration et relisez la ligne de refus. Elle a souvent raison.