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 Virtual for Bare Metal.

Si necesitas asistencia adicional, comunícate con Atención al cliente de Cloud.

Kubernetes siempre programa los Pods en el mismo conjunto de nodos

Este error puede observarse de diferentes maneras:

  • Uso de clúster desequilibrado. Puedes inspeccionar el uso del clúster para cada nodo con el comando kubectl top nodes. El siguiente resultado de ejemplo exagerado muestra un uso pronunciado en ciertos 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 realizan solicitudes HTTP, es posible que el nodo tenga un límite de frecuencia. El error común que muestra el servidor en esta situación es 429 Too Many Requests.

  • Servicio no disponible. Un servidor web, por ejemplo, alojado en un nodo con carga alta, podría responder a todas las solicitudes con errores 503 Service Unavailable hasta que tenga una carga más ligera.

Para verificar si tienes Pods que siempre están programados en los mismos nodos, sigue estos pasos:

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

    kubectl get pods -o wide -n default
    

    Para ver la distribución de Pods entre los nodos, verifica la columna NODE en el resultado. En el siguiente resultado de ejemplo, todos los Pods se programan 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 una serie de características que te permiten ajustar su comportamiento de programación. Estas características incluyen restricciones de dispersión de topología y reglas de antiafinidad. Puedes usar una de estas funciones o combinarlas. kube-scheduler une los requisitos que defines mediante el operador Y.

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

  1. Aumenta el nivel de verbosidad de registro:

    1. Edita el Deployment kube-scheduler:

      kubectl --kubeconfig ADMIN_CLUSTER_KUBECONFIG edit deployment kube-scheduler \
        -n USER_CLUSTER_NAMESPACE
      
    2. Agrega 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 termines de solucionar el problema, restablece el nivel de verbosidad al predeterminado:

    1. Edita el Deployment kube-scheduler:

      kubectl --kubeconfig ADMIN_CLUSTER_KUBECONFIG edit deployment kube-scheduler \
        -n USER_CLUSTER_NAMESPACE
      
    2. Vuelve a establecer el nivel de verbosidad en el valor predeterminado:

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

Restricciones de la dispersión de la topología

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

En el siguiente manifiesto de ejemplo, se muestra un Deployment que distribuye las réplicas de manera uniforme entre todos los nodos programables mediante las 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

Las siguientes consideraciones se aplican cuando se usan restricciones de dispersión de topología:

  • El labels.app: myapp de un Pod coincide con el labelSelector de la restricción.
  • topologyKey especifica kubernetes.io/hostname. Esta etiqueta se adjunta de forma automática a todos los nodos y se propaga con el nombre de host del nodo.
  • El matchLabelKeys evita que los lanzamientos de implementaciones nuevas consideren los Pods de revisiones anteriores cuando calculan dónde programar un Pod. Una Deployment propaga automáticamente la etiqueta pod-template-hash.

Antiafinidad de pods

La antiafinidad de Pods te permite definir restricciones para las cuales los Pods se pueden ubicar en el mismo nodo.

En el siguiente manifiesto de ejemplo, se muestra una Deployment que usa 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 esta Deployment de ejemplo, se especifican 30 réplicas, pero solo se expande a la cantidad de nodos disponibles en tu clúster.

Las siguientes consideraciones se aplican cuando se usa la antiafinidad de Pods:

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

Extraer imágenes de contenedor previamente

Ante la ausencia de otras restricciones, de forma predeterminada, kube-scheduler prefiere programar Pods en nodos que ya tienen descargada la imagen de contenedor en ellos. Este comportamiento puede ser de interés en clústeres más pequeños sin otras configuraciones de programación en las que sería posible descargar las imágenes en cada nodo. Sin embargo, confiar en este concepto debería considerarse como último recurso. Una mejor solución es usar nodeSelector, restricciones de dispersión de topología o afinidad / antiafinidad. Para obtener más información, consulta la página sobre cómo asignar Pods a nodos.

Si deseas asegurarte de que las imágenes de contenedor se extraigan previamente a todos los nodos, puedes usar un DaemonSet como 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

Una vez que el Pod tenga el estado Running en todos los nodos, vuelve a implementar los Pods para ver si los contenedores ahora están distribuidos de manera uniforme entre los nodos.

¿Qué sigue?

Si necesitas asistencia adicional, comunícate con Atención al cliente de Cloud.