Política de agendamento (afinidade/antiafinidade)
O Kubernetes suporta afinidade de nó e afinidade/antiafinidade de pod. Você pode configurar regras personalizadas para obter o agendamento de afinidade e antiafinidade. Por exemplo, você pode implementar pods de front-end e pods de back-end juntos, implementar o mesmo tipo de aplicações em um nó específico ou implementar aplicações diferentes em nós diferentes.
A afinidade do Kubernetes se aplica a nós e pods.
- nodeAffinity: semelhante ao nodeSelector do pod, e ambos agendam pods apenas para os nós com rótulos especificados. A diferença entre nodeAffinity e nodeSelector reside no fato de que nodeAffinity apresenta uma expressão mais forte que nodeSelector e permite especificar restrições suaves selecionadas preferencialmente. Os dois tipos de afinidade de nó são os seguintes:
- requiredDuringSchedulingIgnoredDuringExecution: restrição rígida que deve ser atendida. O agendador pode realizar o agendamento somente quando a regra é atendida. Essa função é semelhante ao nodeSelector, mas apresenta uma expressão de sintaxe mais forte. Para mais detalhes, consulte Node Affinity (nodeAffinity).
- preferredDuringSchedulingIgnoredDuringExecution: restrição suave que é atendida o máximo possível. O agendador tenta encontrar o nó que atende à regra. Se nenhum nó correspondente for encontrado, o agendador ainda agendará o pod. Para mais detalhes, consulte Regra de preferência de nó.
- Afinidade da carga de trabalho (podAffinity)/Workload Anti-affinity (podAntiAffinity): os nós para os quais um pod pode ser agendado são determinados com base no rótulo do pod em execução em um nó, mas não no rótulo do nó. Semelhante à afinidade de nó, a afinidade de carga de trabalho e a antiafinidade também são dos tipos de requiredDuringSchedulingIgnoredDuringExecution e preferredDuringSchedulingIgnoredDuringExecution.
A afinidade e a antiafinidade da carga de trabalho exigem uma certa quantidade de tempo de computação, o que retarda significativamente o agendamento em clusters de grande escala. Não ative afinidade e antiafinidade de carga de trabalho em um cluster que contém centenas de nós.
Você pode criar as políticas de afinidade anteriores no console. Para mais detalhes, consulte Configurar políticas de agendamento.
Configurar políticas de agendamento
- Efetue logon no console do CCE.
- Ao criar uma carga de trabalho, clique em Scheduling na área Advanced Settings.
Tabela 1 Configurações de afinidade de nó Parâmetro
Descrição
Required
Restrição rígida, que corresponde requiredDuringSchedulingIgnoredDuringExecution para especificar as condições que devem ser atendidas.
Se várias regras que devem ser atendidas forem adicionadas, o agendamento será realizado quando apenas uma regra for atendida.
Preferred
Restrição suave, que corresponde a preferredDuringSchedulingIgnoredDuringExecution para especificar as condições que devem ser atendidas o maior número possível.
Se forem adicionadas várias regras que devem ser cumpridas tanto quanto possível, o agendamento será realizado mesmo que uma ou nenhuma das regras seja cumprida.
- Clique em em Node Affinity, Workload Affinity ou Workload Anti-Affinity para adicionar políticas de agendamento. Na caixa de diálogo exibida, adicione diretamente as políticas. Como alternativa, você pode especificar nós ou AZs a serem agendados no console.
A especificação de nós e AZs também é implementada por meio de rótulos. O console libera você da inserção manual de rótulos de nó. O rótulo kubernetes.io/hostname é usado quando você especifica um nó, e o rótulo failure-domain.beta.kubernetes.io/zone é usado quando você especifica uma AZ.Figura 1 Adicionar uma política de agendamento
Tabela 2 Parâmetros para configurar a política de agendamento Parâmetro
Descrição
Label
Rótulo do nó. Você pode usar o rótulo padrão ou personalizar um rótulo.
Operator
As seguintes relações são compatíveis: In, NotIn, Exists, DoesNotExist, Gt e Lt.
- In: o rótulo do objeto de afinidade ou antiafinidade está na lista de valores do rótulo (campo values).
- NotIn: o rótulo do objeto de afinidade ou antiafinidade não está na lista de valores do rótulo (campo values).
- Exists: o objeto de afinidade ou antiafinidade tem um nome de rótulo especificado.
- DoesNotExist: o objeto de afinidade ou antiafinidade não tem o nome de rótulo especificado.
- Gt: (disponível apenas para afinidade de nó) o valor do rótulo do nó agendado é maior que o valor da lista (comparação de cadeias).
- Lt: (disponível somente para afinidade de nó) o valor do rótulo do nó de agendamento é menor que o valor da lista (comparação de cadeias).
Label Value
Valor do rótulo.
Namespace
Esse parâmetro está disponível somente em uma política de agendamento de afinidade ou antiafinidade de carga de trabalho.
Namespace para o qual a política de agendamento entra em vigor.
Topology Key
Esse parâmetro pode ser usado somente em uma política de agendamento de afinidade ou antiafinidade de carga de trabalho.
Selecione o escopo especificado por topologyKey e, em seguida, selecione o conteúdo definido pela política.
Weight
Esse parâmetro pode ser definido apenas em uma política de agendamento Preferred.
Node Affinity (nodeAffinity)
As regras de afinidade de nó de carga de trabalho são implementadas usando rótulos de nó. Quando um nó é criado em um cluster do CCE, determinados rótulos são adicionados automaticamente. Você pode executar o comando kubectl describe node para exibir os rótulos. O seguinte é um exemplo:
$ kubectl describe node 192.168.0.212 Name: 192.168.0.212 Roles: <none> Labels: beta.kubernetes.io/arch=amd64 beta.kubernetes.io/os=linux failure-domain.beta.kubernetes.io/is-baremetal=false failure-domain.beta.kubernetes.io/region=****** failure-domain.beta.kubernetes.io/zone=****** kubernetes.io/arch=amd64 kubernetes.io/availablezone=****** kubernetes.io/eniquota=12 kubernetes.io/hostname=192.168.0.212 kubernetes.io/os=linux node.kubernetes.io/subnetid=fd43acad-33e7-48b2-a85a-24833f362e0e os.architecture=amd64 os.name=EulerOS_2.0_SP5 os.version=3.10.0-862.14.1.5.h328.eulerosv2r7.x86_64
No agendamento de carga de trabalho, os rótulos de nó comuns são os seguintes:
- failure-domain.beta.kubernetes.io/region: região onde o nó está localizado.
- failure-domain.beta.kubernetes.io/zone: zona de disponibilidade à qual o nó pertence.
- kubernetes.io/hostname: nome do host do nó.
O Kubernetes fornece o campo nodeSelector. Ao criar uma carga de trabalho, você pode definir esse campo para especificar que o pod só pode ser implementado em um nó com o rótulo específico. O exemplo a seguir mostra como usar um nodeSelector para implantar o pod somente no nó com o rótulo gpu=true.
apiVersion: v1 kind: Pod metadata: name: nginx spec: nodeSelector: # Node selection. A pod is deployed on a node only when the node has the gpu=true label. gpu: true ...
- requiredDuringSchedulingIgnoredDuringExecution: o Kubernetes não pode agendar o pod a menos que a regra seja cumprida.
- PreferredDuringSchedulingIgnoredDuringExecution: o Kubernetes tenta encontrar um nó que atenda à regra. Se um nó correspondente não estiver disponível, o Kubernetes ainda agendará o pod.
Nesses dois tipos de afinidade de nó, requiredDuringScheduling ou preferredDuringScheduling indica que o pod pode ser agendado para um nó somente quando todas as regras definidas forem atendidas (obrigatório). IgnoredDuringExecution indica que, se o rótulo do nó for alterado após o Kubernetes agendar o pod, o pod continuará em execução e não será reagendado.
Veja a seguir um exemplo de definição de afinidade de nó:
apiVersion: apps/v1 kind: Deployment metadata: name: gpu labels: app: gpu spec: selector: matchLabels: app: gpu replicas: 3 template: metadata: labels: app: gpu spec: containers: - image: nginx:alpine name: gpu resources: requests: cpu: 100m memory: 200Mi limits: cpu: 100m memory: 200Mi imagePullSecrets: - name: default-secret affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: gpu operator: In values: - "true"
Neste exemplo, o nó programado deve conter um rótulo com a chave chamada gpu. O valor do operator é In, indicando que o valor do rótulo deve estar na lista de values. Ou seja, o valor da chave do rótulo gpu do nó é true. Para obter detalhes sobre outros valores de operator, consulte Descrição do valor do operador. Observe que não existe nodeAntiAffinity porque os operadores NotIn e DoesNotExist fornecem a mesma função.
A seguir, descreve-se como verificar se a regra entra em vigor. Suponha que um cluster tenha três nós.
$ kubectl get node NAME STATUS ROLES AGE VERSION 192.168.0.212 Ready <none> 13m v1.15.6-r1-20.3.0.2.B001-15.30.2 192.168.0.94 Ready <none> 13m v1.15.6-r1-20.3.0.2.B001-15.30.2 192.168.0.97 Ready <none> 13m v1.15.6-r1-20.3.0.2.B001-15.30.2
Adicione o rótulo gpu=true ao nó 192.168.0.212.
$ kubectl label node 192.168.0.212 gpu=true node/192.168.0.212 labeled $ kubectl get node -L gpu NAME STATUS ROLES AGE VERSION GPU 192.168.0.212 Ready <none> 13m v1.15.6-r1-20.3.0.2.B001-15.30.2 true 192.168.0.94 Ready <none> 13m v1.15.6-r1-20.3.0.2.B001-15.30.2 192.168.0.97 Ready <none> 13m v1.15.6-r1-20.3.0.2.B001-15.30.2
Crie a Implementação. Você pode descobrir que todos os pods estão implementados no nó 192.168.0.212.
$ kubectl create -f affinity.yaml deployment.apps/gpu created $ kubectl get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE gpu-6df65c44cf-42xw4 1/1 Running 0 15s 172.16.0.37 192.168.0.212 gpu-6df65c44cf-jzjvs 1/1 Running 0 15s 172.16.0.36 192.168.0.212 gpu-6df65c44cf-zv5cl 1/1 Running 0 15s 172.16.0.38 192.168.0.212
Regra de preferência de nó
A regra requiredDuringSchedulingIgnoredDuringExecution é uma regra de seleção rígida. Existe outro tipo de regra de seleção, ou seja, preferredDuringSchedulingIgnoredDuringExecution. Ela é usada para especificar quais nós são preferidos durante o agendamento.
Para obter esse efeito, adicione um nó anexado com discos SAS ao cluster, adicione o rótulo DISK=SAS ao nó e adicione o rótulo DISK=SSD aos outros três nós.
$ kubectl get node -L DISK,gpu NAME STATUS ROLES AGE VERSION DISK GPU 192.168.0.100 Ready <none> 7h23m v1.15.6-r1-20.3.0.2.B001-15.30.2 SAS 192.168.0.212 Ready <none> 8h v1.15.6-r1-20.3.0.2.B001-15.30.2 SSD true 192.168.0.94 Ready <none> 8h v1.15.6-r1-20.3.0.2.B001-15.30.2 SSD 192.168.0.97 Ready <none> 8h v1.15.6-r1-20.3.0.2.B001-15.30.2 SSD
Defina uma Implementação. Use a regra preferredDuringSchedulingIgnoredDuringExecution para definir o peso dos nós com o disco SSD instalado como 80 e os nós com o rótulo gpu=true como 20. Desta forma, os pods são preferencialmente implementados nos nós com o disco SSD instalado.
apiVersion: apps/v1 kind: Deployment metadata: name: gpu labels: app: gpu spec: selector: matchLabels: app: gpu replicas: 10 template: metadata: labels: app: gpu spec: containers: - image: nginx:alpine name: gpu resources: requests: cpu: 100m memory: 200Mi limits: cpu: 100m memory: 200Mi imagePullSecrets: - name: default-secret affinity: nodeAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 80 preference: matchExpressions: - key: DISK operator: In values: - SSD - weight: 20 preference: matchExpressions: - key: gpu operator: In values: - "true"
Após a implementação, há cinco pods implementados no nó 192.168.0.212 (rótulo: DISK=SSD e GPU=true), três pods implementados no nó 192.168.0.97 (rótulo: DISK=SSD) e dois pods implementados no nó 192.168.0.100 (rótulo: DISK=SAS).
A partir da saída anterior, você pode descobrir que nenhum pod da Implementação está agendado para o nó 192.168.0.94 (label: DISK=SSD). Isso ocorre porque o nó já possui muitos pods e seu uso de recursos é alto. Isso também indica que a regra preferredDuringSchedulingIgnoredDuringExecution define uma preferência em vez de um requisito rígido.
$ kubectl create -f affinity2.yaml deployment.apps/gpu created $ kubectl get po -o wide NAME READY STATUS RESTARTS AGE IP NODE gpu-585455d466-5bmcz 1/1 Running 0 2m29s 172.16.0.44 192.168.0.212 gpu-585455d466-cg2l6 1/1 Running 0 2m29s 172.16.0.63 192.168.0.97 gpu-585455d466-f2bt2 1/1 Running 0 2m29s 172.16.0.79 192.168.0.100 gpu-585455d466-hdb5n 1/1 Running 0 2m29s 172.16.0.42 192.168.0.212 gpu-585455d466-hkgvz 1/1 Running 0 2m29s 172.16.0.43 192.168.0.212 gpu-585455d466-mngvn 1/1 Running 0 2m29s 172.16.0.48 192.168.0.97 gpu-585455d466-s26qs 1/1 Running 0 2m29s 172.16.0.62 192.168.0.97 gpu-585455d466-sxtzm 1/1 Running 0 2m29s 172.16.0.45 192.168.0.212 gpu-585455d466-t56cm 1/1 Running 0 2m29s 172.16.0.64 192.168.0.100 gpu-585455d466-t5w5x 1/1 Running 0 2m29s 172.16.0.41 192.168.0.212
No exemplo anterior, a prioridade de agendamento do nó é a seguinte. Os nós com rótulos SSD e gpu=true têm a prioridade mais alta. Os nós com o rótulo SSD, mas sem o rótulo gpu=true, têm a segunda prioridade (peso: 80). Os nós com o rótulo gpu=true, mas nenhum rótulo SSD, têm a terceira prioridade. Os nós sem qualquer um desses dois rótulos têm a prioridade mais baixa.
Afinidade da carga de trabalho (podAffinity)
As regras de afinidade de nó afetam apenas a afinidade entre pods e nós. O Kubernetes também suporta a configuração de regras de afinidade entre pods. Por exemplo, o front-end e o back-end de uma aplicação podem ser implementados juntos em um nó para reduzir a latência de acesso. Há também dois tipos de regras de afinidade entre pods: requiredDuringSchedulingIgnoredDuringExecution e preferredDuringSchedulingIgnoredDuringExecution.
Para afinidade de carga de trabalho, a topologyKey não pode ser deixada em branco quando são usados requiredDuringSchedulingIgnoredDuringExecution e preferredDuringSchedulingIgnoredDuringExecution.
Suponha que o backend de uma aplicação tenha sido criado e tenha o rótulo app=backend.
$ kubectl get po -o wide NAME READY STATUS RESTARTS AGE IP NODE backend-658f6cb858-dlrz8 1/1 Running 0 2m36s 172.16.0.67 192.168.0.100
Você pode configurar a seguinte regra de afinidade do pod para implementar os pods de front-end da aplicação no mesmo nó que os pods de back-end.
apiVersion: apps/v1 kind: Deployment metadata: name: frontend labels: app: frontend spec: selector: matchLabels: app: frontend replicas: 3 template: metadata: labels: app: frontend spec: containers: - image: nginx:alpine name: frontend resources: requests: cpu: 100m memory: 200Mi limits: cpu: 100m memory: 200Mi imagePullSecrets: - name: default-secret affinity: podAffinity: requiredDuringSchedulingIgnoredDuringExecution: - topologyKey: kubernetes.io/hostname labelSelector: matchExpressions: - key: app operator: In values: - backend
Implemente o front-end e você pode descobrir que o front-end é implantado no mesmo nó que o back-end.
$ kubectl create -f affinity3.yaml deployment.apps/frontend created $ kubectl get po -o wide NAME READY STATUS RESTARTS AGE IP NODE backend-658f6cb858-dlrz8 1/1 Running 0 5m38s 172.16.0.67 192.168.0.100 frontend-67ff9b7b97-dsqzn 1/1 Running 0 6s 172.16.0.70 192.168.0.100 frontend-67ff9b7b97-hxm5t 1/1 Running 0 6s 172.16.0.71 192.168.0.100 frontend-67ff9b7b97-z8pdb 1/1 Running 0 6s 172.16.0.72 192.168.0.100
O campo topologyKey é usado para dividir domínios de topologia para especificar o intervalo de seleção. Se o rótulo chaves e valores de nós são os mesmos, os nós são considerados como estando no mesmo domínio topologia. Em seguida, os conteúdos definidos nas regras a seguir são selecionados. O efeito de topologyKey não é totalmente demonstrado no exemplo anterior porque todos os nós têm o rótulo kubernetes.io/hostname, ou seja, todos os nós estão dentro do intervalo.
Para ver como topologyKey funciona, suponha que o back-end do aplicação tenha dois pods, que estão sendo executados em nós diferentes.
$ kubectl get po -o wide NAME READY STATUS RESTARTS AGE IP NODE backend-658f6cb858-5bpd6 1/1 Running 0 23m 172.16.0.40 192.168.0.97 backend-658f6cb858-dlrz8 1/1 Running 0 2m36s 172.16.0.67 192.168.0.100
Adicione o rótulo prefer=true aos nós 192.168.0.97 e 192.168.0.94.
$ kubectl label node 192.168.0.97 prefer=true node/192.168.0.97 labeled $ kubectl label node 192.168.0.94 prefer=true node/192.168.0.94 labeled $ kubectl get node -L prefer NAME STATUS ROLES AGE VERSION PREFER 192.168.0.100 Ready <none> 44m v1.15.6-r1-20.3.0.2.B001-15.30.2 192.168.0.212 Ready <none> 91m v1.15.6-r1-20.3.0.2.B001-15.30.2 192.168.0.94 Ready <none> 91m v1.15.6-r1-20.3.0.2.B001-15.30.2 true 192.168.0.97 Ready <none> 91m v1.15.6-r1-20.3.0.2.B001-15.30.2 true
Se a topologyKey de podAffinity for definida como prefer, os domínios de topologia do nó serão divididos conforme mostrado em Figura 3.
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- topologyKey: prefer
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- backend
Durante o agendamento, os domínios de topologia de nó são divididos com base no rótulo prefer. Neste exemplo, 192.168.0.97 e 192.168.0.94 são divididos no mesmo domínio de topologia. Se um pod com o rótulo app=backend for executado no domínio da topologia, mesmo que nem todos os nós no domínio da topologia executem o pod com o rótulo app=backend (neste exemplo, somente o nó 192.168.0.97 possui tal pod), frontend também é implementado nesse domínio de topologia (192.168.0.97 ou 192.168.0.94).
$ kubectl create -f affinity3.yaml deployment.apps/frontend created $ kubectl get po -o wide NAME READY STATUS RESTARTS AGE IP NODE backend-658f6cb858-5bpd6 1/1 Running 0 26m 172.16.0.40 192.168.0.97 backend-658f6cb858-dlrz8 1/1 Running 0 5m38s 172.16.0.67 192.168.0.100 frontend-67ff9b7b97-dsqzn 1/1 Running 0 6s 172.16.0.70 192.168.0.97 frontend-67ff9b7b97-hxm5t 1/1 Running 0 6s 172.16.0.71 192.168.0.97 frontend-67ff9b7b97-z8pdb 1/1 Running 0 6s 172.16.0.72 192.168.0.97
Antiafinidade da carga de trabalho (podAntiAffinity)
Ao contrário dos cenários em que os pods são preferidos para serem agendados no mesmo nó, às vezes, pode ser exatamente o oposto. Por exemplo, se certos pods forem implementados juntos, eles afetarão o desempenho.
Para antiafinidade de carga de trabalho, quando requiredDuringSchedulingIgnoredDuringExecution é usado, o controlador de acesso padrão LimitPodHardAntiAffinityTopology do Kubernetes exige que topologyKey só possa ser kubernetes.io/hostname. Para usar outra lógica de topologia personalizada, modifique ou desabilite o controlador de acesso.
O seguinte é um exemplo de definição de uma regra de antiafinidade. Essa regra divide os domínios de topologia de nó pelo rótulo kubernetes.io/hostname. Se um pod com o rótulo app=frontend já existir em um nó no domínio de topologia, os pods com o mesmo rótulo não poderão ser agendados para outros nós no domínio de topologia.
apiVersion: apps/v1 kind: Deployment metadata: name: frontend labels: app: frontend spec: selector: matchLabels: app: frontend replicas: 5 template: metadata: labels: app: frontend spec: containers: - image: nginx:alpine name: frontend resources: requests: cpu: 100m memory: 200Mi limits: cpu: 100m memory: 200Mi imagePullSecrets: - name: default-secret affinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - topologyKey: kubernetes.io/hostname # Topology domain of the node labelSelector: # Pod label matching rule matchExpressions: - key: app operator: In values: - frontend
Crie uma regra de antiafinidade e visualize o resultado da implementação. No exemplo, os domínios de topologia de nó são divididos pelo rótulo kubernetes.io/hostname. Os valores de rótulo dos nós com o rótulo kubernetes.io/hostname são diferentes, portanto, há apenas um nó em um domínio de topologia. Se um pod de frontend já existir em um domínio de topologia, os pods com o mesmo rótulo não serão agendados para o domínio de topologia. Neste exemplo, existem apenas quatro nós. Portanto, há um pod que está no estado Pending e não pode ser agendado.
$ kubectl create -f affinity4.yaml deployment.apps/frontend created $ kubectl get po -o wide NAME READY STATUS RESTARTS AGE IP NODE frontend-6f686d8d87-8dlsc 1/1 Running 0 18s 172.16.0.76 192.168.0.100 frontend-6f686d8d87-d6l8p 0/1 Pending 0 18s <none> <none> frontend-6f686d8d87-hgcq2 1/1 Running 0 18s 172.16.0.54 192.168.0.97 frontend-6f686d8d87-q7cfq 1/1 Running 0 18s 172.16.0.47 192.168.0.212 frontend-6f686d8d87-xl8hx 1/1 Running 0 18s 172.16.0.23 192.168.0.94
Descrição do valor do operador
Você pode usar o campo operator para definir o relacionamento lógico da regra de uso. O valor do operator pode ser:
- In: o rótulo do objeto de afinidade ou antiafinidade está na lista de valores do rótulo (campo values).
- NotIn: o rótulo do objeto de afinidade ou antiafinidade não está na lista de valores do rótulo (campo values).
- Exists: o objeto de afinidade ou antiafinidade tem um nome de rótulo especificado.
- DoesNotExist: o objeto de afinidade ou antiafinidade não tem o nome de rótulo especificado.
- Gt: (disponível apenas para afinidade de nó) o valor do rótulo do nó agendado é maior que o valor da lista (comparação de cadeias).
- Lt: (disponível somente para afinidade de nó) o valor do rótulo do nó de agendamento é menor que o valor da lista (comparação de cadeias).