Container Insights for HPA on EKS
This guide wires Amazon CloudWatch metrics into Horizontal Pod Autoscaler (HPA) on EKS using:
- Amazon CloudWatch Observability EKS add-on (Container Insights metrics and agents).
- CloudWatch metrics adapter for Kubernetes (
external.metrics.k8s.io) — the pattern AWS documents for scaling deployments with CloudWatch metrics.
Overview: Autoscaling on EKS. PromQL-based scaling: Prometheus Adapter for HPA on EKS. CloudWatch basics: AWS Monitoring.
When to use
Section titled “When to use”| Use this path | Use Prometheus Adapter instead |
|---|---|
| AWS-centric operations and dashboards | Portable PromQL, multi-cloud |
| Scale on ALB, SQS, RDS, or Container Insights namespaces | Rich in-cluster app metrics already in Prometheus |
| Standardize on CloudWatch alarms and billing | Avoid CloudWatch custom metric API charges for high-cardinality apps |
CloudWatch custom metrics and high-cardinality dimensions can increase cost — review AWS cost management before production.
Architecture
Section titled “Architecture” Pods / nodes → CloudWatch agent (Observability add-on) → CloudWatch metrics ↑ HPA ← external.metrics API ← CloudWatch metrics adapter ←──┘metrics-server remains required if you combine CPU/memory Resource metrics with External metrics on the same HPA.
Prerequisites
Section titled “Prerequisites”- EKS 1.23+.
- AWS CLI and
kubectlwith cluster admin. - IAM permissions to create IRSA roles (or use the add-on’s recommended IAM policy attachment flow).
- Cluster name and region for metric dimensions.
Step 1: Install CloudWatch Observability add-on
Section titled “Step 1: Install CloudWatch Observability add-on”Use the EKS console, eksctl, or AWS CLI. Example with AWS CLI (replace cluster name, region, and account):
aws eks create-addon \ --cluster-name my-cluster \ --addon-name amazon-cloudwatch-observability \ --resolve-conflicts OVERWRITEWait until the add-on is ACTIVE:
aws eks describe-addon \ --cluster-name my-cluster \ --addon-name amazon-cloudwatch-observability \ --query addon.statusVerify agent pods (namespaces can vary by version):
kubectl get pods -n amazon-cloudwatchkubectl get pods -A | grep -i cloudwatchOfficial reference: Install CloudWatch Observability on EKS.
Confirm Container Insights metrics
Section titled “Confirm Container Insights metrics”In the CloudWatch console → Container Insights → your cluster, confirm node and pod metrics appear after a few minutes. Metrics commonly live under the ContainerInsights namespace.
Step 2: IAM for the CloudWatch metrics adapter
Section titled “Step 2: IAM for the CloudWatch metrics adapter”The adapter needs cloudwatch:GetMetricData (and related read actions) on the metric namespaces you query.
Create an IAM policy (adjust ARNs and optional namespace scoping):
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "cloudwatch:GetMetricData", "cloudwatch:GetMetricStatistics", "cloudwatch:ListMetrics" ], "Resource": "*" } ]}Associate the policy with an IRSA role for the adapter ServiceAccount. If your cluster already uses IRSA from Terraform: EKS cluster, follow the same OIDC provider pattern for a dedicated cloudwatch-adapter role.
Step 3: Install CloudWatch metrics adapter
Section titled “Step 3: Install CloudWatch metrics adapter”Deploy the Kubernetes CloudWatch metrics adapter (implements External Metrics API). Options include the maintained amazon-cloudwatch-metrics / SIG adapter manifests or your platform’s packaged Helm chart — pin a version tested on your EKS version.
Typical install pattern:
# Example: apply upstream manifests for your release tag (verify before production)kubectl apply -f https://raw.githubusercontent.com/awslabs/k8s-cloudwatch-adapter/master/deploy/adapter.yamlThen patch the ServiceAccount with your IRSA annotation:
apiVersion: v1kind: ServiceAccountmetadata: name: cloudwatch-adapter namespace: amazon-cloudwatch annotations: eks.amazonaws.com/role-arn: arn:aws:iam::<account-id>:role/cloudwatch-adapterVerify:
kubectl get apiservice v1beta1.external.metrics.k8s.iokubectl get pods -n amazon-cloudwatch -l app=cloudwatch-adapterkubectl logs -n amazon-cloudwatch -l app=cloudwatch-adapter --tail=50Step 4: Configure an external metric
Section titled “Step 4: Configure an external metric”Adapter configuration maps CloudWatch metric names and dimensions to Kubernetes external metrics. Example ConfigMap fragment (exact schema depends on adapter version):
apiVersion: v1kind: ConfigMapmetadata: name: cloudwatch-adapter-config namespace: amazon-cloudwatchdata: config.yaml: | metrics: - awsNamespace: AWS/SQS awsMetricName: ApproximateNumberOfMessagesVisible awsDimensions: - QueueName kubernetesNamespace: default kubernetesMetricName: sqs_approximate_number_of_messages_visibleReload or restart the adapter per your manifest docs after changing config.
List external metrics:
kubectl get --raw "/apis/external.metrics.k8s.io/v1beta1" | head -c 2000Step 5: HPA on an external metric
Section titled “Step 5: HPA on an external metric”Example HPA scaling a Deployment when an SQS queue depth metric is exposed (metric name must match adapter config):
apiVersion: autoscaling/v2kind: HorizontalPodAutoscalermetadata: name: worker-hpa namespace: defaultspec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: worker minReplicas: 1 maxReplicas: 20 metrics: - type: External external: metric: name: sqs_approximate_number_of_messages_visible selector: matchLabels: queue_name: my-queue target: type: AverageValue averageValue: "30"kubectl apply -f worker-hpa.yamlkubectl describe hpa worker-hpaFor queue-driven workloads without maintaining adapter config, KEDA is often simpler — see Autoscaling on EKS — KEDA.
Alternative: ADOT + Prometheus + adapter
Section titled “Alternative: ADOT + Prometheus + adapter”Some teams collect OpenTelemetry metrics with ADOT, query via AMP or in-cluster Prometheus, then use prometheus-adapter instead of CloudWatch external metrics. Use that path when PromQL is the source of truth — Prometheus Adapter for HPA on EKS.
Troubleshooting
Section titled “Troubleshooting”| Symptom | Likely cause | Action |
|---|---|---|
| No Container Insights data | Add-on not ACTIVE, IAM on agents | describe-addon, agent pod logs |
| APIService external.metrics unavailable | Adapter not running | Pod status, RBAC, APIService registration |
HPA unable to get metrics | Wrong metric name or labels | get --raw external API; match adapter config |
AccessDenied in adapter logs | IRSA policy or trust | Role trust policy, eks.amazonaws.com/role-arn annotation |
| Metric flat / stale | Wrong region or dimensions | CloudWatch console vs adapter config |
kubectl describe hpa <name> -n <namespace>aws cloudwatch list-metrics --namespace AWS/SQS --region <region>Comparison with Prometheus Adapter
Section titled “Comparison with Prometheus Adapter”| Container Insights + CW adapter | Prometheus Adapter | |
|---|---|---|
| Metric source | CloudWatch | Prometheus / AMP |
| HPA type | Often External | Pods / Object / custom |
| AWS integration | Native | Optional via exporters |
| Ops model | CloudWatch alarms and dashboards | Grafana / AMP |