16 de março de 2026 • 20 min de leitura
Amazon EKS Arquitetura e Primeiros Passos
Containers trouxeram agilidade e padronização para o desenvolvimento moderno, mas orquestrar workloads em escala exige muito mais do que apenas subir pods. No contexto da AWS, o Amazon EKS simplifica a operação do Kubernetes ao oferecer um control plane totalmente gerenciado, altamente disponível e integrado ao ecossistema da nuvem.
Executar Kubernetes em produção vai muito além de criar um cluster e subir aplicações. É preciso entender como o control plane se comunica com os nós, como o modelo de rede influencia a comunicação entre pods e serviços, e quais decisões arquiteturais impactam segurança, custo e escalabilidade. No contexto da AWS, o Amazon EKS remove a complexidade de gerenciar o plano de controle, mas ainda exige escolhas conscientes sobre VPC, IAM, node groups, add-ons e observabilidade.
Neste artigo, você vai aprender:
- Como o Amazon EKS funciona internamente
- Arquitetura do control plane e data plane
- Tipos de nodes e quando usar cada um
- Plugins essenciais (VPC CNI, CoreDNS, kube-proxy)
- Modelos de autenticação (aws-auth vs Access Entries)
- Pod Identity para acesso a serviços AWS
- Rede no EKS: como pods se comunicam
- Observabilidade e logging
- Evoluções recentes: Karpenter e EKS Auto Mode
No final, você terá uma compreensão profunda de como o EKS funciona e como tomar decisões arquiteturais informadas.
Arquitetura do Amazon EKS
Control Plane vs Data Plane
O EKS separa claramente duas responsabilidades:
Control Plane (gerenciado pela AWS):
- API Server (kube-apiserver)
- Scheduler (kube-scheduler)
- Controller Manager (kube-controller-manager)
- etcd (armazenamento de estado)
- Cloud Controller Manager (integração AWS)
Data Plane (gerenciado por você):
- Worker nodes (EC2, Fargate, ou híbridos)
- Pods e containers
- Kubelet e kube-proxy
- Container runtime (containerd)
- Add-ons e plugins
Como funciona o Control Plane
O control plane do EKS roda em uma VPC gerenciada pela AWS, completamente isolada da sua conta:
Características do Control Plane:
- Multi-AZ por padrão: Distribuído em pelo menos 2 AZs
- Altamente disponível: SLA de 99.95%
- Versionado: Você escolhe a versão do Kubernetes
- Atualizado pela AWS: Patches de segurança automáticos
- Isolado: Não consome recursos da sua conta
Comunicação Control Plane ↔ Data Plane
A comunicação acontece através de ENIs (Elastic Network Interfaces) criadas na sua VPC:
- Cluster Endpoint: URL pública ou privada para acessar o API Server
- ENIs Cross-Account: Interfaces de rede que conectam o control plane aos nodes
- Security Groups: Controlam o tráfego entre control plane e nodes
Modos de acesso ao cluster:
- Public: API Server acessível pela internet (com controle de IPs)
- Private: API Server acessível apenas de dentro da VPC
- Public and Private: Ambos habilitados (recomendado)
# Ver configuração de endpoint
aws eks describe-cluster --name meu-cluster \
--query 'cluster.resourcesVpcConfig'
# Resultado:
{
"subnetIds": ["subnet-abc", "subnet-def"],
"securityGroupIds": ["sg-123"],
"clusterSecurityGroupId": "sg-456",
"vpcId": "vpc-789",
"endpointPublicAccess": true,
"endpointPrivateAccess": true,
"publicAccessCidrs": ["0.0.0.0/0"]
}Tipos de Nodes
O EKS suporta três tipos de nodes, cada um com características específicas:
1. Managed Node Groups (EC2)
O que é: Grupos de instâncias EC2 gerenciados pelo EKS.
Como funciona:
- EKS cria e gerencia Auto Scaling Groups
- Nodes são automaticamente registrados no cluster
- Atualizações podem ser automatizadas
- Integração nativa com AWS Systems Manager
Quando usar:
- Workloads stateful (bancos de dados, caches)
- Aplicações que precisam de GPU
- Controle sobre tipo de instância
- Necessidade de acesso SSH aos nodes
Limitações:
- Você paga pelas instâncias EC2 (mesmo ociosas)
- Precisa gerenciar capacidade
- Startup time de ~2-3 minutos
Criando via CLI:
aws eks create-nodegroup \
--cluster-name meu-cluster \
--nodegroup-name workers \
--subnets subnet-abc subnet-def \
--instance-types t3.medium \
--scaling-config minSize=2,maxSize=10,desiredSize=3 \
--disk-size 20 \
--node-role arn:aws:iam::123456789012:role/EKSNodeRole2. AWS Fargate
O que é: Serverless compute para Kubernetes. Você não gerencia nodes.
Como funciona:
- Cada pod roda em uma micro-VM isolada
- Recursos alocados sob demanda
- Cobrança por vCPU e memória consumidos
- Sem acesso ao node subjacente
Quando usar:
- Workloads stateless (APIs, workers)
- Ambientes de desenvolvimento/staging
- Cargas de trabalho com padrões previsíveis
- Quando você quer zero gerenciamento de nodes
Limitações:
- Não suporta DaemonSets
- Não suporta privileged containers
- Startup time de ~30-60 segundos
Configurando Fargate Profile:
aws eks create-fargate-profile \
--cluster-name meu-cluster \
--fargate-profile-name app-profile \
--pod-execution-role-arn arn:aws:iam::123456789012:role/FargatePodRole \
--subnets subnet-abc subnet-def \
--selectors namespace=production,labels={app=api}Como funciona o scheduling:
# Este pod será agendado no Fargate
apiVersion: v1
kind: Pod
metadata:
name: api-pod
namespace: production
labels:
app: api
spec:
containers:
- name: app
image: myapp:latest
resources:
requests:
cpu: 500m
memory: 1Gi3. Self-Managed Nodes
O que é: Instâncias EC2 que você cria e registra manualmente no cluster.
Como funciona:
- Você cria as instâncias EC2
- Instala kubelet e registra no cluster
- Controle total sobre configuração
Quando usar:
- Requisitos muito específicos de OS
- Instâncias spot com lógica customizada
- Integração com ferramentas próprias
- Ambientes híbridos (on-premises)
Limitações:
- Mais trabalho operacional
- Você gerencia tudo (AMI, patches, updates)
- Sem integração automática com EKS
Plugins essenciais do EKS
O EKS vem com três add-ons críticos que fazem o cluster funcionar:
1. Amazon VPC CNI
O que faz: Gerencia a rede dos pods, atribuindo IPs da VPC diretamente aos pods.
Como funciona:
Características:
- Pods recebem IPs reais da VPC
- Comunicação direta entre pods sem NAT
- Integração nativa com Security Groups
- Suporta Network Policies
Limitações importantes:
- Número de pods por node limitado por ENIs e IPs secundários
- Consome IPs da sua VPC rapidamente
- Planeje bem o CIDR da VPC
Exemplo de limites:
| Tipo de Instância | ENIs | IPs por ENI | Pods máximos |
|---|---|---|---|
| t3.small | 3 | 4 | 11 |
| t3.medium | 3 | 6 | 17 |
| t3.large | 3 | 12 | 35 |
| m5.large | 3 | 10 | 29 |
| m5.xlarge | 4 | 15 | 58 |
Configurações importantes:
# Ver configuração do CNI
kubectl get daemonset aws-node -n kube-system -o yaml
# Variáveis de ambiente importantes:
# ENABLE_PREFIX_DELEGATION: Aumenta pods por node
# WARM_IP_TARGET: IPs pré-alocados
# MINIMUM_IP_TARGET: Mínimo de IPs disponíveis2. CoreDNS
O que faz: Resolve nomes DNS dentro do cluster.
Como funciona:
- Deployment com 2 réplicas por padrão
- Resolve
service.namespace.svc.cluster.local - Cache de resoluções DNS
- Integração com Route 53 para DNS externo
Exemplo de resolução:
# Dentro de um pod
nslookup api-service.production.svc.cluster.local
# Resultado:
Server: 10.100.0.10
Address: 10.100.0.10:53
Name: api-service.production.svc.cluster.local
Address: 10.100.45.123Configuração:
# Ver configuração do CoreDNS
kubectl get configmap coredns -n kube-system -o yaml
# Escalar CoreDNS
kubectl scale deployment coredns -n kube-system --replicas=33. kube-proxy
O que faz: Gerencia regras de rede para Services.
Como funciona:
- DaemonSet rodando em cada node
- Cria regras iptables/ipvs para roteamento
- Implementa load balancing entre pods
Modos de operação:
- iptables (padrão): Usa iptables para roteamento
- ipvs: Mais performático para muitos services
# Ver modo do kube-proxy
kubectl get configmap kube-proxy-config -n kube-system -o yaml
# Trocar para ipvs
kubectl edit configmap kube-proxy-config -n kube-system
# Alterar mode: "ipvs"
# Reiniciar kube-proxy
kubectl rollout restart daemonset kube-proxy -n kube-systemAutenticação e Autorização
O EKS oferece dois modelos para gerenciar acesso ao cluster:
Modelo Legado: aws-auth ConfigMap
Como funciona:
O ConfigMap aws-auth mapeia IAM roles/users para grupos do Kubernetes:
apiVersion: v1
kind: ConfigMap
metadata:
name: aws-auth
namespace: kube-system
data:
mapRoles: |
- rolearn: arn:aws:iam::123456789012:role/EKSNodeRole
username: system:node:{{EC2PrivateDNSName}}
groups:
- system:bootstrappers
- system:nodes
- rolearn: arn:aws:iam::123456789012:role/DevTeamRole
username: dev-user
groups:
- developers
mapUsers: |
- userarn: arn:aws:iam::123456789012:user/admin
username: admin
groups:
- system:mastersProblemas do aws-auth:
- ConfigMap pode ser sobrescrito acidentalmente
- Difícil de auditar mudanças
- Não suporta versionamento
- Requer acesso ao cluster para modificar
- Não integra com CloudTrail
Modelo Novo: Access Entries (EKS Access API)
Como funciona:
Access Entries são gerenciados via API da AWS, fora do cluster:
# Criar access entry
aws eks create-access-entry \
--cluster-name meu-cluster \
--principal-arn arn:aws:iam::123456789012:role/DevTeamRole \
--type STANDARD
# Associar policy
aws eks associate-access-policy \
--cluster-name meu-cluster \
--principal-arn arn:aws:iam::123456789012:role/DevTeamRole \
--policy-arn arn:aws:eks::aws:cluster-access-policy/AmazonEKSClusterAdminPolicy \
--access-scope type=clusterPolíticas disponíveis:
| Policy | Permissões | Uso |
|---|---|---|
| AmazonEKSClusterAdminPolicy | Acesso total | Administradores |
| AmazonEKSAdminPolicy | Admin sem cluster-scoped | Admins de namespace |
| AmazonEKSEditPolicy | Criar/modificar recursos | Desenvolvedores |
| AmazonEKSViewPolicy | Somente leitura | Auditores |
Vantagens do Access Entries:
- Gerenciado via AWS API (CloudTrail audit)
- Versionamento automático
- Não requer acesso ao cluster
- Suporta namespaces específicos
- Integração com IAM Identity Center
Migração de aws-auth para Access Entries:
# 1. Listar entradas atuais do aws-auth
kubectl get configmap aws-auth -n kube-system -o yaml
# 2. Criar access entries equivalentes
aws eks create-access-entry \
--cluster-name meu-cluster \
--principal-arn arn:aws:iam::123456789012:role/DevTeamRole
# 3. Testar acesso
aws eks update-kubeconfig --name meu-cluster --role-arn arn:aws:iam::123456789012:role/DevTeamRole
kubectl get pods
# 4. Remover do aws-auth após validar
kubectl edit configmap aws-auth -n kube-systemPod Identity: Acesso a serviços AWS
Pods frequentemente precisam acessar serviços AWS (S3, DynamoDB, SQS). Existem três formas de fazer isso:
1. IRSA (IAM Roles for Service Accounts) - Legado
Como funciona:
Configuração:
# ServiceAccount
apiVersion: v1
kind: ServiceAccount
metadata:
name: app-sa
namespace: production
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/AppRole
---
# Pod usando o ServiceAccount
apiVersion: v1
kind: Pod
metadata:
name: app
namespace: production
spec:
serviceAccountName: app-sa
containers:
- name: app
image: myapp:latest
# AWS SDK automaticamente usa as credenciaisIAM Role Trust Policy:
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::123456789012:oidc-provider/oidc.eks.us-east-1.amazonaws.com/id/EXAMPLED539D4633E53DE1B71EXAMPLE"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"oidc.eks.us-east-1.amazonaws.com/id/EXAMPLED539D4633E53DE1B71EXAMPLE:sub": "system:serviceaccount:production:app-sa"
}
}
}]
}2. EKS Pod Identity - Novo e Recomendado
Como funciona:
Pod Identity simplifica o processo, removendo a necessidade de OIDC provider e trust policies complexas:
# 1. Criar associação de Pod Identity
aws eks create-pod-identity-association \
--cluster-name meu-cluster \
--namespace production \
--service-account app-sa \
--role-arn arn:aws:iam::123456789012:role/AppRoleVantagens sobre IRSA:
- Não precisa configurar OIDC provider
- Trust policy mais simples
- Gerenciado via AWS API (auditável)
- Suporta múltiplos clusters facilmente
- Melhor performance (menos chamadas STS)
Trust Policy simplificada:
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {
"Service": "pods.eks.amazonaws.com"
},
"Action": [
"sts:AssumeRole",
"sts:TagSession"
]
}]
}Uso no pod (idêntico ao IRSA):
apiVersion: v1
kind: Pod
metadata:
name: app
namespace: production
spec:
serviceAccountName: app-sa
containers:
- name: app
image: myapp:latest
# AWS SDK automaticamente usa Pod Identity3. Node IAM Role - Não Recomendado
Como funciona: Todos os pods no node herdam as permissões do node.
Problemas:
- Sem isolamento entre pods
- Princípio do menor privilégio violado
- Difícil de auditar quem acessou o quê
- Risco de segurança
Quando usar: Apenas para add-ons do sistema (aws-node, ebs-csi-driver).
Rede no EKS: Como pods se comunicam
Comunicação Pod-to-Pod
Com o VPC CNI, pods se comunicam diretamente usando IPs da VPC:
Pod A (10.0.1.20) → Pod B (10.0.2.30)
↓
Roteamento direto via VPC
↓
Sem NAT, sem overlayVantagens:
- Performance nativa (sem overhead)
- Compatível com Security Groups
- Troubleshooting mais simples (IPs reais)
Desvantagens:
- Consome muitos IPs da VPC
- Planejamento de CIDR crítico
Comunicação via Services
Services criam um IP virtual (ClusterIP) que load-balanceia entre pods:
apiVersion: v1
kind: Service
metadata:
name: api-service
spec:
selector:
app: api
ports:
- port: 80
targetPort: 8080
type: ClusterIPComo funciona:
Client Pod → api-service (10.100.45.123:80)
↓
kube-proxy (iptables)
↓
Load balance entre:
├─ Pod 1 (10.0.1.20:8080)
├─ Pod 2 (10.0.1.21:8080)
└─ Pod 3 (10.0.2.30:8080)Tipos de Services
ClusterIP (padrão):
- Acessível apenas dentro do cluster
- Ideal para comunicação interna
NodePort:
- Expõe porta em todos os nodes
- Acessível via
<NodeIP>:<NodePort> - Raramente usado em produção
LoadBalancer:
- Cria um AWS Load Balancer (NLB ou CLB)
- Expõe serviço para internet ou VPC
- Custo adicional por LB
apiVersion: v1
kind: Service
metadata:
name: api-public
annotations:
service.beta.kubernetes.io/aws-load-balancer-type: "nlb"
service.beta.kubernetes.io/aws-load-balancer-scheme: "internet-facing"
spec:
type: LoadBalancer
selector:
app: api
ports:
- port: 443
targetPort: 8080Ingress com AWS Load Balancer Controller
Para gerenciar múltiplos serviços com um único Load Balancer:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-ingress
annotations:
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/target-type: ip
spec:
ingressClassName: alb
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api-service
port:
number: 80
- host: admin.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: admin-service
port:
number: 80Como funciona:
Internet
↓
Application Load Balancer
↓
┌──────────────────────────────┐
│ Target Group 1 (api) │ → api-service pods
│ Target Group 2 (admin) │ → admin-service pods
└──────────────────────────────┘Observabilidade e Logging
Control Plane Logs
O EKS pode enviar logs do control plane para CloudWatch. Isso é essencial para auditoria, troubleshooting e compliance.
# Habilitar todos os tipos de logs
aws eks update-cluster-config \
--name meu-cluster \
--logging '{"clusterLogging":[{"types":["api","audit","authenticator","controllerManager","scheduler"],"enabled":true}]}'Tipos de logs e quando usar:
| Tipo | O que registra | Quando habilitar | Custo |
|---|---|---|---|
| audit | Todas as ações no cluster | Sempre (compliance) | Alto |
| authenticator | Tentativas de autenticação | Sempre (segurança) | Baixo |
| api | Requisições ao API Server | Troubleshooting | Médio |
| controllerManager | Ações dos controllers | Debug avançado | Baixo |
| scheduler | Decisões de scheduling | Debug de pods pending | Baixo |
Recomendação: Habilite pelo menos audit e authenticator em produção.
Audit Logs: O que você precisa saber
Audit logs registram TODAS as ações no cluster. Cada linha é um evento no formato JSON.
Estrutura de um audit log:
{
"kind": "Event",
"apiVersion": "audit.k8s.io/v1",
"level": "Metadata",
"auditID": "abc-123-def",
"stage": "ResponseComplete",
"requestURI": "/api/v1/namespaces/production/pods",
"verb": "create",
"user": {
"username": "[email protected]",
"uid": "aws-iam-authenticator:123456789012:AIDAI...",
"groups": ["system:authenticated"]
},
"sourceIPs": ["203.0.113.45"],
"userAgent": "kubectl/v1.28.0",
"objectRef": {
"resource": "pods",
"namespace": "production",
"name": "api-pod",
"apiVersion": "v1"
},
"responseStatus": {
"code": 201
},
"requestReceivedTimestamp": "2026-03-27T10:30:00.123456Z",
"stageTimestamp": "2026-03-27T10:30:00.234567Z"
}Campos importantes:
verb: Ação realizada (get, list, create, update, delete, patch)user.username: Quem fez a açãoobjectRef: Qual recurso foi afetadoresponseStatus.code: Sucesso (2xx) ou erro (4xx, 5xx)sourceIPs: De onde veio a requisição
CloudWatch Insights: Queries práticas
1. Quem deletou um recurso?
fields @timestamp, user.username, objectRef.namespace, objectRef.name, sourceIPs.0
| filter verb = "delete"
| filter objectRef.resource = "pods"
| sort @timestamp desc
| limit 502. Tentativas de acesso negadas (403)
fields @timestamp, user.username, verb, objectRef.resource, objectRef.namespace, responseStatus.code
| filter responseStatus.code = 403
| stats count() by user.username, verb, objectRef.resource
| sort count desc3. Quem criou secrets recentemente?
fields @timestamp, user.username, objectRef.namespace, objectRef.name
| filter verb = "create"
| filter objectRef.resource = "secrets"
| filter @timestamp > ago(7d)
| sort @timestamp desc4. Mudanças em recursos críticos (deployments, services)
fields @timestamp, user.username, verb, objectRef.resource, objectRef.namespace, objectRef.name
| filter verb in ["create", "update", "delete", "patch"]
| filter objectRef.resource in ["deployments", "services", "ingresses"]
| filter objectRef.namespace = "production"
| sort @timestamp desc
| limit 1005. Atividade de um usuário específico
fields @timestamp, verb, objectRef.resource, objectRef.namespace, objectRef.name, responseStatus.code
| filter user.username = "[email protected]"
| filter @timestamp > ago(24h)
| sort @timestamp desc6. Pods que falharam ao criar
fields @timestamp, user.username, objectRef.namespace, objectRef.name, responseStatus.message
| filter verb = "create"
| filter objectRef.resource = "pods"
| filter responseStatus.code >= 400
| sort @timestamp desc7. Exec em pods (acesso shell)
fields @timestamp, user.username, objectRef.namespace, objectRef.name, sourceIPs.0
| filter verb = "create"
| filter objectRef.subresource = "exec"
| sort @timestamp desc8. Mudanças em RBAC (roles, rolebindings)
fields @timestamp, user.username, verb, objectRef.resource, objectRef.namespace, objectRef.name
| filter objectRef.resource in ["roles", "rolebindings", "clusterroles", "clusterrolebindings"]
| filter verb in ["create", "update", "delete", "patch"]
| sort @timestamp desc9. Atividade por namespace
fields @timestamp, verb, objectRef.resource, objectRef.name
| filter objectRef.namespace = "production"
| filter verb in ["create", "delete"]
| stats count() by verb, objectRef.resource
| sort count desc10. Erros de autenticação
fields @timestamp, user.username, sourceIPs.0, responseStatus.message
| filter responseStatus.code = 401
| stats count() by user.username, sourceIPs.0
| sort count desc11. Recursos criados fora do horário comercial
fields @timestamp, user.username, verb, objectRef.resource, objectRef.namespace, objectRef.name
| filter verb = "create"
| filter datefloor(@timestamp, 1h) < datefloor(now() - 12h, 1h) or datefloor(@timestamp, 1h) > datefloor(now() - 8h, 1h)
| sort @timestamp desc12. Top usuários mais ativos
fields user.username, verb
| filter verb in ["create", "update", "delete", "patch"]
| stats count() as actions by user.username
| sort actions desc
| limit 10Configurando alertas baseados em audit logs
Alerta: Deleção de recursos em produção
# Criar métrica customizada
aws logs put-metric-filter \
--log-group-name /aws/eks/meu-cluster/cluster \
--filter-name ProductionDeletions \
--filter-pattern '{ $.verb = "delete" && $.objectRef.namespace = "production" }' \
--metric-transformations \
metricName=ProductionDeletions,\
metricNamespace=EKS/Audit,\
metricValue=1
# Criar alarme
aws cloudwatch put-metric-alarm \
--alarm-name eks-production-deletions \
--alarm-description "Alert on deletions in production namespace" \
--metric-name ProductionDeletions \
--namespace EKS/Audit \
--statistic Sum \
--period 300 \
--evaluation-periods 1 \
--threshold 1 \
--comparison-operator GreaterThanThreshold \
--alarm-actions arn:aws:sns:us-east-1:123456789012:security-alertsAlerta: Tentativas de acesso negadas
aws logs put-metric-filter \
--log-group-name /aws/eks/meu-cluster/cluster \
--filter-name AccessDenied \
--filter-pattern '{ $.responseStatus.code = 403 }' \
--metric-transformations \
metricName=AccessDenied,\
metricNamespace=EKS/Audit,\
metricValue=1
aws cloudwatch put-metric-alarm \
--alarm-name eks-access-denied-spike \
--metric-name AccessDenied \
--namespace EKS/Audit \
--statistic Sum \
--period 300 \
--evaluation-periods 1 \
--threshold 10 \
--comparison-operator GreaterThanThreshold \
--alarm-actions arn:aws:sns:us-east-1:123456789012:security-alertsRetenção e custos de logs
Recomendações de retenção :
```bash
# Audit logs: 90 dias (compliance)
aws logs put-retention-policy \
--log-group-name /aws/eks/meu-cluster/cluster \
--retention-in-days 90
# API logs: 30 dias (troubleshooting)
aws logs put-retention-policy \
--log-group-name /aws/eks/meu-cluster/cluster \
--retention-in-days 30
# Authenticator: 30 dias
aws logs put-retention-policy \
--log-group-name /aws/eks/meu-cluster/cluster \
--retention-in-days 30Otimização de custos:
- Exporte para S3 após 7 dias (muito mais barato)
- Use S3 Intelligent-Tiering para arquivamento
- Habilite apenas logs necessários em dev/staging
- Use filtros para reduzir volume (ex: ignore health checks)
Application Logs
Para logs de aplicação, use Fluent Bit ou CloudWatch Container Insights:
Fluent Bit (recomendado):
# Instalar via Helm
helm repo add eks https://aws.github.io/eks-charts
helm install aws-for-fluent-bit eks/aws-for-fluent-bit \
--namespace kube-system \
--set cloudWatch.region=us-east-1 \
--set cloudWatch.logGroupName=/aws/eks/meu-cluster/applicationContainer Insights:
# Instalar CloudWatch agent
kubectl apply -f https://raw.githubusercontent.com/aws-samples/amazon-cloudwatch-container-insights/latest/k8s-deployment-manifest-templates/deployment-mode/daemonset/container-insights-monitoring/quickstart/cwagent-fluentd-quickstart.yamlMétricas
Metrics Server (obrigatório para HPA):
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
# Verificar
kubectl top nodes
kubectl top pods -APrometheus + Grafana:
# Instalar kube-prometheus-stack
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm install prometheus prometheus-community/kube-prometheus-stack \
--namespace monitoring \
--create-namespaceEvoluções do EKS: Karpenter e Auto Mode
Karpenter: Autoscaling inteligente
O que é: Autoscaler de nodes mais eficiente que o Cluster Autoscaler.
Diferenças do Cluster Autoscaler:
| Cluster Autoscaler | Karpenter |
|---|---|
| Baseado em node groups | Provisiona nodes individuais |
| Reativo (espera pending pods) | Proativo (antecipa necessidades) |
| Limitado a tipos pré-definidos | Escolhe melhor tipo automaticamente |
| Escala em grupos | Escala granularmente |
| Lento (~5 min) | Rápido (~2 min) |
Como funciona:
Pending Pod
↓
Karpenter analisa:
- CPU/Memory requests
- Node selectors
- Taints/Tolerations
- Topology constraints
↓
Escolhe melhor instância:
- Spot vs On-Demand
- Tipo de instância
- AZ
↓
Provisiona em ~2 minutosExemplo de NodePool:
apiVersion: karpenter.sh/v1beta1
kind: NodePool
metadata:
name: default
spec:
template:
spec:
requirements:
- key: karpenter.sh/capacity-type
operator: In
values: ["spot", "on-demand"]
- key: kubernetes.io/arch
operator: In
values: ["amd64"]
- key: karpenter.k8s.aws/instance-category
operator: In
values: ["c", "m", "r"]
limits:
cpu: 1000
memory: 1000Gi
disruption:
consolidationPolicy: WhenUnderutilized
expireAfter: 720hBenefícios:
- Redução de custos (30-50% com spot)
- Melhor bin-packing
- Consolidação automática
- Menos desperdício de recursos
Tradicional:
- Control plane: $0.10/hora
- Nodes: Preço da instância EC2
Auto Mode:
- Control plane: $0.10/hora
- Compute: $0.10/vCPU/hora + $0.01/GB/hora
- Cobrança por pod, não por nodePrimeiros passos práticos
1. Criando seu primeiro cluster
# Instalar eksctl (ferramenta CLI para EKS)
curl --silent --location "https://github.com/weaveworks/eksctl/releases/latest/download/eksctl_$(uname -s)_amd64.tar.gz" | tar xz -C /tmp
sudo mv /tmp/eksctl /usr/local/bin
# Criar cluster básico
eksctl create cluster \
--name meu-primeiro-cluster \
--region us-east-1 \
--nodegroup-name workers \
--node-type t3.medium \
--nodes 2 \
--nodes-min 1 \
--nodes-max 4 \
--managed
# Aguardar ~15-20 minutos2. Configurando kubectl
# Atualizar kubeconfig
aws eks update-kubeconfig --name meu-primeiro-cluster --region us-east-1
# Verificar conexão
kubectl get nodes
kubectl get pods -A3. Deploy de primeira aplicação
# app.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 200m
memory: 256Mi
---
apiVersion: v1
kind: Service
metadata:
name: nginx
spec:
type: LoadBalancer
selector:
app: nginx
ports:
- port: 80
targetPort: 80# Aplicar
kubectl apply -f app.yaml
# Verificar
kubectl get deployments
kubectl get pods
kubectl get services
# Obter URL do LoadBalancer
kubectl get service nginx -o jsonpath='{.status.loadBalancer.ingress[0].hostname}'4. Instalando add-ons essenciais
AWS Load Balancer Controller:
# Criar IAM policy
curl -o iam_policy.json https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/main/docs/install/iam_policy.json
aws iam create-policy \
--policy-name AWSLoadBalancerControllerIAMPolicy \
--policy-document file://iam_policy.json
# Instalar via Helm
helm repo add eks https://aws.github.io/eks-charts
helm install aws-load-balancer-controller eks/aws-load-balancer-controller \
-n kube-system \
--set clusterName=meu-primeiro-cluster \
--set serviceAccount.create=true \
--set serviceAccount.annotations."eks\.amazonaws\.com/role-arn"=arn:aws:iam::ACCOUNT_ID:role/AmazonEKSLoadBalancerControllerRoleEBS CSI Driver (para volumes persistentes):
# Habilitar add-on
aws eks create-addon \
--cluster-name meu-primeiro-cluster \
--addon-name aws-ebs-csi-driver \
--service-account-role-arn arn:aws:iam::ACCOUNT_ID:role/AmazonEKS_EBS_CSI_DriverRoleMetrics Server:
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yamlDecisões arquiteturais importantes
1. Planejamento de VPC e CIDR
Erro comum: CIDR muito pequeno
Errado:
VPC: 10.0.0.0/24 (256 IPs)
- Subnets: 10.0.0.0/26 (64 IPs cada)
- Resultado: ~10 pods por subnet
Correto:
VPC: 10.0.0.0/16 (65,536 IPs)
- Subnets: 10.0.0.0/20 (4,096 IPs cada)
- Resultado: Centenas de pods por subnetRecomendação:
- VPC: /16 (65k IPs)
- Subnets públicas: /20 (4k IPs)
- Subnets privadas: /19 (8k IPs)
- Reserve espaço para crescimento
2. Escolha de tipos de nodes
Workloads stateless → Fargate ou Spot instances Workloads stateful → On-Demand instances Workloads batch → Spot instances com Karpenter Workloads GPU → Instâncias p3/g4
3. Multi-AZ vs Single-AZ
Production → Sempre Multi-AZ:
- Control plane já é Multi-AZ
- Distribua nodes em pelo menos 2 AZs
- Use topology spread constraints
spec:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: critical-appDevelopment/Staging → Single-AZ pode ser aceitável
4. Endpoint privado: Boa prática de segurança
Boa prática: Em ambientes de produção, o acesso ao API Server do EKS deve ser feito preferencialmente através de endpoint privado, com acesso via VPN ou AWS Direct Connect.
O EKS permite configurar como o API Server do Kubernetes será acessado. Existem três modos:
Modo 1: Apenas Privado (Private Only) RECOMENDADO PARA PRODUÇÃO
Como funciona:
- API Server acessível apenas de dentro da VPC
- Acesso externo via VPN, AWS Direct Connect ou bastion host
- Nodes acessam via rede privada
- Zero exposição à internet
VPN/Direct Connect
↓
VPC
├─ API Server (privado)
└─ Nodes (via rede privada)Por que é a melhor prática:
Segurança máxima: API Server nunca exposto à internet Compliance: Atende requisitos rigorosos de segurança Menor superfície de ataque: Sem endpoint público Auditoria simplificada: Todo acesso rastreável via VPN Performance: Latência menor para nodes Isolamento: Tráfego nunca sai da rede privada
Quando usar:
- Ambientes de produção (sempre)
- Clusters com dados sensíveis
- Requisitos de compliance (PCI-DSS, HIPAA, SOC2)
- Empresas com VPN corporativa estabelecida
Configuração:
# Habilitar apenas endpoint privado
aws eks update-cluster-config \
--name meu-cluster-prod \
--resources-vpc-config \
endpointPublicAccess=false,\
endpointPrivateAccess=trueAcesso via VPN:
# 1. Conectar à VPN corporativa
# 2. Configurar kubectl
aws eks update-kubeconfig --name meu-cluster-prod --region us-east-1
# 3. Verificar acesso
kubectl get nodes
# O tráfego flui:
# Seu laptop → VPN → VPC → API Server (privado)Acesso via AWS Client VPN:
Para configurar o AWS Client VPN Endpoint, veja o guia completo sobre AWS VPN Client Endpoint que cobre desde a criação dos certificados até a configuração do cliente.
Resumo da configuração:
# Criar Client VPN Endpoint
aws ec2 create-client-vpn-endpoint \
--client-cidr-block 10.100.0.0/22 \
--server-certificate-arn arn:aws:acm:us-east-1:123456789012:certificate/abc123 \
--authentication-options Type=certificate-authentication,MutualAuthentication={ClientRootCertificateChainArn=arn:aws:acm:us-east-1:123456789012:certificate/def456} \
--connection-log-options Enabled=true,CloudwatchLogGroup=/aws/vpn/client \
--vpc-id vpc-abc123
# Associar com subnet
aws ec2 associate-client-vpn-target-network \
--client-vpn-endpoint-id cvpn-endpoint-abc123 \
--subnet-id subnet-def456
# Autorizar acesso
aws ec2 authorize-client-vpn-ingress \
--client-vpn-endpoint-id cvpn-endpoint-abc123 \
--target-network-cidr 10.0.0.0/16 \
--authorize-all-groupsAcesso via bastion host (alternativa):
# 1. SSH para bastion na VPC
ssh -i key.pem [email protected]
# 2. Dentro do bastion, usar kubectl
kubectl get nodesModo 2: Público e Privado (Public and Private)
Como funciona:
- API Server tem endpoint público E privado
- Nodes usam endpoint privado automaticamente
- Acesso externo via endpoint público (com restrições de IP)
Internet (IPs restritos) VPC
↓ ↓
API Server ←────────→ Nodes
(público) (via privado)Quando usar:
- Ambientes de staging/desenvolvimento
- Transição para modelo totalmente privado
- Equipes distribuídas sem VPN estabelecida
- CI/CD rodando fora da VPC
Configuração com restrição de IPs:
# Habilitar ambos, mas restringir público
aws eks update-cluster-config \
--name meu-cluster-staging \
--resources-vpc-config \
endpointPublicAccess=true,\
endpointPrivateAccess=true,\
publicAccessCidrs="203.0.113.0/24,198.51.100.0/24"
# Apenas IPs do escritório e CI/CD podem acessarImportante: Mesmo com endpoint público, os nodes SEMPRE usam o endpoint privado quando disponível.
Modo 3: Apenas Público (Public Only) NÃO RECOMENDADO
Como funciona:
- API Server exposto à internet
- Nodes acessam pela internet
Quando usar:
- Apenas para testes rápidos
- Ambientes de desenvolvimento pessoal
- Nunca em produção
Problemas:
- API Server exposto à internet
- Nodes precisam de internet para falar com control plane
- Maior latência
- Maior superfície de ataque
- Não atende compliance
Migração para endpoint privado
Passo a passo para migrar produção:
# 1. Verificar configuração atual
aws eks describe-cluster --name meu-cluster \
--query 'cluster.resourcesVpcConfig'
# 2. Habilitar endpoint privado (mantém público temporariamente)
aws eks update-cluster-config \
--name meu-cluster \
--resources-vpc-config \
endpointPublicAccess=true,\
endpointPrivateAccess=true
# 3. Aguardar propagação (~10 minutos)
aws eks wait cluster-active --name meu-cluster
# 4. Configurar VPN/Direct Connect
# (configuração específica da sua infraestrutura)
# 5. Testar acesso via VPN
# Conectar VPN e executar:
kubectl get nodes
# 6. Validar que nodes usam endpoint privado
kubectl get nodes -o wide
# Verificar que IPs são privados
# 7. Desabilitar endpoint público
aws eks update-cluster-config \
--name meu-cluster \
--resources-vpc-config \
endpointPublicAccess=false,\
endpointPrivateAccess=true
# 8. Validar acesso
# Sem VPN: deve falhar
# Com VPN: deve funcionarExemplo prático: Acesso via AWS Client VPN
Para um guia completo sobre como configurar o AWS Client VPN Endpoint do zero, incluindo geração de certificados, configuração de autenticação e troubleshooting, consulte o artigo Utilizando o AWS VPN Client Endpoint.
Fluxo de uso após configuração:
# 1. Baixar configuração do Client VPN
aws ec2 export-client-vpn-client-configuration \
--client-vpn-endpoint-id cvpn-endpoint-abc123 \
--output text > eks-vpn.ovpn
# 2. Importar no cliente OpenVPN
# (AWS VPN Client, Tunnelblick, OpenVPN Connect)
# 3. Conectar à VPN
# 4. Usar kubectl normalmente
kubectl get pods -A
kubectl logs -f deployment/app
# 5. Desconectar quando terminar5. Versão do Kubernetes
Regra: Sempre use N-1 ou N-2 (nunca a mais recente)
Atual: 1.35
Recomendado: 1.34 ou 1.33Ciclo de suporte EKS:
- Cada versão suportada por ~14 meses
- Planeje upgrades anuais
- Teste em staging primeiro
Conclusão
O Amazon EKS remove a complexidade de gerenciar o control plane do Kubernetes, mas ainda exige decisões arquiteturais importantes sobre rede, compute, segurança e observabilidade.
A diferença entre clusters que escalam com sucesso e aqueles que acumulam problemas está nas decisões arquiteturais iniciais. Não há "desfazer" um CIDR muito pequeno ou uma arquitetura de rede mal planejada.
EKS não é apenas "Kubernetes gerenciado". É uma plataforma completa que integra profundamente com o ecossistema AWS. Entender essa integração é a chave para arquiteturas bem-sucedidas.