Les conteneurs partagent le noyau Linux de l’hôte. Une faille dans le noyau ou un appel système mal filtré peut permettre à un conteneur malveillant de s’échapper vers l’hôte. Les runtimes sandboxés (gVisor, Kata Containers) ajoutent une couche d’isolation supplémentaire entre le conteneur et le noyau hôte, réduisant drastiquement la surface d’attaque. La ressource Kubernetes RuntimeClass permet de diriger certains Pods vers ces runtimes alternatifs. Ce guide couvre la configuration, l’utilisation et les critères de choix pour l’examen CKS.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Pourquoi les conteneurs classiques ne suffisent pas pour certains workloads
- Comment gVisor et Kata Containers isolent les conteneurs
- Configurer containerd pour utiliser un runtime alternatif
- Créer une ressource RuntimeClass et l’affecter à un Pod
- Choisir le bon runtime selon vos contraintes
Prérequis
Section intitulée « Prérequis »- Cluster Kubernetes avec accès administrateur
- Familiarité avec les Pods et les Security Contexts
- Accès SSH aux nœuds du cluster (pour configurer containerd)
Pourquoi isoler davantage les conteneurs
Section intitulée « Pourquoi isoler davantage les conteneurs »Un conteneur Linux standard (runc) s’exécute comme un processus isolé grâce aux namespaces et cgroups, mais il partage le noyau de l’hôte. Chaque appel système du conteneur est traité directement par le noyau hôte.
Analogie : un conteneur classique, c’est comme un appartement dans un immeuble. Les murs (namespaces) séparent les locataires, mais ils partagent les fondations (le noyau). Si les fondations sont compromises, tous les appartements sont exposés.
Ce partage du noyau pose deux problèmes de sécurité :
- Surface d’attaque large : le noyau Linux expose plus de 300 appels système. Une vulnérabilité dans l’un d’eux peut permettre une évasion de conteneur (container escape).
- Isolation incomplète : certaines ressources du noyau (comme
/proc,/sys) ne sont pas entièrement cloisonnées par les namespaces.
Les runtimes sandboxés résolvent ce problème en interposant une couche supplémentaire entre le conteneur et le noyau hôte.
| Approche | Isolation | Performance | Compatibilité |
|---|---|---|---|
| runc (standard) | Namespaces + cgroups | Native | Totale |
| gVisor (runsc) | Noyau applicatif en userspace | Overhead syscalls | Élevée pour les workloads courants |
| Kata Containers | VM légère par Pod | Overhead démarrage | Quasi-totale |
gVisor : un noyau applicatif en userspace
Section intitulée « gVisor : un noyau applicatif en userspace »gVisor est un projet open source de Google qui intercepte les appels système des conteneurs et les traite dans un noyau applicatif écrit en Go, appelé le Sentry. Les processus du conteneur n’appellent pas directement le noyau hôte : leurs appels système sont interceptés et servis par le Sentry, qui ne s’appuie sur le noyau hôte que de manière contrôlée.
Architecture de gVisor
Section intitulée « Architecture de gVisor »gVisor se compose de deux processus principaux :
- Sentry : le noyau applicatif. Il implémente les appels système Linux (gestion mémoire, threading, signaux, réseau) et ne s’appuie sur le noyau hôte que pour un ensemble restreint d’opérations. Il s’exécute en userspace avec un profil seccomp très restrictif.
- Gofer : un processus médiateur qui gère les accès au système de fichiers via le protocole 9P. Le Sentry n’a pas d’accès direct aux fichiers de l’hôte.
L’exécutable OCI de gVisor s’appelle runsc (run sandboxed container). Il s’intègre avec containerd et CRI-O comme n’importe quel runtime OCI.
Avantages et limites
Section intitulée « Avantages et limites »Avantages :
- Faible empreinte : pas de VM, pas de noyau invité complet. Un sandbox gVisor consomme peu de mémoire supplémentaire.
- Démarrage rapide : comparable à runc, car il n’y a pas de VM à lancer.
- Écrit en Go : le fait que gVisor soit majoritairement écrit en Go réduit certaines classes classiques de vulnérabilités mémoire (use-after-free, buffer overflow) par rapport à du code noyau en C/C++.
Limites :
- Overhead par syscall : chaque appel système passe par le Sentry, ce qui ajoute de la latence. Les workloads intensifs en I/O ou en syscalls sont les plus impactés.
- Compatibilité : gVisor n’implémente pas tous les appels système Linux. Certaines applications très spécifiques peuvent ne pas fonctionner.
- Pas de modules noyau : les applications qui nécessitent des modules noyau spécifiques (iptables avancé, eBPF) ne sont pas compatibles.
Kata Containers : une VM légère par Pod
Section intitulée « Kata Containers : une VM légère par Pod »Kata Containers prend l’approche inverse de gVisor : chaque Pod s’exécute dans une machine virtuelle légère dédiée. Le conteneur dispose de son propre noyau Linux invité, isolé du noyau hôte par un hyperviseur (QEMU/KVM, Cloud Hypervisor ou Firecracker).
Avantages et limites
Section intitulée « Avantages et limites »Avantages :
- Isolation matérielle : la séparation est assurée par l’hyperviseur et les mécanismes de virtualisation du CPU (VT-x/AMD-V). L’isolation est comparable à celle d’une VM classique.
- Compatibilité quasi-totale : le conteneur s’exécute sur un vrai noyau Linux, sans restriction d’appels système.
Limites :
- Overhead de démarrage : lancer une micro-VM prend quelques secondes, contre quelques millisecondes pour runc.
- Consommation mémoire : chaque Pod nécessite son propre noyau invité en mémoire.
- Virtualisation imbriquée : si vos nœuds Kubernetes tournent eux-mêmes dans des VM, Kata peut nécessiter la virtualisation imbriquée (nested virtualization), ce qui dépend du fournisseur cloud et du type d’instance.
gVisor vs Kata Containers
Section intitulée « gVisor vs Kata Containers »| Critère | gVisor | Kata Containers |
|---|---|---|
| Mécanisme | Noyau userspace (Go) | VM légère (hyperviseur) |
| Isolation | Forte (interception syscalls) | Très forte (noyau séparé) |
| Performance | Overhead par syscall | Overhead démarrage + mémoire |
| Compatibilité | Élevée pour les workloads courants | Quasi-totale |
| Nested virt. | Non requise | Requise si nœuds virtualisés |
| Cas d’usage | Multi-tenant, CI/CD, serverless | Workloads réglementés, défense en profondeur |
RuntimeClass : diriger les Pods vers un runtime
Section intitulée « RuntimeClass : diriger les Pods vers un runtime »La ressource Kubernetes RuntimeClass permet d’affecter un Pod à un runtime CRI spécifique. C’est une ressource non-namespacée (cluster-scope), stable depuis Kubernetes v1.20, qui mappe un nom lisible vers un handler CRI configuré sur les nœuds.
Créer un RuntimeClass
Section intitulée « Créer un RuntimeClass »apiVersion: node.k8s.io/v1kind: RuntimeClassmetadata: name: gvisor# Le handler doit correspondre au nom de configuration exposé par le CRIhandler: runscLe champ handler doit correspondre exactement au nom de configuration exposé par l’implémentation CRI du nœud, par exemple containerd ou CRI-O.
Utiliser un RuntimeClass dans un Pod
Section intitulée « Utiliser un RuntimeClass dans un Pod »Pour exécuter un Pod avec un runtime sandboxé, ajoutez le champ runtimeClassName dans la spec du Pod :
apiVersion: v1kind: Podmetadata: name: sandbox-testspec: runtimeClassName: gvisor containers: - name: app image: nginx:1.27Si le RuntimeClass référencé n’existe pas, la création du Pod est rejetée à l’admission (Forbidden: RuntimeClass not found). Si le RuntimeClass existe mais que le handler n’est pas configuré sur le nœud, le Pod reste bloqué en ContainerCreating avec un événement FailedCreatePodSandBox.
Scheduling et nodeSelector
Section intitulée « Scheduling et nodeSelector »Par défaut, RuntimeClass suppose un cluster homogène où tous les nœuds supportent le runtime. En pratique, seuls certains nœuds ont gVisor ou Kata installés.
RuntimeClass supporte un champ scheduling qui injecte un nodeSelector et des tolerations dans les Pods à l’admission. Ces contraintes se combinent avec celles déjà présentes dans le Pod ; elles ne les remplacent pas :
apiVersion: node.k8s.io/v1kind: RuntimeClassmetadata: name: gvisorhandler: runscscheduling: nodeSelector: runtime: gvisor tolerations: - key: runtime value: gvisor effect: NoScheduleAvec cette configuration, les Pods utilisant runtimeClassName: gvisor seront automatiquement dirigés vers les nœuds labellisés runtime=gvisor.
Pod Overhead
Section intitulée « Pod Overhead »Les runtimes sandboxés consomment des ressources supplémentaires (mémoire pour le Sentry gVisor, ou pour le noyau invité Kata). Le champ overhead permet de déclarer cette consommation pour que le scheduler en tienne compte :
apiVersion: node.k8s.io/v1kind: RuntimeClassmetadata: name: katahandler: kataoverhead: podFixed: memory: "120Mi" cpu: "250m"L’overhead est injecté dans le Pod à l’admission à partir du RuntimeClass, puis pris en compte pour le scheduling, les resource quotas, le cgroup du Pod et les décisions d’éviction.
Configurer containerd pour un runtime alternatif
Section intitulée « Configurer containerd pour un runtime alternatif »L’installation du runtime sur les nœuds se fait hors de Kubernetes. Voici la procédure pour containerd, le runtime CRI le plus courant.
-
Installer runsc sur chaque nœud cible
Exemple d’installation depuis les releases officielles (adaptez à votre OS et architecture) :
Fenêtre de terminal ARCH=$(uname -m)URL=https://storage.googleapis.com/gvisor/releases/release/latest/${ARCH}wget ${URL}/runsc ${URL}/runsc.sha512 \${URL}/containerd-shim-runsc-v1 ${URL}/containerd-shim-runsc-v1.sha512sha512sum -c runsc.sha512 -c containerd-shim-runsc-v1.sha512rm -f *.sha512chmod a+rx runsc containerd-shim-runsc-v1sudo mv runsc containerd-shim-runsc-v1 /usr/local/bin -
Configurer containerd pour déclarer le handler
runscAjouter dans
/etc/containerd/config.toml(syntaxe containerd 2.x / config v3) :[plugins."io.containerd.cri.v1.runtime".containerd.runtimes.runsc]runtime_type = "io.containerd.runsc.v1" -
Redémarrer containerd
Fenêtre de terminal sudo systemctl restart containerd -
Vérifier que le runtime est disponible
Fenêtre de terminal sudo crictl info | grep -A 5 runsc
-
Installer Kata Containers sur chaque nœud cible
Exemple d’installation depuis les releases GitHub (adaptez à votre OS et hyperviseur) :
Fenêtre de terminal # Consulter https://github.com/kata-containers/kata-containers/releases# pour la version et l'archive adaptées à votre environnement.KATA_VERSION=$(curl -fsSL https://api.github.com/repos/kata-containers/kata-containers/releases/latest | grep tag_name | cut -d '"' -f 4)ARCH=$(dpkg --print-architecture) # amd64, arm64...curl -fsSL -o kata-static.tar.zst \"https://github.com/kata-containers/kata-containers/releases/download/${KATA_VERSION}/kata-static-${KATA_VERSION}-${ARCH}.tar.zst"sudo tar xf kata-static.tar.zst -C /sudo ln -sf /opt/kata/bin/kata-runtime /usr/local/bin/kata-runtimesudo ln -sf /opt/kata/bin/containerd-shim-kata-v2 /usr/local/bin/containerd-shim-kata-v2 -
Configurer containerd pour déclarer le handler
kataAjouter dans
/etc/containerd/config.toml(syntaxe containerd 2.x / config v3) :[plugins."io.containerd.cri.v1.runtime".containerd.runtimes.kata]runtime_type = "io.containerd.kata.v2" -
Redémarrer containerd
Fenêtre de terminal sudo systemctl restart containerd -
Vérifier le support de la virtualisation
Fenêtre de terminal kata-runtime check
Workflow complet : du nœud au Pod
Section intitulée « Workflow complet : du nœud au Pod »Voici la procédure complète pour déployer un Pod sandboxé :
-
Installer le runtime sur les nœuds ciblés (gVisor ou Kata)
-
Configurer containerd avec le handler correspondant
-
Labelliser les nœuds qui supportent le runtime
Fenêtre de terminal kubectl label nodes <node-name> runtime=gvisor -
Créer le RuntimeClass dans le cluster
Fenêtre de terminal kubectl apply -f - <<EOFapiVersion: node.k8s.io/v1kind: RuntimeClassmetadata:name: gvisorhandler: runscscheduling:nodeSelector:runtime: gvisorEOF -
Déployer un Pod avec le RuntimeClass
Fenêtre de terminal kubectl apply -f - <<EOFapiVersion: v1kind: Podmetadata:name: sandbox-testspec:runtimeClassName: gvisorcontainers:- name: testimage: nginx:1.27command: ["sh", "-c", "uname -a && sleep 3600"]EOF -
Vérifier que le Pod utilise le bon runtime
Fenêtre de terminal # Confirmer le runtimeClassName dans la speckubectl get pod sandbox-test -o jsonpath='{.spec.runtimeClassName}'# gvisor# Comparer le noyau vu par le conteneur (gVisor rapporte 4.4.0)kubectl exec sandbox-test -- uname -r# 4.4.0 (au lieu du noyau hôte, ex. 6.8.x)# dmesg affiche les messages du Sentry gVisorkubectl exec sandbox-test -- dmesg | head -3# [0.000000] Starting gVisor...# Pour un Pod Kata, uname -r affiche le noyau invité (ex. 6.18.x)# et dmesg montre un boot Linux complet (BIOS, kata-containers.target)
Bonnes pratiques
Section intitulée « Bonnes pratiques »Sécurité
Section intitulée « Sécurité »- Restreindre la création de RuntimeClass aux administrateurs cluster (RBAC). Un utilisateur ne devrait pas pouvoir créer ou modifier des RuntimeClass.
- Forcer le RuntimeClass sur les namespaces sensibles avec un admission controller (Kyverno, Gatekeeper) pour garantir que les workloads multi-tenant utilisent toujours un runtime sandboxé.
- Combiner avec d’autres mécanismes : RuntimeClass ne remplace pas les Pod Security Standards, AppArmor/Seccomp ou les Network Policies. C’est une couche supplémentaire de défense en profondeur.
Performance
Section intitulée « Performance »- Tester avant de déployer : mesurez l’impact sur vos workloads réels. gVisor ajoute un overhead variable selon le nombre d’appels système.
- Séparer les nœuds : dédiez certains nœuds aux workloads sandboxés pour éviter que l’overhead n’impacte les applications standards.
- Déclarer le Pod Overhead : configurez le champ
overheaddans le RuntimeClass pour que le scheduler planifie correctement les Pods.
Dépannage
Section intitulée « Dépannage »| Symptôme | Cause probable | Solution |
|---|---|---|
Rejet à la création : Forbidden: RuntimeClass "xxx" not found | RuntimeClass absent du cluster | Vérifier kubectl get runtimeclass |
Pod bloqué en ContainerCreating avec événement FailedCreatePodSandBox | Handler non configuré dans containerd / CRI-O | Vérifier /etc/containerd/config.toml (chemin selon la version), redémarrer containerd |
Pod Pending indéfiniment | Pas de nœud avec le label requis | Vérifier les labels (kubectl get nodes --show-labels) et le scheduling.nodeSelector du RuntimeClass |
Erreur not supported avec Kata | Virtualisation matérielle non disponible | Vérifier avec kata-runtime check ; activer le nested virt. si nœuds virtualisés |
| Application qui plante sous gVisor | Appel système non implémenté | Consulter les logs gVisor (runsc debug) ; vérifier la compatibilité sur gvisor.dev |
À retenir
Section intitulée « À retenir »- Les conteneurs standard partagent le noyau hôte, ce qui expose une large surface d’attaque via les appels système
- gVisor intercepte les appels système dans un noyau applicatif en Go — adapté aux workloads multi-tenant et CI/CD
- Kata Containers exécute chaque Pod dans une VM légère — isolation matérielle maximale, mais overhead plus important
- RuntimeClass est la ressource Kubernetes qui mappe un nom vers un handler CRI configuré sur les nœuds
- Le champ
runtimeClassNamedans la spec du Pod active le runtime alternatif - Le champ
schedulingdu RuntimeClass permet de cibler automatiquement les nœuds compatibles - Le champ
overheaddéclare les ressources supplémentaires du runtime pour le scheduler - Les runtimes sandboxés complètent (et ne remplacent pas) les autres mécanismes de sécurité Kubernetes
Prochaines étapes
Section intitulée « Prochaines étapes »Ressources
Section intitulée « Ressources »- RuntimeClass : Documentation officielle Kubernetes
- gVisor : gvisor.dev
- Kata Containers : katacontainers.io