Criando um agendador kube adicional com um conjunto personalizado de regras de agendamento

Criando um agendador kube adicional com um conjunto personalizado de regras de agendamento

Kube-scheduler é um componente integral do Kubernetes, responsável por agendar pods entre nós de acordo com políticas especificadas. Muitas vezes, durante a operação de um cluster Kubernetes, não precisamos pensar sobre quais políticas são usadas para agendar pods, uma vez que o conjunto de políticas do agendador kube padrão é adequado para a maioria das tarefas diárias. No entanto, há situações em que é importante ajustarmos o processo de alocação de pods, e há duas maneiras de realizar essa tarefa:

  1. Crie um agendador kube com um conjunto personalizado de regras
  2. Escreva seu próprio agendador e ensine-o a trabalhar com solicitações de servidor API

Neste artigo descreverei a implementação do primeiro ponto para resolver o problema de programação desigual de focos em um de nossos projetos.

Uma breve introdução sobre como funciona o kube-scheduler

É importante notar especialmente o fato de que o kube-scheduler não é responsável por agendar diretamente os pods - ele é apenas responsável por determinar o nó no qual colocar o pod. Em outras palavras, o resultado do trabalho do kube-scheduler é o nome do nó, que ele retorna ao servidor API para uma solicitação de agendamento, e é aí que seu trabalho termina.

Primeiro, o kube-scheduler compila uma lista de nós nos quais o pod pode ser agendado de acordo com as políticas de predicados. Além disso, cada nó desta lista recebe um determinado número de pontos de acordo com as políticas prioritárias. Como resultado, o nó com o número máximo de pontos é selecionado. Se houver nós com a mesma pontuação máxima, um nó aleatório será selecionado. Uma lista e descrição das políticas de predicados (filtragem) e prioridades (pontuação) podem ser encontradas em documentação.

Descrição do corpo do problema

Apesar do grande número de diferentes clusters Kubernetes mantidos no Nixys, encontramos pela primeira vez o problema de agendamento de pods apenas recentemente, quando um de nossos projetos precisava executar um grande número de tarefas periódicas (~100 entidades CronJob). Para simplificar ao máximo a descrição do problema, tomaremos como exemplo um microsserviço, dentro do qual uma tarefa cron é iniciada uma vez por minuto, criando alguma carga na CPU. Para executar a tarefa cron, foram alocados três nós com características absolutamente idênticas (24 vCPUs em cada).

Ao mesmo tempo, é impossível dizer com precisão quanto tempo o CronJob levará para ser executado, uma vez que o volume de dados de entrada muda constantemente. Em média, durante a operação normal do kube-scheduler, cada nó executa de 3 a 4 instâncias de trabalho, que criam aproximadamente 20 a 30% da carga na CPU de cada nó:

Criando um agendador kube adicional com um conjunto personalizado de regras de agendamento

O problema em si é que às vezes os pods de tarefas cron paravam de ser agendados em um dos três nós. Ou seja, em algum momento, nem um único pod foi planejado para um dos nós, enquanto nos outros dois nós 6 a 8 cópias da tarefa estavam em execução, criando aproximadamente 40 a 60% da carga da CPU:

Criando um agendador kube adicional com um conjunto personalizado de regras de agendamento

O problema ocorreu com frequência absolutamente aleatória e ocasionalmente correlacionado com o momento em que uma nova versão do código foi lançada.

Ao aumentar o nível de registro do kube-scheduler para o nível 10 (-v=10), começamos a registrar quantos pontos cada nó ganhou durante o processo de avaliação. Durante a operação normal de planejamento, as seguintes informações podem ser vistas nos logs:

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

Aqueles. a julgar pelas informações obtidas nos logs, cada um dos nós obteve igual número de pontos finais e um nó aleatório foi selecionado para planejamento. No momento do planejamento problemático, os logs eram assim:

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

A partir daí pode-se perceber que um dos nós obteve menos pontos finais que os demais e, portanto, o planejamento foi realizado apenas para os dois nós que obtiveram pontuação máxima. Assim, ficamos definitivamente convencidos de que o problema está justamente no agendamento dos pods.

O algoritmo adicional para resolver o problema era óbvio para nós - analisar os logs, entender com que prioridade o nó não marcou pontos e, se necessário, ajustar as políticas do agendador kube padrão. No entanto, aqui nos deparamos com duas dificuldades significativas:

  1. No nível máximo de registro (10), os pontos ganhos apenas para algumas prioridades são refletidos. No trecho de logs acima, você pode ver que para todas as prioridades refletidas nos logs, os nós marcam o mesmo número de pontos no agendamento normal e de problemas, mas o resultado final no caso de planejamento de problemas é diferente. Assim, podemos concluir que para algumas prioridades a pontuação ocorre “nos bastidores”, e não temos como entender para qual prioridade o nó não obteve pontos. Descrevemos esse problema em detalhes em emitem Repositório Kubernetes no Github. No momento em que este artigo foi escrito, foi recebida uma resposta dos desenvolvedores informando que o suporte de registro será adicionado nas atualizações do Kubernetes v1.15,1.16, 1.17 e XNUMX.
  2. Não há uma maneira fácil de entender com qual conjunto específico de políticas o kube-scheduler está trabalhando atualmente. Sim, em documentação esta lista está listada, mas não contém informações sobre quais pesos específicos são atribuídos a cada uma das políticas prioritárias. Você pode ver os pesos ou editar as políticas do agendador kube padrão apenas em códigos-fonte.

Vale ressaltar que uma vez conseguimos registrar que um nó não recebeu pontos de acordo com a política ImageLocalityPriority, que atribui pontos a um nó caso ele já possua a imagem necessária para executar a aplicação. Ou seja, no momento em que uma nova versão do aplicativo foi lançada, a tarefa cron conseguiu rodar em dois nós, baixando uma nova imagem do registro do docker para eles, e assim dois nós receberam uma pontuação final maior em relação ao terceiro .

Como escrevi acima, nos logs não vemos informações sobre a avaliação da política ImageLocalityPriority, portanto, para verificar nossa suposição, despejamos a imagem com a nova versão do aplicativo no terceiro nó, após o qual o agendamento funcionou corretamente . Foi precisamente por causa da política ImageLocalityPriority que o problema de escalonamento foi observado muito raramente; mais frequentemente, estava associado a outra coisa. Devido ao fato de não podermos depurar totalmente cada uma das políticas na lista de prioridades do agendador kube padrão, precisávamos de um gerenciamento flexível das políticas de agendamento de pod.

Formulação do problema

Queríamos que a solução do problema fosse o mais específica possível, ou seja, as principais entidades do Kubernetes (aqui nos referimos ao agendador kube padrão) deveriam permanecer inalteradas. Não queríamos resolver um problema em um lugar e criá-lo em outro. Assim, chegamos a duas opções para solucionar o problema, que foram anunciadas na introdução do artigo - criar um agendador adicional ou escrever o seu próprio. O principal requisito para agendar tarefas cron é distribuir a carga uniformemente entre três nós. Este requisito pode ser satisfeito pelas políticas existentes do agendador kube, portanto, para resolver nosso problema, não faz sentido escrever seu próprio agendador.

As instruções para criar e implantar um agendador kube adicional são descritas em documentação. No entanto, pareceu-nos que a entidade Deployment não era suficiente para garantir tolerância a falhas na operação de um serviço tão crítico como o kube-scheduler, por isso decidimos implantar um novo kube-scheduler como um Static Pod, que seria monitorado diretamente por Kubelet. Assim, temos os seguintes requisitos para o novo agendador kube:

  1. O serviço deve ser implantado como um pod estático em todos os clusters mestres
  2. A tolerância a falhas deve ser fornecida caso o pod ativo com kube-scheduler esteja indisponível
  3. A principal prioridade no planejamento deve ser o número de recursos disponíveis no nó (LeastRequestedPriority)

Soluções de implementação

Vale ressaltar desde já que realizaremos todo o trabalho no Kubernetes v1.14.7, pois Esta é a versão que foi utilizada no projeto. Vamos começar escrevendo um manifesto para nosso novo agendador kube. Vamos pegar o manifesto padrão (/etc/kubernetes/manifests/kube-scheduler.yaml) como base e trazê-lo para o seguinte formato:

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

Resumidamente sobre as principais mudanças:

  1. Alterado o nome do pod e do contêiner para kube-scheduler-cron
  2. Especificou o uso das portas 10151 e 10159 conforme a opção foi definida hostNetwork: true e não podemos usar as mesmas portas do agendador kube padrão (10251 e 10259)
  3. Usando o parâmetro --config, especificamos o arquivo de configuração com o qual o serviço deve ser iniciado
  4. Montagem configurada do arquivo de configuração (scheduler-custom.conf) e arquivo de política de agendamento (scheduler-custom-policy-config.json) do host

Não esqueça que nosso agendador kube precisará de direitos semelhantes ao padrão. Edite a função do cluster:

kubectl edit clusterrole system:kube-scheduler

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

Agora vamos falar sobre o que deve estar contido no arquivo de configuração e no arquivo de política de agendamento:

  • Arquivo de configuração (scheduler-custom.conf)
    Para obter a configuração padrão do kube-scheduler, você deve usar o parâmetro --write-config-to de documentação. Colocaremos a configuração resultante no arquivo /etc/kubernetes/scheduler-custom.conf e a reduziremos para o seguinte formato:

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"

Resumidamente sobre as principais mudanças:

  1. Definimos schedulerName como o nome do nosso serviço kube-scheduler-cron.
  2. No parâmetro lockObjectName você também precisa definir o nome do nosso serviço e certificar-se de que o parâmetro leaderElect definido como verdadeiro (se você tiver um nó mestre, poderá defini-lo como falso).
  3. Especificou o caminho para o arquivo com uma descrição das políticas de agendamento no parâmetro algorithmSource.

Vale a pena dar uma olhada mais de perto no segundo ponto, onde editamos os parâmetros da chave leaderElection. Para garantir a tolerância a falhas, habilitamos (leaderElect) o processo de seleção de um líder (mestre) entre os pods do nosso agendador kube usando um único endpoint para eles (resourceLock) chamado kube-scheduler-cron (lockObjectName) no namespace do sistema kube (lockObjectNamespace). Como o Kubernetes garante alta disponibilidade dos componentes principais (incluindo o kube-scheduler) pode ser encontrado em статье.

  • Arquivo de política de agendamento (scheduler-custom-policy-config.json)
    Como escrevi anteriormente, podemos descobrir com quais políticas específicas o agendador kube padrão funciona apenas analisando seu código. Ou seja, não podemos obter um arquivo com políticas de agendamento para o agendador kube padrão da mesma forma que um arquivo de configuração. Vamos descrever as políticas de agendamento nas quais estamos interessados ​​no arquivo /etc/kubernetes/scheduler-custom-policy-config.json da seguinte forma:

{
  "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
}

Assim, o kube-scheduler primeiro compila uma lista de nós para os quais um pod pode ser agendado de acordo com a política GeneralPredicates (que inclui um conjunto de políticas PodFitsResources, PodFitsHostPorts, HostName e MatchNodeSelector). E então cada nó é avaliado de acordo com o conjunto de políticas na matriz de prioridades. Para cumprir as condições da nossa tarefa, considerámos que tal conjunto de políticas seria a solução óptima. Deixe-me lembrá-lo que um conjunto de políticas com suas descrições detalhadas está disponível em documentação. Para realizar sua tarefa, basta alterar o conjunto de políticas usadas e atribuir pesos apropriados a elas.

Vamos chamar o manifesto do novo kube-scheduler, que criamos no início do capítulo, kube-scheduler-custom.yaml e colocá-lo no seguinte caminho /etc/kubernetes/manifests em três nós mestres. Se tudo for feito corretamente, o Kubelet iniciará um pod em cada nó e nos logs do nosso novo agendador kube veremos informações de que nosso arquivo de política foi aplicado com sucesso:

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:{}]'

Agora só falta indicar na especificação do nosso CronJob que todas as solicitações de agendamento de seus pods deverão ser processadas pelo nosso novo kube-scheduler:

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

Conclusão

No final das contas, obtivemos um agendador kube adicional com um conjunto exclusivo de políticas de agendamento, cujo trabalho é monitorado diretamente pelo kubelet. Além disso, configuramos a eleição de um novo líder entre os pods do nosso agendador kube, caso o antigo líder fique indisponível por algum motivo.

Aplicativos e serviços regulares continuam a ser agendados por meio do agendador kube padrão, e todas as tarefas cron foram completamente transferidas para o novo. A carga criada pelas tarefas cron agora é distribuída uniformemente por todos os nós. Considerando que a maioria das tarefas cron são executadas nos mesmos nós das principais aplicações do projeto, isso reduziu significativamente o risco de movimentação de pods por falta de recursos. Após a introdução do kube-scheduler adicional, não surgiram mais problemas com agendamento desigual de tarefas cron.

Leia também outros artigos em nosso blog:

Fonte: habr.com

Adicionar um comentário