O que é e como configurar affinity e anti-affinity no Kubernetes

Embora o Kubernetes faça um ótimo trabalho em escalonar os pods de um cluster, algumas vezes, e SOMENTE algumas vezes (note o negrito, itálico e sublinhado), precisamos controlar como e onde os pods serão escalonados.

Para resolver esses casos, que são (ou pelo menos deveriam ser) raros de acontecer, utilizamos as funcionalidades de Affinity e Anti-Affinity do Kubernetes.

Ao utilizar essas funcionalidades, podemos decidir em quais tipos de nodes os pods serão escalonados, além de especificar que determinados pods sejam escalonados próximos ou distantes uns dos outros.

Também “dizemos” ao Kubernetes qual é a expectativa sobre as condições definidas no Affinity ou Anti-Affinity. Em outras palavras, precisamos dizer se as condições precisam ser atendidas (modo hard), ou se seria bom que elas fossem atendidas (modo soft).

Para o modo hard, utilizamos a chave requiredDuringSchedulingIgnoreDuringExecution. Já para o modo soft, a chave preferredDuringSchedulingIgnoreDuringExecution.

Note que, embora as duas sejam um “palavrão”, o que muda entre elas é o início da palavra, onde a que obriga que as condições sejam atendidas diz required, e a que gostaria que as condições fossem atendidas diz preferred.

Embora configurar essa funcionalidade para node ou para pod seja muito similar, para não causar confusões, vamos separar os exemplos.

NodeAffinity

Essa configuração é utilizadas para que determinados pods seja escalonados em determinados nodes. Um exemplo de utilização, é garantir que um pod que precise de GPU não seja escalonado em um node que não tenha tal recurso.

spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: gpu
            operator: In
            values:
            - true

Na configuração acima, como utilizamos o modo hard (requiredDuringSchedulingIgnoredDuringExecution), o pod só será escalonado em um node que tenha a label gpu: true.

Caso não seja necessário essa obrigatoriedade, como explicado antes, podemos utilizar o modo soft.

spec:
  affinity:
    nodeAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 10
        preference:
          matchExpressions:
          - key: label-1
            operator: In
            values:
            - key-1
      - weight: 100
        preference:
          matchExpressions:
          - key: label-2
            operator: In
            values:
            - key-2

Diferente do modo hard, no modo soft conseguimos atribuir um peso (weight) para cada uma das condições. No exemplo acima, o Kubernetes dará 10x mais prioridade para a segunda regra (label-2: key-2).

PodAffinity

Quando configuramos o PodAffinity, dizemos ao Kubernetes para escalonar os pods próximos, ou seja, no mesmo node.

Embora pareça a mesma coisa, tenha em mente que, utilizar PodAffinity sem o NodeAffinity, busca que os pods sejam escalonados próximos, mas não obrigatoriamente em um node específico.

spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoreDuringExecution:
        labelSelector:
	  - matchExpressions:
	    - key: app
	      operator: In
	      values: 
	      - cache
	  topologyKey: topology.kubernetes.io/zone

No exemplo acima, falamos para o Kubernetes escalonar o pod próximo à um pod com a label app: cache.

Caso o Kubernetes não encontre um node onde consiga escalonar esse pod, por exemplo por falta de recurso, então o pod não será escalonado em lugar nenhum.

De qualquer forma se os dois Pods tiverem que estar obrigatoriamente no mesmo lugar, coloque seus contêineres no mesmo Pod. Se for apenas preferível que estejam no mesmo lugar, utilize uma afinidade de Pod que seja soft (preferredDuringSchedulingIngoredDuringExecution).

spec:
  affinity:
    podAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 100
        podAffinityTerm:
          labelSelector:
            matchExpressions:
            - key: app
              operator: In
              values:
              - cache
          topologyKey: topology.kubernetes.io/zone

PodAntiAffinity

Ao contrário do podAffinity, o podAntiAffinity faz com que os pods não sejam escalonados no mesmo node.

spec:
  affinity:
    podAntiAffinity:
      requiredDuringSchedulingIgnoreDuringExecution:
        labelSelector:
	- matchExpressions:
	  - key: app
	    operator: In
	    values: 
	    - server
	  topologyKey: topology.kubernetes.io/zone

Assim como no exemplo anterior, caso o Kubernetes não encontre um node onde não tenha um pod com a label app: server, o pod não será escalonado em lugar nenhum.

Lembre-se que, caso seja preferível mas não obrigatório que os pods não estejam no mesmo node, defina a condição no modo soft.

spec:
  affinity:
    podAntiAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 100
        podAffinityTerm:
          labelSelector:
            matchExpressions:
            - key: app
              operator: In
              values:
              - server
          topologyKey: topology.kubernetes.io/zone

Operadores

Seja para nodeAffinity, podAffinity ou podAntiAffinity, além do operador In, podemos utilizar os operadores NotIn, Exists, DoesNotExists, Gt (greater than) e Lt(lesser than).

Utilizar os operadores NotIn e DoesNotExists em funções de afinidade (podAffinity e nodeAffinity), fará com que ela funcione como uma anti-afinidade. De forma alternativa, você poderá utilizar o podAntiAffinity para os pods ou, no caso dos nodes, configurar taints (abordaremos esse assunto em outro post).

Conclusão

Como eu disse no inicio do post, o Kubernetes consegue gerenciar muito bem o escalonamento dos pods dentro de um cluster. No entanto, quando temos pods que necessitam de determinados recursos para serem executados, temos o nodeAffinity para recorrer.

Quando precisamos utilizar podAffinity no modo hard, é recomendado que os contêineres sejam criados dentro do mesmo pod.

Já no caso do podAntiAffinity, só deve ser utilizado caso algum problema tenha sido observado ao ter os pods sendo executados no mesmo node. Caso contrário, deixe o Kubernetes gerenciar o escalonamento dos pods automaticamente.

Por último, se você não quer perder os próximos posts sobre Kubernetes, especialmente o sobre taints, se inscreva na nossa newsletter.

Espero que tenha gostado.

Até a próxima!


Se inscreva na nossa newsletter

* indicates required

Um comentário sobre “O que é e como configurar affinity e anti-affinity no Kubernetes

Deixe uma resposta