## Introduction

Les classes d'exécution (Runtime Classes) dans Kubernetes permettent de sélectionner un runtime de conteneur pour un pod, comme un bac à sable léger tel que gVisor, un runtime haute performance comme Kata Containers, ou un runtime personnalisé avec des fonctionnalités spécifiques. La sauvegarde et la restauration des ressources RuntimeClass sont essentielles pour la reprise après sinistre et la migration de cluster. Si une RuntimeClass est absente ou mal configurée, les pods qui y font référence échoueront à se planifier, provoquant des interruptions. Ce guide couvre les procédures pratiques de sauvegarde et de restauration avec des commandes concrètes, des étapes de validation et des stratégies de récupération.

Vous apprendrez à :

- Inventorier les RuntimeClass existantes et leurs configurations.

- Sauvegarder les objets RuntimeClass à l'aide des outils Kubernetes standard.

- Restaurer les RuntimeClass dans un cluster nouveau ou récupéré.

- Valider que les RuntimeClass restaurées fonctionnent correctement.

- Gérer les modes de défaillance courants et effectuer des retours en arrière en toute sécurité.

Ce guide s'adresse aux développeurs, ingénieurs DevOps et opérateurs de plateforme qui gèrent des clusters Kubernetes et doivent garantir que les configurations de runtime survivent aux sinistres.

## Inventaire de la version et de l'environnement

Avant de sauvegarder les RuntimeClass, comprenez votre environnement de cluster. Cette section montre comment vérifier la version de Kubernetes, confirmer la disponibilité de l'API et lister les RuntimeClass actuelles.

### Prérequis

- Un cluster Kubernetes en cours d'exécution avec kubectl configuré.

- Accès pour consulter les RuntimeClass (généralement cluster-admin ou similaire).

### Vérifier la version de Kubernetes

RuntimeClass est disponible en version stable v1 depuis Kubernetes 1.20. Les versions antérieures utilisaient l'API node.k8s.io/v1beta1 . Exécutez :

kubectl version --short 
 Sortie attendue (exemple) :

Client Version: v1.27.3
Kustomize Version: v5.0.1
Server Version: v1.27.3 
 Si la version du serveur est inférieure à 1.20, ajustez les versions d'API en conséquence. Pour ce guide, nous supposons la version v1.

### Lister les RuntimeClass existantes

Pour voir toutes les RuntimeClass :

kubectl get runtimeclass 
 Exemple de sortie :

NAME HANDLER AGE
runc runc 10d
gvisor runsc 10d
kata kata 10d 
 La colonne HANDLER indique le nom du gestionnaire de runtime configuré dans le runtime de conteneur. Il doit correspondre au gestionnaire dans la configuration du runtime du nœud.

### Inspecter une RuntimeClass spécifique

Affichez la définition complète d'une RuntimeClass :

kubectl get runtimeclass gvisor -o yaml 
 Exemple de sortie :

apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
 name: gvisor
 annotations:
 example.com/description: "Sandboxed runtime for untrusted workloads"
handler: runsc
scheduling:
 nodeSelector:
 runtime: gvisor
 tolerations:
 - key: "sandboxed"
 operator: "Equal"
 value: "true"
 effect: "NoSchedule"
overhead:
 podFixed:
 cpu: "100m"
 memory: "128Mi" 
 Cette RuntimeClass spécifie un gestionnaire, un sélecteur de nœud, des tolérances et des frais généraux de pod. Tous ces champs doivent être sauvegardés.

<style>
.eno-quiz-widget{margin:2rem 0;padding:1.5rem;border-radius:12px;background:var(--eno-surface-lowest,#f7f7f8);border:1px solid var(--eno-border-soft,#e2e2e6);font-family:var(--eno-font-body,Inter,sans-serif)}
.eno-quiz-widget .eno-quiz-kicker{font-family:var(--eno-font-label,"JetBrains Mono",monospace);font-size:.75rem;letter-spacing:.05em;text-transform:uppercase;color:var(--eno-text-muted,#5f5f68);margin:0 0 .5rem}
.eno-quiz-widget .eno-quiz-question{font-family:var(--eno-font-heading,"Hanken Grotesk",sans-serif);font-size:1.0625rem;font-weight:600;margin:0 0 1rem;color:var(--eno-text-strong,#1a1a1f)}
.eno-quiz-widget .eno-quiz-options{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:.5rem}
.eno-quiz-widget .eno-quiz-option{display:block;width:100%;min-height:44px;text-align:left;padding:.625rem .875rem;border-radius:8px;border:1.5px solid var(--eno-border-soft,#e2e2e6);background:#fff;font-size:.9375rem;cursor:pointer;transition:border-color var(--eno-motion-base,180ms ease-out),background var(--eno-motion-base,180ms ease-out)}
.eno-quiz-widget .eno-quiz-option:hover{border-color:var(--eno-primary,#0059bb)}
.eno-quiz-widget .eno-quiz-option:focus-visible{outline:none;box-shadow:0 0 0 3px rgb(0 89 187 / 0.15)}
.eno-quiz-widget .eno-quiz-option[aria-pressed="true"]{border-color:var(--eno-primary,#0059bb);background:rgb(0 89 187 / 0.06)}
.eno-quiz-widget .eno-quiz-option[data-correct="true"].eno-quiz-revealed{border-color:var(--eno-success,#17803a);background:rgb(23 128 58 / 0.08)}
.eno-quiz-widget .eno-quiz-option[data-correct="false"].eno-quiz-revealed.eno-quiz-was-selected{border-color:var(--eno-error,#ba1a1a);background:var(--eno-error-soft,#ffdad6)}
.eno-quiz-widget .eno-quiz-option-icon{display:inline-block;width:1.1em;margin-right:.4em;font-weight:700}
.eno-quiz-widget .eno-quiz-submit{margin-top:1rem;min-height:44px;padding:.5rem 1.25rem;border-radius:8px;border:none;background:var(--eno-primary,#0059bb);color:#fff;font-weight:600;font-size:.9375rem;cursor:pointer;transition:opacity var(--eno-motion-base,180ms ease-out)}
.eno-quiz-widget .eno-quiz-submit:disabled{opacity:.5;cursor:not-allowed}
.eno-quiz-widget .eno-quiz-submit:focus-visible{outline:none;box-shadow:0 0 0 3px rgb(0 89 187 / 0.15)}
.eno-quiz-widget .eno-quiz-explanation{margin-top:1rem;padding:.875rem 1rem;border-radius:8px;font-size:.9375rem;line-height:1.5;display:none}
.eno-quiz-widget .eno-quiz-explanation.eno-quiz-visible{display:block}
.eno-quiz-widget .eno-quiz-explanation.eno-quiz-correct{background:rgb(23 128 58 / 0.08);color:var(--eno-success,#17803a)}
.eno-quiz-widget .eno-quiz-explanation.eno-quiz-incorrect{background:var(--eno-error-soft,#ffdad6);color:var(--eno-error,#ba1a1a)}
@media (prefers-reduced-motion: reduce){.eno-quiz-widget *{transition:none!important}}
</style><div class="eno-quiz-widget" role="group" aria-label="Question rapide">
Question rapide 1 sur 2

Quel est le but d'un RuntimeClass dans Kubernetes ?

- Spécifier le runtime de conteneur de bas niveau pour un Pod
- Définir la politique réseau pour un Pod
- Définir les limites de ressources pour un conteneur
- Configurer le stockage persistant pour un Pod

<button type="button" class="eno-quiz-submit" disabled>Valider</button>
<div class="eno-quiz-explanation">Un RuntimeClass vous permet de spécifier le runtime de conteneur de bas niveau pour un Pod, comme décrit dans la référence.</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
  document.querySelectorAll('.eno-quiz-widget:not([data-eno-quiz-bound])').forEach(function (widget) {
    widget.setAttribute('data-eno-quiz-bound', '1');
    var options = Array.prototype.slice.call(widget.querySelectorAll('.eno-quiz-option'));
    var submitBtn = widget.querySelector('.eno-quiz-submit');
    var explanation = widget.querySelector('.eno-quiz-explanation');
    var selected = null;
    options.forEach(function (opt) {
      opt.addEventListener('click', function () {
        if (widget.hasAttribute('data-eno-quiz-answered')) return;
        options.forEach(function (o) { o.setAttribute('aria-pressed', 'false'); });
        opt.setAttribute('aria-pressed', 'true');
        selected = opt;
        submitBtn.disabled = false;
      });
    });
    submitBtn.addEventListener('click', function () {
      if (!selected || widget.hasAttribute('data-eno-quiz-answered')) return;
      widget.setAttribute('data-eno-quiz-answered', '1');
      submitBtn.disabled = true;
      var correct = selected.getAttribute('data-correct') === 'true';
      options.forEach(function (o) {
        o.classList.add('eno-quiz-revealed');
        if (o === selected) o.classList.add('eno-quiz-was-selected');
        var icon = o.querySelector('.eno-quiz-option-icon');
        if (o.getAttribute('data-correct') === 'true') icon.textContent = '\u2713';
        else if (o === selected) icon.textContent = '\u2717';
      });
      explanation.classList.add('eno-quiz-visible', correct ? 'eno-quiz-correct' : 'eno-quiz-incorrect');
    });
  });
});
</script>

## Chemin de configuration sûr

Cette section couvre la manière sûre de capturer les configurations de RuntimeClass pour la sauvegarde. Nous utilisons d'abord des opérations en lecture seule pour éviter les modifications accidentelles.

### Extraire toutes les RuntimeClass en YAML

Pour sauvegarder toutes les RuntimeClass de manière globale (les RuntimeClass sont de portée cluster, donc pas d'espace de noms) :

kubectl get runtimeclass -o yaml > runtimeclasses-backup.yaml 
 Cette commande capture les définitions complètes. Cependant, la sortie inclut des champs comme status et des métadonnées comme resourceVersion qui ne sont pas nécessaires et peuvent causer des problèmes lors de la restauration. Pour obtenir une sauvegarde plus propre, utilisez --export (obsolète dans les versions plus récentes) ou traitez le YAML. Dans Kubernetes 1.18+, --export est obsolète. À la place, utilisez un outil comme kubectl neat ou supprimez manuellement les champs indésirables. Pour simplifier, nous conserverons la sortie complète et noterons que kubectl apply ignore certains champs automatiquement, mais resourceVersion doit être supprimé avant l'application pour éviter les erreurs de conflit.

Pour supprimer par programme les champs indésirables, utilisez kubectl get -o json et redirigez vers jq pour supprimer metadata.resourceVersion , metadata.uid , metadata.creationTimestamp et status :

kubectl get runtimeclass -o json | jq 'del(.items[].metadata.resourceVersion, .items[].metadata.uid, .items[].metadata.creationTimestamp, .items[].metadata.generation, .items[].status)' > runtimeclasses-clean.json 
 Ce JSON peut être stocké comme sauvegarde.

### Sauvegarder également les définitions de ressources personnalisées (le cas échéant)

Si vos RuntimeClass référencent des gestionnaires personnalisés définis par des extensions, assurez-vous que la configuration de ces extensions est également sauvegardée. Par exemple, si vous utilisez un runtime personnalisé installé via un DaemonSet, sauvegardez ce DaemonSet et les ConfigMaps associés. Explorez l'utilisation de kubectl get daemonset -A -o yaml pour des runtimes comme gVisor déployés en DaemonSets.

### Vérifier le fichier de sauvegarde

Vérifiez que le fichier de sauvegarde existe et contient des données :

wc -l runtimeclasses-clean.json 
 Ouvrez-le pour confirmer qu'il n'y a pas de structures vides.

## Vérification et diagnostics

Après la sauvegarde, vérifiez que la sauvegarde est complète et correcte. Testez la restauration dans un cluster non production pour vous assurer que le processus fonctionne.

### Créer un pod de test utilisant la RuntimeClass

Avant qu'un sinistre ne survienne, testez que la RuntimeClass fonctionne. Créez un pod qui utilise gvisor :

# test-gvisor-pod.yaml
apiVersion: v1
kind: Pod
metadata:
 name: test-gvisor
spec:
 runtimeClassName: gvisor
 containers:
 - name: test
 image: busybox
 command: ["sleep", "3600"] 
 Appliquez et vérifiez le statut :

kubectl apply -f test-gvisor-pod.yaml
kubectl get pod test-gvisor 
 Attendu : statut Running. S'il échoue avec FailedCreatePodSandBox ou CreatePodSandbox for pod failed , le gestionnaire de runtime n'est pas disponible sur le nœud.

### Vérifier la disponibilité des nœuds pour le runtime

Vérifiez que les nœuds ont le runtime configuré :

kubectl get nodes -l runtime=gvisor 
 Si aucun nœud n'est retourné, le nodeSelector dans la RuntimeClass ne correspond à aucun nœud, et les pods ne se planifieront pas.

### Tester la planification et les frais généraux

Créez un pod avec des demandes de ressources pour vérifier que les frais généraux sont appliqués :

kubectl apply -f test-gvisor-pod.yaml
kubectl describe pod test-gvisor | grep -A5 "Requests" 
 La sortie attendue montre les frais généraux ajoutés aux demandes du conteneur.

<style>
.eno-quiz-widget{margin:2rem 0;padding:1.5rem;border-radius:12px;background:var(--eno-surface-lowest,#f7f7f8);border:1px solid var(--eno-border-soft,#e2e2e6);font-family:var(--eno-font-body,Inter,sans-serif)}
.eno-quiz-widget .eno-quiz-kicker{font-family:var(--eno-font-label,"JetBrains Mono",monospace);font-size:.75rem;letter-spacing:.05em;text-transform:uppercase;color:var(--eno-text-muted,#5f5f68);margin:0 0 .5rem}
.eno-quiz-widget .eno-quiz-question{font-family:var(--eno-font-heading,"Hanken Grotesk",sans-serif);font-size:1.0625rem;font-weight:600;margin:0 0 1rem;color:var(--eno-text-strong,#1a1a1f)}
.eno-quiz-widget .eno-quiz-options{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:.5rem}
.eno-quiz-widget .eno-quiz-option{display:block;width:100%;min-height:44px;text-align:left;padding:.625rem .875rem;border-radius:8px;border:1.5px solid var(--eno-border-soft,#e2e2e6);background:#fff;font-size:.9375rem;cursor:pointer;transition:border-color var(--eno-motion-base,180ms ease-out),background var(--eno-motion-base,180ms ease-out)}
.eno-quiz-widget .eno-quiz-option:hover{border-color:var(--eno-primary,#0059bb)}
.eno-quiz-widget .eno-quiz-option:focus-visible{outline:none;box-shadow:0 0 0 3px rgb(0 89 187 / 0.15)}
.eno-quiz-widget .eno-quiz-option[aria-pressed="true"]{border-color:var(--eno-primary,#0059bb);background:rgb(0 89 187 / 0.06)}
.eno-quiz-widget .eno-quiz-option[data-correct="true"].eno-quiz-revealed{border-color:var(--eno-success,#17803a);background:rgb(23 128 58 / 0.08)}
.eno-quiz-widget .eno-quiz-option[data-correct="false"].eno-quiz-revealed.eno-quiz-was-selected{border-color:var(--eno-error,#ba1a1a);background:var(--eno-error-soft,#ffdad6)}
.eno-quiz-widget .eno-quiz-option-icon{display:inline-block;width:1.1em;margin-right:.4em;font-weight:700}
.eno-quiz-widget .eno-quiz-submit{margin-top:1rem;min-height:44px;padding:.5rem 1.25rem;border-radius:8px;border:none;background:var(--eno-primary,#0059bb);color:#fff;font-weight:600;font-size:.9375rem;cursor:pointer;transition:opacity var(--eno-motion-base,180ms ease-out)}
.eno-quiz-widget .eno-quiz-submit:disabled{opacity:.5;cursor:not-allowed}
.eno-quiz-widget .eno-quiz-submit:focus-visible{outline:none;box-shadow:0 0 0 3px rgb(0 89 187 / 0.15)}
.eno-quiz-widget .eno-quiz-explanation{margin-top:1rem;padding:.875rem 1rem;border-radius:8px;font-size:.9375rem;line-height:1.5;display:none}
.eno-quiz-widget .eno-quiz-explanation.eno-quiz-visible{display:block}
.eno-quiz-widget .eno-quiz-explanation.eno-quiz-correct{background:rgb(23 128 58 / 0.08);color:var(--eno-success,#17803a)}
.eno-quiz-widget .eno-quiz-explanation.eno-quiz-incorrect{background:var(--eno-error-soft,#ffdad6);color:var(--eno-error,#ba1a1a)}
@media (prefers-reduced-motion: reduce){.eno-quiz-widget *{transition:none!important}}
</style><div class="eno-quiz-widget" role="group" aria-label="Question rapide">
Question rapide 2 sur 2

Quel champ dans la spécification du Pod est utilisé pour référencer un RuntimeClass ?

- runtimeClass
- runtimeClassName
- containerRuntime
- runtime

<button type="button" class="eno-quiz-submit" disabled>Valider</button>
<div class="eno-quiz-explanation">Le passage de référence montre l&#39;utilisation de `runtimeClassName` dans une spécification de Pod.</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
  document.querySelectorAll('.eno-quiz-widget:not([data-eno-quiz-bound])').forEach(function (widget) {
    widget.setAttribute('data-eno-quiz-bound', '1');
    var options = Array.prototype.slice.call(widget.querySelectorAll('.eno-quiz-option'));
    var submitBtn = widget.querySelector('.eno-quiz-submit');
    var explanation = widget.querySelector('.eno-quiz-explanation');
    var selected = null;
    options.forEach(function (opt) {
      opt.addEventListener('click', function () {
        if (widget.hasAttribute('data-eno-quiz-answered')) return;
        options.forEach(function (o) { o.setAttribute('aria-pressed', 'false'); });
        opt.setAttribute('aria-pressed', 'true');
        selected = opt;
        submitBtn.disabled = false;
      });
    });
    submitBtn.addEventListener('click', function () {
      if (!selected || widget.hasAttribute('data-eno-quiz-answered')) return;
      widget.setAttribute('data-eno-quiz-answered', '1');
      submitBtn.disabled = true;
      var correct = selected.getAttribute('data-correct') === 'true';
      options.forEach(function (o) {
        o.classList.add('eno-quiz-revealed');
        if (o === selected) o.classList.add('eno-quiz-was-selected');
        var icon = o.querySelector('.eno-quiz-option-icon');
        if (o.getAttribute('data-correct') === 'true') icon.textContent = '\u2713';
        else if (o === selected) icon.textContent = '\u2717';
      });
      explanation.classList.add('eno-quiz-visible', correct ? 'eno-quiz-correct' : 'eno-quiz-incorrect');
    });
  });
});
</script>

## Modes de défaillance et récupération

Cette section décrit les scénarios de défaillance courants et comment s'en remettre.

### RuntimeClass manquante lors de la restauration

Si vous tentez de restaurer un pod qui référence une RuntimeClass inexistante, le pod échouera à se planifier. Pour récupérer :

- Vérifiez les événements :

kubectl describe pod mypod | grep -A10 Events 
 Cherchez FailedScheduling avec le message "runtimeclass.node.k8s.io/gvisor not found".

- Restaurez la RuntimeClass depuis la sauvegarde :

kubectl apply -f runtimeclasses-backup.yaml 

- Recréez le pod si nécessaire.

### Configuration du gestionnaire cassée

 Si le gestionnaire dans la RuntimeClass ne correspond à aucun runtime configuré sur le nœud, les pods échoueront à créer le bac à sable. Corrigez en rectifiant le gestionnaire dans la RuntimeClass ou en ajoutant la configuration du runtime sur les nœuds. Par exemple, si vous avez défini par erreur le gestionnaire sur runc alors que les nœuds n'ont que runsc pour cette classe, mettez à jour la RuntimeClass :

kubectl patch runtimeclass gvisor --type='merge' -p '{"handler":"runsc"}' 
 Ensuite, vérifiez la création du pod.

### Retour en arrière des modifications de RuntimeClass

Si vous modifiez une RuntimeClass et que cela cause des problèmes, revenez à une version précédente. Conservez des sauvegardes versionnées. Pour restaurer une version spécifique :

kubectl apply -f backups/runtimeclass-gvisor-20250301.yaml 
 Utilisez une convention de nommage avec horodatage.

### Reprise après sinistre à l'échelle du cluster

En cas de perte totale du cluster, restaurez les RuntimeClass dans le cadre de votre processus d'amorçage de cluster. Après la création du cluster, appliquez les sauvegardes avant de restaurer les charges de travail :

kubectl apply -f runtimeclasses-clean.json 
 Ensuite, restaurez les autres ressources. Assurez l'ordre : RuntimeClass avant les pods/déploiements.

## Liste de contrôle opérationnelle

Utilisez cette liste pour garantir un processus de sauvegarde et de restauration complet :

- [ ] Version de Kubernetes enregistrée (kubectl version).

- [ ] Toutes les RuntimeClass listées et exportées dans un fichier sous contrôle de version.

- [ ] Fichier de sauvegarde nettoyé des champs resourceVersion et status.

- [ ] Sauvegarde stockée hors cluster (par exemple, git, stockage d'objets).

- [ ] Processus de restauration testé dans un cluster de préproduction.

- [ ] Planification de pod testée avec chaque RuntimeClass.

- [ ] Étiquettes de nœud et tolérances vérifiées.

- [ ] Correspondance entre gestionnaires et runtimes réels documentée.

- [ ] Plan de retour en arrière pour les modifications de RuntimeClass.

- [ ] Sauvegarde de RuntimeClass incluse dans les travaux de sauvegarde réguliers du cluster.

Exemple de script de sauvegarde pour une tâche cron :

#!/bin/bash
kubectl get runtimeclass -o json | jq 'del(.items[].metadata.resourceVersion, .items[].metadata.uid, .items[].metadata.creationTimestamp, .items[].metadata.generation, .items[].status)' > /backups/runtimeclasses-$(date +%Y%m%d%H%M).json 

## Conclusion

 La sauvegarde et la restauration des classes d'exécution Kubernetes sont une tâche simple mais critique. En suivant les étapes de ce guide, vous pouvez garantir que vos configurations de runtime survivent aux sinistres et que les pods référençant des runtimes spécialisés continuent de fonctionner. Les points clés sont : inventorier vos RuntimeClass, les sauvegarder proprement, tester minutieusement la restauration et être préparé aux modes de défaillance courants. Des sauvegardes et validations régulières doivent faire partie de votre routine opérationnelle. Commencez par implémenter la commande de sauvegarde de base, puis étendez à des sauvegardes planifiées automatisées avec stockage hors cluster.