## Introduction

Les conteneurs d'initialisation Kubernetes s'exécutent avant les conteneurs d'application principaux dans un Pod. Ils sont idéaux pour les tâches de configuration telles que les migrations de base de données, l'attente de dépendances ou la récupération de configuration. Cependant, de nombreuses équipes traitent les conteneurs d'initialisation comme une réflexion après coup et négligent la planification de capacité. Cela conduit à des échecs de planification des Pods, à des conflits de ressources ou à des temps de démarrage lents. Ce guide explique comment estimer les ressources pour les conteneurs d'initialisation, définir des requêtes et des limites appropriées, et planifier la mise à l'échelle à l'aide d'exemples pratiques et de commandes. En suivant ces étapes, vous éviterez les pièges courants et assurerez une initialisation fiable des Pods en production.

## Inventaire des versions et de l'environnement

Avant de modifier les ressources des conteneurs d'initialisation, confirmez la version de votre cluster et sa topologie. Les conteneurs d'initialisation sont stables depuis Kubernetes 1.6, mais les versions ultérieures offrent une meilleure gestion des ressources. Utilisez Kubernetes 1.18 ou plus récent pour ce guide.

Vérifiez la version de votre cluster :

kubectl version --short 
 Exemple de sortie :

Client Version: v1.24.0
Server Version: v1.24.0 
 Prérequis :

- Un cluster Kubernetes en cours d'exécution (mono-nœud ou multi-nœuds)

- kubectl configuré avec les droits d'administrateur de cluster ou d'administrateur d'espace de noms

- Compréhension de base des requêtes et limites de ressources des Pods

Pour ce guide, nous supposons un cluster à 3 nœuds avec 4 vCPU et 8 Go de RAM par nœud, exécutant Kubernetes v1.24. Votre topologie réelle variera, mais ces chiffres fournissent une référence concrète pour les exemples.

## Comprendre la sémantique des ressources des conteneurs d'initialisation

Avant de plonger dans la configuration, il est crucial de comprendre comment Kubernetes gère les ressources pour les conteneurs d'initialisation. Ces connaissances évitent les erreurs de configuration et les surprises de planification.

Points clés :

- Les conteneurs d'initialisation s'exécutent séquentiellement, un à la fois, avant le démarrage de tout conteneur d'application.

- Chaque conteneur d'initialisation peut avoir ses propres requêtes et limites de ressources, indépendamment des autres conteneurs d'initialisation et des conteneurs d'application.

- Le planificateur calcule les besoins totaux en ressources d'un Pod comme la somme des requêtes des conteneurs d'application plus la requête maximale parmi tous les conteneurs d'initialisation. En effet, un seul conteneur d'initialisation s'exécute à la fois, mais le Pod doit réserver suffisamment de capacité pour le plus grand conteneur d'initialisation en plus des conteneurs d'application.

- Les limites sont appliquées par conteneur, et non par Pod. Un conteneur d'initialisation dépassant sa limite CPU est limité (throttling) ; dépassant sa limite mémoire, il est tué (OOMKilled).

- Si un conteneur d'initialisation échoue, le Pod le redémarre selon la restartPolicy (généralement Always), et toute l'initialisation du Pod reprend depuis le début.

Exemple : Supposons qu'un Pod a deux conteneurs d'application demandant chacun 200m CPU et 128Mo de mémoire, et deux conteneurs d'initialisation : init A demande 500m CPU et 256Mo de mémoire, init B demande 100m CPU et 64Mo de mémoire. La requête totale du Pod est la somme des requêtes des applications (400m CPU, 256Mo mémoire) plus la requête maximale d'initialisation (500m CPU, 256Mo mémoire) = 900m CPU et 512Mo mémoire. C'est ce que le planificateur utilise pour trouver un nœud avec suffisamment de ressources allouables.

<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 l'objectif principal des conteneurs d'initialisation dans un Pod Kubernetes ?

- S'exécuter en continu aux côtés des conteneurs d'application principaux
- S'exécuter avant les conteneurs d'application principaux et se terminer avant leur démarrage
- Remplacer les conteneurs d'application principaux
- Servir de conteneurs sidecar pour la journalisation

<button type="button" class="eno-quiz-submit" disabled>Valider</button>
<div class="eno-quiz-explanation">Les conteneurs d&#39;initialisation s&#39;exécutent avant les conteneurs d&#39;application et doivent se terminer avec succès avant que le suivant ne démarre, comme décrit dans l&#39;article.</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

Commencez avec un seul conteneur d'initialisation dans une charge de travail familière. Les requêtes et limites de ressources sont définies dans la spécification du Pod sous initContainers . Voici un manifeste de Pod complet avec un conteneur d'initialisation qui attend un service et un conteneur d'application exécutant nginx :

apiVersion: v1
kind: Pod
metadata:
 name: myapp-pod
spec:
 initContainers:
 - name: init-myservice
 image: busybox:1.28
 command: ['sh', '-c', 'echo "Waiting for service"; sleep 10']
 resources:
 requests:
 cpu: "100m"
 memory: "64Mi"
 limits:
 cpu: "200m"
 memory: "128Mi"
 containers:
 - name: myapp-container
 image: nginx:1.14.2
 resources:
 requests:
 cpu: "100m"
 memory: "64Mi"
 limits:
 cpu: "200m"
 memory: "128Mi" 
 Notez que le conteneur d'initialisation a des paramètres de ressources distincts du conteneur d'application. La requête totale du Pod est le maximum de toutes les requêtes de conteneurs, mais le planificateur considère la somme des requêtes des conteneurs d'application plus la plus grande requête des conteneurs d'initialisation. (Voir la section précédente pour plus de détails.)

Choix de portée :

- Utilisez requests pour garantir une quantité minimale de ressources pour le conteneur. Le planificateur utilise les requêtes pour décider quel nœud peut héberger le Pod.

- Utilisez limits pour plafonner l'utilisation des ressources. Pour le CPU, dépasser la limite entraîne une limitation. Pour la mémoire, dépasser la limite peut provoquer l'arrêt du conteneur (OOMKilled).

- Pour les conteneurs d'initialisation qui effectuent des tâches lourdes (par exemple, migrations de base de données), définissez des limites suffisamment élevées pour terminer rapidement, mais pas au point d'affamer les autres Pods. Commencez avec des valeurs modestes et ajustez en fonction de l'utilisation observée.

Approche pratique :

- Estimez l'utilisation maximale des ressources du conteneur d'initialisation en fonction de sa tâche. Par exemple, un outil de migration de base de données comme Flyway peut nécessiter 500m CPU et 512Mo de mémoire pour un changement de schéma important.

- Définissez les requêtes égales à votre estimation de base et les limites à 1,5 à 2 fois cette valeur pour permettre des pics.

- Déployez dans un environnement de test et mesurez l'utilisation réelle avec kubectl top ou des outils de surveillance.

- Ajustez les requêtes et les limites en conséquence, en gardant toujours requests <= limits .

## Vérification et diagnostics

Après avoir appliqué la spécification du Pod, vérifiez que le conteneur d'initialisation se termine avec succès et que le Pod passe à l'état Running.

Appliquez le manifeste :

kubectl apply -f pod.yaml
pod/myapp-pod created 
 Surveillez le statut du Pod :

kubectl get pods
NAME READY STATUS RESTARTS AGE
myapp-pod 0/1 Init:0/1 0 5s 
 Après la fin du conteneur d'initialisation :

kubectl get pods
NAME READY STATUS RESTARTS AGE
myapp-pod 1/1 Running 0 25s 
 Consultez les journaux du conteneur d'initialisation :

kubectl logs myapp-pod -c init-myservice 
 Sortie attendue :

Waiting for service 
 Pour voir l'utilisation réelle des ressources, utilisez kubectl top (nécessite metrics-server) :

kubectl top pod myapp-pod --containers 
 Exemple de sortie :

POD NAME CPU(cores) MEMORY(bytes)
myapp-pod init-myservice 0m 0Mi
myapp-pod myapp-container 1m 3Mi 
 Si le conteneur d'initialisation échoue, par exemple avec OOMKilled, inspectez les événements :

kubectl describe pod myapp-pod 
 Regardez la section Events :

Events:
 Type Reason Age From Message
 ---- ------ ---- ---- -------
 Normal Scheduled 10s default-scheduler Successfully assigned default/myapp-pod to node1
 Warning Failed 8s kubelet Error: failed to start container "init-myservice": Error response from daemon: OCI runtime create failed: container_linux.go:380: starting container process caused: process_linux.go:545: container init caused: running with non-zero exit code: exit status 137 
 Le code de sortie 137 indique un arrêt OOM (128 + 9, SIGKILL). Augmentez les limites de mémoire en conséquence. Vérifiez également si le conteneur d'initialisation a dépassé sa limite CPU ; il sera limité mais pas tué.

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

Les modes de défaillance courants incluent :

- OOMKilled : Le conteneur d'initialisation dépasse la limite de mémoire. Le conteneur est tué. Solution : augmenter la limite de mémoire ou réduire l'empreinte mémoire de la charge de travail du conteneur d'initialisation.

- Limitation CPU : Le conteneur d'initialisation atteint la limite CPU ; il s'exécute lentement mais n'échoue pas. Cela peut entraîner des retards de démarrage ou des délais d'attente. Solution : augmenter la limite CPU ou accepter un démarrage plus lent.

- Échec de planification : Le Pod ne peut pas être planifié car aucun nœud ne satisfait les requêtes de ressources du conteneur d'initialisation. Solution : réduire les requêtes ou ajouter des nœuds.

- Le conteneur d'initialisation ne se termine jamais : Il peut attendre une dépendance qui n'apparaît jamais. Solution : ajouter un délai d'attente à la commande ou utiliser une sonde de disponibilité.

Étapes de récupération détaillées :

- OOMKilled

- Augmentez limits.memory dans la spécification du conteneur d'initialisation.

- Exemple : si le conteneur d'initialisation a besoin de 200Mo mais que la limite est de 128Mo, passez la limite à 256Mo.

- Réappliquez le Pod.

- Limitation CPU

- Vérifiez l'utilisation du CPU avec kubectl top pod --containers pendant que le conteneur d'initialisation s'exécute.

- Si l'utilisation atteint constamment la limite CPU, augmentez limits.cpu .

- Remarque : les requêtes peuvent rester inchangées si la planification n'est pas un problème.

- Échec de planification

- Exécutez kubectl describe pod <nom-du-pod> et recherchez des événements comme « 0/3 nodes are available: 3 Insufficient cpu. »

- Réduisez les requests du conteneur d'initialisation ou mettez à l'échelle les nœuds du cluster.

- Alternativement, déplacez le Pod vers un espace de noms avec des quotas de ressources différents, le cas échéant.

- Le conteneur d'initialisation ne se termine jamais

- Consultez les journaux pour voir où il est bloqué : kubectl logs <nom-du-pod> -c <nom-du-conteneur-init>

- Ajoutez un délai d'attente dans la commande, par exemple, timeout 60s ./wait-for-db.sh

- Utilisez des commandes de conteneur d'initialisation qui se terminent en cas d'échec plutôt que de rester indéfiniment en attente.

Retour en arrière : Si les changements causent des problèmes, revenez aux valeurs de ressources précédentes en utilisant kubectl apply -f previous-pod.yaml ou kubectl edit pod myapp-pod et ajustez les ressources. Pour récupérer rapidement un Pod bloqué, supprimez-le :

kubectl delete pod myapp-pod
pod "myapp-pod" deleted 
 Puis réappliquez avec des ressources corrigées. Pour les problèmes persistants, vérifiez les journaux et les événements avant de redéployer.

<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

Que se passe-t-il si un conteneur d'initialisation échoue pendant le démarrage d'un Pod ?

- Le Pod devient immédiatement Ready
- Le kubelet redémarre le conteneur d'initialisation jusqu'à ce qu'il réussisse
- Les autres conteneurs d'initialisation commencent à s'exécuter
- Les conteneurs principaux démarrent quand même

<button type="button" class="eno-quiz-submit" disabled>Valider</button>
<div class="eno-quiz-explanation">Selon l&#39;article, si le conteneur d&#39;initialisation d&#39;un Pod échoue, le kubelet redémarre à plusieurs reprises ce conteneur d&#39;initialisation jusqu&#39;à ce qu&#39;il réussisse.</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>

## Planification avancée de la capacité pour les conteneurs d'initialisation

### Estimer les besoins en ressources à partir des caractéristiques de la charge de travail

La manière la plus précise de planifier la capacité est de mesurer la consommation réelle de ressources du conteneur d'initialisation dans des conditions réalistes. Voici une approche systématique :

- Identifiez le type de tâche du conteneur d'initialisation :

- Migration de base de données : l'utilisation du CPU et de la mémoire dépend de la taille du schéma et des transformations de données.

- Attente de dépendance : ressources minimales ; une simple boucle sleep ou curl .

- Récupération de configuration : l'utilisation des ressources dépend des E/S réseau et de la décompression.

- Génération de fichiers : le CPU et la mémoire évoluent avec la taille de sortie et le traitement.

- Exécutez le conteneur d'initialisation manuellement en dehors de Kubernetes ou dans un Pod de test avec des limites généreuses. Mesurez l'utilisation maximale à l'aide de kubectl top ou docker stats .

- Appliquez un facteur de sécurité : définissez les requêtes au 90e centile de l'utilisation observée, les limites à 1,5 à 2 fois cette valeur.

Exemple : Conteneur d'initialisation de migration de base de données

Supposons que vous ayez besoin d'un conteneur d'initialisation exécutant Flyway pour appliquer des migrations de base de données avant le démarrage de l'application principale. Vous exécutez une migration de test sur une copie de la base de données et observez :

- CPU maximal : 350m

- Mémoire maximale : 400Mo

Vous définissez alors :

initContainers:
- name: db-migrate
 image: flyway/flyway:9.22.3
 command: ["flyway", "migrate"]
 resources:
 requests:
 cpu: "400m"
 memory: "450Mi"
 limits:
 cpu: "800m"
 memory: "800Mi" 
 Cela donne une marge pour des migrations plus importantes sans surallouer inutilement les ressources du cluster.

### Utiliser Vertical Pod Autoscaler (VPA) pour les conteneurs d'initialisation

VPA peut ajuster automatiquement les requêtes de ressources en fonction de l'historique d'utilisation. Cependant, le support de VPA pour les conteneurs d'initialisation est limité ; il cible principalement les conteneurs réguliers. À partir de Kubernetes 1.24, VPA ne gère pas directement les ressources des conteneurs d'initialisation. Vous pouvez utiliser VPA en mode recommandation pour obtenir des suggestions pour les conteneurs d'application et appliquer une logique similaire aux conteneurs d'initialisation manuellement.

Pour obtenir des recommandations VPA, installez VPA et créez un objet VPA pour le Pod. Les recommandations incluront les requêtes au niveau des conteneurs. Pour les conteneurs d'initialisation, vous devrez peut-être créer un VPA séparé ou ajuster manuellement en fonction de l'utilisation observée.

### Planification de capacité à grande échelle

Lorsque vous exécutez de nombreux Pods avec des conteneurs d'initialisation, la consommation agrégée des ressources doit être prise en compte.

- Si chaque Pod démarre avec un conteneur d'initialisation qui utilise des ressources importantes, la demande de pointe se produit pendant les déploiements ou les événements de mise à l'échelle.

- Planifiez la capacité des nœuds pour gérer le nombre maximal de conteneurs d'initialisation simultanés plus les conteneurs d'application.

- Utilisez kubectl describe nodes pour voir les ressources allouables et les requêtes actuelles.

Exemple : Supposons que vous avez 10 répliques d'un déploiement, chaque Pod a un conteneur d'initialisation avec une requête de 500m CPU et 256Mo de mémoire, et un conteneur d'application avec une requête de 200m CPU et 128Mo de mémoire. La requête CPU totale par Pod est de 500m + 200m = 700m (puisqu'un seul conteneur d'initialisation à la fois, requête max d'init 500m, plus application 200m). Pour 10 Pods, la requête CPU totale est de 7 cœurs. Avec trois nœuds de 4 vCPU chacun (12 cœurs au total), vous pouvez planifier tous les Pods s'ils tiennent nœud par nœud. Mais si chaque Pod est planifié sur un nœud différent, chaque nœud doit accueillir au moins la requête d'un Pod. C'est gérable. Cependant, si vous avez 100 Pods, vous avez besoin de 70 cœurs, ce qui peut dépasser la capacité du cluster. Planifiez en conséquence.

### Quotas de ressources et LimitRanges

Dans les clusters multi-locataires, les ResourceQuotas et les LimitRanges peuvent affecter la planification de capacité des conteneurs d'initialisation.

- Les ResourceQuotas peuvent limiter le total des requêtes/limites dans un espace de noms, y compris les conteneurs d'initialisation.

- Les LimitRanges peuvent définir des requêtes/limites par défaut si elles ne sont pas spécifiées, et appliquer des valeurs minimales/maximales.

Exemple de LimitRange :

apiVersion: v1
kind: LimitRange
metadata:
 name: mem-limit-range
spec:
 limits:
 - default:
 memory: 512Mi
 cpu: 500m
 defaultRequest:
 memory: 256Mi
 cpu: 200m
 max:
 memory: 1Gi
 cpu: 1
 min:
 memory: 64Mi
 cpu: 50m
 type: Container 
 Cela s'applique également aux conteneurs d'initialisation, sauf si type: InitContainer est spécifié séparément. Soyez conscient de ces contraintes lors de la définition des ressources des conteneurs d'initialisation.

## Liste de contrôle opérationnelle

Utilisez cette liste de contrôle avant et après le déploiement de conteneurs d'initialisation pour assurer un fonctionnement sans heurts :

<div class="my-stack-md overflow-x-auto">
<table class="min-w-[42rem] border-collapse text-left">
<thead><tr><th scope="col" class="border border-outline-variant bg-surface-container-low px-4 py-3 text-left font-label-md font-semibold text-on-surface">Étape</th><th scope="col" class="border border-outline-variant bg-surface-container-low px-4 py-3 text-left font-label-md font-semibold text-on-surface">Action</th><th scope="col" class="border border-outline-variant bg-surface-container-low px-4 py-3 text-left font-label-md font-semibold text-on-surface">Résultat attendu</th></tr></thead>
<tbody><tr><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">1</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Vérifiez la version du cluster avec <code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded">kubectl version --short</code></td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">La version du serveur est 1.18 ou plus récente</td></tr>
<tr><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">2</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Identifiez le but du conteneur d&#39;initialisation</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Définition claire de la tâche (par exemple, migration, attente de service)</td></tr>
<tr><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">3</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Estimez les besoins en ressources en fonction de la complexité de la tâche et des données historiques</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Estimations documentées pour le CPU et la mémoire</td></tr>
<tr><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">4</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Définissez les requêtes et les limites</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Requêtes &lt;= limites ; valeurs basées sur des estimations avec facteur de sécurité</td></tr>
<tr><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">5</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Appliquez la spécification du Pod avec <code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded">kubectl apply -f pod.yaml</code></td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Pod créé avec succès</td></tr>
<tr><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">6</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Surveillez le statut du Pod avec <code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded">kubectl get pods -w</code></td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Le conteneur d&#39;initialisation se termine ; le Pod passe à Running</td></tr>
<tr><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">7</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Vérifiez les journaux du conteneur d&#39;initialisation avec <code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded">kubectl logs &lt;pod&gt; -c &lt;init-container&gt;</code></td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Sortie attendue, aucune erreur</td></tr>
<tr><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">8</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Surveillez l&#39;utilisation des ressources avec <code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded">kubectl top pod --containers</code></td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Utilisation dans les limites, pas de limitation ni d&#39;OOM</td></tr>
<tr><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">9</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Testez sous charge en mettant à l&#39;échelle les répliques</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Tous les Pods sont planifiés et s&#39;initialisent sans délai</td></tr>
<tr><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">10</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Documentez les valeurs finales des ressources et mettez à jour les runbooks</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Valeurs enregistrées pour référence future</td></tr></tbody>
</table>
</div>
Passez régulièrement en revue les ressources des conteneurs d'initialisation, surtout après des changements de code ou de données. Les signaux de mise à l'échelle incluent une durée accrue du conteneur d'initialisation, des arrêts OOM ou des retards de planification.

## Exemple concret : scénario de bout en bout

Parcourons un exemple complet de planification de capacité pour un conteneur d'initialisation qui attend qu'une base de données soit prête avant de démarrer une application web.

Cluster : 3 nœuds, chacun avec 4 vCPU et 8 Go de mémoire.

Application : Une application web avec deux conteneurs : serveur web (nginx) et serveur d'application (personnalisé). Le conteneur d'initialisation exécute un script qui vérifie la connectivité à la base de données en utilisant nc -z dans une boucle.

Spécification initiale du Pod sans limites de ressources :

apiVersion: v1
kind: Pod
metadata:
 name: webapp-pod
spec:
 initContainers:
 - name: wait-for-db
 image: busybox:1.28
 command: ['sh', '-c', 'until nc -z db-service 5432; do echo waiting for db; sleep 2; done;']
 containers:
 - name: web
 image: nginx:1.14.2
 ports:
 - containerPort: 80
 - name: app
 image: myapp:1.0
 ports:
 - containerPort: 8080 
 Ce Pod n'a pas de ressources explicites, il utilise donc les valeurs par défaut s'il existe un LimitRange, ou aucune requête/limite. Dans un cluster de production, cela pourrait conduire à une surallocation et à de mauvaises performances.

Étape 1 : Mesurer l'utilisation réelle

Exécutez le conteneur d'initialisation dans un Pod de test avec des limites généreuses :

initContainers:
- name: wait-for-db
 image: busybox:1.28
 command: ['sh', '-c', 'until nc -z db-service 5432; do echo waiting for db; sleep 2; done;']
 resources:
 requests:
 cpu: "10m"
 memory: "16Mi"
 limits:
 cpu: "100m"
 memory: "64Mi" 
 Observez avec kubectl top pendant que le conteneur d'initialisation s'exécute. Il montre une utilisation très faible : CPU 1m, mémoire 2Mo. Cela a du sens car il s'agit simplement d'une boucle shell avec des vérifications réseau.

Étape 2 : Définir les ressources finales

Sur la base de la mesure, définissez :

resources:
 requests:
 cpu: "10m"
 memory: "16Mi"
 limits:
 cpu: "50m"
 memory: "32Mi" 
 Définissez également des ressources pour les conteneurs d'application :

containers:
- name: web
 image: nginx:1.14.2
 resources:
 requests:
 cpu: "100m"
 memory: "64Mi"
 limits:
 cpu: "200m"
 memory: "128Mi"
- name: app
 image: myapp:1.0
 resources:
 requests:
 cpu: "200m"
 memory: "128Mi"
 limits:
 cpu: "500m"
 memory: "256Mi" 
 Requêtes totales du Pod : somme des conteneurs d'application = 100m+200m=300m CPU, 64Mo+128Mo=192Mo mémoire. La requête maximale d'initialisation est de 10m CPU, 16Mo mémoire. Total = 310m CPU et 208Mo mémoire. C'est bien dans la capacité du nœud.

Étape 3 : Test de mise à l'échelle

Créez un déploiement avec 10 répliques. La requête CPU totale pour tous les Pods est de 3100m = 3,1 cœurs. Le cluster a 12 cœurs au total, donc cela convient. Cependant, la planification dépend de la capacité par nœud. Chaque nœud a 4 cœurs ; un Pod nécessite 310m, donc un nœud peut héberger plusieurs Pods. Avec la planification par défaut, tous les Pods devraient se planifier sans problème.

Étape 4 : Surveiller et ajuster

Après le déploiement, vérifiez que les conteneurs d'initialisation se terminent rapidement et qu'il n'y a pas d'événements OOM. Ajustez si nécessaire.

Cet exemple démontre un conteneur d'initialisation léger. Pour des tâches plus lourdes, suivez l'exemple de migration de base de données dans la section avancée.

## Conclusion

La planification de capacité pour les conteneurs d'initialisation Kubernetes est essentielle pour un démarrage fiable des Pods. En suivant les étapes de ce guide --- commencer par une base de référence, définir des requêtes et limites appropriées, vérifier avec les journaux et les métriques, et se préparer aux modes de défaillance courants --- vous pouvez éviter les pannes liées aux ressources. Commencez par un petit pilote mesurable comme suggéré, puis itérez en fonction de l'utilisation réelle. Utilisez la liste de contrôle opérationnelle pour maintenir la cohérence entre les déploiements. Avec une planification minutieuse, les conteneurs d'initialisation s'exécuteront efficacement sans gaspiller les ressources du cluster ni provoquer d'échecs de planification. N'oubliez pas de revoir régulièrement les ressources et de les ajuster à mesure que votre application évolue.