This the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

Escalonamento

No Kubernetes, agendamento refere-se a garantia de que os pods correspondam aos nós para que o kubelet possa executá-los. Remoção é o processo de falha proativa de um ou mais pods em nós com falta de recursos.

1 - Escalonador do Kubernetes

No Kubernetes, escalonamento refere-se a garantir que os Pods sejam correspondidos aos Nodes para que o Kubelet possa executá-los.

Visão geral do Escalonamento

Um escalonador observa Pods recém-criados que não possuem um Node atribuído. Para cada Pod que o escalonador descobre, ele se torna responsável por encontrar o melhor Node para execução do Pod. O escalonador chega a essa decisão de alocação levando em consideração os princípios de programação descritos abaixo.

Se você quiser entender por que os Pods são alocados em um Node específico ou se planeja implementar um escalonador personalizado, esta página ajudará você a aprender sobre escalonamento.

kube-scheduler

kube-scheduler é o escalonador padrão do Kubernetes e é executado como parte do control plane. O kube-scheduler é projetado para que, se você quiser e precisar, possa escrever seu próprio componente de escalonamento e usá-lo.

Para cada Pod recém-criado ou outros Pods não escalonados, o kube-scheduler seleciona um Node ideal para execução. No entanto, todos os contêineres nos Pods têm requisitos diferentes de recursos e cada Pod também possui requisitos diferentes. Portanto, os Nodes existentes precisam ser filtrados de acordo com os requisitos de escalonamento específicos.

Em um cluster, Nodes que atendem aos requisitos de escalonamento para um Pod são chamados de Nodes viáveis. Se nenhum dos Nodes for adequado, o Pod permanece não escalonado até que o escalonador possa alocá-lo.

O escalonador encontra Nodes viáveis para um Pod e, em seguida, executa um conjunto de funções para pontuar os Nodes viáveis e escolhe um Node com a maior pontuação entre os possíveis para executar o Pod. O escalonador então notifica o servidor da API sobre essa decisão em um processo chamado binding.

Fatores que precisam ser levados em consideração para decisões de escalonamento incluem requisitos individuais e coletivos de recursos, restrições de hardware / software / política, especificações de afinidade e anti-afinidade, localidade de dados, interferência entre cargas de trabalho e assim por diante.

Seleção do Node no kube-scheduler

O kube-scheduler seleciona um Node para o Pod em uma operação que consiste em duas etapas:

  1. Filtragem
  2. Pontuação

A etapa de filtragem localiza o conjunto de Nodes onde é possível alocar o Pod. Por exemplo, o filtro PodFitsResources verifica se um Node candidato possui recursos disponíveis suficientes para atender às solicitações de recursos específicas de um Pod. Após esta etapa, a lista de Nodes contém quaisquer Nodes adequados; frequentemente, haverá mais de um. Se a lista estiver vazia, esse Pod (ainda) não é escalonável.

Na etapa de pontuação, o escalonador classifica os Nodes restantes para escolher o mais adequado. O escalonador atribui uma pontuação a cada Node que sobreviveu à filtragem, baseando essa pontuação nas regras de pontuação ativa.

Por fim, o kube-scheduler atribui o Pod ao Node com a classificação mais alta. Se houver mais de um Node com pontuações iguais, o kube-scheduler seleciona um deles aleatoriamente.

Existem duas maneiras suportadas de configurar o comportamento de filtragem e pontuação do escalonador:

  1. Políticas de Escalonamento permitem configurar Predicados para filtragem e Prioridades para pontuação.

  2. Perfis de Escalonamento permitem configurar Plugins que implementam diferentes estágios de escalonamento, incluindo: QueueSort, Filter, Score, Bind, Reserve, Permit, e outros. Você também pode configurar o kube-scheduler para executar diferentes perfis.

What's next

2 - Sobrecarga de Pod

FEATURE STATE: Kubernetes v1.18 [beta]

Quando você executa um Pod num nó, o próprio Pod usa uma quantidade de recursos do sistema. Estes recursos são adicionais aos recursos necessários para executar o(s) contêiner(s) dentro do Pod. Sobrecarga de Pod, do inglês Pod Overhead, é uma funcionalidade que serve para contabilizar os recursos consumidos pela infraestrutura do Pod para além das solicitações e limites do contêiner.

No Kubernetes, a sobrecarga de Pods é definido no tempo de admissão de acordo com a sobrecarga associada à RuntimeClass do Pod.

Quando é ativada a Sobrecarga de Pod, a sobrecarga é considerada adicionalmente à soma das solicitações de recursos do contêiner ao agendar um Pod. Semelhantemente, o kubelet incluirá a sobrecarga do Pod ao dimensionar o cgroup do Pod e ao executar a classificação de prioridade de migração do Pod em caso de drain do Node.

Habilitando a Sobrecarga de Pod

Terá de garantir que o Feature Gate PodOverhead esteja ativo (está ativo por padrão a partir da versão 1.18) em todo o cluster, e uma RuntimeClass utilizada que defina o campo overhead.

Exemplo de uso

Para usar a funcionalidade PodOverhead, é necessário uma RuntimeClass que define o campo overhead. Por exemplo, poderia usar a definição da RuntimeClass abaixo com um agente de execução de contêiner virtualizado que use cerca de 120MiB por Pod para a máquina virtual e o sistema operacional convidado:

---
kind: RuntimeClass
apiVersion: node.k8s.io/v1beta1
metadata:
    name: kata-fc
handler: kata-fc
overhead:
    podFixed:
        memory: "120Mi"
        cpu: "250m"

As cargas de trabalho que são criadas e que especificam o manipulador RuntimeClass kata-fc irão usar a sobrecarga de memória e cpu em conta para os cálculos da quota de recursos, agendamento de nós, assim como dimensionamento do cgroup do Pod.

Considere executar a seguinte carga de trabalho de exemplo, test-pod:

apiVersion: v1
kind: Pod
metadata:
  name: test-pod
spec:
  runtimeClassName: kata-fc
  containers:
  - name: busybox-ctr
    image: busybox
    stdin: true
    tty: true
    resources:
      limits:
        cpu: 500m
        memory: 100Mi
  - name: nginx-ctr
    image: nginx
    resources:
      limits:
        cpu: 1500m
        memory: 100Mi

No tempo de admissão o controlador de admissão RuntimeClass atualiza o PodSpec da carga de trabalho de forma a incluir o overhead como descrito na RuntimeClass. Se o PodSpec já tiver este campo definido o Pod será rejeitado. No exemplo dado, como apenas o nome do RuntimeClass é especificado, o controlador de admissão muda o Pod de forma a incluir um overhead.

Depois do controlador de admissão RuntimeClass, pode verificar o PodSpec atualizado:

kubectl get pod test-pod -o jsonpath='{.spec.overhead}'

A saída é:

map[cpu:250m memory:120Mi]

Se for definido um ResourceQuota, a soma das requisições dos contêineres assim como o campo overhead são contados.

Quando o kube-scheduler está decidindo que nó deve executar um novo Pod, o agendador considera o overhead do pod, assim como a soma de pedidos aos contêineres para esse Pod. Para este exemplo, o agendador adiciona as requisições e a sobrecarga, depois procura um nó com 2.25 CPU e 320 MiB de memória disponível.

Assim que um Pod é agendado a um nó, o kubelet nesse nó cria um novo cgroup para o Pod. É dentro deste Pod que o agente de execução de contêiners subjacente vai criar contêineres.

Se o recurso tiver um limite definido para cada contêiner (QoS garantida ou Burstrable QoS com limites definidos), o kubelet definirá um limite superior para o cgroup do Pod associado a esse recurso (cpu.cfs_quota_us para CPU e memory.limit_in_bytes de memória). Este limite superior é baseado na soma dos limites do contêiner mais o overhead definido no PodSpec.

Para CPU, se o Pod for QoS garantida ou Burstrable QoS, o kubelet vai definir cpu.shares baseado na soma dos pedidos ao contêiner mais o overhead definido no PodSpec.

Olhando para o nosso exemplo, verifique as requisições ao contêiner para a carga de trabalho:

kubectl get pod test-pod -o jsonpath='{.spec.containers[*].resources.limits}'

O total de requisições ao contêiner são 2000m CPU e 200MiB de memória:

map[cpu: 500m memory:100Mi] map[cpu:1500m memory:100Mi]

Verifique isto comparado ao que é observado pelo nó:

kubectl describe node | grep test-pod -B2

A saída mostra que 2250m CPU e 320MiB de memória são solicitados, que inclui PodOverhead:

  Namespace                   Name                CPU Requests  CPU Limits   Memory Requests  Memory Limits  AGE
  ---------                   ----                ------------  ----------   ---------------  -------------  ---
  default                     test-pod            2250m (56%)   2250m (56%)  320Mi (1%)       320Mi (1%)     36m

Verificar os limites cgroup do Pod

Verifique os cgroups de memória do Pod no nó onde a carga de trabalho está em execução. No seguinte exemplo, crictl é usado no nó, que fornece uma CLI para agentes de execução compatíveis com CRI. Isto é um exemplo avançado para mostrar o comportamento do PodOverhead, e não é esperado que os usuários precisem verificar cgroups diretamente no nó.

Primeiro, no nó em particular, determine o identificador do Pod:

# Execute no nó onde o Pod está agendado
POD_ID="$(sudo crictl pods --name test-pod -q)"

A partir disto, pode determinar o caminho do cgroup para o Pod:

# Execute no nó onde o Pod está agendado
sudo crictl inspectp -o=json $POD_ID | grep cgroupsPath

O caminho do cgroup resultante inclui o contêiner pause do Pod. O cgroup no nível do Pod está um diretório acima.

        "cgroupsPath": "/kubepods/podd7f4b509-cf94-4951-9417-d1087c92a5b2/7ccf55aee35dd16aca4189c952d83487297f3cd760f1bbf09620e206e7d0c27a"

Neste caso especifico, o caminho do cgroup do Pod é kubepods/podd7f4b509-cf94-4951-9417-d1087c92a5b2. Verifique a configuração cgroup de nível do Pod para a memória:

# Execute no nó onde o Pod está agendado
# Mude também o nome do cgroup para combinar com o cgroup alocado ao Pod.
 cat /sys/fs/cgroup/memory/kubepods/podd7f4b509-cf94-4951-9417-d1087c92a5b2/memory.limit_in_bytes

Isto é 320 MiB, como esperado:

335544320

Observabilidade

Uma métrica kube_pod_overhead está disponível em kube-state-metrics para ajudar a identificar quando o PodOverhead está sendo utilizado e para ajudar a observar a estabilidade das cargas de trabalho em execução com uma sobrecarga (Overhead) definida. Esta funcionalidade não está disponível na versão 1.9 do kube-state-metrics, mas é esperado em uma próxima versão. Os usuários necessitarão entretanto construir o kube-state-metrics a partir do código fonte.

What's next