Kubernetes: boas práticas
Esta aula aborda boas práticas essenciais para operar Kubernetes em produção: health checks (liveness/readiness), limites de recursos, uso de namespaces e segurança básica. Você aprenderá a configurar cada aspecto com exemplos práticos em YAML e comandos kubectl.
Nesta aula, vamos explorar as boas práticas fundamentais para operar clusters Kubernetes de forma confiável e segura. Esses conceitos são essenciais para qualquer profissional de DevOps que deseja executar cargas de trabalho em produção, pois ajudam a garantir a disponibilidade, eficiência e proteção dos seus serviços.
Vamos nos aprofundar em quatro pilares: health checks, limites de recursos, namespaces e segurança básica. Cada um desses tópicos desempenha um papel crítico na operação diária de um cluster, e dominá-los permitirá que você evite falhas comuns e otimize o uso da infraestrutura.
Health checks (liveness/readiness)
Health checks são sondagens que o kubelet realiza para determinar o estado dos contêineres em execução. Eles são essenciais para que o Kubernetes possa tomar decisões automáticas sobre reinicialização, roteamento de tráfego e disponibilidade do serviço. Existem dois tipos principais: liveness e readiness.
O liveness probe indica se o contêiner está vivo ou se precisa ser reiniciado. Se a sonda falhar, o kubelet mata o contêiner e o reinicia conforme a política de restart. O readiness probe indica se o contêiner está pronto para receber tráfego. Se falhar, o contêiner é removido dos endpoints dos Services, mas não é reiniciado. Isso é crucial para aplicações que demoram a inicializar ou que dependem de recursos externos.
Exemplo de configuração em um Deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
name: minha-app
spec:
replicas: 3
selector:
matchLabels:
app: minha-app
template:
metadata:
labels:
app: minha-app
spec:
containers:
- name: minha-app
image: nginx:1.25
ports:
- containerPort: 80
livenessProbe:
httpGet:
path: /healthz
port: 80
initialDelaySeconds: 3
periodSeconds: 3
readinessProbe:
httpGet:
path: /ready
port: 80
initialDelaySeconds: 5
periodSeconds: 5
As sondas podem ser do tipo httpGet, tcpSocket ou exec. A escolha depende do tipo de aplicação. Para aplicações web, httpGet é comum; para servidores TCP, tcpSocket; e para aplicações que não expõem portas, exec executa um comando dentro do contêiner. Ajuste os parâmetros initialDelaySeconds, periodSeconds, timeoutSeconds e failureThreshold conforme a necessidade da sua aplicação.
Limites de recursos
Definir limites de recursos (CPU e memória) é uma prática crucial para evitar que um único contêiner consuma todos os recursos do nó, causando instabilidade no cluster. O Kubernetes permite especificar requests (o mínimo garantido) e limits (o máximo permitido).
Os requests são usados pelo scheduler para decidir em qual nó colocar o pod, garantindo que o nó tenha capacidade suficiente. Os limits impõem um teto de consumo; se um contêiner exceder o limite de CPU, ele é throttled (limitado), e se exceder o limite de memória, pode ser encerrado (OOMKilled).
Exemplo de definição de recursos:
apiVersion: v1
kind: Pod
metadata:
name: pod-com-recursos
spec:
containers:
- name: app
image: busybox
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
É importante monitorar o uso real e ajustar esses valores. Ferramentas como kubectl top e o Metrics Server ajudam a visualizar o consumo. Definir apenas limites sem requests pode causar problemas de escalonamento; defina ambos sempre que possível.
Namespaces
Namespaces são uma forma de particionar recursos do cluster em grupos lógicos. Eles são usados para separar ambientes (dev, staging, prod), equipes ou projetos, permitindo políticas de acesso e quotas de recursos específicas.
Criar namespaces é simples, mas seu poder está na organização e no controle. Você pode aplicar ResourceQuotas para limitar o consumo total de recursos em um namespace, e NetworkPolicies para controlar o tráfego de rede entre pods. Além disso, o RBAC (Role-Based Access Control) pode ser configurado por namespace para dar permissões granulares.
Comandos úteis:
kubectl create namespace dev
kubectl get namespaces
kubectl config set-context --current --namespace=dev
Exemplo de ResourceQuota:
apiVersion: v1
kind: ResourceQuota
metadata:
name: quota-dev
namespace: dev
spec:
hard:
requests.cpu: "4"
requests.memory: 8Gi
limits.cpu: "8"
limits.memory: 16Gi
Use namespaces para isolar ambientes e equipes, mas lembre-se de que alguns recursos (como Nodes e PersistentVolumes) são globais e não podem ser limitados por namespace.
Segurança básica
Segurança em Kubernetes é um tópico amplo, mas algumas práticas básicas são indispensáveis: executar contêineres como usuário não root, evitar privilégios desnecessários, usar imagens confiáveis e aplicar políticas de segurança.
O securityContext permite definir configurações de segurança no nível do pod ou do contêiner, como runAsNonRoot, readOnlyRootFilesystem e capabilities. Exemplo:
apiVersion: v1
kind: Pod
metadata:
name: pod-seguro
spec:
securityContext:
runAsNonRoot: true
containers:
- name: app
image: minha-imagem
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
Além disso, é recomendável usar NetworkPolicies para restringir o tráfego de rede, e PodSecurityAdmission (ou o antigo PodSecurityPolicy) para aplicar políticas de segurança em nível de cluster. Sempre escaneie suas imagens por vulnerabilidades e use registries privados com autenticação.
Referências
- Kubernetes: Configure Liveness, Readiness and Startup Probes
- Kubernetes: Managing Resources for Containers
- Kubernetes: Namespaces
- Kubernetes: Pod Security Standards
- Kubernetes: Security Overview
- Kubernetes: Declare Network Policy
Exercícios
Você tem um Deployment chamado
webcom uma aplicação que escuta na porta 8080. Adicione um liveness probe que faça um GET em/healtha cada 5 segundos, com um atraso inicial de 10 segundos e timeout de 1 segundo. Escreva o trecho YAML correspondente.✓ Resposta:livenessProbe: httpGet: path: /health port: 8080 initialDelaySeconds: 10 periodSeconds: 5 timeoutSeconds: 1Defina um pod com requests de 256Mi de memória e 0.5 CPU, e limites de 512Mi de memória e 1 CPU. Escreva o YAML do pod.
✓ Resposta:apiVersion: v1 kind: Pod metadata: name: pod-recursos spec: containers: - name: app image: nginx resources: requests: memory: "256Mi" cpu: "500m" limits: memory: "512Mi" cpu: "1"Crie um namespace chamado
prode aplique uma ResourceQuota que limite o total de requests de CPU a 2 e de memória a 4Gi. Escreva os comandos kubectl e o YAML da quota.✓ Resposta:kubectl create namespace prodapiVersion: v1 kind: ResourceQuota metadata: name: quota-prod namespace: prod spec: hard: requests.cpu: "2" requests.memory: 4GiEscreva um securityContext para um contêiner que rode como usuário não root, com sistema de arquivos raiz somente leitura e que descarte todas as capacidades Linux.
✓ Resposta:securityContext: runAsNonRoot: true readOnlyRootFilesystem: true capabilities: drop: ["ALL"]Explique a diferença entre liveness e readiness probes e em qual cenário você usaria cada um.
✓ Resposta:Liveness probe determina se o contêiner está vivo; se falhar, o contêiner é reiniciado. Use para detectar deadlocks ou estados irrecuperáveis. Readiness probe determina se o contêiner está pronto para receber tráfego; se falhar, o pod é removido dos endpoints do Service, mas não é reiniciado. Use para aplicações que precisam de tempo de inicialização ou dependem de serviços externos.