{"id":54055,"date":"2025-11-12T08:59:58","date_gmt":"2025-11-12T08:59:58","guid":{"rendered":"https:\/\/www.devopsschool.com\/blog\/?p=54055"},"modified":"2026-02-21T08:28:46","modified_gmt":"2026-02-21T08:28:46","slug":"kubernetes-tutorials-traffic-driven-autoscaling-on-kubernetes-comparing-keda-hpa-vpa-custom-adapters","status":"publish","type":"post","link":"https:\/\/www.devopsschool.com\/blog\/kubernetes-tutorials-traffic-driven-autoscaling-on-kubernetes-comparing-keda-hpa-vpa-custom-adapters\/","title":{"rendered":"Kubernetes Tutorials: Traffic-Driven Autoscaling on Kubernetes &amp; Comparing KEDA, HPA, VPA &amp; Custom Adapters"},"content":{"rendered":"\n<h3 class=\"wp-block-heading\"><em>Comparing KEDA, HPA, VPA &amp; Custom Adapters for Real-World Scaling with Cost, Complexity &amp; Best Practices<\/em><\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th><strong>Category<\/strong><\/th><th><strong>KEDA (Event-driven)<\/strong><\/th><th><strong>Prometheus-based (Adapter\/HPA)<\/strong><\/th><th><strong>Datadog-based (Cluster Agent)<\/strong><\/th><th><strong>CloudWatch-based (Adapter\/HPA)<\/strong><\/th><\/tr><\/thead><tbody><tr><td><strong>Primary Function<\/strong><\/td><td>Event-driven autoscaler (creates HPAs dynamically from external metrics like ALB, SQS, Kafka, etc.)<\/td><td>Uses HPA with Prometheus metrics via adapter (e.g., kube-metrics-adapter or prometheus-adapter)<\/td><td>Uses Datadog metrics (via Cluster Agent) as external metrics for HPA<\/td><td>Uses CloudWatch metrics (via AWS CloudWatch Metrics Adapter) for HPA<\/td><\/tr><tr><td><strong>Metric Source<\/strong><\/td><td>Multiple external sources: CloudWatch, Prometheus, SQS, Kafka, HTTP, etc. (50+ scalers)<\/td><td>Prometheus time-series metrics (scraped from exporters or apps)<\/td><td>Datadog platform metrics (ingested from AWS, custom apps, APM)<\/td><td>AWS CloudWatch (e.g., ALB metrics, RDS, SQS, Lambda, etc.)<\/td><\/tr><tr><td><strong>Data Flow Model<\/strong><\/td><td>Pull metrics or events \u2192 internal HPA \u2192 scale<\/td><td>Prometheus scrapes \u2192 adapter \u2192 Kubernetes Metrics API \u2192 HPA<\/td><td>Datadog agent \u2192 Cluster Agent \u2192 External Metrics API \u2192 HPA<\/td><td>CloudWatch Adapter \u2192 External Metrics API \u2192 HPA<\/td><\/tr><tr><td><strong>Setup Complexity<\/strong><\/td><td>\ud83d\udfe2 Medium (Helm + few YAMLs; no exporter needed)<\/td><td>\ud83d\udd35 Medium-High (need Prometheus + adapter configuration)<\/td><td>\ud83d\udfe3 Medium (if Datadog is already deployed)<\/td><td>\ud83d\udfe0 Medium (adapter installation + IAM + mappings)<\/td><\/tr><tr><td><strong>Integration with ALB Traffic<\/strong><\/td><td>\u2705 Native (via CloudWatch scaler \u2013 uses <code>RequestCountPerTarget<\/code>, <code>TargetResponseTime<\/code>)<\/td><td>\u26a0\ufe0f Requires Prometheus CloudWatch exporter (YACE or similar)<\/td><td>\u2705 Native (Datadog already pulls ALB metrics)<\/td><td>\u2705 Native (direct access to ALB metrics)<\/td><\/tr><tr><td><strong>Supports Scale-to-Zero<\/strong><\/td><td>\u2705 Yes<\/td><td>\u274c No (HPA cannot scale to zero)<\/td><td>\u274c No<\/td><td>\u274c No<\/td><\/tr><tr><td><strong>Responsiveness \/ Latency<\/strong><\/td><td>~30\u201360 seconds (depends on CloudWatch polling)<\/td><td>~15\u201330 seconds (depends on scrape interval)<\/td><td>~30\u201360 seconds (depends on Datadog ingestion)<\/td><td>~60 seconds (CloudWatch metric delay)<\/td><\/tr><tr><td><strong>Operational Cost<\/strong><\/td><td>\ud83d\udcb2 Low (CloudWatch API calls only)<\/td><td>\ud83d\udcb2\ud83d\udcb2 Medium (Prometheus infra + storage + exporter costs)<\/td><td>\ud83d\udcb2\ud83d\udcb2\ud83d\udcb2 High (Datadog licensing per host\/container)<\/td><td>\ud83d\udcb2 Low (CloudWatch API calls)<\/td><\/tr><tr><td><strong>Infrastructure Overhead<\/strong><\/td><td>Lightweight (1 KEDA controller)<\/td><td>Heavy (Prometheus, exporters, adapter)<\/td><td>Moderate (Datadog Cluster Agent)<\/td><td>Moderate (Adapter deployment)<\/td><\/tr><tr><td><strong>Ease of Maintenance<\/strong><\/td><td>\ud83d\udfe2 Easy \u2013 one Helm upgrade for all namespaces<\/td><td>\ud83d\udd35 Moderate \u2013 maintain adapter &amp; Prometheus<\/td><td>\ud83d\udfe3 Easy if Datadog already managed<\/td><td>\ud83d\udfe0 Moderate \u2013 periodic IAM &amp; adapter updates<\/td><\/tr><tr><td><strong>EKS Auto Mode Compatibility<\/strong><\/td><td>\u2705 Fully compatible \u2013 scales pods; NodePools handle nodes<\/td><td>\u2705 Compatible<\/td><td>\u2705 Compatible<\/td><td>\u2705 Compatible<\/td><\/tr><tr><td><strong>Multi-Namespace Scaling<\/strong><\/td><td>\u2705 Native support (Scoped per namespace)<\/td><td>\u2705 Supported<\/td><td>\u2705 Supported<\/td><td>\u2705 Supported<\/td><\/tr><tr><td><strong>Security \/ IAM<\/strong><\/td><td>Uses IRSA or static keys for AWS APIs<\/td><td>No AWS permissions required (depends on Prometheus)<\/td><td>Uses Datadog API key &amp; IAM integration<\/td><td>Uses IRSA for AWS CloudWatch read access<\/td><\/tr><tr><td><strong>Supported Triggers \/ Metrics<\/strong><\/td><td>50+ sources (CloudWatch, Kafka, RabbitMQ, HTTP, Redis, MySQL, etc.)<\/td><td>Limited to Prometheus metrics<\/td><td>Limited to Datadog metrics<\/td><td>Limited to AWS metrics<\/td><\/tr><tr><td><strong>Scales on Events (not metrics)<\/strong><\/td><td>\u2705 Yes<\/td><td>\u274c No<\/td><td>\u274c No<\/td><td>\u274c No<\/td><\/tr><tr><td><strong>Can Combine Multiple Triggers<\/strong><\/td><td>\u2705 Yes (multi-trigger scaling rules)<\/td><td>\u26a0\ufe0f Only via complex PromQL expressions<\/td><td>\u26a0\ufe0f Limited (Datadog composite metrics)<\/td><td>\u26a0\ufe0f Limited (one metric per HPA)<\/td><\/tr><tr><td><strong>Recommended For<\/strong><\/td><td>Event-driven \/ traffic-based workloads (ALB, queues, web APIs)<\/td><td>Resource or app-metric-based workloads<\/td><td>Organizations using Datadog for monitoring &amp; APM<\/td><td>AWS-centric workloads without Prometheus<\/td><\/tr><tr><td><strong>Learning Curve<\/strong><\/td><td>\ud83d\udfe2 Low<\/td><td>\ud83d\udd35 Medium<\/td><td>\ud83d\udfe3 Low<\/td><td>\ud83d\udfe0 Medium<\/td><\/tr><tr><td><strong>Vendor Lock-in<\/strong><\/td><td>Low (Open Source)<\/td><td>Low (OSS ecosystem)<\/td><td>High (Datadog SaaS)<\/td><td>Medium (AWS-only)<\/td><\/tr><tr><td><strong>Community &amp; Ecosystem<\/strong><\/td><td>Very active (CNCF Sandbox Project)<\/td><td>Large (K8s ecosystem standard)<\/td><td>Proprietary (Datadog documentation)<\/td><td>AWS-maintained (moderate community)<\/td><\/tr><tr><td><strong>Use with WAF + ALB<\/strong><\/td><td>\u2705 Seamless (uses ALB TG metrics directly)<\/td><td>\u26a0\ufe0f Need exporter for ALB metrics<\/td><td>\u2705 Seamless (Datadog ALB integration)<\/td><td>\u2705 Seamless (ALB metrics native in CloudWatch)<\/td><\/tr><tr><td><strong>Example Metric<\/strong><\/td><td>CloudWatch \u2192 <code>RequestCountPerTarget<\/code>, <code>TargetResponseTime<\/code><\/td><td>Prometheus \u2192 <code>nginx_ingress_controller_requests_total<\/code><\/td><td>Datadog \u2192 <code>aws.applicationelb.request_count<\/code><\/td><td>CloudWatch \u2192 <code>RequestCountPerTarget<\/code><\/td><\/tr><tr><td><strong>Scale Behavior Visualization<\/strong><\/td><td>KEDA Metrics API + Grafana dashboards<\/td><td>Prometheus \/ Grafana<\/td><td>Datadog Dashboards<\/td><td>CloudWatch Dashboards<\/td><\/tr><tr><td><strong>Maturity (as of 2026)<\/strong><\/td><td>\u2b50\u2b50\u2b50\u2b50\u2b50 (CNCF Incubating)<\/td><td>\u2b50\u2b50\u2b50\u2b50<\/td><td>\u2b50\u2b50\u2b50\u2b50<\/td><td>\u2b50\u2b50\u2b50\u2b50<\/td><\/tr><tr><td><strong>Overall Recommendation (for EKS + ALB)<\/strong><\/td><td>\u2705\u2705\u2705 <strong>Best Option<\/strong><\/td><td>\u2705 Good (if Prometheus is already in place)<\/td><td>\u2699\ufe0f Suitable for Datadog-native orgs<\/td><td>\u2705 Good fallback if KEDA not allowed<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h2 class=\"wp-block-heading\">\ud83c\udf10 1 | Architecture Overview<\/h2>\n\n\n\n<p><strong>Flow:<\/strong><br><code>Client \u2192 DNS \u2192 WAF \u2192 ALB \u2192 TargetGroup \u2192 EKS Service\/Pods<\/code><\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Layer<\/th><th>Purpose<\/th><th>Key AWS \/ K8s Component<\/th><\/tr><\/thead><tbody><tr><td><strong>Edge Security<\/strong><\/td><td>Filter malicious traffic<\/td><td>AWS WAF (Web ACL)<\/td><\/tr><tr><td><strong>Load Balancing<\/strong><\/td><td>Distribute inbound requests<\/td><td>ALB (AWS Load Balancer Controller)<\/td><\/tr><tr><td><strong>Routing<\/strong><\/td><td>Path\/host-based dispatch to namespaces<\/td><td>Kubernetes Ingress<\/td><\/tr><tr><td><strong>Compute<\/strong><\/td><td>Run workloads<\/td><td>EKS Pods\/Deployments<\/td><\/tr><tr><td><strong>Node Capacity<\/strong><\/td><td>Provision nodes automatically<\/td><td>EKS Auto Mode NodePools (Karpenter)<\/td><\/tr><tr><td><strong>Autoscaling Brain<\/strong><\/td><td>Adjust replicas dynamically<\/td><td>KEDA \/ HPA \/ VPA \/ Custom Adapter<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>With <strong>EKS Auto Mode<\/strong>, AWS manages node scaling.<br>Your responsibility is <strong>pod-level scaling<\/strong> \u2014 deciding <em>how many replicas each service needs<\/em> based on traffic or resource metrics.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h2 class=\"wp-block-heading\">\ud83e\udde9 2 | Namespace-Scoped Design Pattern<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Each microservice (e.g., <code>booking<\/code>, <code>auth<\/code>, <code>medical<\/code>, <code>telematics<\/code>) lives in its own <strong>namespace<\/strong>.<\/li>\n\n\n\n<li>Each namespace has its <strong>Ingress, Service, Deployment, ConfigMap, and autoscaler<\/strong> objects.<\/li>\n\n\n\n<li>Optionally, multiple namespaces can share <strong>one ALB<\/strong> via <code>alb.ingress.kubernetes.io\/group.name<\/code> to save cost while keeping per-namespace isolation.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h2 class=\"wp-block-heading\">\u2699\ufe0f 3 | Ingress &amp; WAF Setup (Shared ALB Example)<\/h2>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">apiVersion: networking.k8s.io\/v1\nkind: Ingress\nmetadata:\n  name: booking-ing\n  namespace: booking\n  annotations:\n    kubernetes.io\/ingress.class: alb\n    alb.ingress.kubernetes.io\/group.name: shared-edge\n    alb.ingress.kubernetes.io\/group.order: \"20\"\n    alb.ingress.kubernetes.io\/target-type: ip\n    alb.ingress.kubernetes.io\/scheme: internet-facing\n    alb.ingress.kubernetes.io\/wafv2-acl-arn: arn:aws:wafv2:ap-northeast-1:111111111111:regional\/webacl\/mywebacl\/abcd1234\nspec:\n  rules:\n  - host: api.example.com\n    http:\n      paths:\n      - path: \/booking\n        pathType: Prefix\n        backend:\n          service:\n            name: booking-svc\n            port:\n              number: 80\n<\/code><\/span><\/pre>\n\n\n<p>Each namespace can repeat this pattern using different paths (<code>\/auth<\/code>, <code>\/legal<\/code>, etc.) but share the same <code>group.name<\/code> \u2192 one ALB under one WAF.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h2 class=\"wp-block-heading\">\ud83d\ude80 4 | Autoscaling Options for Pod Level Control<\/h2>\n\n\n\n<p>Below are <strong>five viable mechanisms<\/strong> for pod autoscaling inside EKS.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>#<\/th><th>Method<\/th><th>Scaling Source<\/th><th>Scales To Zero<\/th><th>Works with ALB Metrics<\/th><th>Typical Latency<\/th><th>Setup Time<\/th><th>Maint. Effort<\/th><th>Approx. Cost*<\/th><th>Skill Level<\/th><\/tr><\/thead><tbody><tr><td>1<\/td><td><strong>KEDA<\/strong><\/td><td>External events (CloudWatch ALB, SQS, Prometheus, etc.)<\/td><td>\u2705<\/td><td>\u2705 (native scaler)<\/td><td>30-60 s<\/td><td>\u2699\ufe0f Medium<\/td><td>\ud83e\udde9 Low (once installed)<\/td><td>\ud83d\udcb2\ud83d\udcb2 CloudWatch API calls<\/td><td>Intermediate<\/td><\/tr><tr><td>2<\/td><td><strong>HPA<\/strong><\/td><td>CPU \/ memory \/ custom metrics<\/td><td>\u274c<\/td><td>\u26a0\ufe0f via adapter<\/td><td>15-30 s<\/td><td>\u2699\ufe0f Low<\/td><td>\ud83e\udde9 Low<\/td><td>\ud83d\udcb2 free<\/td><td>Beginner<\/td><\/tr><tr><td>3<\/td><td><strong>VPA<\/strong><\/td><td>Internal resource usage<\/td><td>\u274c<\/td><td>\u274c<\/td><td>N\/A<\/td><td>\u2699\ufe0f Medium<\/td><td>\ud83e\udde9 Low<\/td><td>\ud83d\udcb2 free<\/td><td>Intermediate<\/td><\/tr><tr><td>4<\/td><td><strong>Custom Metric Adapter<\/strong><\/td><td>Prometheus \/ CloudWatch<\/td><td>\u274c<\/td><td>\u2705 with manual mapping<\/td><td>45-60 s<\/td><td>\u2699\ufe0f High<\/td><td>\ud83e\udde9 High<\/td><td>\ud83d\udcb2\ud83d\udcb2 metrics infra<\/td><td>Advanced<\/td><\/tr><tr><td>5<\/td><td><strong>Manual Scaling<\/strong><\/td><td>Human input<\/td><td>\u274c<\/td><td>\u274c<\/td><td>N\/A<\/td><td>\u2699\ufe0f Instant<\/td><td>\ud83e\udde9 High Opex<\/td><td>\ud83d\udcb2 none<\/td><td>Basic<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>* Cost = relative AWS service charges + operational overhead<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h2 class=\"wp-block-heading\">\ud83e\uddee 5 | Detailed Analysis of Each Approach<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">\ud83d\udd39 A | KEDA (Event-Driven Autoscaler)<\/h3>\n\n\n\n<p><strong>How it works:<\/strong><br>KEDA reads external metrics (CloudWatch ALB RequestCountPerTarget, TargetResponseTime, SQS depth, PromQL queries, etc.) and creates an internal HPA.<\/p>\n\n\n\n<p><strong>Pros<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Supports 50 + scalers (AWS, Azure, Kafka, Prometheus, etc.).<\/li>\n\n\n\n<li>Scales <strong>to zero<\/strong> during idle.<\/li>\n\n\n\n<li>Simple YAML (<code>ScaledObject<\/code>) per Deployment.<\/li>\n\n\n\n<li>Works seamlessly with <strong>EKS Auto Mode<\/strong> and <strong>NodePools<\/strong>.<\/li>\n\n\n\n<li>Natively integrates with <strong>CloudWatch ALB<\/strong> metrics.<\/li>\n<\/ul>\n\n\n\n<p><strong>Cons<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Extra component to operate.<\/li>\n\n\n\n<li>CloudWatch polling \u2192 small metric costs and \u2248 1 min delay.<\/li>\n\n\n\n<li>Needs IRSA permissions for CloudWatch API.<\/li>\n<\/ul>\n\n\n\n<p><strong>Setup time:<\/strong> \u2248 1 hr (Helm install + ScaledObject YAMLs)<br><strong>Maintenance:<\/strong> Low (central Helm upgrade + namespace YAMLs)<br><strong>Recommended for:<\/strong> Multi-namespace EKS clusters with real-traffic scaling.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h3 class=\"wp-block-heading\">\ud83d\udd39 B | HPA (Native Horizontal Pod Autoscaler)<\/h3>\n\n\n\n<p><strong>How it works:<\/strong><br>Built into Kubernetes; scales based on CPU and memory by default.<br>Can also use custom metrics with an adapter.<\/p>\n\n\n\n<p><strong>Pros<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Native, stable, zero extra components.<\/li>\n\n\n\n<li>Predictable behavior and fine-grained control.<\/li>\n<\/ul>\n\n\n\n<p><strong>Cons<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Default metrics = CPU \/ memory only.<\/li>\n\n\n\n<li>Cannot scale to zero.<\/li>\n\n\n\n<li>Needs a metric adapter to use ALB metrics.<\/li>\n\n\n\n<li>Not event-driven; reactive after load hits CPU.<\/li>\n<\/ul>\n\n\n\n<p><strong>Setup time:<\/strong> \u2248 30 min<br><strong>Maintenance:<\/strong> Minimal<br><strong>Recommended for:<\/strong> Steady workloads or CPU-bound apps.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h3 class=\"wp-block-heading\">\ud83d\udd39 C | VPA (Vertical Pod Autoscaler)<\/h3>\n\n\n\n<p><strong>How it works:<\/strong><br>Adjusts CPU and memory requests\/limits per pod automatically.<\/p>\n\n\n\n<p><strong>Pros<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Prevents over\/under-provisioning.<\/li>\n\n\n\n<li>Complements KEDA\/HPA.<\/li>\n<\/ul>\n\n\n\n<p><strong>Cons<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>No replica count scaling.<\/li>\n\n\n\n<li>Not suited for traffic bursts.<\/li>\n<\/ul>\n\n\n\n<p><strong>Setup time:<\/strong> \u2248 45 min<br><strong>Maintenance:<\/strong> Low<br><strong>Recommended for:<\/strong> Batch or steady apps to optimize resources.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h3 class=\"wp-block-heading\">\ud83d\udd39 D | Custom Metric Adapters (Prometheus \/ CloudWatch)<\/h3>\n\n\n\n<p><strong>How it works:<\/strong><br>Deploy an <strong>external-metrics adapter<\/strong> exposing selected metrics to HPA.<br>HPA then scales on those metrics.<\/p>\n\n\n\n<p><strong>Pros<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Fine control; use any metric you own.<\/li>\n\n\n\n<li>Integrates into existing monitoring plane.<\/li>\n<\/ul>\n\n\n\n<p><strong>Cons<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Complex to deploy and maintain.<\/li>\n\n\n\n<li>Harder to debug.<\/li>\n\n\n\n<li>No scale-to-zero.<\/li>\n\n\n\n<li>Usually delayed by scrape interval + adapter polling.<\/li>\n<\/ul>\n\n\n\n<p><strong>Setup time:<\/strong> 1 \u2013 2 hrs<br><strong>Maintenance:<\/strong> High<br><strong>Recommended for:<\/strong> Large orgs with centralized Prometheus or Datadog.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h3 class=\"wp-block-heading\">\ud83d\udd39 E | Manual Scaling<\/h3>\n\n\n\n<p><code>kubectl scale deployment &lt;name&gt; --replicas=N<\/code><\/p>\n\n\n\n<p><strong>Pros:<\/strong> 100 % control, simple to understand.<br><strong>Cons:<\/strong> No automation; wastes capacity; high operational risk.<br><strong>Use only for:<\/strong> testing or stable low-traffic sites.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h2 class=\"wp-block-heading\">\ud83d\udca1 6 | KEDA Setup Walkthrough (for EKS + ALB)<\/h2>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Install KEDA<\/strong> <code>helm repo add kedacore https:\/\/kedacore.github.io\/charts helm install keda kedacore\/keda -n keda --create-namespace<\/code><\/li>\n\n\n\n<li><strong>Enable IRSA (for CloudWatch)<\/strong> <code>eksctl utils associate-iam-oidc-provider --cluster my-eks --approve<\/code><\/li>\n\n\n\n<li><strong>IAM Policy<\/strong> <code>{ \"Version\":\"2012-10-17\", \"Statement\":[{\"Effect\":\"Allow\",\"Action\":[ \"cloudwatch:GetMetricData\",\"cloudwatch:GetMetricStatistics\", \"cloudwatch:ListMetrics\",\"cloudwatch:DescribeAlarms\" ],\"Resource\":\"*\"}] }<\/code><\/li>\n\n\n\n<li><strong>ServiceAccount + TriggerAuthentication<\/strong> <code>apiVersion: v1 kind: ServiceAccount metadata: name: svc-traffic-autoscale namespace: booking annotations: eks.amazonaws.com\/role-arn: arn:aws:iam::&lt;ACCOUNT_ID&gt;:role\/eks-traffic-autoscale --- apiVersion: keda.sh\/v1alpha1 kind: TriggerAuthentication metadata: name: alb-cw-auth namespace: booking spec: podIdentity: provider: aws<\/code><\/li>\n\n\n\n<li><strong>ScaledObject<\/strong> <code>apiVersion: keda.sh\/v1alpha1 kind: ScaledObject metadata: name: booking-traffic namespace: booking spec: scaleTargetRef: name: booking-deployment minReplicaCount: 2 maxReplicaCount: 30 triggers: - type: aws-cloudwatch authenticationRef: name: alb-cw-auth metadata: namespace: AWS\/ApplicationELB metricName: RequestCountPerTarget dimensionName: TargetGroup dimensionValue: targetgroup\/k8s-xyz\/abc123456 statistic: Sum period: \"60\" metricUnit: Count targetValue: \"100\"<\/code><\/li>\n\n\n\n<li><strong>Observe Scaling<\/strong> <code>kubectl get hpa -n booking kubectl get pods -n booking -w<\/code><\/li>\n<\/ol>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h2 class=\"wp-block-heading\">\ud83d\udcc8 7 | Performance &amp; Cost Considerations<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Factor<\/th><th>KEDA<\/th><th>HPA<\/th><th>VPA<\/th><th>Custom Adapter<\/th><\/tr><\/thead><tbody><tr><td><strong>Responsiveness<\/strong><\/td><td>30-60 s<\/td><td>15-30 s<\/td><td>N\/A<\/td><td>45-60 s<\/td><\/tr><tr><td><strong>Infra Cost<\/strong><\/td><td>Low (CloudWatch polling)<\/td><td>None<\/td><td>None<\/td><td>Medium (Prometheus infra)<\/td><\/tr><tr><td><strong>Setup Overhead<\/strong><\/td><td>Medium<\/td><td>Low<\/td><td>Medium<\/td><td>High<\/td><\/tr><tr><td><strong>Maintenance<\/strong><\/td><td>Low<\/td><td>Low<\/td><td>Low<\/td><td>High<\/td><\/tr><tr><td><strong>Complexity<\/strong><\/td><td>Medium<\/td><td>Low<\/td><td>Low<\/td><td>High<\/td><\/tr><tr><td><strong>Best for<\/strong><\/td><td>Traffic \/ Event driven<\/td><td>CPU\/Mem<\/td><td>Resource tuning<\/td><td>Centralized metrics<\/td><\/tr><tr><td><strong>Scale-to-Zero<\/strong><\/td><td>\u2705<\/td><td>\u274c<\/td><td>\u274c<\/td><td>\u274c<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h2 class=\"wp-block-heading\">\ud83e\udde0 8 | Decision Matrix<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Requirement<\/th><th>Best Choice<\/th><th>Reason<\/th><\/tr><\/thead><tbody><tr><td>Real ALB traffic scaling<\/td><td><strong>KEDA<\/strong><\/td><td>Direct CloudWatch integration<\/td><\/tr><tr><td>CPU\/memory bound apps<\/td><td><strong>HPA<\/strong><\/td><td>Native simple autoscaler<\/td><\/tr><tr><td>Optimize pod resources over time<\/td><td><strong>VPA<\/strong><\/td><td>Adjusts requests\/limits<\/td><\/tr><tr><td>Central metrics team wants Prometheus-based control<\/td><td><strong>Custom Adapter + HPA<\/strong><\/td><td>Full metric plane<\/td><\/tr><tr><td>Low-traffic or manual control<\/td><td><strong>Manual<\/strong><\/td><td>No automation needed<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h2 class=\"wp-block-heading\">\ud83e\uddf0 9 | Combining Approaches<\/h2>\n\n\n\n<p>A production-grade EKS stack often mixes them:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Layer<\/th><th>Tool<\/th><th>Role<\/th><\/tr><\/thead><tbody><tr><td><strong>Replica Scaling<\/strong><\/td><td>KEDA + HPA<\/td><td>Respond to traffic &amp; CPU<\/td><\/tr><tr><td><strong>Resource Tuning<\/strong><\/td><td>VPA<\/td><td>Adjust limits automatically<\/td><\/tr><tr><td><strong>Node Scaling<\/strong><\/td><td>EKS Auto Mode (NodePools)<\/td><td>Provide capacity<\/td><\/tr><tr><td><strong>Monitoring<\/strong><\/td><td>CloudWatch + AMP + Grafana<\/td><td>Visibility into metrics<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h2 class=\"wp-block-heading\">\ud83d\udd12 10 | Security and Auth Notes<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Keep <strong>Firebase OIDC at pod level<\/strong> (not ALB listener), which avoids auth redirect limits.<\/li>\n\n\n\n<li>Enable <strong>IRSA<\/strong> for KEDA &amp; pods requiring AWS API access.<\/li>\n\n\n\n<li>WAF rules protect ALB from volumetric attacks before KEDA reacts.<\/li>\n\n\n\n<li>Monitor <strong>5xx errors<\/strong> + <strong>TargetResponseTime<\/strong> to guard against scaling loops.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h2 class=\"wp-block-heading\">\ud83e\udded 11 | Final Recommendation<\/h2>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>For your multi-namespace, WAF-protected, ALB-routed EKS cluster running in Full Auto Mode,<br><strong>KEDA is the best fit<\/strong> for traffic-driven autoscaling:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Event-driven and responsive to real user load.<\/li>\n\n\n\n<li>Scales independently per namespace\/service.<\/li>\n\n\n\n<li>Integrates cleanly with EKS NodePools for capacity.<\/li>\n\n\n\n<li>Minimizes cost via scale-to-zero and fine-grained rules.<\/li>\n<\/ul>\n<\/blockquote>\n\n\n\n<p>Use <strong>HPA<\/strong> as a fallback for CPU-based logic, <strong>VPA<\/strong> for optimization, and <strong>custom adapters<\/strong> only when you already maintain Prometheus or Datadog metric infrastructure.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h3 class=\"wp-block-heading\">\ud83c\udfc1 Summary Matrix<\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Dimension<\/th><th>Best Fit<\/th><\/tr><\/thead><tbody><tr><td><strong>Speed to implement<\/strong><\/td><td>HPA<\/td><\/tr><tr><td><strong>Responsiveness to traffic<\/strong><\/td><td>KEDA<\/td><\/tr><tr><td><strong>Ease of maintenance<\/strong><\/td><td>KEDA \/ HPA<\/td><\/tr><tr><td><strong>Cost efficiency<\/strong><\/td><td>KEDA (scale-to-zero)<\/td><\/tr><tr><td><strong>Complex metric logic<\/strong><\/td><td>Custom Adapter<\/td><\/tr><tr><td><strong>Resource tuning<\/strong><\/td><td>VPA<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h3 class=\"wp-block-heading\">\ud83d\udcda References &amp; Further Reading<\/h3>\n\n\n\n<p>AWS Blog \u2013 <a href=\"https:\/\/aws.amazon.com\/blogs\/mt\/proactive-autoscaling-of-kubernetes-workloads-with-keda-using-metrics-ingested-into-amazon-cloudwatch\/\" target=\"_blank\" rel=\"noopener\">Autoscaling EKS with KEDA &amp; CloudWatch<\/a><br>AWS Docs \u2013 <a href=\"https:\/\/docs.aws.amazon.com\/eks\/latest\/best-practices\/automode.html\" target=\"_blank\" rel=\"noopener\">EKS Auto Mode &amp; NodePools<\/a><br>KEDA Docs \u2013 <a href=\"https:\/\/keda.sh\/docs\/latest\/scalers\/aws-cloudwatch\/\" target=\"_blank\" rel=\"noopener\">CloudWatch Scaler<\/a><br>SpectroCloud \u2013 <a href=\"https:\/\/www.spectrocloud.com\/blog\/kubernetes-autoscaling-patterns-hpa-vpa-and-keda\" target=\"_blank\" rel=\"noopener\">Kubernetes Autoscaling Patterns: HPA, VPA &amp; KEDA<\/a><\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<p>\u2705 <strong>Final Takeaway:<\/strong><br>If you need <em>hands-off, event-driven, traffic-aware, namespace-isolated<\/em> scaling for an ALB-fronted EKS cluster,<br><strong>KEDA + EKS Auto Mode (NodePools)<\/strong> is the modern production-grade combination\u2014<br>balancing performance, cost, and operational simplicity for any multi-service cloud platform.<\/p>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Comparing KEDA, HPA, VPA &amp; Custom Adapters for Real-World Scaling with Cost, Complexity &amp; Best Practices Category KEDA (Event-driven) Prometheus-based (Adapter\/HPA) Datadog-based (Cluster Agent) CloudWatch-based (Adapter\/HPA) Primary Function Event-driven autoscaler&#8230; <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"_joinchat":[],"footnotes":""},"categories":[11138],"tags":[],"class_list":["post-54055","post","type-post","status-publish","format-standard","hentry","category-best-tools"],"_links":{"self":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/54055","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/comments?post=54055"}],"version-history":[{"count":2,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/54055\/revisions"}],"predecessor-version":[{"id":59868,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/54055\/revisions\/59868"}],"wp:attachment":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/media?parent=54055"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/categories?post=54055"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/tags?post=54055"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}