Container Insights no EKS com Terraform: Métricas e Logs com o Add-on do CloudWatch

Um cluster EKS sem Container Insights obriga você a descobrir problemas pelo kubectl, pod por pod. Com ele, CPU, memória, reinícios e logs de todos os containers caem no CloudWatch, com painéis prontos e consultas em segundos. E dá para ligar tudo com um único add-on gerenciado.

Este é o oitavo artigo da série artigos-eks, que começou com o cluster EKS com Terraform do zero. Aqui o add-on amazon-cloudwatch-observability recebe permissão pelo EKS Pod Identity, os logs ganham retenção definida e o custo de ingestão fica sob controle.

Segurança: os comandos deste guia usam o perfil do AWS CLI para autenticar. Nunca coloque access keys no código ou em .tfvars versionado; prefira aws sso login ou um perfil com credenciais temporárias. Logs de aplicação podem conter dados pessoais: revise o que os seus pods escrevem antes de enviar tudo ao CloudWatch.

Sumário

Pré-requisitos

Você vai precisar do AWS CLI v2 autenticado, do Terraform >= 1.5, do kubectl e de permissão para criar IAM roles, log groups e recursos do EKS. O provider usado é o hashicorp/aws ~> 6.0 e o cluster está na versão 1.36.

O ambiente deste artigo é autocontido: ele recria a VPC, o cluster e o node group do primeiro artigo e acrescenta o Container Insights. Se você já tem o cluster da série, basta copiar os blocos novos para o seu ambiente.

Antes de tudo, confira se o add-on tem versão para a versão do seu cluster e qual permissão ele pede:

aws eks describe-addon-versions \
  --addon-name amazon-cloudwatch-observability \
  --kubernetes-version 1.36 \
  --query 'addons[0].addonVersions[0].addonVersion' --output text

aws eks describe-addon-configuration \
  --addon-name amazon-cloudwatch-observability \
  --addon-version <versao-acima> \
  --query 'podIdentityConfiguration'

O segundo comando devolve a service account cloudwatch-agent e a política recomendada CloudWatchAgentServerPolicy. É exatamente o que o Terraform vai configurar.

Como o Container Insights funciona no EKS

O add-on amazon-cloudwatch-observability instala no namespace amazon-cloudwatch dois coletores que rodam como DaemonSet, um pod por nó:

  • CloudWatch agent: coleta métricas de cluster, nó, pod e container e alimenta o Container Insights com observabilidade aprimorada, com painéis prontos no console do CloudWatch.
  • Fluent Bit: lê os logs dos containers e do nó e envia para o CloudWatch Logs.

Como é um add-on gerenciado, o EKS cuida da versão compatível com o cluster. Você não mantém Helm chart nem manifesto de DaemonSet. Os dois coletores precisam de permissão para escrever no CloudWatch, e é aí que entra o Pod Identity.

Log groups do Container Insights com retenção

O Container Insights usa quatro log groups por cluster, no padrão /aws/containerinsights/<cluster>/<tipo>:

  • application: stdout e stderr dos containers.
  • dataplane: kubelet, kube-proxy e runtime de containers.
  • host: logs do sistema operacional dos nós.
  • performance: eventos de desempenho que viram as métricas do Container Insights.

Se você deixar o Fluent Bit criar esses grupos, eles nascem com retenção “nunca expira”. O armazenamento cresce todo mês e ninguém percebe até a fatura. Por isso o ambiente cria os grupos antes do add-on, já com retenção:

resource "aws_cloudwatch_log_group" "container_insights" {
  for_each = toset(["application", "dataplane", "host", "performance"])

  name              = "/aws/containerinsights/${var.cluster_name}/${each.key}"
  retention_in_days = var.log_retention_days
}

Com os grupos existindo, o Fluent Bit só escreve neles. O padrão do ambiente é 7 dias. Ajuste log_retention_days conforme a sua política de auditoria.

Permissão do CloudWatch agent com Pod Identity

module "pi_cloudwatch" {
  source = "../../terraform-eks-pod-identity-modules"

  cluster_name     = module.eks.name
  role_name        = "${var.cluster_name}-cloudwatch-agent"
  service_accounts = []

  policy_arns = ["arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy"]
}

O módulo cria a IAM role que confia em pods.eks.amazonaws.com e anexa a CloudWatchAgentServerPolicy. Repare em service_accounts = []: a associação com a service account fica a cargo do próprio add-on, no bloco pod_identity_association. Se o módulo também criasse a associação, haveria duas apontando para a mesma service account e o apply falharia.

A vantagem sobre dar a política para a role dos nós é o escopo. Só os pods do agent recebem permissão de escrita no CloudWatch, e não qualquer pod que rode no nó.

Add-on amazon-cloudwatch-observability

module "addons" {
  source = "../../terraform-eks-addon-modules"

  cluster_name       = module.eks.name
  kubernetes_version = module.eks.kubernetes_version

  addons = {
    vpc-cni                = {}
    coredns                = {}
    kube-proxy             = {}
    eks-pod-identity-agent = {}
    amazon-cloudwatch-observability = {
      pod_identity_role_arn = module.pi_cloudwatch.role_arn
      service_account       = "cloudwatch-agent"
      configuration_values = jsonencode({
        containerLogs = { enabled = var.enable_container_logs }
      })
    }
  }

  depends_on = [module.ng_default, aws_cloudwatch_log_group.container_insights]
}

Três pontos do bloco:

  • eks-pod-identity-agent precisa estar no cluster. É ele que entrega as credenciais temporárias aos pods do agent.
  • configuration_values recebe o JSON de configuração do add-on. Aqui só ligamos ou desligamos os logs de container (containerLogs.enabled). As outras chaves aceitas aparecem no configurationSchema do describe-addon-configuration.
  • depends_on garante nós para os DaemonSets e log groups já criados, com retenção, antes de o Fluent Bit começar a escrever.

Se você já usa outra ferramenta de logs no cluster, defina enable_container_logs = false. As métricas continuam e o Fluent Bit sai do cluster.

terraform plan

terraform init
terraform validate
terraform plan -var 'public_access_cidrs=["SEU_IP/32"]' -out=tfplan

O código deste artigo foi validado com terraform validate e terraform plan contra uma conta AWS real, sem apply. O plan fecha em 35 recursos: os 28 da base do cluster mais os quatro log groups, a role do agent, a anexação da política e o add-on.

  # aws_cloudwatch_log_group.container_insights["application"] will be created
  # aws_cloudwatch_log_group.container_insights["dataplane"] will be created
  # aws_cloudwatch_log_group.container_insights["host"] will be created
  # aws_cloudwatch_log_group.container_insights["performance"] will be created
  # module.pi_cloudwatch.aws_iam_role.this will be created
  # module.addons.aws_eks_addon.this["amazon-cloudwatch-observability"] will be created
  + resource "aws_eks_addon" "this" {
      + addon_name                  = "amazon-cloudwatch-observability"
      + addon_version               = (known after apply)
      + configuration_values        = jsonencode(
            {
              + containerLogs = {
                  + enabled = true
                }
            }
        )
  ...
Plan: 35 to add, 0 to change, 0 to destroy.

A versão do add-on aparece como (known after apply) porque o módulo busca a mais recente compatível com o cluster no momento do apply.

Outputs do Terraform

O ambiente expõe cinco outputs. Como o código não foi aplicado, o bloco abaixo é o Changes to Outputs do plan: o que só existe depois do apply aparece como (known after apply).

Changes to Outputs:
  + addon_versions            = {
      + amazon-cloudwatch-observability = (known after apply)
      + coredns                         = (known after apply)
      + eks-pod-identity-agent          = (known after apply)
      + kube-proxy                      = (known after apply)
      + vpc-cni                         = (known after apply)
    }
  + cloudwatch_agent_role_arn = (known after apply)
  + cluster_name              = "eks-blog-castilho"
  + kubeconfig_command        = "aws eks update-kubeconfig --region us-east-1 --name eks-blog-castilho"
  + log_groups                = [
      + "/aws/containerinsights/eks-blog-castilho/application",
      + "/aws/containerinsights/eks-blog-castilho/dataplane",
      + "/aws/containerinsights/eks-blog-castilho/host",
      + "/aws/containerinsights/eks-blog-castilho/performance",
    ]
  • cluster_name: nome do cluster, para usar em scripts e no AWS CLI (aws eks describe-addon --cluster-name $(terraform output -raw cluster_name) ...).
  • addon_versions: versão instalada de cada add-on. Útil para registrar o que está rodando antes de um upgrade: terraform output -json addon_versions.
  • cloudwatch_agent_role_arn: ARN da role do CloudWatch agent. Serve para conferir a associação do Pod Identity e para auditar quem escreve no CloudWatch. Depois do apply, ele terá o formato arn:aws:iam::111122223333:role/eks-blog-castilho-cloudwatch-agent (exemplo, com conta fictícia).
  • log_groups: os quatro log groups com retenção. Use o nome direto nas consultas do Logs Insights ou em aws logs tail.
  • kubeconfig_command: comando pronto para configurar o kubectl. Rode com $(terraform output -raw kubeconfig_command).

Verificando o Container Insights no cluster

Depois do apply, estes comandos confirmam que o add-on subiu e que os pods do agent receberam a role:

$(terraform output -raw kubeconfig_command)

aws eks describe-addon \
  --cluster-name eks-blog-castilho \
  --addon-name amazon-cloudwatch-observability \
  --query 'addon.status'

kubectl get pods -n amazon-cloudwatch -o wide

aws eks list-pod-identity-associations \
  --cluster-name eks-blog-castilho \
  --namespace amazon-cloudwatch

aws logs tail /aws/containerinsights/eks-blog-castilho/application --since 10m

O status esperado do add-on é ACTIVE, com um pod do agent e um do Fluent Bit por nó. Os painéis aparecem no console do CloudWatch, em Insights > Container Insights, alguns minutos depois do primeiro envio.

Consultas úteis no Logs Insights

Os painéis respondem “o que está alto”. O Logs Insights responde “por quê”. Três consultas que valem ter salvas.

Erros recentes por pod, no log group application:

fields @timestamp, kubernetes.namespace_name, kubernetes.pod_name, log
| filter log like /(?i)error/
| sort @timestamp desc
| limit 50

Quem mais gera log, no mesmo grupo. É a consulta que mostra onde está o custo:

stats count(*) as linhas by kubernetes.namespace_name, kubernetes.pod_name
| sort linhas desc
| limit 20

Pods com mais uso de CPU e memória, no log group performance:

filter Type = "Pod"
| stats avg(pod_cpu_utilization) as cpu, avg(pod_memory_utilization) as memoria by Namespace, PodName
| sort cpu desc
| limit 20

Restrinja sempre o intervalo de tempo da consulta. O Logs Insights cobra pelo volume de dados lido, e uma consulta de 30 dias no grupo application de um cluster movimentado pesa mais do que parece.

Custos e limpeza

Na maioria dos clusters, o item que mais pesa no Container Insights é a ingestão de logs, cobrada por GB enviado ao CloudWatch Logs. As métricas da observabilidade aprimorada têm cobrança própria, por observação. Confira os dois na página de preços do CloudWatch para a sua região. Formas de reduzir:

  • Retenção curta nos log groups, como no ambiente deste artigo. Ela resolve o armazenamento, mas não a ingestão.
  • Menos log na origem: nível info em produção, sem debug ligado esquecido. A segunda consulta do Logs Insights mostra quem são os campeões.
  • Desligar os logs de container (enable_container_logs = false) se outra ferramenta já coleta os logs. Pagar duas vezes pelo mesmo log é comum.
  • Classe Infrequent Access nos log groups que são só para auditoria. A ingestão é mais barata, mas o grupo perde alguns recursos, como filtros de métrica. Avalie antes de trocar.

Some a isso o custo da base do cluster: control plane, NAT Gateway e nós, detalhados no primeiro artigo da série. Para limpar o lab:

terraform destroy

O destroy apaga também os log groups e os logs dentro deles. Se precisar guardar algo, exporte antes.

Conclusão

O Container Insights no EKS cabe em três blocos de Terraform: log groups com retenção, uma role com Pod Identity e o add-on gerenciado. O resultado são métricas e logs de todo o cluster sem Helm chart para manter e com a permissão de escrita no CloudWatch restrita aos pods do agent.

O cuidado que fica é com o volume de logs. Crie os log groups antes do add-on e acompanhe a consulta de quem mais gera log desde o primeiro dia.

Referências oficiais

Interessado em saber mais sobre esse assunto?

Deixe uma resposta

Descubra mais sobre Blog do Castilho - Tecnologia | FinOps | DevOps | Cloud

Assine agora mesmo para continuar lendo e ter acesso ao arquivo completo.

Continuar lendo