<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:
- "true"
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: "true"
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: "scheduled"
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><!-- FIM --></p>