Introdução ao EKS: Contexto e Arquitetura
O Amazon EKS (Elastic Kubernetes Service) é um serviço gerenciado de Kubernetes oferecido pela AWS que abstrai a complexidade de manter um plano de controle Kubernetes. Diferentemente de um cluster Kubernetes tradional onde você precisa gerenciar master nodes, certificados, atualizações e alta disponibilidade, o EKS delega essa responsabilidade à AWS enquanto você concentra-se na infraestrutura de computação e aplicações.
A arquitetura do EKS funciona em duas camadas: o plano de controle (control plane), totalmente gerenciado pela AWS, e o plano de dados (data plane), composto pelos nós workers que você provisiona através de Node Groups. O EKS é compatível com a versão upstream do Kubernetes, permitindo que você use as mesmas ferramentas, conhecimentos e manifestos que usaria em qualquer outro cluster Kubernetes, sem vendor lock-in significativo.
Provisionamento do Cluster EKS
Criando um Cluster com AWS CLI e Terraform
O provisionamento de um cluster EKS envolve várias etapas: criar a role IAM para o cluster, configurar VPC e subnets, e finalmente provisionar o cluster. Vou demonstrar usando Terraform, que é a abordagem Infrastructure as Code mais robusta para produção.
Primeiro, você precisa criar uma role IAM que conceda permissões ao EKS para gerenciar recursos AWS em seu nome:
# iam.tf
resource "aws_iam_role" "eks_cluster_role" {
name = "eks-cluster-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "eks.amazonaws.com"
}
}
]
})
}
resource "aws_iam_role_policy_attachment" "eks_cluster_policy" {
policy_arn = "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy"
role = aws_iam_role.eks_cluster_role.name
}
resource "aws_iam_role_policy_attachment" "eks_vpc_resource_controller" {
policy_arn = "arn:aws:iam::aws:policy/AmazonEKSVPCResourceController"
role = aws_iam_role.eks_cluster_role.name
}
Em seguida, configure a VPC e os security groups que permitirão comunicação entre o plano de controle e os nós:
# vpc.tf
resource "aws_vpc" "eks_vpc" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
enable_dns_support = true
tags = {
Name = "eks-vpc"
}
}
resource "aws_subnet" "eks_subnet_1" {
vpc_id = aws_vpc.eks_vpc.id
cidr_block = "10.0.1.0/24"
availability_zone = "us-east-1a"
map_public_ip_on_launch = true
tags = {
Name = "eks-subnet-1"
"kubernetes.io/cluster/my-cluster" = "shared"
"kubernetes.io/role/elb" = "1"
}
}
resource "aws_subnet" "eks_subnet_2" {
vpc_id = aws_vpc.eks_vpc.id
cidr_block = "10.0.2.0/24"
availability_zone = "us-east-1b"
map_public_ip_on_launch = true
tags = {
Name = "eks-subnet-2"
"kubernetes.io/cluster/my-cluster" = "shared"
"kubernetes.io/role/elb" = "1"
}
}
resource "aws_security_group" "eks_cluster_sg" {
name = "eks-cluster-sg"
vpc_id = aws_vpc.eks_vpc.id
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "eks-cluster-sg"
}
}
Agora, crie o cluster EKS propriamente dito:
# eks.tf
resource "aws_eks_cluster" "main" {
name = "my-cluster"
role_arn = aws_iam_role.eks_cluster_role.arn
version = "1.28"
vpc_config {
subnet_ids = [aws_subnet.eks_subnet_1.id, aws_subnet.eks_subnet_2.id]
security_group_ids = [aws_security_group.eks_cluster_sg.id]
endpoint_private_access = true
endpoint_public_access = true
}
depends_on = [
aws_iam_role_policy_attachment.eks_cluster_policy,
aws_iam_role_policy_attachment.eks_vpc_resource_controller
]
tags = {
Name = "my-cluster"
}
}
output "cluster_endpoint" {
value = aws_eks_cluster.main.endpoint
}
output "cluster_name" {
value = aws_eks_cluster.main.name
}
Após aplicar essa configuração com terraform apply, você terá um cluster EKS totalmente funcional. O EKS automaticamente provisiona três master nodes distribuídos em múltiplas zonas de disponibilidade, oferecendo alta disponibilidade nativa.
Autenticação e Acesso ao Cluster
Após provisionar o cluster, você precisa configurar o kubectl para acessá-lo. O EKS usa o AWS IAM como mecanismo de autenticação através de um token temporário:
# Configure o kubeconfig
aws eks update-kubeconfig --region us-east-1 --name my-cluster
# Verifique a conexão
kubectl get nodes
O comando update-kubeconfig adiciona uma entrada no seu arquivo ~/.kube/config que usa o AWS CLI para gerar tokens de autenticação. Internamente, ele utiliza o webhook de autenticação aws-iam-authenticator, que mapeia identidades IAM para usuários e grupos Kubernetes.
Node Groups: Provisionamento de Nós Workers
O que são Node Groups
Node Groups são abstrações do EKS que simplificam o provisionamento e gerenciamento de grupos de nós EC2 com configuração uniforme. Sem Node Groups, você teria que gerenciar manualmente Launch Templates e Auto Scaling Groups. Com Node Groups, você define especificações uma vez (tipo de instância, tamanho mínimo/máximo, AMI) e o EKS orquestra toda a infraestrutura subjacente.
Existem três tipos de Node Groups: Node Groups padrão (EC2 sob demanda), Node Groups com Spot Instances (para otimizar custo), e Node Groups sem servidor usando AWS Fargate (sem gerenciar nós).
Criando Node Groups com Terraform
# node_group.tf
resource "aws_iam_role" "eks_node_role" {
name = "eks-node-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "ec2.amazonaws.com"
}
}
]
})
}
# Políticas obrigatórias para worker nodes
resource "aws_iam_role_policy_attachment" "eks_worker_node_policy" {
policy_arn = "arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy"
role = aws_iam_role.eks_node_role.name
}
resource "aws_iam_role_policy_attachment" "eks_cni_policy" {
policy_arn = "arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy"
role = aws_iam_role.eks_node_role.name
}
resource "aws_iam_role_policy_attachment" "eks_container_registry_policy" {
policy_arn = "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly"
role = aws_iam_role.eks_node_role.name
}
resource "aws_security_group" "eks_node_sg" {
name = "eks-node-sg"
vpc_id = aws_vpc.eks_vpc.id
ingress {
from_port = 1025
to_port = 65535
protocol = "tcp"
cidr_blocks = ["10.0.0.0/16"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "eks-node-sg"
}
}
resource "aws_eks_node_group" "primary" {
cluster_name = aws_eks_cluster.main.name
node_group_name = "primary"
node_role_arn = aws_iam_role.eks_node_role.arn
subnet_ids = [aws_subnet.eks_subnet_1.id, aws_subnet.eks_subnet_2.id]
scaling_config {
desired_size = 2
max_size = 5
min_size = 1
}
instance_types = ["t3.medium"]
vpc_config {
security_groups = [aws_security_group.eks_node_sg.id]
}
tags = {
Name = "primary-node-group"
}
depends_on = [
aws_iam_role_policy_attachment.eks_worker_node_policy,
aws_iam_role_policy_attachment.eks_cni_policy,
aws_iam_role_policy_attachment.eks_container_registry_policy
]
}
Este bloco de código cria um Node Group com 2 nós desejados, escalonando automaticamente entre 1 e 5 nós conforme a demanda. O EKS usa o AWS Auto Scaling Group internamente para gerenciar a capacidade. As políticas IAM anexadas permitem que os nós baixem imagens do ECR, gerenciem endereços IP (para o VPC CNI plugin) e se comuniquem com o plano de controle.
Node Groups com Spot Instances para Economia
Se seu workload permite interrupções, você pode usar Spot Instances que custam até 90% menos que instâncias sob demanda:
resource "aws_eks_node_group" "spot" {
cluster_name = aws_eks_cluster.main.name
node_group_name = "spot"
node_role_arn = aws_iam_role.eks_node_role.arn
subnet_ids = [aws_subnet.eks_subnet_1.id, aws_subnet.eks_subnet_2.id]
scaling_config {
desired_size = 2
max_size = 5
min_size = 1
}
instance_types = ["t3.medium", "t3.large", "t2.medium"]
capacity_type = "SPOT"
tags = {
Name = "spot-node-group"
}
depends_on = [
aws_iam_role_policy_attachment.eks_worker_node_policy,
aws_iam_role_policy_attachment.eks_cni_policy,
aws_iam_role_policy_attachment.eks_container_registry_policy
]
}
Ao definir capacity_type = "SPOT" e múltiplos instance_types, o EKS distribui os nós entre diferentes tipos de instância para aumentar a probabilidade de que pelo menos alguns nós permaneçam disponíveis quando interrupções ocorrem. Use taints e tolerâncias no Kubernetes para direcionar workloads apropriados para estes nós.
Add-ons Gerenciados do EKS
Compreendendo Add-ons
Add-ons são componentes essenciais de um cluster Kubernetes que o EKS pode gerenciar automaticamente. Sem add-ons, você teria que instalar manualmente componentes como o VPC CNI (para networking), CoreDNS (para resolução de DNS), e kube-proxy (para serviços). Com add-ons gerenciados, o EKS mantém essas dependências atualizadas automáticamente e compatíveis com a versão do Kubernetes.
Os add-ons mais críticos são: vpc-cni (necessário para comunicação entre pods), kube-proxy (necessário para serviços Kubernetes) e coredns (necessário para descoberta de serviços). Outros add-ons opcionais incluem aws-ebs-csi-driver (para volumes EBS persistentes) e aws-efs-csi-driver (para EFS).
Instalando Add-ons com Terraform
# addons.tf
resource "aws_eks_addon" "vpc_cni" {
cluster_name = aws_eks_cluster.main.name
addon_name = "vpc-cni"
addon_version = "v1.14.1-eksbuild.1"
resolve_conflicts = "OVERWRITE"
service_account_role_arn = aws_iam_role.vpc_cni_role.arn
tags = {
Name = "vpc-cni"
}
}
resource "aws_eks_addon" "coredns" {
cluster_name = aws_eks_cluster.main.name
addon_name = "coredns"
addon_version = "v1.9.3-eksbuild.2"
resolve_conflicts = "OVERWRITE"
tags = {
Name = "coredns"
}
}
resource "aws_eks_addon" "kube_proxy" {
cluster_name = aws_eks_cluster.main.name
addon_name = "kube-proxy"
addon_version = "v1.28.1-eksbuild.1"
resolve_conflicts = "OVERWRITE"
tags = {
Name = "kube-proxy"
}
}
resource "aws_eks_addon" "ebs_csi_driver" {
cluster_name = aws_eks_cluster.main.name
addon_name = "aws-ebs-csi-driver"
addon_version = "v1.24.0-eksbuild.1"
resolve_conflicts = "OVERWRITE"
service_account_role_arn = aws_iam_role.ebs_csi_role.arn
tags = {
Name = "aws-ebs-csi-driver"
}
}
Configurando IAM para Add-ons (IRSA)
Alguns add-ons precisam de permissões para acessar recursos AWS. O EKS fornece um mecanismo chamado IRSA (IAM Roles for Service Accounts) que permite associar uma role IAM a uma service account do Kubernetes. O add-on vpc-cni, por exemplo, precisa gerenciar endereços IP elásticos.
Primeiro, crie o OIDC Provider que permite que o Kubernetes confie em uma role IAM:
# oidc.tf
data "tls_certificate" "cluster" {
url = aws_eks_cluster.main.identity[0].oidc[0].issuer
}
resource "aws_iam_openid_connect_provider" "eks" {
client_id_list = ["sts.amazonaws.com"]
thumbprint_list = [data.tls_certificate.cluster.certificates[0].sha1_fingerprint]
url = aws_eks_cluster.main.identity[0].oidc[0].issuer
}
Agora crie as roles IAM para os add-ons:
# irsa_roles.tf
resource "aws_iam_role" "vpc_cni_role" {
name = "eks-vpc-cni-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRoleWithWebIdentity"
Effect = "Allow"
Principal = {
Federated = aws_iam_openid_connect_provider.eks.arn
}
Condition = {
StringEquals = {
"${replace(aws_iam_openid_connect_provider.eks.url, "https://", "")}:sub" = "system:serviceaccount:kube-system:aws-node"
"${replace(aws_iam_openid_connect_provider.eks.url, "https://", "")}:aud" = "sts.amazonaws.com"
}
}
}
]
})
}
resource "aws_iam_role_policy_attachment" "vpc_cni_policy" {
policy_arn = "arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy"
role = aws_iam_role.vpc_cni_role.name
}
resource "aws_iam_role" "ebs_csi_role" {
name = "eks-ebs-csi-driver-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRoleWithWebIdentity"
Effect = "Allow"
Principal = {
Federated = aws_iam_openid_connect_provider.eks.arn
}
Condition = {
StringEquals = {
"${replace(aws_iam_openid_connect_provider.eks.url, "https://", "")}:sub" = "system:serviceaccount:kube-system:ebs-csi-controller-sa"
"${replace(aws_iam_openid_connect_provider.eks.url, "https://", "")}:aud" = "sts.amazonaws.com"
}
}
}
]
})
}
resource "aws_iam_role_policy_attachment" "ebs_csi_policy" {
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonEBSCSIDriverPolicy"
role = aws_iam_role.ebs_csi_role.name
}
Este padrão IRSA (IAM Roles for Service Accounts) é a maneira segura e recomendada de conceder permissões AWS a pods específicos, sem precisar de credenciais de longa vida ou variáveis de ambiente.
Usando Volumes EBS com Add-ons
Com o EBS CSI Driver instalado como add-on, você pode provisionar volumes EBS persistentes dinamicamente. Primeiro, crie uma StorageClass:
# storage-class.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: ebs-sc
provisioner: ebs.csi.aws.com
parameters:
type: gp3
iops: "3000"
throughput: "125"
encrypted: "true"
reclaimPolicy: Delete
allowVolumeExpansion: true
Agora você pode criar um PersistentVolumeClaim (PVC) que será automaticamente provisionado como um volume EBS:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-pvc
spec:
accessModes:
- ReadWriteOnce
storageClassName: ebs-sc
resources:
requests:
storage: 10Gi
---
apiVersion: v1
kind: Pod
metadata:
name: app-with-ebs
spec:
containers:
- name: app
image: nginx:latest
volumeMounts:
- name: data
mountPath: /data
volumes:
- name: data
persistentVolumeClaim:
claimName: my-pvc
Quando você aplica este manifesto com kubectl apply -f, o EBS CSI Driver automatically cria um volume EBS de 10GB, o anexa ao nó onde o pod está rodando, e monta o sistema de arquivos no caminho /data dentro do container.
Conclusão
Dominar o EKS envolve compreender três pilares interdependentes: primeiro, o provisionamento do cluster usando Terraform e configuração adequada de VPC, IAM e segurança; segundo, o gerenciamento de Node Groups para oferecer capacidade computacional com flexibilidade (sob demanda, Spot, Fargate) e escalabilidade automática; terceiro, a configuração de add-ons gerenciados que mantêm componentes críticos do Kubernetes atualizados e integrados com serviços AWS através de IRSA. Estes três conceitos formam a base de um cluster EKS robusto e pronto para produção, permitindo que você concentre-se em suas aplicações em vez de gerenciar infraestrutura Kubernetes.