Docker & Kubernetes

Node Affinity, Taints e Tolerations em Kubernetes: Do Básico ao Avançado

12 min de leitura

Node Affinity, Taints e Tolerations em Kubernetes: Do Básico ao Avançado

Node Affinity: Controlando a Colocação de Pods Node Affinity é um mecanismo que permite especificar regras para influenciar em qual nó (node) um pod será escalonado. Diferentemente de NodeSelector, que é mais simples e limitado, Node Affinity oferece operadores mais sofisticados como , , , , e , permitindo uma granularidade muito maior no controle de alocação de recursos. O conceito funciona em dois níveis: (obrigatório no momento do escalonamento) e (preferência, sem garantia). A primeira garante que o pod só será alocado se as regras forem atendidas; a segunda tenta atender as regras, mas permite alocação em nós que não as atendem se necessário. Exemplo Prático: Afinidade Obrigatória Você possui nós com labels específicos e precisa que certos pods rodem apenas em nós GPU. Primeiro, você rotula os nós: Em seguida, cria um pod com afinidade obrigatória: Este manifesto garante que o pod será escalonado apenas em nós com o label . Se nenhum nó atender ao critério, o

<h2>Node Affinity: Controlando a Colocação de Pods</h2>

<p>Node Affinity é um mecanismo que permite especificar regras para influenciar em qual nó (node) um pod será escalonado. Diferentemente de NodeSelector, que é mais simples e limitado, Node Affinity oferece operadores mais sofisticados como <code>In</code>, <code>NotIn</code>, <code>Exists</code>, <code>DoesNotExist</code>, <code>Gt</code> e <code>Lt</code>, permitindo uma granularidade muito maior no controle de alocação de recursos.</p>

<p>O conceito funciona em dois níveis: <code>requiredDuringSchedulingIgnoredDuringExecution</code> (obrigatório no momento do escalonamento) e <code>preferredDuringSchedulingIgnoredDuringExecution</code> (preferência, sem garantia). A primeira garante que o pod só será alocado se as regras forem atendidas; a segunda tenta atender as regras, mas permite alocação em nós que não as atendem se necessário.</p>

<h3>Exemplo Prático: Afinidade Obrigatória</h3>

<p>Você possui nós com labels específicos e precisa que certos pods rodem apenas em nós GPU. Primeiro, você rotula os nós:</p>

<pre><code class="language-bash">kubectl label nodes worker-1 gpu=true

kubectl label nodes worker-2 disktype=ssd</code></pre>

<p>Em seguida, cria um pod com afinidade obrigatória:</p>

<pre><code class="language-yaml">apiVersion: v1

kind: Pod

metadata:

name: gpu-intensive-app

spec:

affinity:

nodeAffinity:

requiredDuringSchedulingIgnoredDuringExecution:

nodeSelectorTerms:

  • matchExpressions:
  • key: gpu

operator: In

values:

  • &quot;true&quot;

containers:

  • name: app

image: nvidia/cuda:11.0-base

resources:

limits:

nvidia.com/gpu: 1</code></pre>

<p>Este manifesto garante que o pod será escalonado apenas em nós com o label <code>gpu=true</code>. Se nenhum nó atender ao critério, o pod permanece em estado <code>Pending</code>.</p>

<h3>Exemplo Prático: Afinidade com Preferência</h3>

<p>Quando você quer que o pod prefira nós SSD, mas aceita nós sem essa característica se necessário:</p>

<pre><code class="language-yaml">apiVersion: v1

kind: Pod

metadata:

name: database-pod

spec:

affinity:

nodeAffinity:

preferredDuringSchedulingIgnoredDuringExecution:

  • weight: 100

preference:

matchExpressions:

  • key: disktype

operator: In

values:

  • ssd
  • weight: 50

preference:

matchExpressions:

  • key: zone

operator: In

values:

  • us-east-1a

containers:

  • name: db

image: postgres:14</code></pre>

<p>O peso (weight) determina a prioridade: nós com <code>disktype=ssd</code> recebem 100 pontos, enquanto nós na zona <code>us-east-1a</code> recebem 50. O escalonador soma os pesos e escolhe o nó com maior pontuação.</p>

<p>---</p>

<h2>Taints: Repelindo Pods de Nós</h2>

<p>Taints (manchas) são propriedades aplicadas a nós que repelem pods, a menos que esses pods tenham tolerações correspondentes. Enquanto Node Affinity atrai pods para nós, Taints fazem o oposto: afastam pods. Um caso de uso comum é reservar nós para workloads específicas (como GPU, storage de alta performance) ou para tarefas de administração do cluster.</p>

<p>Um taint é composto por três componentes: <code>key</code>, <code>value</code> e <code>effect</code>. O <code>effect</code> pode ser <code>NoSchedule</code> (não agenda), <code>NoExecute</code> (não executa e remove pods existentes) ou <code>PreferNoSchedule</code> (evita, mas permite se necessário).</p>

<h3>Aplicando Taints em Nós</h3>

<p>Suponha que você tem um nó dedicado exclusivamente para máquinas virtuais críticas. Você aplica um taint:</p>

<pre><code class="language-bash">kubectl taint nodes worker-gpu gpu=true:NoSchedule</code></pre>

<p>Este comando adiciona um taint <code>gpu=true</code> com efeito <code>NoSchedule</code>. A partir deste momento, nenhum pod será escalonado neste nó a menos que tenha uma toleração correspondente.</p>

<p>Para listar taints de um nó:</p>

<pre><code class="language-bash">kubectl describe node worker-gpu | grep Taints</code></pre>

<p>Para remover um taint, adicione um hífen no final:</p>

<pre><code class="language-bash">kubectl taint nodes worker-gpu gpu=true:NoSchedule-</code></pre>

<h3>Taints com Efeito NoExecute</h3>

<p>O efeito <code>NoExecute</code> é mais agressivo: remove pods já em execução que não toleram o taint. Ideal para situações de manutenção ou quando você precisa liberar recursos imediatamente:</p>

<pre><code class="language-bash">kubectl taint nodes worker-old os=deprecated:NoExecute</code></pre>

<p>Qualquer pod sem toleração para este taint será removido do nó. Pods com toleração podem permanecer, respeitando <code>tolerationSeconds</code> se definido.</p>

<p>---</p>

<h2>Tolerations: Permitindo Pods em Nós com Taints</h2>

<p>Tolerations são definidas nos specs de pods e permitem que eles sejam escalonados em nós com taints específicos. Uma toleração não atrai o pod para um nó (isso é tarefa do Node Affinity), mas remove a restrição imposta pelo taint, permitindo alocação.</p>

<p>Uma toleração é composta por <code>key</code>, <code>operator</code>, <code>value</code> e <code>effect</code>, e opcionalmente <code>tolerationSeconds</code>. O <code>operator</code> pode ser <code>Equal</code> (valor exato) ou <code>Exists</code> (chave existe, independente do valor).</p>

<h3>Exemplo: Pod com Toleração para GPU</h3>

<p>Você deseja que um pod de treinamento de IA rode em um nó com taint de GPU:</p>

<pre><code class="language-yaml">apiVersion: v1

kind: Pod

metadata:

name: ml-training

spec:

tolerations:

  • key: gpu

operator: Equal

value: &quot;true&quot;

effect: NoSchedule

containers:

  • name: trainer

image: tensorflow/tensorflow:latest-gpu</code></pre>

<p>Este pod tolera o taint <code>gpu=true:NoSchedule</code>. Se esse nó estiver disponível (e preferível via Node Affinity), o pod será escalonado. Sem a toleração, seria rejeitado.</p>

<h3>Toleração Genérica com Exists</h3>

<p>Às vezes, você quer aceitar qualquer valor para uma chave específica:</p>

<pre><code class="language-yaml">apiVersion: v1

kind: Pod

metadata:

name: flexible-app

spec:

tolerations:

  • key: workload-type

operator: Exists

effect: NoSchedule

containers:

  • name: app

image: my-app:latest</code></pre>

<p>Este pod tolera qualquer taint com chave <code>workload-type</code>, independente do valor. Útil quando você tem múltiplas variações de um taint e quer cobertura genérica.</p>

<h3>Toleração com Duração: NoExecute</h3>

<p>Para nós em manutenção, você quer remover pods, mas dando tempo para graceful shutdown:</p>

<pre><code class="language-yaml">apiVersion: v1

kind: Pod

metadata:

name: temporary-app

spec:

tolerations:

  • key: maintenance

operator: Equal

value: &quot;scheduled&quot;

effect: NoExecute

tolerationSeconds: 300

containers:

  • name: app

image: my-app:latest</code></pre>

<p>Se o taint <code>maintenance=scheduled:NoExecute</code> for aplicado, o pod tem 300 segundos (5 minutos) para encerrar gracefully antes de ser forçadamente removido.</p>

<p>---</p>

<h2>Combinando Affinity, Taints e Tolerations: Um Cenário Real</h2>

<p>Na prática, você combina esses três conceitos para criar arquiteturas robustas e eficientes. Considere um cluster com nós de diferentes tipos: padrão, GPU e armazenamento em SSD. Você quer:</p>

<ol>

<li>Nós GPU exclusivos para workloads que usam GPU</li>

<li>Nós SSD preferidos para banco de dados</li>

<li>Nós padrão para aplicações gerais</li>

</ol>

<p>Primeiro, você rotula e taint os nós:</p>

<pre><code class="language-bash"># Nós GPU

kubectl label nodes gpu-1 node-type=gpu

kubectl taint nodes gpu-1 workload-type=gpu:NoSchedule

Nós SSD

kubectl label nodes ssd-1 node-type=ssd

kubectl taint nodes ssd-1 workload-type=ssd:NoSchedule

Nós padrão (sem taints)

kubectl label nodes standard-1 node-type=standard</code></pre>

<p>Agora você cria um deployment para um treinamento de IA que <strong>exige</strong> GPU e <strong>prefere</strong> estar em um nó dedicado:</p>

<pre><code class="language-yaml">apiVersion: apps/v1

kind: Deployment

metadata:

name: ml-training-job

spec:

replicas: 1

selector:

matchLabels:

app: ml-training

template:

metadata:

labels:

app: ml-training

spec:

affinity:

nodeAffinity:

requiredDuringSchedulingIgnoredDuringExecution:

nodeSelectorTerms:

  • matchExpressions:
  • key: node-type

operator: In

values:

  • gpu

preferredDuringSchedulingIgnoredDuringExecution:

  • weight: 100

preference:

matchExpressions:

  • key: workload-type

operator: In

values:

  • gpu

tolerations:

  • key: workload-type

operator: Equal

value: gpu

effect: NoSchedule

containers:

  • name: trainer

image: tensorflow/tensorflow:latest-gpu

resources:

limits:

nvidia.com/gpu: 1</code></pre>

<p>E um deployment para PostgreSQL que <strong>prefere</strong> SSD e <strong>tolera</strong> o taint correspondente:</p>

<pre><code class="language-yaml">apiVersion: apps/v1

kind: Deployment

metadata:

name: postgres-db

spec:

replicas: 1

selector:

matchLabels:

app: database

template:

metadata:

labels:

app: database

spec:

affinity:

nodeAffinity:

preferredDuringSchedulingIgnoredDuringExecution:

  • weight: 100

preference:

matchExpressions:

  • key: node-type

operator: In

values:

  • ssd

tolerations:

  • key: workload-type

operator: Equal

value: ssd

effect: NoSchedule

containers:

  • name: postgres

image: postgres:14

env:

  • name: POSTGRES_PASSWORD

value: secret123

volumeMounts:

  • name: data

mountPath: /var/lib/postgresql/data

volumes:

  • name: data

emptyDir: {}</code></pre>

<p>O treinamento de IA <strong>deve</strong> ir para GPU (required affinity) e <strong>tolera</strong> o taint. O PostgreSQL <strong>prefere</strong> SSD (preferred affinity) e <strong>tolera</strong> o taint, mas pode rodar em nós padrão se necessário. Aplicações genéricas sem tolerações não rodam em nós com taints.</p>

<p>---</p>

<h2>Conclusão</h2>

<p>Dominar Node Affinity, Taints e Tolerations é fundamental para otimizar o uso de recursos em Kubernetes. <strong>Node Affinity permite atrair pods para nós específicos</strong> através de labels e regras de afinidade (obrigatórias ou preferenciais), enquanto <strong>Taints criam barreiras repelindo pods</strong>, garantindo que nós especializados sejam usados apenas para suas finalidades. <strong>Tolerations são o mecanismo que permite exceções</strong>, permitindo que pods autorizados rodem em nós com taints. Juntos, esses recursos criam um sistema de controle fino sobre alocação de workloads, essencial em clusters heterogêneos com requisitos de hardware variados.</p>

<p>---</p>

<h2>Referências</h2>

<ul>

<li><a href="https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/" target="_blank" rel="noopener noreferrer">Kubernetes Official Documentation - Assigning Pods to Nodes</a></li>

<li><a href="https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/" target="_blank" rel="noopener noreferrer">Kubernetes Official Documentation - Taints and Tolerations</a></li>

<li><a href="https://kubernetes.io/docs/concepts/scheduling-eviction/pod-priority-preemption/" target="_blank" rel="noopener noreferrer">Kubernetes Official Documentation - Pod Priority and Preemption</a></li>

<li><a href="https://linuxacademy.com/" target="_blank" rel="noopener noreferrer">Linux Academy - Kubernetes Advanced Scheduling</a></li>

<li><a href="https://www.kubernetes.book/" target="_blank" rel="noopener noreferrer">The Kubernetes Book by Nigel Poulton</a></li>

</ul>

<p>&lt;!-- FIM --&gt;</p>

Comentários

Mais em Docker & Kubernetes

Dominando Custom Resource Definitions: Estendendo a API do Kubernetes em Projetos Reais
Dominando Custom Resource Definitions: Estendendo a API do Kubernetes em Projetos Reais

O que são Custom Resource Definitions (CRDs)? Custom Resource Definitions são...

Redes em Docker: bridge, host, overlay e macvlan na Prática na Prática
Redes em Docker: bridge, host, overlay e macvlan na Prática na Prática

Entendendo as Redes no Docker: Uma Perspectiva Prática Quando você começa a t...

Como Usar Container Registry Privado: Harbor, GHCR e AWS ECR na Prática em Produção
Como Usar Container Registry Privado: Harbor, GHCR e AWS ECR na Prática em Produção

Introdução: Por Que Um Container Registry Privado? Quando você trabalha com c...