Résoudre les problèmes liés au planificateur Kubernetes

Cette page explique comment résoudre les problèmes liés au planificateur Kubernetes (kube-scheduler) pour Google Distributed Cloud.

Si vous avez besoin d'aide supplémentaire, contactez l'assistance Cloud Customer Care.

Kubernetes planifie toujours les pods sur le même ensemble de nœuds

Cette erreur peut se manifester de différentes manières :

  • Utilisation inégale des clusters. Vous pouvez inspecter l'utilisation du cluster pour chaque nœud à l'aide de la commande kubectl top nodes. L'exemple de résultat exagéré suivant montre l'utilisation prononcée sur certains nœuds :

    NAME                   CPU(cores)   CPU%      MEMORY(bytes)   MEMORY%
    XXX.gke.internal       222m         101%       3237Mi          61%
    YYY.gke.internal       91m          0%         2217Mi          0%
    ZZZ.gke.internal       512m         0%         8214Mi          0%
    
  • Trop de requêtes. Si vous planifiez de nombreux pods en même temps sur le même nœud et que ces pods effectuent des requêtes HTTP, il est possible que le nœud soit limité en débit. L'erreur courante renvoyée par le serveur dans ce scénario est 429 Too Many Requests.

  • Service indisponible. Par exemple, un serveur Web hébergé sur un nœud soumis à une charge élevée peut répondre à toutes les requêtes avec des erreurs 503 Service Unavailable jusqu'à ce qu'il soit soumis à une charge plus faible.

Pour vérifier si des pods sont toujours planifiés sur les mêmes nœuds, procédez comme suit :

  1. Exécutez la commande kubectl suivante pour afficher l'état des pods :

    kubectl get pods -o wide -n default
    

    Pour afficher la répartition des pods entre les nœuds, consultez la colonne NODE dans la sortie. Dans l'exemple de résultat suivant, tous les pods sont programmés sur le même nœud :

    NAME                               READY  STATUS   RESTARTS  AGE  IP             NODE
    nginx-deployment-84c6674589-cxp55  1/1    Running  0         55s  10.20.152.138  10.128.224.44
    nginx-deployment-84c6674589-hzmnn  1/1    Running  0         55s  10.20.155.70   10.128.226.44
    nginx-deployment-84c6674589-vq4l2  1/1    Running  0         55s  10.20.225.7    10.128.226.44
    

Les pods disposent d'un certain nombre de fonctionnalités qui vous permettent d'affiner leur comportement de planification. Ces fonctionnalités incluent des contraintes de répartition de la topologie et des règles d'anti-affinité. Vous pouvez utiliser une ou plusieurs de ces fonctionnalités. Les exigences que vous définissez sont liées par l'opérateur kube-scheduler.

Les journaux du planificateur ne sont pas capturés au niveau de la verbosité de journalisation par défaut. Si vous avez besoin des journaux du planificateur pour résoudre un problème, procédez comme suit pour les capturer :

  1. Augmentez le niveau de verbosité de la journalisation :

    1. Modifiez l'objet Deployment kube-scheduler :

      kubectl --kubeconfig ADMIN_CLUSTER_KUBECONFIG edit deployment kube-scheduler \
        -n USER_CLUSTER_NAMESPACE
      
    2. Ajoutez l'indicateur --v=5 sous la section spec.containers.command :

      containers:
      - command:
      - kube-scheduler
      - --profiling=false
      - --kubeconfig=/etc/kubernetes/scheduler.conf
      - --leader-elect=true
      - --v=5
      
  2. Une fois le dépannage terminé, rétablissez le niveau de verbosité par défaut :

    1. Modifiez l'objet Deployment kube-scheduler :

      kubectl --kubeconfig ADMIN_CLUSTER_KUBECONFIG edit deployment kube-scheduler \
        -n USER_CLUSTER_NAMESPACE
      
    2. Rétablissez le niveau d'affichage sur la valeur par défaut :

      containers:
      - command:
      - kube-scheduler
      - --profiling=false
      - --kubeconfig=/etc/kubernetes/scheduler.conf
      - --leader-elect=true
      

Contraintes de répartition de la topologie

Les contraintes de répartition de la topologie permettent de répartir uniformément les pods entre les nœuds en fonction de leur zones, regions, node ou d'une autre topologie personnalisée.

L'exemple de fichier manifeste suivant montre un déploiement qui répartit les instances répliquées de manière uniforme entre tous les nœuds programmables à l'aide de contraintes de répartition de la topologie :

apiVersion: apps/v1
kind: Deployment
metadata:
  name: topology-spread-deployment
  labels:
    app: myapp
spec:
  replicas: 30
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      topologySpreadConstraints:
      - maxSkew: 1 # Default. Spreads evenly. Maximum difference in scheduled Pods per Node.
        topologyKey: kubernetes.io/hostname
        whenUnsatisfiable: DoNotSchedule # Default. Alternatively can be ScheduleAnyway
        labelSelector:
          matchLabels:
            app: myapp
        matchLabelKeys: # beta in 1.27
        - pod-template-hash
      containers:
      # pause is a lightweight container that simply sleeps
      - name: pause
        image: registry.k8s.io/pause:3.2

Les considérations suivantes s'appliquent lors de l'utilisation de contraintes de répartition de la topologie :

  • L'élément labels.app: myapp d'un pod est mis en correspondance par l'élément labelSelector de la contrainte.
  • topologyKey spécifie kubernetes.io/hostname. Ce libellé est automatiquement associé à tous les nœuds et est renseigné avec le nom d'hôte du nœud.
  • matchLabelKeys empêche les nouveaux déploiements de prendre en compte les pods d'anciennes révisions lors du calcul de l'emplacement de planification d'un pod. Le libellé pod-template-hash est automatiquement renseigné par un déploiement.

Anti-affinité de pod

L'anti-affinité de pod vous permet de définir des contraintes pour lesquelles les pods peuvent être colocalisés sur le même nœud.

L'exemple de fichier manifeste suivant montre un objet Deployment qui utilise l'anti-affinité pour limiter les instances répliquées à un pod par nœud :

apiVersion: apps/v1
kind: Deployment
metadata:
  name: pod-affinity-deployment
  labels:
    app: myapp
spec:
  replicas: 30
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      name: with-pod-affinity
      labels:
        app: myapp
    spec:
      affinity:
        podAntiAffinity:
          # requiredDuringSchedulingIgnoredDuringExecution
          # prevents Pod from being scheduled on a Node if it
          # does not meet criteria.
          # Alternatively can use 'preferred' with a weight
          # rather than 'required'.
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - myapp
            # Your nodes might be configured with other keys
            # to use as `topologyKey`. `kubernetes.io/region`
            # and `kubernetes.io/zone` are common.
            topologyKey: kubernetes.io/hostname
      containers:
      # pause is a lightweight container that simply sleeps
      - name: pause
        image: registry.k8s.io/pause:3.2

Cet exemple de déploiement spécifie les instances répliquées 30, mais ne s'étend que jusqu'au nombre de nœuds disponibles dans votre cluster.

Les considérations suivantes s'appliquent lorsque vous utilisez l'anti-affinité de pods :

  • Le labels.app: myapp d'un pod est mis en correspondance avec le labelSelector de la contrainte.
  • topologyKey spécifie kubernetes.io/hostname. Ce libellé est automatiquement associé à tous les nœuds et renseigné avec le nom d'hôte du nœud. Vous pouvez choisir d'utiliser d'autres libellés, tels que region ou zone, si votre cluster les accepte.

Prétraiter des images de conteneurs

En l'absence d'autres contraintes, kube-scheduler préfère par défaut planifier les pods sur les nœuds sur lesquels l'image de conteneur est déjà téléchargée. Ce comportement peut être intéressant dans les clusters plus petits sans autres configurations de planification, où il est possible de télécharger les images sur chaque nœud. Cependant, cette approche ne doit être envisagée qu'en dernier recours. Une meilleure solution consiste à utiliser nodeSelector, des contraintes de répartition de la topologie ou des contraintes d'affinité/anti-affinité. Pour plus d'informations, consultez l'article Affecter des pods à des nœuds.

Si vous souhaitez vous assurer que les images de conteneur sont prétéléchargées sur tous les nœuds, vous pouvez utiliser un DaemonSet comme dans l'exemple suivant :

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: prepulled-images
spec:
  selector:
    matchLabels:
      name: prepulled-images
  template:
    metadata:
      labels:
        name: prepulled-images
    spec:
      initContainers:
        - name: prepulled-image
          image: IMAGE
          # Use a command the terminates immediately
          command: ["sh", "-c", "'true'"]
      containers:
      # pause is a lightweight container that simply sleeps
      - name: pause
        image: registry.k8s.io/pause:3.2

Une fois que le pod est à l'état Running sur tous les nœuds, redéployez vos pods pour voir si les conteneurs sont désormais répartis équitablement entre les nœuds.

Étape suivante

Si vous avez besoin d'une aide supplémentaire, contactez Cloud Customer Care.