🕵️‍♂️ Decoding Kubernetes: When CrashLoopBackOff Turns into an "Error" State

A senior engineer's guide to troubleshooting pods with 1/2 ready containers.

If you spend enough time managing Kubernetes clusters, you will inevitably encounter the infamous CrashLoopBackOff status. It is a rite of passage for every DevOps engineer.

Usually, when a pod enters CrashLoopBackOff, it means the application inside the container is crashing immediately after startup. Kubernetes, trying to be helpful, restarts it. When it crashes again, Kubernetes waits 10 seconds, then 20 seconds, then 40 seconds—backing off exponentially to save CPU cycles.

But recently, I ran into a more interesting variation of this problem. A pod in my cluster was stuck in a restart loop, but instead of just showing CrashLoopBackOff, the status began toggling back and forth to Error, specifically showing 1/2 ready containers.

List of Crashed Kubernetes Pods with Error state

Here is how I approached troubleshooting this like a Senior DevOps Engineer, and how you can fix it in your own clusters.

🔍 Step 1: Understanding the "1/2" State

The biggest clue here isn't the word "Error"—it's the 1/2.

This tells us that there are two containers defined inside this single pod. One of the containers successfully started and is perfectly healthy (the 1), but the second container is the one crashing and causing the Error / CrashLoopBackOff loop.

In modern Kubernetes architectures, running two containers in a pod usually means one of two things:

  • An Init Container: A setup script that runs before the main app starts.
  • A Sidecar Container: A helper container (like a database proxy, a logging agent, or an Istio/Linkerd mesh proxy) running alongside your main application.

🛠️ Step 2: The Senior Engineer's Debugging Workflow

When faced with this, you shouldn't just guess or blindly restart the deployment. You need to ask Kubernetes for the evidence.

1. Identify the Crashing Container

First, we need to know the names of the two containers inside the pod. Run the describe command:

kubectl describe pod my-microservice-deployment-c6f985468-gmlc2

Scroll down to the Containers: section. You will see two containers listed. Look at their State. One will say Running, and the other will say Waiting (Reason: CrashLoopBackOff) or Terminated (Reason: Error). Note the name of the failing container.

2. Check the Exit Code

While looking at the describe output, look at the Exit Code of the terminated container. This is a massive hint:

  • Exit Code 1: Application error (a bug in the code, a missing configuration file, or a bad command).
  • Exit Code 137: OOMKilled (Out of Memory). The container tried to use more RAM than its limits allowed, and the Linux kernel assassinated it.
  • Exit Code 0: Normal exit. The process finished its job and shut down gracefully. (If a main container exits with 0, Kubernetes still restarts it because standard pods are expected to run forever).

3. Isolate the Logs

Normally, kubectl logs <pod-name> shows the logs. But because there are two containers, Kubernetes might complain or only show the healthy one. You must specify the exact name of the crashing container you found in Step 1:

kubectl logs my-microservice-deployment-c6f985468-gmlc2 -c <name-of-crashing-container>

(Pro-Tip: If the container crashed so fast that the current logs are empty, append the --previous flag to see the logs from the previous failed attempt!)

kubectl logs my-microservice-deployment-c6f985468-gmlc2 -c <name-of-crashing-container> --previous

💡 Common Causes and Fixes

Based on the logs and the Exit Code, here is how you resolve the most common culprits behind the 1/2 Error state:

Scenario A: The Sidecar Proxy is Failing (Network Issue)

If the failing container is a sidecar (like an Envoy proxy or Cloud SQL Auth Proxy), the Error is usually caused by misconfigured credentials or a bad connection string.

The Fix: Verify that the sidecar has the correct Kubernetes Secrets mounted and that the IAM permissions or database credentials are valid.

Scenario B: The Main App is Missing a Dependency

If the sidecar is healthy (e.g., the proxy started successfully) but your main application container is the one crashing, it often means your app tried to connect to a database or external API that wasn't ready yet.

The Fix: Implement a retry/backoff logic directly in your application code. Alternatively, use an InitContainer to ping the database; the main container will only start once the InitContainer successfully connects.

Scenario C: Misconfigured Liveness Probes

Sometimes the app is actually running fine, but it takes 60 seconds to boot up. If your Kubernetes livenessProbe is configured to check the app after only 10 seconds, Kubernetes assumes the app is broken and kills it, triggering the Error and CrashLoopBackOff.

The Fix: Increase the initialDelaySeconds in your deployment YAML, or better yet, use a startupProbe (designed specifically for slow-starting legacy applications).

🎯 Conclusion

A CrashLoopBackOff accompanied by an Error state is Kubernetes working exactly as designed—it is protecting the cluster from a faulty process. By systematically breaking down the pod using kubectl describe and isolating the container logs, you can quickly move past the scary status messages and directly address the root cause.

🕵️‍♂️ Décoder Kubernetes : Quand un CrashLoopBackOff devient un état "Error"

Le guide d'un ingénieur senior pour dépanner les pods avec des conteneurs 1/2 prêts.

Si vous passez suffisamment de temps à gérer des clusters Kubernetes, vous finirez inévitablement par rencontrer le tristement célèbre statut CrashLoopBackOff. C'est un rite de passage pour tout ingénieur DevOps.

Habituellement, lorsqu'un pod entre en CrashLoopBackOff, cela signifie que l'application à l'intérieur du conteneur plante immédiatement après son démarrage. Kubernetes, essayant d'être utile, le redémarre. Lorsqu'il plante à nouveau, Kubernetes attend 10 secondes, puis 20 secondes, puis 40 secondes—reculant de manière exponentielle pour économiser des cycles CPU.

Mais récemment, j'ai rencontré une variation plus intéressante de ce problème. Un pod dans mon cluster était bloqué dans une boucle de redémarrage, mais au lieu d'afficher simplement CrashLoopBackOff, le statut a commencé à basculer vers Error, affichant spécifiquement 1/2 conteneurs prêts.

Liste des Pods Kubernetes Crashés en état d'Erreur

Voici comment j'ai abordé le dépannage de ce problème en tant qu'ingénieur DevOps Senior, et comment vous pouvez le résoudre dans vos propres clusters.

🔍 Étape 1 : Comprendre l'état "1/2"

Le plus grand indice ici n'est pas le mot "Error"—c'est le 1/2.

Cela nous indique qu'il y a deux conteneurs définis à l'intérieur de ce même pod. L'un des conteneurs a démarré avec succès et est parfaitement sain (le 1), mais le second conteneur est celui qui plante et provoque la boucle Error / CrashLoopBackOff.

Dans les architectures Kubernetes modernes, exécuter deux conteneurs dans un pod signifie généralement l'une de ces deux choses :

  • Un Init Container : Un script de configuration qui s'exécute avant le démarrage de l'application principale.
  • Un Sidecar Container : Un conteneur de support (comme un proxy de base de données, un agent de journalisation ou un proxy de service mesh Istio/Linkerd) exécuté aux côtés de votre application principale.

🛠️ Étape 2 : Le Workflow de Débogage de l'Ingénieur Senior

Confronté à cela, vous ne devriez pas vous contenter de deviner ou de redémarrer aveuglément le déploiement. Vous devez demander à Kubernetes des preuves basées sur des faits.

1. Identifier le Conteneur en Échec

Tout d'abord, nous devons connaître les noms des deux conteneurs à l'intérieur du pod. Exécutez la commande describe :

kubectl describe pod my-microservice-deployment-c6f985468-gmlc2

Faites défiler jusqu'à la section Containers:. Vous verrez deux conteneurs listés. Regardez leur état (State). L'un dira Running, et l'autre dira Waiting (Reason: CrashLoopBackOff) ou Terminated (Reason: Error). Notez le nom du conteneur qui échoue.

2. Vérifier le Code de Sortie (Exit Code)

En observant la sortie de la commande describe, regardez l'Exit Code du conteneur terminé. C'est un indice crucial :

  • Exit Code 1 : Erreur d'application (un bug dans le code, un fichier de configuration manquant, ou une mauvaise commande).
  • Exit Code 137 : OOMKilled (Out of Memory). Le conteneur a tenté d'utiliser plus de RAM que ses limites ne le permettaient, et le noyau Linux l'a arrêté net.
  • Exit Code 0 : Sortie normale. Le processus a terminé son travail et s'est arrêté proprement. (Si un conteneur principal sort avec 0, Kubernetes le redémarre quand même car on s'attend à ce que les pods standards fonctionnent indéfiniment).

3. Isoler les Journaux (Logs)

Normalement, kubectl logs <pod-name> affiche les journaux. Mais parce qu'il y a deux conteneurs, Kubernetes pourrait refuser ou n'afficher que celui qui est sain. Vous devez spécifier le nom exact du conteneur qui plante, identifié à l'Étape 1 :

kubectl logs my-microservice-deployment-c6f985468-gmlc2 -c <nom-du-conteneur-en-echec>

(Astuce de Pro : Si le conteneur a planté si vite que les journaux actuels sont vides, ajoutez l'option --previous pour voir les journaux de la tentative de plantage précédente !)

kubectl logs my-microservice-deployment-c6f985468-gmlc2 -c <nom-du-conteneur-en-echec> --previous

💡 Causes Communes et Solutions

Sur la base des logs et du code de sortie, voici comment résoudre les causes les plus probables de l'état d'erreur 1/2 :

Scénario A : Le Proxy Sidecar Échoue (Problème Réseau)

Si le conteneur en échec est un sidecar (comme un proxy Envoy ou Cloud SQL Auth Proxy), l'erreur (Error) est généralement causée par des identifiants mal configurés ou une chaîne de connexion incorrecte.

La Solution : Vérifiez que le sidecar a les bons "Secrets" Kubernetes montés et que les autorisations IAM ou les identifiants de la base de données sont valides.

Scénario B : L'Application Principale manque d'une Dépendance

Si le sidecar est sain (par exemple, le proxy a démarré avec succès) mais que votre conteneur applicatif principal est celui qui plante, cela signifie souvent que votre application a tenté de se connecter à une base de données ou à une API externe qui n'était pas encore prête.

La Solution : Implémentez une logique de relance/recule (retry/backoff) directement dans le code de votre application. Alternativement, utilisez un InitContainer pour envoyer un ping à la base de données ; le conteneur principal ne démarrera qu'une fois que l'InitContainer se sera connecté avec succès.

Scénario C : Sondes de Vivacité (Liveness Probes) Mal Configurées

Parfois l'application fonctionne parfaitement bien, mais elle met 60 secondes à démarrer. Si votre livenessProbe Kubernetes est configurée pour vérifier l'application après seulement 10 secondes, Kubernetes suppose que l'application est en panne et la tue, déclenchant les statuts Error et CrashLoopBackOff.

La Solution : Augmentez la valeur initialDelaySeconds dans votre YAML de déploiement, ou mieux encore, utilisez une startupProbe (conçue spécifiquement pour les applications héritées/legacy ayant un démarrage lent).

🎯 Conclusion

Un CrashLoopBackOff accompagné d'un état Error indique que Kubernetes fonctionne exactement comme prévu : il protège le cluster contre un processus défectueux. En décomposant méthodiquement l'état du pod à l'aide de kubectl describe et en isolant les logs du conteneur en cause, vous pouvez rapidement outrepasser les messages d'état initiaux et vous attaquer directement à la source de l'erreur.