Amazon EKS — KEDA

2 minute read

KEDA (Kubernetes Event-Driven Autoscaling) extends HPA with event-driven triggers. It can scale pods based on external metrics like SQS queue depth, Kinesis stream lag, CloudWatch metrics, and dozens of other sources — including scaling to zero.


Why KEDA?

Feature HPA KEDA
Metric sources CPU, memory, custom metrics API 60+ external scalers
Scale to zero No (minimum 1 replica) Yes
AWS service integration Manual (Prometheus Adapter) Native SQS, Kinesis, CloudWatch scalers
Job scaling No Yes (ScaledJob)

Installation

helm repo add kedacore https://kedacore.github.io/charts
helm repo update

helm install keda kedacore/keda \
  --namespace keda --create-namespace \
  --set serviceAccount.annotations."eks\.amazonaws\.com/role-arn"=arn:aws:iam::123456789:role/KedaRole

IRSA for KEDA

cat > keda-policy.json << 'EOF'
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "sqs:GetQueueAttributes",
        "sqs:GetQueueUrl",
        "cloudwatch:GetMetricData",
        "kinesis:DescribeStreamSummary"
      ],
      "Resource": "*"
    }
  ]
}
EOF

aws iam create-policy --policy-name KedaPolicy --policy-document file://keda-policy.json

eksctl create iamserviceaccount \
  --cluster my-cluster \
  --namespace keda \
  --name keda-operator \
  --attach-policy-arn arn:aws:iam::$(aws sts get-caller-identity --query Account --output text):policy/KedaPolicy \
  --approve

ScaledObject — SQS Queue

Scale a deployment based on SQS queue depth:

apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: sqs-consumer
  namespace: production
spec:
  scaleTargetRef:
    name: queue-processor
  pollingInterval: 15
  cooldownPeriod: 60
  minReplicaCount: 0        # Scale to zero!
  maxReplicaCount: 50
  triggers:
    - type: aws-sqs-queue
      metadata:
        queueURL: https://sqs.us-east-1.amazonaws.com/123456789/my-queue
        queueLength: "5"     # Messages per replica
        awsRegion: us-east-1
      authenticationRef:
        name: keda-aws-credentials

ScaledObject — CloudWatch Metric

apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: cloudwatch-scaler
  namespace: production
spec:
  scaleTargetRef:
    name: api-server
  minReplicaCount: 2
  maxReplicaCount: 30
  triggers:
    - type: aws-cloudwatch
      metadata:
        namespace: AWS/ApplicationELB
        dimensionName: LoadBalancer
        dimensionValue: app/my-alb/1234567890
        metricName: RequestCountPerTarget
        targetMetricValue: "100"
        minMetricValue: "0"
        awsRegion: us-east-1
        metricStatPeriod: "60"
        metricStat: Average

ScaledJob — Batch Processing

For one-off jobs triggered by events:

apiVersion: keda.sh/v1alpha1
kind: ScaledJob
metadata:
  name: image-processor
  namespace: production
spec:
  jobTargetRef:
    template:
      spec:
        containers:
          - name: processor
            image: myapp/image-processor:latest
            env:
              - name: QUEUE_URL
                value: https://sqs.us-east-1.amazonaws.com/123456789/image-queue
        restartPolicy: Never
    backoffLimit: 3
  pollingInterval: 10
  maxReplicaCount: 20
  triggers:
    - type: aws-sqs-queue
      metadata:
        queueURL: https://sqs.us-east-1.amazonaws.com/123456789/image-queue
        queueLength: "1"
        awsRegion: us-east-1

Authentication

TriggerAuthentication with IRSA

apiVersion: keda.sh/v1alpha1
kind: TriggerAuthentication
metadata:
  name: keda-aws-credentials
  namespace: production
spec:
  podIdentity:
    provider: aws-eks

KEDA vs HPA

Feature HPA KEDA
Built-in Yes Add-on
Scale to zero No Yes
SQS trigger No (needs adapter) Native
CloudWatch trigger No (needs adapter) Native
Cron-based scaling No Yes
Job scaling No ScaledJob
Custom metrics Prometheus Adapter 60+ built-in scalers

KEDA creates HPA resources under the hood — it extends HPA, it doesn’t replace it.


Scaler Trigger Use Case
aws-sqs-queue Queue depth Message processing
aws-cloudwatch Any CloudWatch metric Custom business metrics
aws-kinesis-stream Shard iterator lag Stream processing
aws-dynamodb-streams Stream records Event processing
cron Time-based Scheduled scaling
prometheus PromQL query Custom app metrics

Best Practices

  1. Use KEDA for event-driven workloads — don’t force HPA to do SQS-based scaling
  2. Scale to zero for batch processors — no cost when queue is empty
  3. Set appropriate queueLength — messages per replica determines scaling speed
  4. Use cooldownPeriod to prevent rapid scale-down after burst
  5. Use ScaledJob for one-shot tasks — ScaledObject for long-running services
  6. Use IRSA/Pod Identity for KEDA operator — scope to only the metrics it needs

Updated: