Introdução ao Kubernetes e Sua Arquitetura
Kubernetes, frequentemente abreviado como K8s, é uma plataforma de orquestração de containers desenvolvida pelo Google e mantida pela Cloud Native Computing Foundation. Ela resolve um problema fundamental na computação moderna: como gerenciar, escalar e manter aplicações containerizadas em ambientes de produção distribuídos.
A arquitetura do Kubernetes é baseada em um modelo cliente-servidor onde um cluster é dividido em dois componentes principais: o Control Plane (antes chamado de Master) e os Worker Nodes. Essa divisão permite que você execute aplicações de forma resiliente, com alta disponibilidade e escalabilidade automática. Neste artigo, vamos desmontar cada componente e entender como eles trabalham juntos para orquestrar sua infraestrutura containerizada.
Arquitetura Geral do Cluster Kubernetes
A Estrutura Hierárquica
Um cluster Kubernetes funciona como um sistema distribuído onde o Control Plane atua como o "cérebro" do sistema, tomando decisões sobre o estado desejado dos containers, enquanto os Worker Nodes são os "músculos" que executam efetivamente os containers. A comunicação entre eles ocorre através de APIs RESTful e segue um padrão declarativo: você descreve o estado desejado, e o Kubernetes trabalha para alcançá-lo.
A arquitetura é completamente descentralizada em termos de execução. Se um Worker Node falha, o Control Plane detecta a falha e reconstrói os containers em outro nó saudável. Isso é possível porque o Kubernetes mantém um registro do estado desejado e constantemente verifica se a realidade corresponde a esse desejo, em um padrão conhecido como reconciliation loop.
┌─────────────────────────────────────────────────────────────┐
│ KUBERNETES CLUSTER │
├────────────────────┬────────────────────────────────────────┤
│ CONTROL PLANE │ WORKER NODES │
│ (Master Node) │ │
│ │ ┌──────────┐ ┌──────────┐ │
│ ┌──────────────┐ │ │ Node 1 │ │ Node 2 │ │
│ │ API Server │ │ │ ┌──────┐ │ │ ┌──────┐ │ │
│ └──────────────┘ │ │ │Pod A │ │ │ │Pod C │ │ │
│ │ │ ├──────┤ │ │ ├──────┤ │ │
│ ┌──────────────┐ │ │ │Pod B │ │ │ │Pod D │ │ │
│ │ Scheduler │ │ │ └──────┘ │ │ └──────┘ │ │
│ └──────────────┘ │ └──────────┘ └──────────┘ │
│ │ │
│ ┌──────────────┐ │ │
│ │Controller Mgr│ │ │
│ └──────────────┘ │ │
│ │ │
│ ┌──────────────┐ │ │
│ │ etcd (DB) │ │ │
│ └──────────────┘ │ │
└────────────────────┴────────────────────────────────────────┘
O Control Plane: O Cérebro do Cluster
Componentes Principais do Control Plane
O Control Plane é composto por vários componentes que trabalham em conjunto. O API Server é o ponto central de comunicação; toda solicitação ao cluster passa por ele, seja para criar novos recursos ou consultar o estado atual. Ele valida as requisições, autoriza as ações e persiste as mudanças no banco de dados.
O Scheduler é responsável por decidir qual Worker Node executará cada Pod. Ele analisa os requisitos de recursos (CPU, memória), constraints de afinidade e outras políticas para tomar decisões otimizadas. O Controller Manager executa diversos controladores que monitoram o estado do cluster e trabalham continuamente para manter o estado desejado. Por fim, o etcd é um banco de dados chave-valor distribuído que armazena todo o estado do cluster de forma durável.
# Exemplo: Consultando componentes do Control Plane em um cluster real
apiVersion: v1
kind: Pod
metadata:
name: diagnostico-control-plane
namespace: kube-system
spec:
containers:
- name: check-components
image: alpine:3.18
command:
- sh
- -c
- |
echo "=== Verificando componentes do Control Plane ==="
# Em um cluster real, você veria pods como:
# kube-apiserver-master-1
# kube-scheduler-master-1
# kube-controller-manager-master-1
# etcd-master-1
sleep 3600
Como o API Server Funciona
O API Server é a base de toda operação em Kubernetes. Quando você executa kubectl apply, o comando é convertido em uma requisição HTTP para o API Server. Ele valida a sintaxe YAML, verifica permissões (RBAC), aplica mutating webhooks, e finalmente armazena o recurso no etcd.
# Exemplo prático: Criar um Deployment e acompanhar o fluxo
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.25
ports:
- containerPort: 80
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 200m
memory: 256Mi
Quando este YAML é aplicado:
1. O API Server recebe a requisição e valida a estrutura
2. O Scheduler observa que há uma nova Deployment com 3 réplicas
3. O Scheduler atribui cada Pod a um Worker Node específico
4. O kubelet em cada nó recebe a instrução e puxa a imagem Docker
5. O container é iniciado e o etcd registra o estado
O etcd: Persistência do Estado
O etcd é o coração da durabilidade do cluster. Todo recurso criado no Kubernetes é armazenado aqui. É um banco de dados chave-valor distribuído que oferece forte consistência. Nunca perca seus dados do etcd em produção; implemente snapshots regulares e replicação entre múltiplos nós do Control Plane.
# Exemplo: Fazendo backup do etcd (executar no master)
# Nota: Em clusters gerenciados (EKS, GKE, AKS), isso é automatizado
ETCDCTL_API=3 etcdctl snapshot save /backup/etcd-snapshot.db \
--endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key
# Verificando o snapshot
ETCDCTL_API=3 etcdctl snapshot status /backup/etcd-snapshot.db
Worker Nodes: Onde a Ação Acontece
Componentes de um Worker Node
Cada Worker Node é uma máquina (física ou virtual) que executa containers. O componente mais importante é o kubelet, um agente que roda em cada nó e é responsável por garantir que os containers especificados estão rodando. Ele comunica-se constantemente com o API Server para obter as instruções e relata o status.
O kube-proxy gerencia a rede no nó. Ele implementa o Kubernetes Service, criando regras de firewall e redirecionamento de tráfego para que os Pods possam se comunicar entre si e com o mundo exterior. O container runtime é o mecanismo que realmente executa os containers; pode ser Docker, containerd, CRI-O ou outro compatível com a Container Runtime Interface (CRI).
# Exemplo: Inspecionando um Worker Node
apiVersion: v1
kind: Node
metadata:
name: worker-node-1
labels:
kubernetes.io/os: linux
node.kubernetes.io/instance-type: t3.medium
spec:
# Taints são restrições que impedem pods de serem agendados
taints:
- key: dedicated
value: worker
effect: NoSchedule
# Podemos adicionar labels customizados
labels:
disktype: ssd
node-role: compute-heavy
status:
capacity:
cpu: "2"
memory: "4Gi"
pods: "110"
allocatable:
cpu: "1900m"
memory: "3800Mi"
pods: "110"
conditions:
- type: Ready
status: "True"
lastHeartbeatTime: "2024-01-15T10:30:00Z"
O Kubelet: O Agente Local
O kubelet é um daemon que roda em cada nó e sincroniza constantemente com o API Server. Ele recebe especificações de Pods, executa-as através do container runtime, monitora sua saúde e relata status. Se um container falha, o kubelet tenta reiniciá-lo conforme a política de restart.
# Exemplo: Verificar logs do kubelet em um nó
# SSH no worker node
ssh ubuntu@worker-node-1
# Visualizar status do kubelet
sudo systemctl status kubelet
# Ver logs em tempo real
sudo journalctl -u kubelet -f
# Verificar configuração do kubelet
cat /var/lib/kubelet/kubeconfig.conf
# Reiniciar o kubelet (cuidado em produção!)
sudo systemctl restart kubelet
Networking e o kube-proxy
O kube-proxy implementa a abstração de Service do Kubernetes. Quando você cria um Service, o kube-proxy cria regras de iptables (ou IPVS em clusters maiores) que redirecionam o tráfego para os Pods corretos. Isso permite comunicação entre Pods sem conhecer seus IPs específicos.
# Exemplo: Service e como o kube-proxy roteia tráfego
apiVersion: v1
kind: Service
metadata:
name: web-service
spec:
type: ClusterIP
selector:
app: web
ports:
- protocol: TCP
port: 80
targetPort: 8080
---
apiVersion: v1
kind: Pod
metadata:
name: web-pod-1
labels:
app: web
spec:
containers:
- name: web
image: nginx:1.25
ports:
- containerPort: 8080
Quando o Service é criado, o kube-proxy em cada nó cria regras que fazem com que requisições para web-service:80 sejam redirecionadas para um dos Pods rotulados com app: web na porta 8080.
Comunicação Entre Componentes
O Fluxo de uma Requisição do Usuário
Quando você executa kubectl apply -f deployment.yaml, aqui está o que acontece internamente. O kubectl envia a requisição YAML para o API Server no Control Plane. O API Server valida a requisição, aplica políticas de admissão e armazena o recurso no etcd. Simultaneamente, o Deployment Controller observa a mudança e cria ReplicaSets conforme especificado. O ReplicaSet Controller cria Pods conforme a réplica desejada. O Scheduler observa os Pods pendentes e atribui-os aos Worker Nodes. Por fim, o kubelet em cada nó detecta que há um Pod assignado a ele e executa o container.
# Exemplo: Acompanhar este fluxo em tempo real
# Terminal 1: Observar eventos do cluster
kubectl get events --all-namespaces --watch
# Terminal 2: Criar uma deployment
kubectl apply -f deployment.yaml
# Terminal 3: Observar o status dos pods
kubectl get pods --watch
# Você verá a progressão: Pending → ContainerCreating → Running
Heartbeat e Health Checks
O kubelet periodicamente envia informações sobre o nó e os Pods para o API Server. Se um nó não enviar heartbeat por mais de alguns minutos, o Control Plane marca-o como NotReady e começa a reagendar seus Pods em outros nós. Dentro dos Pods, você pode configurar liveness probes para detectar aplicações travadas e restart probes para recuperar-se de falhas.
# Exemplo: Configurar health checks em um Pod
apiVersion: v1
kind: Pod
metadata:
name: app-com-health-checks
spec:
containers:
- name: app
image: my-app:1.0
ports:
- containerPort: 8000
# Liveness probe: reinicia o container se falhar
livenessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 2
failureThreshold: 3
# Readiness probe: remove do tráfego se falhar
readinessProbe:
httpGet:
path: /ready
port: 8000
initialDelaySeconds: 3
periodSeconds: 5
# Startup probe: aguarda aplicação iniciar
startupProbe:
httpGet:
path: /startup
port: 8000
failureThreshold: 30
periodSeconds: 10
Multi-Master: Alta Disponibilidade do Control Plane
Configuração de Redundância
Em ambientes de produção, nunca execute um Control Plane com apenas um nó. Se aquele nó falhar, todo seu cluster fica indisponível. A melhor prática é ter no mínimo 3 nós de Control Plane para garantir quórum em caso de falhas. Todos os nós do Control Plane executam os mesmos componentes, mas o etcd é replicado entre eles para garantir consistência.
# Exemplo: Verificar nós do Control Plane em um cluster
# Execute: kubectl get nodes -L node-role.kubernetes.io/control-plane
# Saída esperada:
# NAME STATUS ROLES AGE
# master-1 Ready control-plane,master 30d
# master-2 Ready control-plane,master 30d
# master-3 Ready control-plane,master 30d
# worker-1 Ready <none> 25d
# worker-2 Ready <none> 25d
Load Balancing do API Server
Para que os clientes (como kubectl) se conectem ao Control Plane sem conhecer qual nó master está saudável, você precisa de um load balancer na frente dos API Servers. Em ambientes cloud, use os load balancers nativos (AWS ELB, Azure Load Balancer, etc.). Em on-premise, considere HAProxy ou similares.
# Exemplo: Configuração de HAProxy para load balancer do K8s API
# /etc/haproxy/haproxy.cfg
global
log /dev/log local0
maxconn 4096
defaults
mode tcp
timeout connect 10s
timeout client 30s
timeout server 30s
frontend kubernetes-api
bind 10.0.0.10:6443
mode tcp
default_backend kubernetes-api-backend
backend kubernetes-api-backend
mode tcp
balance roundrobin
server master-1 10.0.0.11:6443 check
server master-2 10.0.0.12:6443 check
server master-3 10.0.0.13:6443 check
# Então, todos os clientes se conectam a 10.0.0.10:6443
# E o HAProxy roteia para o master disponível
Conclusão
Dominar a arquitetura do Kubernetes é fundamental para operar clusters em produção. Os três pontos principais que você deve levar para casa são:
-
A dualidade Control Plane e Worker Nodes: O Control Plane é o "gerenciador" que toma decisões sobre o que deve rodar e como deve rodar, enquanto os Worker Nodes são os executores que realmente mantêm os containers funcionando. A comunicação entre eles segue um padrão declarativo onde você descreve o estado desejado.
-
Reconciliation e resiliência contínua: O Kubernetes não é um sistema "fire and forget". Ele constantemente verifica se a realidade corresponde ao desejo declarado e corrige desvios. Se um Pod falha, ele é reiniciado. Se um nó morre, seus Pods são reagendados. Esta natureza auto-recuperável é o que torna o Kubernetes poderoso.
-
Alta disponibilidade no design: De múltiplos nós de Control Plane ao etcd replicado e kubelet robusto, Kubernetes foi construído para tolerância a falhas. Compreender essas camadas de redundância permite que você construa sistemas realmente resilientes que continuam funcionando mesmo com falhas parciais.