Résoudre les problèmes liés aux événements OOM

Si vos applications Google Kubernetes Engine (GKE) rencontrent des arrêts de conteneurs inattendus, une instabilité ou des erreurs CrashLoopBackOff, cela peut être dû à des événements de mémoire insuffisante (OOM). Les événements OOM se produisent lorsqu'un conteneur ou un nœud manque de mémoire, ce qui entraîne l'arrêt des processus par le robot tueur OOM Linux pour récupérer des ressources.

Cette page vous permet de déterminer si un événement OOM s'est produit au niveau du conteneur ou du nœud, et d'appliquer des stratégies d'atténuation efficaces pour éviter qu'il ne se reproduise.

Ces informations sont importantes pour les développeurs d'applications qui doivent s'assurer que leurs applications sont configurées avec des demandes et des limites de mémoire appropriées, et qu'elles ne présentent pas de fuites de mémoire. Elles sont également importantes pour les administrateurs et opérateurs de plate-forme qui surveillent l'utilisation des ressources des nœuds et s'assurent que le cluster dispose d'une capacité de mémoire suffisante pour prendre en charge les charges de travail déployées. Pour en savoir plus sur les rôles courants et les exemples de tâches que nous citons dans le Cloud de Confiance by S3NS contenu, consultez Rôles utilisateur et tâches courantes de GKE.

Causes courantes des événements OOM

Les événements OOM se produisent généralement lors de pics de charge ou de trafic, lorsque l'utilisation de la mémoire de l'application augmente et atteint la limite de mémoire configurée pour le conteneur.

Les scénarios suivants peuvent entraîner un événement OOM :

  • Limite de mémoire insuffisante : le paramètre resources.limits.memory du fichier manifeste du pod est trop faible pour les besoins de mémoire habituels ou maximaux de l'application.
  • Demandes ou limites de mémoire non définies : si resources.limits.memory et resources.requests.memory ne sont pas définis, l'utilisation de la mémoire du conteneur est illimitée.
  • Charge élevée ou irrégulière : des pics de charge soudains et extrêmes peuvent submerger les ressources d'un système, y compris la mémoire, même si les limites sont généralement suffisantes.
  • Fuite de mémoire : l'application peut présenter un défaut de code qui l'empêche de libérer correctement la mémoire.

Les événements OOM peuvent entraîner une défaillance en cascade, car moins de conteneurs restent disponibles pour gérer le trafic, ce qui augmente la charge sur les conteneurs restants. Ces conteneurs peuvent alors également être arrêtés.

Comment Kubernetes gère les événements OOM

Le robot tueur OOM Linux gère chaque événement OOM. Le robot tueur OOM est un processus de noyau qui s'active lorsque le système manque de mémoire critique. Son objectif est d'empêcher un plantage total du système en arrêtant stratégiquement les processus pour libérer des ressources. Le noyau utilise un système de notation pour sélectionner le processus à arrêter, dans le but de préserver la stabilité du système et de minimiser la perte de données.

Dans un environnement Kubernetes, le robot tueur OOM fonctionne à deux niveaux différents : le groupe de contrôle (cgroup), qui affecte un conteneur, et le système, qui affecte l'ensemble du nœud.

Arrêt OOM au niveau du conteneur

Un arrêt OOM au niveau du conteneur se produit lorsqu'un conteneur tente de dépasser sa limite de mémoire prédéfinie. Kubernetes attribue chaque conteneur à un cgroup spécifique avec une limite de mémoire stricte. Lorsque l'utilisation de la mémoire d'un conteneur atteint cette limite, le noyau tente d'abord de récupérer de la mémoire dans ce cgroup. Si le noyau ne parvient pas à récupérer suffisamment de mémoire à l'aide de ce processus, le robot tueur OOM du cgroup est appelé. Il arrête les processus dans ce cgroup spécifique pour appliquer la limite de ressources.

Lorsque le processus principal d'un conteneur est arrêté de cette manière, Kubernetes observe l'événement et marque l'état du conteneur comme OOMKilled. La restartPolicy configurée du pod détermine alors le résultat :

  • Always ou OnFailure : le conteneur est redémarré.
  • Never : le conteneur n'est pas redémarré et reste à l'état arrêté.

En isolant la défaillance du conteneur défaillant, le robot tueur OOM empêche un seul pod défectueux de planter l'ensemble du nœud.

Comment la version du cgroup affecte le comportement du robot tueur OOM

Le comportement d'arrêt OOM peut varier considérablement entre les versions du cgroup. Si vous ne savez pas quelle version du cgroup vous utilisez, vérifiez le mode cgroup des nœuds de cluster.

  • Dans cgroup v1, un événement OOM dans le cgroup de mémoire d'un conteneur peut entraîner un comportement imprévisible. Le robot tueur OOM peut arrêter n'importe quel processus dans ce cgroup, y compris les processus enfants qui ne sont pas le processus principal du conteneur (PID 1).

    Ce comportement pose un défi important pour Kubernetes. Étant donné que Kubernetes surveille principalement l'état du processus de conteneur principal, il n'est pas au courant de ces arrêts OOM "partiels". Le processus de conteneur principal peut continuer à s'exécuter, même si des processus enfants critiques ont été arrêtés. Ce comportement peut entraîner des défaillances d'application subtiles qui ne sont pas immédiatement visibles pour Kubernetes ou les opérateurs, mais qui sont toujours visibles dans le journal système du nœud (journalctl).

  • cgroup v2 offre un comportement plus prévisible du robot tueur OOM.

    Pour garantir l'intégrité des charges de travail dans un environnement cgroup v2, le robot tueur OOM empêche les arrêts partiels et garantit l'un des deux résultats suivants : soit toutes les tâches appartenant à ce cgroup et à ses descendants sont arrêtées (ce qui rend la défaillance visible pour Kubernetes), soit, lorsque la charge de travail n'a pas de tâches utilisant trop de mémoire, elle est laissée intacte et continue de s'exécuter sans arrêt inattendu du processus interne.

    Pour les scénarios dans lesquels vous souhaitez que le comportement cgroup v1 d'arrêt d'un seul processus soit appliqué, le kubelet fournit l'indicateur singleProcessOOMKill pour cgroup v2. Cet indicateur vous offre un contrôle plus précis, ce qui vous permet d'arrêter des processus individuels lors d'un événement OOM, plutôt que l'ensemble du cgroup.

Arrêt OOM au niveau du système

Un arrêt OOM au niveau du système est un événement plus grave qui se produit lorsque l'ensemble du nœud, et pas seulement un seul conteneur, manque de mémoire disponible. Cet événement peut se produire si l'utilisation combinée de la mémoire de tous les processus (y compris tous les pods et les démons système) dépasse la capacité du nœud.

Lorsque ce nœud manque de mémoire, le robot tueur OOM global évalue tous les processus du nœud et arrête un processus pour récupérer de la mémoire pour l'ensemble du système. Le processus sélectionné est généralement un processus de courte durée qui utilise une grande quantité de mémoire.

Pour éviter les situations OOM graves, Kubernetes utilise l'éviction par pression des nœuds pour gérer les ressources des nœuds. Ce processus consiste à évincer les pods moins critiques d'un nœud lorsque les ressources, telles que la mémoire ou l'espace disque, deviennent critiques. Un arrêt OOM au niveau du système indique que ce processus d'éviction n'a pas pu libérer suffisamment de mémoire assez rapidement pour éviter le problème.

Si le robot tueur OOM arrête le processus d'un conteneur, l'effet est généralement identique à un arrêt déclenché par un cgroup : le conteneur est marqué comme OOMKilled et redémarré en fonction de sa règle. Toutefois, si un processus système critique est arrêté (ce qui est rare), le nœud lui-même peut devenir instable.

Examiner les événements OOM

Les sections suivantes vous aident à détecter et à confirmer un événement OOM, en commençant par les outils Kubernetes les plus simples et en passant à une analyse plus détaillée des journaux.

Vérifier l'état du pod pour les événements OOM visibles

La première étape pour confirmer un événement OOM consiste à vérifier si Kubernetes a observé l'événement OOM. Kubernetes observe l'événement lorsque le processus principal du conteneur est arrêté, ce qui est le comportement standard dans les environnements cgroup v2.

  • Inspectez l'état du pod :

    kubectl describe pod POD_NAME
    

    Remplacez POD_NAME par le nom du pod que vous souhaitez examiner.

    Si un événement OOM visible s'est produit, la sortie est semblable à la suivante :

    ...
      Last State:     Terminated
        Reason:       OOMKilled
        Exit Code:    137
        Started:      Tue, 13 May 2025 19:05:28 +0000
        Finished:     Tue, 13 May 2025 19:05:30 +0000
    ...
    

Si OOMKilled s'affiche dans le champ Reason, vous avez confirmé l'événement. Un Exit Code de 137 indique également un arrêt OOM. Si le champ Reason a une valeur différente ou si le pod est toujours en cours d'exécution malgré des erreurs d'application, passez à la section suivante pour une investigation plus approfondie.

Rechercher des événements OOM invisibles dans les journaux

Un arrêt OOM est "invisible" pour Kubernetes si un processus enfant est arrêté, mais que le processus de conteneur principal continue de s'exécuter (scénario courant dans les environnements cgroup v1). Vous devez rechercher des preuves de ces événements dans les journaux du nœud.

Pour trouver les arrêts OOM invisibles, utilisez l'explorateur de journaux :

  1. Dans la Cloud de Confiance console, accédez à l'explorateur de journaux.

    Accéder à l'explorateur de journaux

  2. Dans le volet de requête, saisissez l'une des requêtes suivantes :

    • Si vous avez déjà un pod qui, selon vous, a rencontré un événement OOM, interrogez ce pod spécifique :

      resource.type="k8s_node"
      jsonPayload.MESSAGE:(("POD_NAME" AND "ContainerDied") OR "TaskOOM event")
      resource.labels.cluster_name="CLUSTER_NAME"
      

      Remplacez les éléments suivants :

      • POD_NAME: nom du pod que vous souhaitez interroger.
      • CLUSTER_NAME: nom du cluster auquel appartient le pod.
    • Pour découvrir quels pods ou nœuds ont rencontré un événement OOM, interrogez toutes les charges de travail GKE :

      resource.type="k8s_node"
      jsonPayload.MESSAGE:("ContainerDied" OR "TaskOOM event")
      resource.labels.cluster_name="CLUSTER_NAME"
      
  3. Cliquez sur Exécuter la requête.

  4. Dans le résultat, recherchez les événements OOM en recherchant les entrées de journal contenant la chaîne TaskOOM.

  5. Facultatif : si vous avez recherché des événements OOM pour toutes les charges de travail GKE et que vous souhaitez identifier le pod spécifique qui a rencontré les événements OOM, procédez comme suit :

    1. Pour chaque événement, notez l'ID de conteneur qui y est associé.
    2. Identifiez les arrêts de conteneur en recherchant les entrées de journal contenant la chaîne ContainerDied et qui se sont produites peu de temps après les événements OOM. Faites correspondre l'ID de conteneur de l'événement OOM à la ligne ContainerDied correspondante.

    3. Une fois que vous avez fait correspondre les container IDs, la ligne ContainerDied inclut généralement le nom du pod associé au conteneur défaillant. Ce pod a été affecté par l'événement OOM.

Utiliser journalctl pour obtenir des informations en temps réel

Si vous devez effectuer une analyse en temps réel de votre système, utilisez les commandes journalctl.

  1. Connectez-vous au nœud à l'aide de SSH :

    gcloud compute ssh NODE_NAME --location ZONE
    

    Remplacez les éléments suivants :

    • NODE_NAME: nom du nœud que vous souhaitez examiner.
    • ZONE: zone Compute Engine à laquelle appartient votre nœud.
  2. Dans le shell, explorez les messages du noyau à partir du journal système du nœud :

    journalctl -k
    
  3. Analysez le résultat pour distinguer le type d'événement :

    • Arrêt au niveau du conteneur : l'entrée de journal contient des termes tels que memory cgroup, mem_cgroup ou memcg, qui indiquent qu'une limite de cgroup a été appliquée.
    • Arrêt au niveau du système : l'entrée de journal est un message général tel que Out of memory: Killed process... sans mentionner de cgroup.

Résoudre les événements OOM

Pour résoudre un événement OOM, essayez les solutions suivantes :

  • Augmenter les limites de mémoire : il s'agit de la solution la plus directe. Modifiez le fichier manifeste du pod pour fournir une valeur resources.limits.memory plus élevée qui s'adapte à l'utilisation maximale de l'application. Pour en savoir plus sur la définition des limites, consultez la section Gestion des ressources pour les pods et les conteneurs dans la documentation Kubernetes.
  • Ajouter ou ajuster les demandes de mémoire : dans le fichier manifeste du pod, vérifiez que le champ resources.requests.memory est défini sur une valeur réaliste pour une utilisation typique. Ce paramètre permet à Kubernetes de planifier le pod sur un nœud disposant de suffisamment de mémoire.
  • Effectuer un scaling horizontal de la charge de travail : pour répartir la charge de trafic et réduire la pression sur la mémoire d'un seul pod, augmentez le nombre d'instances répliquées. Pour que Kubernetes effectue un scaling proactif de la charge de travail, envisagez d'activer l'autoscaling horizontal des pods.
  • Effectuer un scaling vertical des nœuds : si de nombreux pods d'un nœud sont proches de leurs limites, le nœud lui-même est peut-être trop petit. Pour augmenter la taille des nœuds, migrez vos charges de travail vers un pool de nœuds disposant de plus de mémoire. Pour que Kubernetes effectue un scaling proactif des nœuds, envisagez d'activer l'autoscaling vertical des pods.
  • Optimiser votre application : examinez votre application pour identifier et résoudre les fuites de mémoire et optimiser le code qui consomme de grandes quantités de mémoire lors des pics de trafic.
  • Supprimer les charges de travail problématiques : en dernier recours pour les charges de travail non critiques, supprimez le pod pour réduire immédiatement la pression sur le cluster.

Étape suivante