Crear un programador de kube adicional con un conjunto personalizado de reglas de programación

Crear un programador de kube adicional con un conjunto personalizado de reglas de programación

Kube-scheduler es un componente integral de Kubernetes, que es responsable de programar pods entre nodos de acuerdo con políticas específicas. A menudo, durante el funcionamiento de un clúster de Kubernetes, no tenemos que pensar en qué políticas se utilizan para programar pods, ya que el conjunto de políticas del programador de kube predeterminado es adecuado para la mayoría de las tareas cotidianas. Sin embargo, hay situaciones en las que es importante para nosotros ajustar el proceso de asignación de pods, y hay dos formas de realizar esta tarea:

  1. Cree un programador de kube con un conjunto personalizado de reglas
  2. Escriba su propio programador y enséñele a trabajar con solicitudes del servidor API

En este artículo, describiré la implementación del primer punto para resolver el problema de la programación desigual de los hogares en uno de nuestros proyectos.

Una breve introducción a cómo funciona kube-scheduler

Vale la pena señalar especialmente el hecho de que kube-scheduler no es responsable de programar directamente los pods; solo es responsable de determinar el nodo en el que colocar el pod. En otras palabras, el resultado del trabajo de kube-scheduler es el nombre del nodo, que devuelve al servidor API para una solicitud de programación, y ahí es donde termina su trabajo.

Primero, kube-scheduler compila una lista de nodos en los que se puede programar el pod de acuerdo con las políticas de predicados. A continuación, cada nodo de esta lista recibe una determinada cantidad de puntos de acuerdo con las políticas de prioridades. Como resultado, se selecciona el nodo con el máximo número de puntos. Si hay nodos que tienen la misma puntuación máxima, se selecciona uno aleatorio. Puede encontrar una lista y descripción de las políticas de predicados (filtrado) y prioridades (puntuación) en documentación.

Descripción del cuerpo problemático.

A pesar de la gran cantidad de clústeres de Kubernetes diferentes que se mantienen en Nixys, nos encontramos por primera vez con el problema de programar pods recientemente, cuando uno de nuestros proyectos necesitaba ejecutar una gran cantidad de tareas periódicas (~100 entidades CronJob). Para simplificar al máximo la descripción del problema, tomaremos como ejemplo un microservicio, dentro del cual se inicia una tarea cron una vez por minuto, lo que genera cierta carga en la CPU. Para ejecutar la tarea cron, se asignaron tres nodos con características absolutamente idénticas (24 vCPU en cada uno).

Al mismo tiempo, es imposible decir con precisión cuánto tiempo tardará en ejecutarse CronJob, ya que el volumen de datos de entrada cambia constantemente. En promedio, durante el funcionamiento normal de kube-scheduler, cada nodo ejecuta de 3 a 4 instancias de trabajo, lo que crea ~20-30 % de la carga en la CPU de cada nodo:

Crear un programador de kube adicional con un conjunto personalizado de reglas de programación

El problema en sí es que a veces los pods de tareas cron dejaron de programarse en uno de los tres nodos. Es decir, en algún momento, no se planeó ni un solo pod para uno de los nodos, mientras que en los otros dos nodos se ejecutaban entre 6 y 8 copias de la tarea, creando ~40-60% de la carga en la CPU:

Crear un programador de kube adicional con un conjunto personalizado de reglas de programación

El problema se repetía con una frecuencia absolutamente aleatoria y ocasionalmente se correlacionaba con el momento en que se implementaba una nueva versión del código.

Al aumentar el nivel de registro del programador de kube al nivel 10 (-v = 10), comenzamos a registrar cuántos puntos ganó cada nodo durante el proceso de evaluación. Durante la operación de planificación normal, se puede ver la siguiente información en los registros:

resource_allocation.go:78] cronjob-1574828880-mn7m4 -> Node03: BalancedResourceAllocation, capacity 23900 millicores 67167186944 memory bytes, total request 1387 millicores 4161694720 memory bytes, score 9
resource_allocation.go:78] cronjob-1574828880-mn7m4 -> Node02: BalancedResourceAllocation, capacity 23900 millicores 67167186944 memory bytes, total request 1347 millicores 4444810240 memory bytes, score 9
resource_allocation.go:78] cronjob-1574828880-mn7m4 -> Node03: LeastResourceAllocation, capacity 23900 millicores 67167186944 memory bytes, total request 1387 millicores 4161694720 memory bytes, score 9
resource_allocation.go:78] cronjob-1574828880-mn7m4 -> Node01: BalancedResourceAllocation, capacity 23900 millicores 67167186944 memory bytes, total request 1687 millicores 4790840320 memory bytes, score 9
resource_allocation.go:78] cronjob-1574828880-mn7m4 -> Node02: LeastResourceAllocation, capacity 23900 millicores 67167186944 memory bytes, total request 1347 millicores 4444810240 memory bytes, score 9
resource_allocation.go:78] cronjob-1574828880-mn7m4 -> Node01: LeastResourceAllocation, capacity 23900 millicores 67167186944 memory bytes, total request 1687 millicores 4790840320 memory bytes, score 9
generic_scheduler.go:726] cronjob-1574828880-mn7m4_project-stage -> Node01: NodeAffinityPriority, Score: (0)                                                                                       
generic_scheduler.go:726] cronjob-1574828880-mn7m4_project-stage -> Node02: NodeAffinityPriority, Score: (0)                                                                                       
generic_scheduler.go:726] cronjob-1574828880-mn7m4_project-stage -> Node03: NodeAffinityPriority, Score: (0)                                                                                       
interpod_affinity.go:237] cronjob-1574828880-mn7m4 -> Node01: InterPodAffinityPriority, Score: (0)                                                                                                        
generic_scheduler.go:726] cronjob-1574828880-mn7m4_project-stage -> Node01: TaintTolerationPriority, Score: (10)                                                                                   
interpod_affinity.go:237] cronjob-1574828880-mn7m4 -> Node02: InterPodAffinityPriority, Score: (0)                                                                                                        
generic_scheduler.go:726] cronjob-1574828880-mn7m4_project-stage -> Node02: TaintTolerationPriority, Score: (10)                                                                                   
selector_spreading.go:146] cronjob-1574828880-mn7m4 -> Node01: SelectorSpreadPriority, Score: (10)                                                                                                        
interpod_affinity.go:237] cronjob-1574828880-mn7m4 -> Node03: InterPodAffinityPriority, Score: (0)                                                                                                        
generic_scheduler.go:726] cronjob-1574828880-mn7m4_project-stage -> Node03: TaintTolerationPriority, Score: (10)                                                                                   
selector_spreading.go:146] cronjob-1574828880-mn7m4 -> Node02: SelectorSpreadPriority, Score: (10)                                                                                                        
selector_spreading.go:146] cronjob-1574828880-mn7m4 -> Node03: SelectorSpreadPriority, Score: (10)                                                                                                        
generic_scheduler.go:726] cronjob-1574828880-mn7m4_project-stage -> Node01: SelectorSpreadPriority, Score: (10)                                                                                    
generic_scheduler.go:726] cronjob-1574828880-mn7m4_project-stage -> Node02: SelectorSpreadPriority, Score: (10)                                                                                    
generic_scheduler.go:726] cronjob-1574828880-mn7m4_project-stage -> Node03: SelectorSpreadPriority, Score: (10)                                                                                    
generic_scheduler.go:781] Host Node01 => Score 100043                                                                                                                                                                        
generic_scheduler.go:781] Host Node02 => Score 100043                                                                                                                                                                        
generic_scheduler.go:781] Host Node03 => Score 100043

Aquellos. A juzgar por la información obtenida de los registros, cada uno de los nodos obtuvo igual número de puntos finales y se seleccionó uno aleatorio para la planificación. En el momento de la planificación problemática, los registros tenían este aspecto:

resource_allocation.go:78] cronjob-1574211360-bzfkr -> Node02: BalancedResourceAllocation, capacity 23900 millicores 67167186944 memory bytes, total request 1587 millicores 4581125120 memory bytes, score 9
resource_allocation.go:78] cronjob-1574211360-bzfkr -> Node03: BalancedResourceAllocation, capacity 23900 millicores 67167186944 memory bytes, total request 1087 millicores 3532549120 memory bytes, score 9
resource_allocation.go:78] cronjob-1574211360-bzfkr -> Node02: LeastResourceAllocation, capacity 23900 millicores 67167186944 memory bytes, total request 1587 millicores 4581125120 memory bytes, score 9
resource_allocation.go:78] cronjob-1574211360-bzfkr -> Node01: BalancedResourceAllocation, capacity 23900 millicores 67167186944 memory bytes, total request 987 millicores 3322833920 memory bytes, score 9
resource_allocation.go:78] cronjob-1574211360-bzfkr -> Node01: LeastResourceAllocation, capacity 23900 millicores 67167186944 memory bytes, total request 987 millicores 3322833920 memory bytes, score 9 
resource_allocation.go:78] cronjob-1574211360-bzfkr -> Node03: LeastResourceAllocation, capacity 23900 millicores 67167186944 memory bytes, total request 1087 millicores 3532549120 memory bytes, score 9
interpod_affinity.go:237] cronjob-1574211360-bzfkr -> Node03: InterPodAffinityPriority, Score: (0)                                                                                                        
interpod_affinity.go:237] cronjob-1574211360-bzfkr -> Node02: InterPodAffinityPriority, Score: (0)                                                                                                        
interpod_affinity.go:237] cronjob-1574211360-bzfkr -> Node01: InterPodAffinityPriority, Score: (0)                                                                                                        
generic_scheduler.go:726] cronjob-1574211360-bzfkr_project-stage -> Node03: TaintTolerationPriority, Score: (10)                                                                                   
selector_spreading.go:146] cronjob-1574211360-bzfkr -> Node03: SelectorSpreadPriority, Score: (10)                                                                                                        
selector_spreading.go:146] cronjob-1574211360-bzfkr -> Node02: SelectorSpreadPriority, Score: (10)                                                                                                        
generic_scheduler.go:726] cronjob-1574211360-bzfkr_project-stage -> Node02: TaintTolerationPriority, Score: (10)                                                                                   
selector_spreading.go:146] cronjob-1574211360-bzfkr -> Node01: SelectorSpreadPriority, Score: (10)                                                                                                        
generic_scheduler.go:726] cronjob-1574211360-bzfkr_project-stage -> Node03: NodeAffinityPriority, Score: (0)                                                                                       
generic_scheduler.go:726] cronjob-1574211360-bzfkr_project-stage -> Node03: SelectorSpreadPriority, Score: (10)                                                                                    
generic_scheduler.go:726] cronjob-1574211360-bzfkr_project-stage -> Node02: SelectorSpreadPriority, Score: (10)                                                                                    
generic_scheduler.go:726] cronjob-1574211360-bzfkr_project-stage -> Node01: TaintTolerationPriority, Score: (10)                                                                                   
generic_scheduler.go:726] cronjob-1574211360-bzfkr_project-stage -> Node02: NodeAffinityPriority, Score: (0)                                                                                       
generic_scheduler.go:726] cronjob-1574211360-bzfkr_project-stage -> Node01: NodeAffinityPriority, Score: (0)                                                                                       
generic_scheduler.go:726] cronjob-1574211360-bzfkr_project-stage -> Node01: SelectorSpreadPriority, Score: (10)                                                                                    
generic_scheduler.go:781] Host Node03 => Score 100041                                                                                                                                                                        
generic_scheduler.go:781] Host Node02 => Score 100041                                                                                                                                                                        
generic_scheduler.go:781] Host Node01 => Score 100038

De lo cual se puede observar que uno de los nodos obtuvo menos puntos finales que los demás, por lo que la planificación se realizó solo para los dos nodos que obtuvieron el puntaje máximo. Por lo tanto, estábamos definitivamente convencidos de que el problema reside precisamente en la programación de los pods.

El algoritmo adicional para resolver el problema era obvio para nosotros: analizar los registros, comprender con qué prioridad el nodo no obtuvo puntos y, si es necesario, ajustar las políticas del programador de kube predeterminado. Sin embargo, aquí nos enfrentamos a dos dificultades importantes:

  1. En el nivel máximo de registro (10), se reflejan los puntos obtenidos sólo para algunas prioridades. En el extracto de registros anterior, puede ver que para todas las prioridades reflejadas en los registros, los nodos obtienen la misma cantidad de puntos en la programación normal y de problemas, pero el resultado final en el caso de la planificación de problemas es diferente. Por lo tanto, podemos concluir que para algunas prioridades, la puntuación se produce "entre bastidores" y no tenemos forma de entender para qué prioridad el nodo no obtuvo puntos. Describimos este problema en detalle en Repositorio de Kubernetes en Github. Al momento de escribir este artículo, se recibió una respuesta de los desarrolladores de que se agregará soporte de registro en las actualizaciones de Kubernetes v1.15,1.16, 1.17 y XNUMX.
  2. No existe una manera fácil de comprender con qué conjunto específico de políticas está trabajando actualmente kube-scheduler. Si en documentación esta lista aparece, pero no contiene información sobre qué ponderaciones específicas se asignan a cada una de las políticas prioritarias. Puede ver los pesos o editar las políticas del kube-scheduler predeterminado solo en códigos fuente.

Vale la pena señalar que una vez pudimos registrar que un nodo no recibió puntos de acuerdo con la política ImageLocalityPriority, que otorga puntos a un nodo si ya tiene la imagen necesaria para ejecutar la aplicación. Es decir, en el momento en que se lanzó una nueva versión de la aplicación, la tarea cron logró ejecutarse en dos nodos, descargándoles una nueva imagen del registro de Docker y, por lo tanto, dos nodos recibieron una puntuación final más alta en relación con el tercero. .

Como escribí anteriormente, en los registros no vemos información sobre la evaluación de la política ImageLocalityPriority, por lo que para verificar nuestra suposición, volcamos la imagen con la nueva versión de la aplicación en el tercer nodo, después de lo cual la programación funcionó correctamente. . Precisamente debido a la política ImageLocalityPriority, el problema de programación se observó con bastante poca frecuencia; más a menudo estaba asociado con algo más. Debido al hecho de que no pudimos depurar completamente cada una de las políticas en la lista de prioridades del programador de kube predeterminado, necesitábamos una gestión flexible de las políticas de programación de pods.

Formulación del problema

Queríamos que la solución al problema fuera lo más específica posible, es decir, las entidades principales de Kubernetes (aquí nos referimos al programador de kube predeterminado) deberían permanecer sin cambios. No queríamos resolver un problema en un lugar y crearlo en otro. Por lo tanto, llegamos a dos opciones para resolver el problema, que se anunciaron en la introducción del artículo: crear un programador adicional o escribir el suyo propio. El principal requisito para programar tareas cron es distribuir la carga de manera uniforme entre tres nodos. Este requisito puede satisfacerse mediante las políticas existentes del programador de kube, por lo que para resolver nuestro problema no tiene sentido escribir su propio programador.

Las instrucciones para crear e implementar un programador de kube adicional se describen en documentación. Sin embargo, nos pareció que la entidad de implementación no era suficiente para garantizar la tolerancia a fallas en el funcionamiento de un servicio tan crítico como kube-scheduler, por lo que decidimos implementar un nuevo kube-scheduler como Static Pod, que sería monitoreado directamente. por Kubelet. Por tanto, tenemos los siguientes requisitos para el nuevo kube-scheduler:

  1. El servicio debe implementarse como un pod estático en todos los maestros del clúster.
  2. Se debe proporcionar tolerancia a fallas en caso de que el pod activo con kube-scheduler no esté disponible
  3. La principal prioridad a la hora de planificar debe ser la cantidad de recursos disponibles en el nodo (LeastRequestedPriority)

Soluciones de implementación

Vale la pena señalar de inmediato que realizaremos todo el trabajo en Kubernetes v1.14.7, porque Esta es la versión que se utilizó en el proyecto. Comencemos escribiendo un manifiesto para nuestro nuevo kube-scheduler. Tomemos el manifiesto predeterminado (/etc/kubernetes/manifests/kube-scheduler.yaml) como base y lo llevaremos al siguiente formulario:

kind: Pod
metadata:
  labels:
    component: scheduler
    tier: control-plane
  name: kube-scheduler-cron
  namespace: kube-system
spec:
      containers:
      - command:
        - /usr/local/bin/kube-scheduler
        - --address=0.0.0.0
        - --port=10151
        - --secure-port=10159
        - --config=/etc/kubernetes/scheduler-custom.conf
        - --authentication-kubeconfig=/etc/kubernetes/scheduler.conf
        - --authorization-kubeconfig=/etc/kubernetes/scheduler.conf
        - --v=2
        image: gcr.io/google-containers/kube-scheduler:v1.14.7
        imagePullPolicy: IfNotPresent
        livenessProbe:
          failureThreshold: 8
          httpGet:
            host: 127.0.0.1
            path: /healthz
            port: 10151
            scheme: HTTP
          initialDelaySeconds: 15
          timeoutSeconds: 15
        name: kube-scheduler-cron-container
        resources:
          requests:
            cpu: '0.1'
        volumeMounts:
        - mountPath: /etc/kubernetes/scheduler.conf
          name: kube-config
          readOnly: true
        - mountPath: /etc/localtime
          name: localtime
          readOnly: true
        - mountPath: /etc/kubernetes/scheduler-custom.conf
          name: scheduler-config
          readOnly: true
        - mountPath: /etc/kubernetes/scheduler-custom-policy-config.json
          name: policy-config
          readOnly: true
      hostNetwork: true
      priorityClassName: system-cluster-critical
      volumes:
      - hostPath:
          path: /etc/kubernetes/scheduler.conf
          type: FileOrCreate
        name: kube-config
      - hostPath:
          path: /etc/localtime
        name: localtime
      - hostPath:
          path: /etc/kubernetes/scheduler-custom.conf
          type: FileOrCreate
        name: scheduler-config
      - hostPath:
          path: /etc/kubernetes/scheduler-custom-policy-config.json
          type: FileOrCreate
        name: policy-config

Brevemente sobre los principales cambios:

  1. Se cambió el nombre del pod y del contenedor a kube-scheduler-cron
  2. Se especificó el uso de los puertos 10151 y 10159 según se definió la opción. hostNetwork: true y no podemos usar los mismos puertos que el kube-scheduler predeterminado (10251 y 10259)
  3. Usando el parámetro --config, especificamos el archivo de configuración con el que se debe iniciar el servicio
  4. Montaje configurado del archivo de configuración (scheduler-custom.conf) y del archivo de política de programación (scheduler-custom-policy-config.json) desde el host

No olvide que nuestro kube-scheduler necesitará derechos similares al predeterminado. Edite su función de clúster:

kubectl edit clusterrole system:kube-scheduler

...
   resourceNames:
    - kube-scheduler
    - kube-scheduler-cron
...

Ahora hablemos de lo que debe contener el archivo de configuración y el archivo de política de programación:

  • Archivo de configuración (scheduler-custom.conf)
    Para obtener la configuración predeterminada de kube-scheduler, debe usar el parámetro --write-config-to de documentación. Colocaremos la configuración resultante en el archivo /etc/kubernetes/scheduler-custom.conf y la reduciremos a la siguiente forma:

apiVersion: kubescheduler.config.k8s.io/v1alpha1
kind: KubeSchedulerConfiguration
schedulerName: kube-scheduler-cron
bindTimeoutSeconds: 600
clientConnection:
  acceptContentTypes: ""
  burst: 100
  contentType: application/vnd.kubernetes.protobuf
  kubeconfig: /etc/kubernetes/scheduler.conf
  qps: 50
disablePreemption: false
enableContentionProfiling: false
enableProfiling: false
failureDomains: kubernetes.io/hostname,failure-domain.beta.kubernetes.io/zone,failure-domain.beta.kubernetes.io/region
hardPodAffinitySymmetricWeight: 1
healthzBindAddress: 0.0.0.0:10151
leaderElection:
  leaderElect: true
  leaseDuration: 15s
  lockObjectName: kube-scheduler-cron
  lockObjectNamespace: kube-system
  renewDeadline: 10s
  resourceLock: endpoints
  retryPeriod: 2s
metricsBindAddress: 0.0.0.0:10151
percentageOfNodesToScore: 0
algorithmSource:
   policy:
     file:
       path: "/etc/kubernetes/scheduler-custom-policy-config.json"

Brevemente sobre los principales cambios:

  1. Configuramos SchedulerName como el nombre de nuestro servicio kube-scheduler-cron.
  2. en parámetro lockObjectName También debe configurar el nombre de nuestro servicio y asegurarse de que el parámetro leaderElect establecido en verdadero (si tiene un nodo maestro, puede configurarlo en falso).
  3. Especificó la ruta al archivo con una descripción de las políticas de programación en el parámetro algorithmSource.

Vale la pena echar un vistazo más de cerca al segundo punto, donde editamos los parámetros de la clave. leaderElection. Para garantizar la tolerancia a fallos, hemos habilitado (leaderElect) el proceso de seleccionar un líder (maestro) entre los pods de nuestro kube-scheduler usando un único punto final para ellos (resourceLock) llamado kube-scheduler-cron (lockObjectName) en el espacio de nombres del sistema kube (lockObjectNamespace). Cómo Kubernetes garantiza la alta disponibilidad de los componentes principales (incluido kube-scheduler) se puede encontrar en статье.

  • Archivo de política de programación (scheduler-custom-policy-config.json)
    Como escribí anteriormente, podemos descubrir con qué políticas específicas funciona el programador de kube predeterminado solo analizando su código. Es decir, no podemos obtener un archivo con políticas de programación para el kube-scheduler predeterminado de la misma forma que un archivo de configuración. Describamos las políticas de programación que nos interesan en el archivo /etc/kubernetes/scheduler-custom-policy-config.json de la siguiente manera:

{
  "kind": "Policy",
  "apiVersion": "v1",
  "predicates": [
    {
      "name": "GeneralPredicates"
    }
  ],
  "priorities": [
    {
      "name": "ServiceSpreadingPriority",
      "weight": 1
    },
    {
      "name": "EqualPriority",
      "weight": 1
    },
    {
      "name": "LeastRequestedPriority",
      "weight": 1
    },
    {
      "name": "NodePreferAvoidPodsPriority",
      "weight": 10000
    },
    {
      "name": "NodeAffinityPriority",
      "weight": 1
    }
  ],
  "hardPodAffinitySymmetricWeight" : 10,
  "alwaysCheckAllPredicates" : false
}

Por lo tanto, kube-scheduler primero compila una lista de nodos en los que se puede programar un pod de acuerdo con la política GeneralPredicates (que incluye el conjunto de políticas PodFitsResources, PodFitsHostPorts, HostName y MatchNodeSelector). Y luego cada nodo se evalúa de acuerdo con el conjunto de políticas en la matriz de prioridades. Para cumplir con las condiciones de nuestra tarea, consideramos que tal conjunto de políticas sería la solución óptima. Permítanme recordarles que un conjunto de políticas con sus descripciones detalladas está disponible en documentación. Para realizar su tarea, simplemente puede cambiar el conjunto de políticas utilizadas y asignarles las ponderaciones adecuadas.

Llamemos al manifiesto del nuevo kube-scheduler, que creamos al comienzo del capítulo, kube-scheduler-custom.yaml y colóquelo en la siguiente ruta /etc/kubernetes/manifests en tres nodos maestros. Si todo se hace correctamente, Kubelet iniciará un pod en cada nodo y en los registros de nuestro nuevo kube-scheduler veremos información de que nuestro archivo de política se aplicó correctamente:

Creating scheduler from configuration: {{ } [{GeneralPredicates <nil>}] [{ServiceSpreadingPriority 1 <nil>} {EqualPriority 1 <nil>} {LeastRequestedPriority 1 <nil>} {NodePreferAvoidPodsPriority 10000 <nil>} {NodeAffinityPriority 1 <nil>}] [] 10 false}
Registering predicate: GeneralPredicates
Predicate type GeneralPredicates already registered, reusing.
Registering priority: ServiceSpreadingPriority
Priority type ServiceSpreadingPriority already registered, reusing.
Registering priority: EqualPriority
Priority type EqualPriority already registered, reusing.
Registering priority: LeastRequestedPriority
Priority type LeastRequestedPriority already registered, reusing.
Registering priority: NodePreferAvoidPodsPriority
Priority type NodePreferAvoidPodsPriority already registered, reusing.
Registering priority: NodeAffinityPriority
Priority type NodeAffinityPriority already registered, reusing.
Creating scheduler with fit predicates 'map[GeneralPredicates:{}]' and priority functions 'map[EqualPriority:{} LeastRequestedPriority:{} NodeAffinityPriority:{} NodePreferAvoidPodsPriority:{} ServiceSpreadingPriority:{}]'

Ahora solo queda indicar en las especificaciones de nuestro CronJob que todas las solicitudes de programación de sus pods deben ser procesadas por nuestro nuevo kube-scheduler:

...
 jobTemplate:
    spec:
      template:
        spec:
          schedulerName: kube-scheduler-cron
...

Conclusión

Al final, obtuvimos un programador de kube adicional con un conjunto único de políticas de programación, cuyo trabajo es monitoreado directamente por kubelet. Además, hemos configurado la elección de un nuevo líder entre los pods de nuestro kube-scheduler en caso de que el antiguo líder no esté disponible por algún motivo.

Las aplicaciones y servicios habituales se siguen programando a través del programador de kube predeterminado y todas las tareas cron se han transferido por completo al nuevo. La carga creada por las tareas cron ahora se distribuye uniformemente entre todos los nodos. Teniendo en cuenta que la mayoría de las tareas cron se ejecutan en los mismos nodos que las aplicaciones principales del proyecto, esto ha reducido significativamente el riesgo de mover pods por falta de recursos. Después de introducir el programador kube adicional, ya no surgieron problemas con la programación desigual de las tareas cron.

Lea también otros artículos en nuestro blog:

Fuente: habr.com

Añadir un comentario