Solucionar problemas del programador de Kubernetes

En esta página se muestra cómo resolver problemas con el programador de Kubernetes (kube-scheduler) de Google Distributed Cloud.

Kubernetes siempre programa los pods en el mismo conjunto de nodos

Este error puede observarse de varias formas:

  • Utilización desequilibrada de los clústeres. Puedes inspeccionar la utilización del clúster de cada nodo con el comando kubectl top nodes. En el siguiente ejemplo exagerado se muestra una utilización pronunciada en determinados nodos:

    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%
    
  • Demasiadas solicitudes. Si programas muchos pods a la vez en el mismo nodo y esos pods hacen solicitudes HTTP, es posible que el nodo esté limitado por la frecuencia. El error habitual que devuelve el servidor en este caso es 429 Too Many Requests.

  • Servicio no disponible. Por ejemplo, un servidor web alojado en un nodo con una carga elevada podría responder a todas las solicitudes con errores 503 Service Unavailable hasta que la carga sea menor.

Para comprobar si tienes pods que siempre se programan en los mismos nodos, sigue estos pasos:

  1. Ejecuta el siguiente comando kubectl para ver el estado de los pods:

    kubectl get pods -o wide -n default
    

    Para ver la distribución de los pods en los nodos, consulta la columna NODE en la salida. En el siguiente ejemplo de salida, todos los pods están programados en el mismo nodo:

    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
    

Los pods tienen varias funciones que te permiten ajustar su comportamiento de programación. Entre estas funciones se incluyen las restricciones de distribución de topología y las reglas de antiafinidad. Puedes usar una de estas funciones o una combinación de ellas. Los requisitos que definas se combinarán con el operador AND en kube-scheduler.

Los registros del programador no se capturan en el nivel de detalle de registro predeterminado. Si necesitas los registros del programador para solucionar problemas, sigue estos pasos para capturarlos:

  1. Aumenta el nivel de detalle de los registros:

    1. Edita el kube-scheduler Deployment:

      kubectl --kubeconfig ADMIN_CLUSTER_KUBECONFIG edit deployment kube-scheduler \
        -n USER_CLUSTER_NAMESPACE
      
    2. Añade la marca --v=5 en la sección spec.containers.command:

      containers:
      - command:
      - kube-scheduler
      - --profiling=false
      - --kubeconfig=/etc/kubernetes/scheduler.conf
      - --leader-elect=true
      - --v=5
      
  2. Cuando hayas terminado de solucionar el problema, vuelve a definir el nivel de detalle predeterminado:

    1. Edita el kube-scheduler Deployment:

      kubectl --kubeconfig ADMIN_CLUSTER_KUBECONFIG edit deployment kube-scheduler \
        -n USER_CLUSTER_NAMESPACE
      
    2. Vuelve a definir el nivel de detalle con el valor predeterminado:

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

Restricciones de dispersión de la topología

Las restricciones de distribución de la topología se pueden usar para distribuir los pods de forma uniforme entre los nodos según su zones, regions, node u otra topología definida de forma personalizada.

El siguiente manifiesto de ejemplo muestra una implementación que distribuye las réplicas de forma uniforme entre todos los nodos programables mediante restricciones de distribución de topología:

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

Cuando se usan restricciones de distribución de topología, se aplican las siguientes consideraciones:

  • El labels.app: myapp de un pod coincide con el labelSelector de la restricción.
  • El elemento topologyKey especifica kubernetes.io/hostname. Esta etiqueta se asigna automáticamente a todos los nodos y se rellena con el nombre de host del nodo.
  • matchLabelKeys impide que las implementaciones de nuevas versiones tengan en cuenta los pods de versiones antiguas al calcular dónde programar un pod. La etiqueta pod-template-hash se rellena automáticamente con un Deployment.

Antiafinidad de pods

Antiafinidad de pods: te permite definir restricciones sobre qué pods se pueden colocar en el mismo nodo.

El siguiente manifiesto de ejemplo muestra un Deployment que usa la antiafinidad para limitar las réplicas a un Pod por nodo:

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

En este ejemplo de Deployment se especifican 30 réplicas, pero solo se amplía hasta el número de nodos disponibles en el clúster.

Cuando se usa la antiafinidad de pods, se aplican las siguientes consideraciones:

  • El labels.app: myapp de un pod coincide con el labelSelector de la restricción.
  • El elemento topologyKey especifica kubernetes.io/hostname. Esta etiqueta se asigna automáticamente a todos los nodos y se rellena con el nombre de host del nodo. Puedes usar otras etiquetas si tu clúster las admite, como region o zone.

Pre-pull de imágenes de contenedor

Si no hay ninguna otra restricción, de forma predeterminada, kube-scheduler prefiere programar pods en nodos que ya tengan descargada la imagen de contenedor. Este comportamiento puede ser interesante en clústeres más pequeños sin otras configuraciones de programación, donde sería posible descargar las imágenes en todos los nodos. Sin embargo, recurrir a este concepto debe considerarse como último recurso. Una solución mejor es usar nodeSelector, restricciones de dispersión de la topología o afinidad/antiafinidad. Para obtener más información, consulta Asignar pods a nodos.

Si quieres asegurarte de que las imágenes de contenedor se extraigan previamente en todos los nodos, puedes usar un DaemonSet como en el siguiente ejemplo:

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

Cuando el pod esté Running en todos los nodos, vuelve a implementar los pods para comprobar si los contenedores se han distribuido de forma uniforme entre los nodos.

Siguientes pasos

Si necesitas más ayuda, ponte en contacto con el servicio de atención al cliente de Cloud.

También puedes consultar la sección Obtener asistencia para obtener más información sobre los recursos de asistencia, incluidos los siguientes: