<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Carlo Columna</title>
    <description>The latest articles on Forem by Carlo Columna (@carlocolumna).</description>
    <link>https://forem.com/carlocolumna</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1100903%2F065c8d2a-6429-4918-ae3a-49bf31d5adf9.png</url>
      <title>Forem: Carlo Columna</title>
      <link>https://forem.com/carlocolumna</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/carlocolumna"/>
    <language>en</language>
    <item>
      <title>KEDA in Amazon EKS Part 2: Scale Based On AWS SQS Queue</title>
      <dc:creator>Carlo Columna</dc:creator>
      <pubDate>Sat, 01 Jul 2023 02:25:02 +0000</pubDate>
      <link>https://forem.com/carlocolumna/keda-in-amazon-eks-part-2-scale-based-on-aws-sqs-queue-5g4d</link>
      <guid>https://forem.com/carlocolumna/keda-in-amazon-eks-part-2-scale-based-on-aws-sqs-queue-5g4d</guid>
      <description>&lt;p&gt;Kia ora everyone!&lt;/p&gt;

&lt;p&gt;Welcome to the second part of a series of articles discussing the use of KEDA (Kubernetes Event-driven Autoscaling) for the autoscaling of Amazon EKS workloads. If you haven't, check out the first part below to learn why and how to install KEDA in Amazon EKS.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/carlocolumna/keda-in-amazon-eks-why-and-how-to-install-keda-44pa"&gt;KEDA in Amazon EKS Part 1: Why and How to Install KEDA&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This part two of this blog series focuses on scaling based on AWS SQS Queue.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fothlucxdw7cskybsyly8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fothlucxdw7cskybsyly8.png" alt="Meme when the queue jumps and there's only one pod"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The structure of this blog would be as follows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Prerequisites&lt;/li&gt;
&lt;li&gt;Create a demo application&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scale based on two identity owner models:&lt;/strong&gt; Here we'll go deeper in explaining the two models which are not extensively covered in the KEDA documentation.&lt;/li&gt;
&lt;li&gt;Common errors&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before we can proceed, let's lay out the prerequisites:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS account&lt;/li&gt;
&lt;li&gt;Access to an Amazon EKS cluster&lt;/li&gt;
&lt;li&gt;IAM roles for service accounts (IRSA) setup for the EKS cluster. See &lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html" rel="noopener noreferrer"&gt;here&lt;/a&gt; for more details.&lt;/li&gt;
&lt;li&gt;KEDA installed in the EKS cluster, see &lt;a href="https://dev.to/carlocolumna/keda-in-amazon-eks-why-and-how-to-install-keda-44pa"&gt;part 1&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Terminal with kubectl&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Create a demo application
&lt;/h2&gt;

&lt;p&gt;We'll use a demo application and set it up across different accounts to best illustrate the steps we'll go through. We will be using Terraform and Helm to deploy it.&lt;/p&gt;

&lt;p&gt;Our demo application has the following requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It is a simple NGINX application deployed in an EKS cluster in Account A in Oregon.&lt;/li&gt;
&lt;li&gt;It has an IAM Role created in Account C in Canada Central.
KEDA is deployed and running on the same EKS cluster using an IAM Role created in Account B in N. Virginia.&lt;/li&gt;
&lt;li&gt;It scales based on a queue in Account C in Canada Central.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To better understand the scenario, the following is the architecture diagram for KEDA and our demo application.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;KEDA and Demo Application Architecture&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi3rrfda3tmh42v5ihc9s.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi3rrfda3tmh42v5ihc9s.jpg" alt="KEDA and Demo Application Architecture"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Before we can install our demo application, we'll start by creating an IAM Role for it using Terraform, like so,&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;

&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_iam_role"&lt;/span&gt; &lt;span class="s2"&gt;"demo-app"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;provider&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;account_c&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;               &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"demo-app"&lt;/span&gt;
  &lt;span class="nx"&gt;assume_role_policy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;demo-app-trust-policy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;For now, we are creating a role with no permissions. Later on, we'll update the role by granting it permission to access AWS resources.&lt;/p&gt;

&lt;p&gt;We also set up the role's trust policy so our demo application can assume the role.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;

&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_iam_role"&lt;/span&gt; &lt;span class="s2"&gt;"demo-app"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"aws_iam_policy_document"&lt;/span&gt; &lt;span class="s2"&gt;"demo-app-trust-policy"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;statement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;actions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="s2"&gt;"sts:AssumeRoleWithWebIdentity"&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nx"&gt;principals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Federated"&lt;/span&gt;
      &lt;span class="nx"&gt;identifiers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:iam::111122223333:oidc-provider/oidc.eks.region-code.amazonaws.com/id/EXAMPLED539D4633E53DE1B71EXAMPLE"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;condition&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;test&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"StringEquals"&lt;/span&gt;
      &lt;span class="k"&gt;variable&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"oidc.eks.region-code.amazonaws.com/id/EXAMPLED539D4633E53DE1B71EXAMPLE:sub"&lt;/span&gt;
      &lt;span class="nx"&gt;values&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"system:serviceaccount:demo-ns:demo-app"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Run &lt;code&gt;terraform apply&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now, let's create the Helm manifest of our basic deployment using the NGINX image.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deployment&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;demo-app&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;demo-app&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;demo-ns&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;demo-app&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;demo-app&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;serviceAccountName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;demo-app-sa&lt;/span&gt;
      &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ServiceAccount&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;demo-app-sa&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;demo-ns&lt;/span&gt;
  &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;eks.amazonaws.com/role-arn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;arn:aws:iam::111122223333:role/demo-app&lt;/span&gt; &lt;span class="c1"&gt;# demo-app IAM role ARN created from the previous step&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This creates a deployment with a &lt;em&gt;ServiceAccount&lt;/em&gt;. If you noticed, we have added the IAM role we created above as an annotation so our application can access AWS resources. Let's save this file as &lt;code&gt;demo-app.yaml&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To deploy, ideally, you package this manifest into a Helm chart or use kustomize. But to keep things short and simple in this blog, let's simply run a &lt;code&gt;kubectl apply -f demo-app.yaml&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scaling with KEDA
&lt;/h2&gt;

&lt;p&gt;We've come to the more exciting part of this blog, setting up and testing the scaling using AWS SQS Queue. Before we can start, there is one important security consideration we have to make.&lt;/p&gt;

&lt;p&gt;There are two ways that KEDA can get permission to access the SQS Queue and scale a workload. This is determined by setting the &lt;code&gt;identityOwner&lt;/code&gt; to either &lt;code&gt;pod&lt;/code&gt; which is the default, or &lt;code&gt;operator&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;pod&lt;/strong&gt;: This sets the &lt;code&gt;keda-operator&lt;/code&gt; to temporarily assume the role of the demo application. This means that the demo application role should have the necessary permissions to access the SQS Queue. We'll call this the Pod Identity Owner model.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;operator&lt;/strong&gt; : Grant the &lt;code&gt;keda-operator&lt;/code&gt; direct access to the AWS SQS queue. We'll call this the Operator Identity Owner model.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note that in this blog, when talking about permissions and IAM, the KEDAs role is the same as the &lt;code&gt;keda-operator&lt;/code&gt;s IAM role.&lt;/p&gt;

&lt;h3&gt;
  
  
  What model should you use?
&lt;/h3&gt;

&lt;p&gt;This depends on what works best in your setup. Let's summarise the pros and cons of each model.&lt;/p&gt;

&lt;h4&gt;
  
  
  Pod Identity Owner model
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Good&lt;/strong&gt;, because we don't have to know beforehand what privileges we need to give KEDA as long as our application has already access to the event source.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Good&lt;/strong&gt;, in cases where KEDA cannot be given direct access to the event source&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bad&lt;/strong&gt;, because KEDA gets all the permission of all workload that uses this permission model. This probably includes permissions that are not required for scaling purposes (such as write privilege) and thus breaking the principle of least privilege.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bad&lt;/strong&gt;, because it requires a bit more complicated setup&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Operator Identity Owner model
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Good&lt;/strong&gt;, because KEDA only gets the permission it needs to trigger scaling so it follows the principle of least privilege&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Good&lt;/strong&gt;, because it's a simpler setup&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bad&lt;/strong&gt;, because it requires more initial understanding what are the privileges that we need to grant KEDA to access an events source&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bad&lt;/strong&gt;, in cases where the event source cannot be updated or set to be accessed by KEDA (e.g. if access policy cannot be set to the event source)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;From a security and simplicity perspective, the Operator Identity Owner model is a better option. This is more so if you have an application that has access to sensitive information that you don't want &lt;code&gt;keda-operator&lt;/code&gt; to get access to. However, there might be cases where you are restricted to using the Pod Identity Owner model. In these cases, you just have to be aware that the &lt;code&gt;keda-operator&lt;/code&gt; gets all the permissions of the application role.&lt;/p&gt;

&lt;p&gt;In this blog, we'll show how to scale each identity owner model starting with the default &lt;code&gt;identityOwner: pod&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pod Identity Owner Model
&lt;/h3&gt;

&lt;p&gt;For this model to work it's important to understand the requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We need to add an &lt;em&gt;AssumeRole&lt;/em&gt; policy to the demo application roles trust policy to allow &lt;code&gt;keda-operator&lt;/code&gt; to assume it&lt;/li&gt;
&lt;li&gt;The demo application role has the permission to access the queue&lt;/li&gt;
&lt;li&gt;We need to add an &lt;em&gt;AssumeRole&lt;/em&gt; policy to the &lt;code&gt;keda-operator&lt;/code&gt; role so it knows what role to assume&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Going back to our demo applications architecture and incorporating the use of the Pod Identity Owner model, the architecture will look like below.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;KEDA and Demo Application: Pod Identity Owner Model&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe70nk70rpwr2q6ezncxx.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe70nk70rpwr2q6ezncxx.jpg" alt="KEDA and Demo Application: Pod Identity Owner Model"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this model, the demo application through its IAM role has access to the AWS SQS Queue. Meanwhile, KEDA can temporarily assume the demo applications role to get access to the same AWS SQS Queue to trigger scaling.&lt;/p&gt;

&lt;p&gt;Here is the summary of the steps we'll go through.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create AWS SQS Queue&lt;/li&gt;
&lt;li&gt;Update the IAM Role of the demo application&lt;/li&gt;
&lt;li&gt;Update the IAM Role of KEDA&lt;/li&gt;
&lt;li&gt;Create scaler&lt;/li&gt;
&lt;li&gt;Test scaling&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Steps
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;1. Create AWS SQS Queue&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Using Terraform we can simply create a queue like so,&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;

&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_sqs_queue"&lt;/span&gt; &lt;span class="s2"&gt;"aws_sqs"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;provider&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;account_c&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"demo-app-sqs"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"demo_app_sqs_url"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_sqs_queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_sqs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Run &lt;code&gt;terraform apply&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Update the IAM Role of the demo application&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now, let's update the role of our demo application by doing two things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Grant our demo application permission to access the queue&lt;/li&gt;
&lt;li&gt;Add a policy to allow KEDA to assume it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the first one, let's add permission to access the queue created in the previous step like so,&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;

&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_iam_role"&lt;/span&gt; &lt;span class="s2"&gt;"demo-app"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"aws_iam_policy_document"&lt;/span&gt; &lt;span class="s2"&gt;"demo-app-trust-policy"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"aws_iam_policy_document"&lt;/span&gt; &lt;span class="s2"&gt;"sqs-policy-document"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;statement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;sid&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"SQS"&lt;/span&gt;
    &lt;span class="nx"&gt;effect&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Allow"&lt;/span&gt;
    &lt;span class="nx"&gt;actions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="s2"&gt;"sqs:GetQueueAttributes"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;#Add IAM action to the demo-app role&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nx"&gt;resources&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="nx"&gt;aws_sqs_queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_sqs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_iam_role_policy"&lt;/span&gt; &lt;span class="s2"&gt;"sqs-policy"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;depends_on&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;aws_sqs_queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_sqs&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;provider&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;account_c&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"demo-app-sqs-policy"&lt;/span&gt;
  &lt;span class="nx"&gt;role&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_iam_role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;demo-app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;policy&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_iam_policy_document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sqs-policy-document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;For the second one, let's add an &lt;em&gt;sts:AssumeRole&lt;/em&gt; policy to our demo applications trust policy passing in the &lt;code&gt;keda-operator&lt;/code&gt;s role like so,&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;

&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_iam_role"&lt;/span&gt; &lt;span class="s2"&gt;"demo-app"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"aws_iam_policy_document"&lt;/span&gt; &lt;span class="s2"&gt;"demo-app-trust-policy"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="nx"&gt;statement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;sid&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"AssumeRoleKedaOperator"&lt;/span&gt;
    &lt;span class="nx"&gt;effect&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Allow"&lt;/span&gt;
    &lt;span class="nx"&gt;actions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="s2"&gt;"sts:AssumeRole"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nx"&gt;principals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"AWS"&lt;/span&gt;
      &lt;span class="nx"&gt;identifiers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keda_operator_role&lt;/span&gt; &lt;span class="c1"&gt;# "arn:aws:iam::111122223333:role/keda-operator"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;statement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;actions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="s2"&gt;"sts:AssumeRoleWithWebIdentity"&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nx"&gt;principals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Federated"&lt;/span&gt;
      &lt;span class="nx"&gt;identifiers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:iam::111122223333:oidc-provider/oidc.eks.region-code.amazonaws.com/id/EXAMPLED539D4633E53DE1B71EXAMPLE"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;condition&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;test&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"StringEquals"&lt;/span&gt;
      &lt;span class="k"&gt;variable&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"oidc.eks.region-code.amazonaws.com/id/EXAMPLED539D4633E53DE1B71EXAMPLE:sub"&lt;/span&gt;
      &lt;span class="nx"&gt;values&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"system:serviceaccount:demo-ns:demo-app"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Run &lt;code&gt;terraform apply&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Update the IAM Role of KEDA&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's update the role of &lt;code&gt;keda-operator&lt;/code&gt; so it knows what role to assume by adding an &lt;em&gt;sts:AssumeRole&lt;/em&gt; policy passing in our demo application's role.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;

&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_iam_role"&lt;/span&gt; &lt;span class="s2"&gt;"keda-operator"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"aws_iam_policy_document"&lt;/span&gt; &lt;span class="s2"&gt;"keda-operator-trust-policy"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"aws_iam_policy_document"&lt;/span&gt; &lt;span class="s2"&gt;"assume-role-policy-document"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;statement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;sid&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"sts"&lt;/span&gt;
    &lt;span class="nx"&gt;effect&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Allow"&lt;/span&gt;
    &lt;span class="nx"&gt;actions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="s2"&gt;"sts:AssumeRole"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nx"&gt;resources&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;application_roles_list&lt;/span&gt; &lt;span class="c1"&gt;# ["arn:aws:iam::111122223333:role/demo-app"]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_iam_role_policy"&lt;/span&gt; &lt;span class="s2"&gt;"assume-role-policy"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;provider&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;account_b&lt;/span&gt;
  &lt;span class="nx"&gt;count&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;application_roles_list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="c1"&gt;# This is needed otherwise Terraform will fail to create this resource when the role list is empty&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"keda-operator-assume-role-policy"&lt;/span&gt;
  &lt;span class="nx"&gt;role&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_iam_role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keda-operator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;policy&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_iam_policy_document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;assume-role-policy-document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Run &lt;code&gt;terraform apply&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Create scaler&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Okay now, let's shift from AWS stuff to KEDA stuff. Let's start by creating the scaler.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;KEDA scalers can both detect if a deployment should be activated or deactivated, and feed custom metrics for a specific event source.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To create a scaler, we have to define a &lt;em&gt;ScaledObject&lt;/em&gt; custom resource.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The &lt;em&gt;ScaledObject&lt;/em&gt; Custom Resource definition is used to define how KEDA should scale your application and what the triggers are.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The &lt;em&gt;ScaledObject&lt;/em&gt; resource specifications are defined &lt;a href="https://keda.sh/docs/2.9/concepts/scaling-deployments/#scaledobject-spec" rel="noopener noreferrer"&gt;here&lt;/a&gt;. As part of the specification, we also have to define a trigger, which in our case is an AWS SQS Queue. The specification for AWS SQS Queue can be found &lt;a href="https://keda.sh/docs/2.9/scalers/aws-sqs/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Following these specifications, our scaler configuration would be,&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;keda.sh/v1alpha1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ScaledObject&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;demo-app-scaledobject&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;demo-ns&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;minReplicaCount&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;  &lt;span class="c1"&gt;# scale down to 0&lt;/span&gt;
  &lt;span class="na"&gt;maxReplicaCount&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10&lt;/span&gt; 
  &lt;span class="na"&gt;pollingInterval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;30&lt;/span&gt; 
  &lt;span class="na"&gt;scaleTargetRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;demo-app&lt;/span&gt;    &lt;span class="c1"&gt;# name of our demo-app deployment&lt;/span&gt;
  &lt;span class="na"&gt;triggers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws-sqs-queue&lt;/span&gt;
    &lt;span class="na"&gt;authenticationRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;demo-app-trigger-auth-aws-credentials&lt;/span&gt;  &lt;span class="c1"&gt;# trigger auth object name&lt;/span&gt;
    &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;queueURL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://sqs.ca-central-1.amazonaws.com/111122223333/demo-app-sqs&lt;/span&gt;
      &lt;span class="na"&gt;queueLength&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2"&lt;/span&gt; &lt;span class="c1"&gt;# 2 messages per pod &lt;/span&gt;
      &lt;span class="na"&gt;awsRegion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ca-central-1&lt;/span&gt;
      &lt;span class="na"&gt;identityOwner&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pod&lt;/span&gt; &lt;span class="c1"&gt;# defaults to pod if unset&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;keda.sh/v1alpha1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TriggerAuthentication&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;demo-app-trigger-auth-aws-credentials&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;demo-ns&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;podIdentity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws-eks&lt;/span&gt; &lt;span class="c1"&gt;# or aws-kiam when using kiam&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Add this configuration to our manifest file, &lt;code&gt;demo-app.yaml&lt;/code&gt;. We then deploy these resources on the same namespace as our demo application simply by running &lt;code&gt;kubectl apply -f demo-app.yaml&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Describing our &lt;em&gt;ScaledObject&lt;/em&gt; by running &lt;code&gt;kubectl describe ScaledObject demo-app-scaledobject -n demo-ns&lt;/code&gt; shows it's active and happy.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

Status:
  Conditions:
    Message:  ScaledObject is defined correctly and is ready &lt;span class="k"&gt;for &lt;/span&gt;scaling
    Reason:   ScaledObjectReady
    Status:   True
    Type:     Ready
    Message:  Scaling is not performed because triggers are not active
    Reason:   ScalerNotActive
    Status:   False
    Type:     Active
    Message:  No fallbacks are active on this scaled object
    Reason:   NoFallbackFound
    Status:   False
    Type:     Fallback
  External Metric Names:
    s0-aws-sqs-demo-app
  Health:
    s0-aws-sqs-demo-app:
      Number Of Failures:  0
      Status:              Happy
  Hpa Name:                keda-hpa-demo-app
  Last Active Time:        2023-02-16T20:18:48Z
  Original Replica Count:  1
  Scale Target GVKR:
    Group:            apps
    Kind:             Deployment
    Resource:         deployments
    Version:          v1
  Scale Target Kind:  apps/v1.Deployment
Events:               &amp;lt;none&amp;gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Let's check the logs of our KEDA deployments to see what they say,&lt;/p&gt;

&lt;p&gt;&lt;code&gt;keda-operator&lt;/code&gt; log:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

INFO Reconciling ScaledObject &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"controller"&lt;/span&gt;: &lt;span class="s2"&gt;"scaledobject"&lt;/span&gt;, &lt;span class="s2"&gt;"controllerGroup"&lt;/span&gt;: &lt;span class="s2"&gt;"keda.sh"&lt;/span&gt;, &lt;span class="s2"&gt;"controllerKind"&lt;/span&gt;: &lt;span class="s2"&gt;"ScaledObject"&lt;/span&gt;, &lt;span class="s2"&gt;"scaledObject"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"name"&lt;/span&gt;:&lt;span class="s2"&gt;"demo-app-scaledobject"&lt;/span&gt;,&lt;span class="s2"&gt;"namespace"&lt;/span&gt;:&lt;span class="s2"&gt;"demo-ns"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;, &lt;span class="s2"&gt;"namespace"&lt;/span&gt;: &lt;span class="s2"&gt;"demo-ns"&lt;/span&gt;, &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"demo-app"&lt;/span&gt;, &lt;span class="s2"&gt;"reconcileID"&lt;/span&gt;: &lt;span class="s2"&gt;"e40b3625-6794-4358-b285-a9a57194edee"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;code&gt;keda-operator-metrics-apiserver&lt;/code&gt; log:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

1 trace.go:205] Trace[87882572]: &lt;span class="s2"&gt;"List"&lt;/span&gt; url:/apis/external.metrics.k8s.io/v1beta1/namespaces/demo-ns/s0-aws-sqs-demo-app,user-agent:kube-controller-manager/v1.22.16 &lt;span class="o"&gt;(&lt;/span&gt;linux/amd64&lt;span class="o"&gt;)&lt;/span&gt; kubernetes/52e500d/system:serviceaccount:kube-system:horizontal-pod-autoscaler,audit-id:1570ee53-ba50-4de8-81c7-727482dca392,client:172.16.113.69,accept:application/vnd.kubernetes.protobuf, &lt;span class="k"&gt;*&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;,protocol:HTTP/2.0 &lt;span class="o"&gt;(&lt;/span&gt;20-Feb-2023 05:04:01.958&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;total &lt;span class="nb"&gt;time&lt;/span&gt;: 986ms&lt;span class="o"&gt;)&lt;/span&gt;:


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Both KEDA deployments acknowledge and reconciles with the new &lt;em&gt;ScaledObject&lt;/em&gt; we created.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;keda-operator&lt;/code&gt; has also created an &lt;em&gt;HPA&lt;/em&gt; for our demo application.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl get hpa &lt;span class="nt"&gt;-n&lt;/span&gt; demo-ns
NAME                REFERENCE             TARGETS             MINPODS   MAXPODS   REPLICAS   AGE
keda-hpa-demo-app   Deployment/demo-app   &amp;lt;unknown&amp;gt;/1 &lt;span class="o"&gt;(&lt;/span&gt;avg&lt;span class="o"&gt;)&lt;/span&gt;   1         10        0          5s


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;From now on, this &lt;em&gt;HPA&lt;/em&gt; will be managed &lt;code&gt;keda-operator&lt;/code&gt; to set the number of replicas based on the metrics provided by the &lt;code&gt;keda-operator-metrics-apiserver&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Test scaling&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To trigger scaling, let's send some messages to our queue using AWS Console. Let's send 4 messages to target 2 replicas.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftyjh5d7bkl84y9qtshba.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftyjh5d7bkl84y9qtshba.png" alt="Sending messages to the queue"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Going back to the cluster, let's check the events from our &lt;em&gt;ScaledObject&lt;/em&gt; and &lt;em&gt;HPA&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;keda-hpa-demo-app&lt;/code&gt; events:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

Conditions:
  Type            Status  Reason              Message
  &lt;span class="nt"&gt;----&lt;/span&gt;            &lt;span class="nt"&gt;------&lt;/span&gt;  &lt;span class="nt"&gt;------&lt;/span&gt;              &lt;span class="nt"&gt;-------&lt;/span&gt;
  AbleToScale     True    SucceededRescale    the HPA controller was able to update the target scale to 2
  ScalingActive   True    ValidMetricFound    the HPA was able to successfully calculate a replica count from external metric s0-aws-sqs-demo-app&lt;span class="o"&gt;(&lt;/span&gt;&amp;amp;LabelSelector&lt;span class="o"&gt;{&lt;/span&gt;MatchLabels:map[string]string&lt;span class="o"&gt;{&lt;/span&gt;scaledobject.keda.sh/name: demo-app,&lt;span class="o"&gt;}&lt;/span&gt;,MatchExpressions:[]LabelSelectorRequirement&lt;span class="o"&gt;{}&lt;/span&gt;,&lt;span class="o"&gt;})&lt;/span&gt;
  ScalingLimited  False   DesiredWithinRange  the desired count is within the acceptable range
Events:
  Type    Reason             Age               From                       Message
  &lt;span class="nt"&gt;----&lt;/span&gt;    &lt;span class="nt"&gt;------&lt;/span&gt;             &lt;span class="nt"&gt;----&lt;/span&gt;              &lt;span class="nt"&gt;----&lt;/span&gt;                       &lt;span class="nt"&gt;-------&lt;/span&gt;
  Normal  SuccessfulRescale  10s &lt;span class="o"&gt;(&lt;/span&gt;x2 over 4d&lt;span class="o"&gt;)&lt;/span&gt;  horizontal-pod-autoscaler  New size: 2&lt;span class="p"&gt;;&lt;/span&gt; reason: external metric s0-aws-sqs-demo-app&lt;span class="o"&gt;(&lt;/span&gt;&amp;amp;LabelSelector&lt;span class="o"&gt;{&lt;/span&gt;MatchLabels:map[string]string&lt;span class="o"&gt;{&lt;/span&gt;scaledobject.keda.sh/name: demo-app,&lt;span class="o"&gt;}&lt;/span&gt;,MatchExpressions:[]LabelSelectorRequirement&lt;span class="o"&gt;{}&lt;/span&gt;,&lt;span class="o"&gt;})&lt;/span&gt; above target


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;code&gt;demo-app-scaledobject&lt;/code&gt; events:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

Events:
  Type    Reason                    Age               From           Message
  &lt;span class="nt"&gt;----&lt;/span&gt;    &lt;span class="nt"&gt;------&lt;/span&gt;                    &lt;span class="nt"&gt;----&lt;/span&gt;              &lt;span class="nt"&gt;----&lt;/span&gt;           &lt;span class="nt"&gt;-------&lt;/span&gt;
  Normal  KEDAScaleTargetActivated  10s &lt;span class="o"&gt;(&lt;/span&gt;x3 over 4d&lt;span class="o"&gt;)&lt;/span&gt;  keda-operator  Scaled apps/v1.Deployment demo-ns/demo-app from 0 to 2


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Putting a watch on the &lt;code&gt;demo-ns&lt;/code&gt; shows the new pods that get created as a result of the scaling,&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl get pods &lt;span class="nt"&gt;-n&lt;/span&gt; demo-ns &lt;span class="nt"&gt;-w&lt;/span&gt;
NAME                           READY   STATUS              RESTARTS   AGE
demo-app-674d8c455c-n7svp      0/1     Pending             0          0s
demo-app-674d8c455c-n7svp      0/1     Pending             0          0s
demo-app-674d8c455c-n7svp      0/1     ContainerCreating   0          0s
demo-app-674d8c455c-n7svp      1/1     Running             0          3s
demo-app-674d8c455c-64jvw      0/1     Pending             0          0s
demo-app-674d8c455c-64jvw      0/1     Pending             0          0s
demo-app-674d8c455c-64jvw      0/1     ContainerCreating   0          0s
demo-app-674d8c455c-64jvw      1/1     Running             0          2s


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Let's trigger descaling by deleting the messages in the queue. Poll for messages, select all and delete them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F01ylmzwc9cominezas9h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F01ylmzwc9cominezas9h.png" alt="Deleting the messages in the queue pod model"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Getting the events from the &lt;code&gt;demo-ns&lt;/code&gt; shows:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl get events &lt;span class="nt"&gt;-n&lt;/span&gt; demo-ns
LAST SEEN   TYPE     REASON                       OBJECT                                             MESSAGE
4m2s        Normal   Killing                      pod/demo-app-674d8c455c-64jvw    Stopping container demo-app
4m2s        Normal   Killing                      pod/demo-app-674d8c455c-n7svp    Stopping container demo-app
4m2s        Normal   SuccessfulDelete             replicaset/demo-app-674d8c455c   Deleted pod: demo-app-674d8c455c-n7svp
4m2s        Normal   SuccessfulDelete             replicaset/demo-app-674d8c455c   Deleted pod: demo-app-674d8c455c-64jvw
4m3s        Normal   ScalingReplicaSet            deployment/demo-app              Scaled down replica &lt;span class="nb"&gt;set &lt;/span&gt;demo-app-674d8c455c to 0
4m3s        Normal   KEDAScaleTargetDeactivated   scaledobject/demo-app            Deactivated apps/v1.Deployment demo-ns/demo-app from 2 to 0


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Getting the pods shows they are now in terminating state,&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

$ kubectl get pods -n demo-ns -w
NAME                           READY   STATUS        RESTARTS   AGE
demo-app-674d8c455c-n7svp      1/1     Terminating   0          90m
demo-app-674d8c455c-64jvw      1/1     Terminating   0          90m
demo-app-674d8c455c-n7svp      0/1     Terminating   0          90m
demo-app-674d8c455c-n7svp      0/1     Terminating   0          90m
demo-app-674d8c455c-n7svp      0/1     Terminating   0          90m
demo-app-674d8c455c-64jvw      0/1     Terminating   0          90m
demo-app-674d8c455c-64jvw      0/1     Terminating   0          90m
demo-app-674d8c455c-64jvw      0/1     Terminating   0          90m


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Awesome! Great work! You made it this far.&lt;/p&gt;

&lt;p&gt;That's how easy to scale based on a queue using KEDA. Next, we'll look at using the operator identity model which I personally recommend.&lt;/p&gt;

&lt;h3&gt;
  
  
  Operator Identity Owner Model
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;When &lt;code&gt;identityOwner&lt;/code&gt; set to &lt;code&gt;operator&lt;/code&gt; - the only requirement is that the KEDA operator has the correct IAM permissions on the SQS queue. Additional Authentication Parameters are not required.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Going back to our demo applications architecture and incorporating the use of the Operator Identity Owner model, the architecture will look like below.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;KEDA and Demo Application: Operator Identity Owner Model&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4281t1smp9yxu3gsho2s.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4281t1smp9yxu3gsho2s.jpg" alt="KEDA and Demo Application: Operator Identity Owner Model"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's create a new set of resources to differentiate from the resources we created in the previous section. Here is the summary of the steps we'll go through.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create AWS SQS Queue with access policy&lt;/li&gt;
&lt;li&gt;Grant KEDA permissions to the AWS SQS Queue&lt;/li&gt;
&lt;li&gt;Create second demo application&lt;/li&gt;
&lt;li&gt;Create scaler&lt;/li&gt;
&lt;li&gt;Test scaling&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Steps
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;1. Create AWS SQS Queue with access policy&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The first step we have to do is create a queue. Using Terraform we can create it like so,&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;

&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_sqs_queue"&lt;/span&gt; &lt;span class="s2"&gt;"aws_sqs_operator"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;provider&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;account_c&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"demo-app-sqs-operator"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;An access policy is needed to explicitly allow KEDA to access certain SQS API actions. This is needed when doing cross-account access. To create the access policy we need the IAM permission to read a queue, in this case, it's &lt;em&gt;SQS:GetQueueAttributes&lt;/em&gt;, it's the only permission we need. We also need to pass the KEDA IAM role created in &lt;a href="https://dev.to/carlocolumna/keda-in-amazon-eks-why-and-how-to-install-keda-44pa"&gt;Part 1: Why and How to Install KEDA&lt;/a&gt;.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;

&lt;span class="k"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"aws_iam_policy_document"&lt;/span&gt; &lt;span class="s2"&gt;"sqs_access_policy_data_operator"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;statement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;actions&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"SQS:GetQueueAttributes"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nx"&gt;resources&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nx"&gt;aws_sqs_queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_sqs_operator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nx"&gt;effect&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Allow"&lt;/span&gt;
    &lt;span class="nx"&gt;principals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"AWS"&lt;/span&gt;
      &lt;span class="nx"&gt;identifiers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keda_operator_role&lt;/span&gt; &lt;span class="c1"&gt;# "arn:aws:iam::111122223333:role/keda-operator"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_sqs_queue_policy"&lt;/span&gt; &lt;span class="s2"&gt;"sqs_access_policy_operator"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;provider&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;account_c&lt;/span&gt;
  &lt;span class="nx"&gt;depends_on&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;aws_sqs_queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_sqs_operator&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="nx"&gt;queue_url&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_sqs_queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_sqs_operator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;
  &lt;span class="nx"&gt;policy&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_iam_policy_document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sqs_access_policy_data_operator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Run &lt;code&gt;terraform apply&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Grant KEDA access to the AWS SQS Queue&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For this step, we'll have to update our KEDA installation. We need to grant KEDA or in particular the &lt;code&gt;keda-operator&lt;/code&gt; direct permission to the queue we created. We do this by adding the same policy we added on the queue access policy to the &lt;code&gt;keda-operator&lt;/code&gt; role.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;

&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_iam_role"&lt;/span&gt; &lt;span class="s2"&gt;"keda-operator"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"aws_iam_policy_document"&lt;/span&gt; &lt;span class="s2"&gt;"keda-operator-trust-policy"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"keda_operator_role_arn"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_iam_role_policy"&lt;/span&gt; &lt;span class="s2"&gt;"sqs-policy"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"sqs-queue-policy"&lt;/span&gt;
  &lt;span class="nx"&gt;role&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_iam_role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keda-operator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;policy&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_iam_policy_document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sqs-policy-document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"aws_iam_policy_document"&lt;/span&gt; &lt;span class="s2"&gt;"sqs-policy-document"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;statement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;sid&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"SQS"&lt;/span&gt;
    &lt;span class="nx"&gt;effect&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Allow"&lt;/span&gt;
    &lt;span class="nx"&gt;actions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="s2"&gt;"sqs:GetQueueAttributes"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nx"&gt;resources&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"arn:aws:sqs:region-code:111122223333:demo-app-sqs-operator"&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;# queue arn&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Run &lt;code&gt;terraform apply&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Create second demo application&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's deploy a second demo application so we can keep the first one and we can test both as much as we like. We would have to also create an IAM role for it. Scroll to the top to see the Terraform on how to create one.&lt;/p&gt;

&lt;p&gt;Our second demo application will now look as follows:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deployment&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;demo-app-operator&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;demo-app-operator&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;demo-ns&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;demo-app-operator&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;demo-app-operator&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;serviceAccountName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;demo-app-operator-sa&lt;/span&gt;
      &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ServiceAccount&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;demo-app-operator-sa&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;demo-ns&lt;/span&gt;
  &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;eks.amazonaws.com/role-arn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;arn:aws:iam::111122223333:role/demo-app-operator&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Let's save this file as &lt;code&gt;demo-app-operator.yaml&lt;/code&gt;. Let's deploy our second demo application to the same namespace using &lt;code&gt;kubectl&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Create scaler&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's now create our scaler specifications. This time it will be simpler because we don't need the &lt;em&gt;TriggerAuthentication&lt;/em&gt; resource anymore.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;keda.sh/v1alpha1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ScaledObject&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;demo-app-scaledobject-operator&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;demo-ns&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;minReplicaCount&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;  &lt;span class="c1"&gt;# scale down to 0&lt;/span&gt;
  &lt;span class="na"&gt;maxReplicaCount&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10&lt;/span&gt; 
  &lt;span class="na"&gt;pollingInterval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;30&lt;/span&gt; 
  &lt;span class="na"&gt;scaleTargetRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;demo-app-operator&lt;/span&gt;    &lt;span class="c1"&gt;# name of our demo-app deployment&lt;/span&gt;
  &lt;span class="na"&gt;triggers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws-sqs-queue&lt;/span&gt;
    &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;queueURL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://sqs.ca-central-1.amazonaws.com/111122223333/demo-app-sqs-operator&lt;/span&gt;
      &lt;span class="na"&gt;queueLength&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2"&lt;/span&gt; &lt;span class="c1"&gt;# 2 messages per pod &lt;/span&gt;
      &lt;span class="na"&gt;awsRegion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ca-central-1&lt;/span&gt;
      &lt;span class="na"&gt;identityOwner&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;operator&lt;/span&gt; &lt;span class="c1"&gt;# defaults to pod if unset&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Add this configuration to our manifest file, &lt;code&gt;demo-app-operator.yaml&lt;/code&gt;. Let's deploy our &lt;em&gt;ScaledObject&lt;/em&gt; and check if it's happy.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

Status:
  Conditions:
    Message:  ScaledObject is defined correctly and is ready &lt;span class="k"&gt;for &lt;/span&gt;scaling
    Reason:   ScaledObjectReady
    Status:   True
    Type:     Ready
    Message:  Scaling is not performed because triggers are not active
    Reason:   ScalerNotActive
    Status:   False
    Type:     Active
    Message:  No fallbacks are active on this scaled object
    Reason:   NoFallbackFound
    Status:   False
    Type:     Fallback
  External Metric Names:
    s0-aws-sqs-demo-app-operator
  Health:
    s0-aws-sqs-demo-app-operator:
      Number Of Failures:  0
      Status:              Happy
  Hpa Name:                keda-hpa-demo-app-operator
  Last Active Time:        2023-02-16T21:44:24Z
  Original Replica Count:  1
  Scale Target GVKR:
    Group:            apps
    Kind:             Deployment
    Resource:         deployments
    Version:          v1
  Scale Target Kind:  apps/v1.Deployment
Events:               &amp;lt;none&amp;gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Check also the logs of both KEDA deployments if they have reconciled with the new &lt;em&gt;ScaledObject&lt;/em&gt; we created.&lt;br&gt;
The &lt;code&gt;keda-operator&lt;/code&gt; has also created an &lt;em&gt;HPA&lt;/em&gt; for our second demo application.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl get hpa &lt;span class="nt"&gt;-n&lt;/span&gt; demo-ns
NAME                         REFERENCE                                    TARGETS             MINPODS   MAXPODS   REPLICAS   AGE
keda-hpa-demo-app-operator   Deployment/demo-app-operator   &amp;lt;unknown&amp;gt;/1 &lt;span class="o"&gt;(&lt;/span&gt;avg&lt;span class="o"&gt;)&lt;/span&gt;   1         10        0          4d16h
keda-hpa-demo-app            Deployment/demo-app        &amp;lt;unknown&amp;gt;/2 &lt;span class="o"&gt;(&lt;/span&gt;avg&lt;span class="o"&gt;)&lt;/span&gt;   1         10        0          4d16h


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Awesome! It's time to trigger scaling!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Test scaling&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's send messages to our queue.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flq7h2ozxvngums4oiepe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flq7h2ozxvngums4oiepe.png" alt="send messages to our queue"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the cluster, let's get the latest events from our &lt;code&gt;demo-ns&lt;/code&gt;.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl get events &lt;span class="nt"&gt;-n&lt;/span&gt; demo-ns
LAST SEEN   TYPE     REASON                     OBJECT                                                             MESSAGE
5m31s       Normal   SuccessfulRescale          horizontalpodautoscaler/keda-hpa-demo-app-operator   New size: 3&lt;span class="p"&gt;;&lt;/span&gt; reason: external metric s0-aws-sqs-demo-app-operator&lt;span class="o"&gt;(&lt;/span&gt;&amp;amp;LabelSelector&lt;span class="o"&gt;{&lt;/span&gt;MatchLabels:map[string]string&lt;span class="o"&gt;{&lt;/span&gt;scaledobject.keda.sh/name: demo-app-operator,&lt;span class="o"&gt;}&lt;/span&gt;,MatchExpressions:[]LabelSelectorRequirement&lt;span class="o"&gt;{}&lt;/span&gt;,&lt;span class="o"&gt;})&lt;/span&gt; above target
5m31s       Normal   Scheduled                  pod/demo-app-operator-7cff88c99b-5ddjv               Successfully assigned demo-ns/demo-app-operator-7cff88c99b-5ddjv to ip-11-111-111-111.us-west-2.compute.internal
5m29s       Normal   Pulled                     pod/demo-app-operator-7cff88c99b-5ddjv               Container image &lt;span class="s2"&gt;"nginx:latest"&lt;/span&gt; already present on machine
5m29s       Normal   Created                    pod/demo-app-operator-7cff88c99b-5ddjv               Created container demo-app-operator
5m29s       Normal   Started                    pod/demo-app-operator-7cff88c99b-5ddjv               Started container demo-app-operator
5m36s       Normal   Scheduled                  pod/demo-app-operator-7cff88c99b-gwz46               Successfully assigned demo-ns/demo-app-operator-7cff88c99b-gwz46 to ip-11-111-111-111.us-west-2.compute.internal
5m34s       Normal   Pulled                     pod/demo-app-operator-7cff88c99b-gwz46               Container image &lt;span class="s2"&gt;"nginx:latest"&lt;/span&gt; already present on machine
5m34s       Normal   Created                    pod/demo-app-operator-7cff88c99b-gwz46               Created container demo-app-operator
5m34s       Normal   Started                    pod/demo-app-operator-7cff88c99b-gwz46               Started container demo-app-operator
5m31s       Normal   Scheduled                  pod/demo-app-operator-7cff88c99b-xn7c8               Successfully assigned demo-ns/demo-app-operator-7cff88c99b-xn7c8 to ip-11-111-111-111.us-west-2.compute.internal
5m29s       Normal   Pulled                     pod/demo-app-operator-7cff88c99b-xn7c8               Container image &lt;span class="s2"&gt;"nginx:latest"&lt;/span&gt; already present on machine
5m29s       Normal   Created                    pod/demo-app-operator-7cff88c99b-xn7c8               Created container demo-app-operator
5m29s       Normal   Started                    pod/demo-app-operator-7cff88c99b-xn7c8               Started container demo-app-operator
5m36s       Normal   SuccessfulCreate           replicaset/demo-app-operator-7cff88c99b              Created pod: demo-app-operator-7cff88c99b-gwz46
5m31s       Normal   SuccessfulCreate           replicaset/demo-app-operator-7cff88c99b              Created pod: demo-app-operator-7cff88c99b-xn7c8
5m31s       Normal   SuccessfulCreate           replicaset/demo-app-operator-7cff88c99b              Created pod: demo-app-operator-7cff88c99b-5ddjv
5m36s       Normal   ScalingReplicaSet          deployment/demo-app-operator                         Scaled up replica &lt;span class="nb"&gt;set &lt;/span&gt;demo-app-operator-7cff88c99b to 1
5m36s       Normal   KEDAScaleTargetActivated   scaledobject/demo-app-operator                       Scaled apps/v1.Deployment demo-ns/demo-app-operator from 0 to 1
5m31s       Normal   ScalingReplicaSet          deployment/demo-app-operator                         Scaled up replica &lt;span class="nb"&gt;set &lt;/span&gt;demo-app-operator-7cff88c99b to 3


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Putting a watch on the demo-ns shows the new pods that get created as a result of the scaling,&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl get pods &lt;span class="nt"&gt;-n&lt;/span&gt; demo-ns &lt;span class="nt"&gt;-w&lt;/span&gt;
NAME                                 READY   STATUS              RESTARTS   AGE
demo-app-operator-7cff88c99b-gwz46   0/1     Pending             0          0s
demo-app-operator-7cff88c99b-gwz46   0/1     Pending             0          0s
demo-app-operator-7cff88c99b-gwz46   0/1     ContainerCreating   0          0s
demo-app-operator-7cff88c99b-gwz46   1/1     Running             0          3s
demo-app-operator-7cff88c99b-xn7c8   0/1     Pending             0          0s
demo-app-operator-7cff88c99b-xn7c8   0/1     Pending             0          0s
demo-app-operator-7cff88c99b-5ddjv   0/1     Pending             0          0s
demo-app-operator-7cff88c99b-5ddjv   0/1     Pending             0          0s
demo-app-operator-7cff88c99b-xn7c8   0/1     ContainerCreating   0          0s
demo-app-operator-7cff88c99b-5ddjv   0/1     ContainerCreating   0          1s
demo-app-operator-7cff88c99b-5ddjv   1/1     Running             0          2s
demo-app-operator-7cff88c99b-xn7c8   1/1     Running             0          3s


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Let's trigger descaling by deleting the messages in the queue.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5xs5woomm9wdriw19mb5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5xs5woomm9wdriw19mb5.png" alt="Deleting the messages in the queue"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Getting the events from the demo-ns shows:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl get events &lt;span class="nt"&gt;-n&lt;/span&gt; demo-ns
LAST SEEN   TYPE     REASON                       OBJECT                                             MESSAGE
4m2s        Normal   Killing                      pod/demo-app-674d8c455c-64jvw    Stopping container demo-app
4m2s        Normal   Killing                      pod/demo-app-674d8c455c-n7svp    Stopping container demo-app
4m2s        Normal   SuccessfulDelete             replicaset/demo-app-674d8c455c   Deleted pod: demo-app-674d8c455c-n7svp
4m2s        Normal   SuccessfulDelete             replicaset/demo-app-674d8c455c   Deleted pod: demo-app-674d8c455c-64jvw
4m3s        Normal   ScalingReplicaSet            deployment/demo-app              Scaled down replica &lt;span class="nb"&gt;set &lt;/span&gt;demo-app-674d8c455c to 0
4m3s        Normal   KEDAScaleTargetDeactivated   scaledobject/demo-app            Deactivated apps/v1.Deployment demo-ns/demo-app from 2 to 0


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Getting the pods shows,&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl get pods &lt;span class="nt"&gt;-n&lt;/span&gt; demo-ns &lt;span class="nt"&gt;-w&lt;/span&gt;
NAME                                 READY   STATUS              RESTARTS   AGE
demo-app-operator-7cff88c99b-gwz46   1/1     Terminating         0          14m
demo-app-operator-7cff88c99b-xn7c8   1/1     Terminating         0          13m
demo-app-operator-7cff88c99b-5ddjv   1/1     Terminating         0          13m
demo-app-operator-7cff88c99b-5ddjv   0/1     Terminating         0          13m
demo-app-operator-7cff88c99b-xn7c8   0/1     Terminating         0          13m
demo-app-operator-7cff88c99b-xn7c8   0/1     Terminating         0          13m
demo-app-operator-7cff88c99b-xn7c8   0/1     Terminating         0          13m
demo-app-operator-7cff88c99b-5ddjv   0/1     Terminating         0          13m
demo-app-operator-7cff88c99b-5ddjv   0/1     Terminating         0          13m
demo-app-operator-7cff88c99b-gwz46   0/1     Terminating         0          14m
demo-app-operator-7cff88c99b-gwz46   0/1     Terminating         0          14m
demo-app-operator-7cff88c99b-gwz46   0/1     Terminating         0          14m


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Yaaay! Awesome work in following through with this guide. We have scaled our demo application based on a queue using two different identity models.&lt;/p&gt;

&lt;p&gt;If you have run through some errors, check out below for a list of errors that I encountered and how to fix them.&lt;/p&gt;

&lt;p&gt;Watch out for the next part of KEDA in Amazon EKS mini-series. On the next one, we'll use KEDA to scale a workload based on http traffic! This ability is a game-changer for our scaling capabilities.&lt;/p&gt;

&lt;h3&gt;
  
  
  Common Errors
&lt;/h3&gt;

&lt;p&gt;Here are some possible errors that you may encounter following this blog and how to solve them. These errors can be seen by describing the &lt;em&gt;ScaledObject&lt;/em&gt; in your namespace.&lt;/p&gt;

&lt;h4&gt;
  
  
  WebIdentityErr
&lt;/h4&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

Warning  KEDAScalerFailed  18s  keda-operator  WebIdentityErr: failed to retrieve credentials caused by: AccessDenied: Not authorized to perform sts:AssumeRoleWithWebIdentity status code: 403, request &lt;span class="nb"&gt;id&lt;/span&gt;: 24f2181f-8a1b-4a4d-8de3-f85d501787a7


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This is often caused by an issue with the role's trust policy. Double check the values.&lt;/p&gt;

&lt;h4&gt;
  
  
  TriggerAuthentication
&lt;/h4&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

2023-02-16T03:46:46Z ERROR scalehandler Error getting triggerAuth &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"type"&lt;/span&gt;: &lt;span class="s2"&gt;"ScaledObject"&lt;/span&gt;, &lt;span class="s2"&gt;"namespace"&lt;/span&gt;: &lt;span class="s2"&gt;"demo-ns"&lt;/span&gt;, &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"demo-app"&lt;/span&gt;, &lt;span class="s2"&gt;"triggerAuthRef.Name"&lt;/span&gt;: &lt;span class="s2"&gt;"demo-app-trigger-auth-aws-credentials"&lt;/span&gt;, &lt;span class="s2"&gt;"error"&lt;/span&gt;: &lt;span class="s2"&gt;"TriggerAuthentication.keda.sh &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;demo-app-trigger-auth-aws-credentials&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; not found"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Check that the TriggerAuthentication object gets created.&lt;/p&gt;

&lt;h4&gt;
  
  
  NonExistentQueue: wsdl
&lt;/h4&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

Warning  KEDAScalerFailed  78s  keda-operator  AWS.SimpleQueueService.NonExistentQueue: The specified queue does not exist &lt;span class="k"&gt;for &lt;/span&gt;this wsdl version status code: 400, request &lt;span class="nb"&gt;id&lt;/span&gt;: b01e1553-fa5d-5bde-8190-493cc508300f


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;You might have set the incorrect region on the &lt;code&gt;values.yaml&lt;/code&gt; file. Double-check that you are setting the region where the queue is.&lt;/p&gt;

&lt;h4&gt;
  
  
  AccessDenied
&lt;/h4&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

  Warning  KEDAScalerFailed  3m40s  keda-operator  AccessDenied: User: arn:aws:sts::620528608035:assumed-role/keda-operator/1677015712377894955 is not authorized to perform: sts:AssumeRole on resource: arn:aws:iam::046921848075:role/demo-app status code: 403, request &lt;span class="nb"&gt;id&lt;/span&gt;: 6001b079-4cc9-4309-b364-5958316ce52b


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This can be caused by a missing &lt;em&gt;sts:AssumeRole&lt;/em&gt; policy on the &lt;code&gt;keda-operator&lt;/code&gt; role or on your workload's role.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

    Warning  KEDAScalerFailed  0s  keda-operator  AccessDenied: Access to the resource https://sqs.ca-central-1.amazonaws.com/ is denied. status code: 403, request &lt;span class="nb"&gt;id&lt;/span&gt;: c9b51391-2132-551e-b998-4ffe48b8de74


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This can be caused by missing permissions to access the queue. When using the Operator Identity Model, check that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;access policy has been added to the queue&lt;/li&gt;
&lt;li&gt;permissions have been added to the KEDA role&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

Events:
  Type     Reason              Age   From           Message
  &lt;span class="nt"&gt;----&lt;/span&gt;     &lt;span class="nt"&gt;------&lt;/span&gt;              &lt;span class="nt"&gt;----&lt;/span&gt;  &lt;span class="nt"&gt;----&lt;/span&gt;           &lt;span class="nt"&gt;-------&lt;/span&gt;
  Normal   KEDAScalersStarted  109s  keda-operator  Started scalers watch
  Warning  KEDAScalerFailed    106s  keda-operator  AccessDenied: Access to the resource https://sqs.ca-central-1.amazonaws.com/ is denied.
           status code: 403, request &lt;span class="nb"&gt;id&lt;/span&gt;: dabe543b-d61f-57a4-9c6b-089fdde8daa9
  Warning  KEDAScalerFailed  92s  keda-operator  AccessDenied: Access to the resource https://sqs.ca-central-1.amazonaws.com/ is denied.
           status code: 403, request &lt;span class="nb"&gt;id&lt;/span&gt;: 84ae713a-5e5b-5c73-98d9-2d9e525f953d
  Warning  KEDAScalerFailed  77s  keda-operator  AccessDenied: Access to the resource https://sqs.ca-central-1.amazonaws.com/ is denied.
           status code: 403, request &lt;span class="nb"&gt;id&lt;/span&gt;: 3955aad9-0221-5a6f-a7c4-f67d0e32145f
  Normal   ScaledObjectReady  69s &lt;span class="o"&gt;(&lt;/span&gt;x2 over 109s&lt;span class="o"&gt;)&lt;/span&gt;  keda-operator  ScaledObject is ready &lt;span class="k"&gt;for &lt;/span&gt;scaling
  Warning  KEDAScalerFailed   62s                 keda-operator  AccessDenied: Access to the resource https://sqs.ca-central-1.amazonaws.com/ is denied.
           status code: 403, request &lt;span class="nb"&gt;id&lt;/span&gt;: eddee0ca-6e76-520b-b4ac-bc6e62543864
  Warning  KEDAScalerFailed  47s  keda-operator  AccessDenied: Access to the resource https://sqs.ca-central-1.amazonaws.com/ is denied.
           status code: 403, request &lt;span class="nb"&gt;id&lt;/span&gt;: 7f1a95e9-3e9c-550b-82d0-c94d32adf964
  Normal   KEDAScaleTargetDeactivated  33s  keda-operator  Deactivated apps/v1.Deployment demo-ns/demo-app-operator from 1 to 0


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;From the example above, sometimes it may also take a minute before KEDA can access the queue.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reach out for a yarn
&lt;/h3&gt;

&lt;p&gt;If you have some questions, feedback or just want to reach out for a good ol' yarn, please connect and flick me a message at &lt;a href="https://www.linkedin.com/in/carlo-columna/" rel="noopener noreferrer"&gt;https://www.linkedin.com/in/carlo-columna/&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  References:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://keda.sh/" rel="noopener noreferrer"&gt;https://keda.sh/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>kubernetes</category>
      <category>aws</category>
      <category>keda</category>
      <category>autoscaling</category>
    </item>
    <item>
      <title>KEDA in Amazon EKS Part 1: Why and How to Install KEDA</title>
      <dc:creator>Carlo Columna</dc:creator>
      <pubDate>Sun, 25 Jun 2023 05:52:04 +0000</pubDate>
      <link>https://forem.com/carlocolumna/keda-in-amazon-eks-why-and-how-to-install-keda-44pa</link>
      <guid>https://forem.com/carlocolumna/keda-in-amazon-eks-why-and-how-to-install-keda-44pa</guid>
      <description>&lt;p&gt;Talofa everyone!&lt;/p&gt;

&lt;p&gt;This the first part of a series of articles discussing the use of Kubernetes Event-driven Autoscaling (KEDA) for autoscaling of Amazon EKS workloads.&lt;/p&gt;

&lt;p&gt;I highly recommend going through Part 1 but if you really want to jump ahead and see KEDA in action, check out:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/carlocolumna/keda-in-amazon-eks-part-2-scale-based-on-aws-sqs-queue-5g4d"&gt;KEDA in Amazon EKS Part 2: Scale Based On AWS SQS Queue&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feetfe8rpco8qtirtavhv.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feetfe8rpco8qtirtavhv.jpg" alt="Uncle Sam meme I want more"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At the company I work in, we run multi-tenant production grade clusters on top of Amazon EKS and we have been mostly using Horizontal Pod Autoscaler (HPA) for autoscaling. Recently, we've been working on providing new autoscaling options for our customers who are running their workloads in our Kubernetes clusters.&lt;/p&gt;

&lt;p&gt;The main drivers behind this effort are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Overcoming the limitations of HPA&lt;/li&gt;
&lt;li&gt;Increasing Scaling Demands and the Kubernetes External Metrics API Limitation&lt;/li&gt;
&lt;li&gt;Deprecating k8s-cloudwatch-adapter&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We'll go through each of the above.&lt;/p&gt;

&lt;h3&gt;
  
  
  Limitations of Horizontal Pod Autoscaler
&lt;/h3&gt;

&lt;p&gt;Kubernetes natively offers the HPA as a controller to increase and decrease replicas based on demand. HPA provides the ability to scale on pods metrics namely CPU and memory. While this is enough for most workloads, there are limitations to it.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;It cannot scale to zero.&lt;/strong&gt; HPA by default uses metrics namely CPU and memory utilisation to calculate the desired number of replicas. Because these metrics cannot be zero, the desired number of replicas cannot be zero as well. This is not ideal for intermittent and resource intensive workloads when you are trying to optimise your cost.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;It's limited to scaling based on metrics.&lt;/strong&gt; You are unable to scale based on events or http traffic.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;It's dependent on metrics aggregators.&lt;/strong&gt; In order for HPA to work, it needs to fetch metrics from &lt;a href="https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/apiserver-aggregation/" rel="noopener noreferrer"&gt;aggregated APIs&lt;/a&gt;. These APIs are usually provided by other add-ons that you have to install separately such as &lt;a href="https://kubernetes.io/docs/tasks/debug/debug-cluster/resource-metrics-pipeline/#metrics-server" rel="noopener noreferrer"&gt;Metrics Server&lt;/a&gt; which usually provides the &lt;code&gt;metrics.k8s.io&lt;/code&gt; API. Other providers of &lt;code&gt;custom.metrics.k8s.io&lt;/code&gt;, and &lt;code&gt;external.metrics.k8s.io&lt;/code&gt; that we also use are &lt;a href="https://github.com/kubernetes-sigs/prometheus-adapter" rel="noopener noreferrer"&gt;prometheus-adapter&lt;/a&gt; to expose prometheus metrics and the &lt;a href="https://github.com/amazon-archives/k8s-cloudwatch-adapter" rel="noopener noreferrer"&gt;k8s-cloudwatch-adapter&lt;/a&gt; which is now deprecated.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;It often does not scale on the actual target value.&lt;/strong&gt; HPA bases its scaling decisions on a target metric value. However, due to how the HPA &lt;a href="https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/#algorithm-details" rel="noopener noreferrer"&gt;scaling algorithm&lt;/a&gt; works, the current target value on the HPA often does not match with the metrics of the system you are scaling on.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Increasing Scaling Demands and External Metrics API Kubernetes Limitation
&lt;/h3&gt;

&lt;p&gt;As our clusters grow, so is the demand from hosting various workloads with different scaling requirements. We want to be able to scale on metrics not only inside but also outside of the cluster such as from Amazon Managed Prometheus, AWS Cloudwatch and Kafka or any other events source. This will enable our platform to cater to these scaling needs and put us in a good spot as our clusters grow.&lt;/p&gt;

&lt;p&gt;Fortunately, a &lt;a href="https://github.com/kubernetes/design-proposals-archive/blob/main/instrumentation/external-metrics-api.md" rel="noopener noreferrer"&gt;proposal was adopted to extend the HPA by introducing the new External metric type&lt;/a&gt; for autoscaling based on metrics coming from outside of the Kubernetes cluster. This allows us to use metrics adapters that serve a variety of metrics from external services and make them available to autoscale on by using a metric server.&lt;/p&gt;

&lt;p&gt;However, there is one big caveat to this. There is a limitation in Kubernetes where you can only have one running metric server serving &lt;code&gt;external.metrics.k8s.io&lt;/code&gt; metrics per cluster. This is because only one API Service can be registered to handle external metrics. External metrics are metrics that represent the state of an application/service that is running outside of the Kubernetes cluster.&lt;/p&gt;

&lt;p&gt;So this means we'll have to choose which metric server to run and serve metrics from. But which one?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn96gou5rcxdxl7pi629z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn96gou5rcxdxl7pi629z.png" alt="Homer Simpson Don't Make Me Choose Meme"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From the diagram below, CloudWatch Adapter and KEDA cannot be run at the same time because they both implement &lt;code&gt;external.metrics.k8s.io&lt;/code&gt;. Since Prometheus adapter is implementing &lt;code&gt;custom.metrics.k8s.io&lt;/code&gt;, it does not conflict with KEDA or CloudWatch Adapter. Custom metrics are metrics that come from applications solely running on the Kubernetes cluster such as Prometheus.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fccez2p0rge5j664414q1.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fccez2p0rge5j664414q1.jpg" alt="Multiple Metric Servers in Kubernetes"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Deprecated K8s CloudWatch Adapter
&lt;/h3&gt;

&lt;p&gt;One of the scaling tools that we have used in the past is the K8s CloudWatch Adapter. This adapter allowed us to scale our Kubernetes workloads using the Horizontal Pod Autoscaler (HPA) with metrics from AWS CloudWatch. One of the most popular ways of scaling that was used was scaling based on AWS SQS Queue. However, from K8s v1.22, it's no longer working because it uses deprecated API versions which are no longer supported. AWS has also archived and stopped maintaining the project and instead recommends using KEDA.&lt;br&gt;
From these decision drivers, we have started looking at KEDA for autoscaling of Kubernetes workloads.&lt;/p&gt;

&lt;h2&gt;
  
  
  KEDA
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvwf60gu623nicgw235ln.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvwf60gu623nicgw235ln.png" alt="Event Based Autoscaling It's Kinda Big Deal Meme"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;KEDA stands for Kubernetes Event-driven Autoscaling. Additionally, from their website,&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;With KEDA, you can drive the scaling of any container in Kubernetes based on the number of events needing to be processed.&lt;/p&gt;

&lt;p&gt;KEDA is a single-purpose and lightweight component that can be added into any Kubernetes cluster. KEDA works alongside standard Kubernetes components like the Horizontal Pod Autoscaler and can extend functionality without overwriting or duplication. With KEDA you can explicitly map the apps you want to use event-driven scale, with other apps continuing to function. This makes KEDA a flexible and safe option to run alongside any number of any other Kubernetes applications or frameworks.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;KEDA is part of CNCF with a big community backing and is widely used for event scaling in Kubernetes. It offers many features like scaling different types of Kubernetes workloads such as Deployments, StatefulSets, and Jobs based on different type of events. It can also scale custom resources as long as the target Custom Resource must define a &lt;code&gt;/scale&lt;/code&gt; subresource.&lt;/p&gt;

&lt;p&gt;It has a wide range of readily available &lt;a href="https://keda.sh/docs/2.11/scalers/" rel="noopener noreferrer"&gt;scalers&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;KEDA scalers can both detect if a deployment should be activated or deactivated, and feed custom metrics for a specific event source.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Its even extensible which means you can create your own scaler.&lt;/p&gt;

&lt;h3&gt;
  
  
  How KEDA Works
&lt;/h3&gt;

&lt;p&gt;KEDA performs two key roles within Kubernetes:&lt;/p&gt;

&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Agent - KEDA activates and deactivates Kubernetes Deployments to scale to and from zero on no events. This is one of the primary roles of the keda-operator container that runs when you install KEDA.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Metrics - KEDA acts as a Kubernetes metrics server that exposes rich event data like queue length or stream lag to the Horizontal Pod Autoscaler to drive scale out. It is up to the Deployment to consume the events directly from the source. This preserves rich event integration and enables gestures like completing or abandoning queue messages to work out of the box. The metric serving is the primary role of the keda-operator-metrics-apiserver container that runs when you install KEDA.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;

&lt;p&gt;When you setup KEDA, it will install two deployments, &lt;code&gt;keda-operator&lt;/code&gt; and &lt;code&gt;keda-operator-metrics-apiserver&lt;/code&gt;. The &lt;code&gt;keda-operator&lt;/code&gt;, as mentioned above acts as an agent or controller to set the number of desired replicas. It does this by creating and managing an application's Horizontal Pod Autoscaler. It also registers and manages KEDA Custom Resource Definitions (CRDs) such as ScaledObject and TriggerAuthentication.&lt;/p&gt;

&lt;p&gt;As mentioned above, the &lt;code&gt;keda-operator-metrics-apiserver&lt;/code&gt; acts as a metrics server exposing the metrics to trigger the scale out.&lt;/p&gt;

&lt;p&gt;KEDA primarily serves metrics for metric sources outside of the Kubernetes cluster so it uses external metrics and registers &lt;code&gt;v1beta1.external.metrics.k8s.io&lt;/code&gt; namespace in the API service.&lt;/p&gt;

&lt;h3&gt;
  
  
  Architecture
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;The diagram below shows how KEDA works in conjunction with the Kubernetes Horizontal Pod Autoscaler, external event sources, and Kubernetes' etcd data store:&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn1drf6u79si6krbm79sb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn1drf6u79si6krbm79sb.png" alt="KEDA architecture"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Security
&lt;/h3&gt;

&lt;p&gt;KEDA is secure by default. KEDA will run as non-root. You can further increase the security in some cases by setting KEDA to listen on TLS v1.3 only or running KEDA &lt;code&gt;withreadOnlyRootFilesystem=true&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Install KEDA
&lt;/h3&gt;

&lt;p&gt;There are multiple ways to install KEDA in a Kubernetes cluster. Let's go with Helm and Terraform. We will be running KEDA in Amazon EKS.&lt;/p&gt;

&lt;p&gt;Before we can proceed, let's lay out the prerequisites:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS account&lt;/li&gt;
&lt;li&gt;Access to an Amazon EKS cluster&lt;/li&gt;
&lt;li&gt;IAM roles for service accounts (IRSA) setup for the EKS cluster. See &lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html" rel="noopener noreferrer"&gt;here&lt;/a&gt; for more details.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Steps in installing KEDA
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;Create its AWS IAM role&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Before installing KEDA in the cluster, let's first create its AWS IAM role. Giving it a role allows it to communicate with other AWS resources as you can see later on this blog. Add an assume role trust policy for the role to allow the cluster to assume it.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

resource "aws_iam_role" "keda-operator" {
  name               = "keda-operator"
  assume_role_policy = module.keda-operator-trust-policy.json
}

data "aws_iam_policy_document" "keda-operator-trust-policy" {
  statement {
    actions = [
      "sts:AssumeRoleWithWebIdentity"
    ]
    principals {
      type = "Federated"
      identifiers = ["arn:aws:iam::111122223333:oidc-provider/oidc.eks.region-code.amazonaws.com/id/EXAMPLED539D4633E53DE1B71EXAMPLE"]
    }
    condition {
      test     = "StringEquals"
      variable = "oidc.eks.region-code.amazonaws.com/id/EXAMPLED539D4633E53DE1B71EXAMPLE:sub"
      values   = ["system:serviceaccount:keda:keda-operator"]
    }
  }
}

output "keda_operator_role_arn" {
  value = aws_iam_role.keda-operator.arn
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt;Install KEDA in the cluster. Pass the role created on the previous step.&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

resource "helm_release" "keda" {
  name             = "keda"
  repository       = "https://kedacore.github.io/charts"
  chart            = "keda"
  version          = "2.8.2"
  namespace        = "keda"
  create_namespace = true
  values = [
    templatefile("${path.module}/values/keda.yaml", {
      keda_operator_role_arn = "arn:aws:iam::111122223333:role/keda-operator"
    }),
  ]
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;values.yaml&lt;/code&gt; file.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

serviceAccount:
  annotations:
    eks.amazonaws.com/role-arn: ${keda_operator_role_arn}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Applying this terraform and running helm we get,&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

$ helm list -nkeda
NAME NAMESPACE REVISION UPDATED                               STATUS   CHART      APP VERSION
keda keda      1        2023-02-16 16:08:20.945876 +1300 NZDT deployed keda-2.8.2 2.8.1


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Running a kubectl to see if the pods are healthy we get,&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

$ kubectl get pods -n keda
NAME                                               READY   STATUS    RESTARTS   AGE
keda-operator-54bcdc6446-ktx7d                     1/1     Running   0          0s
keda-operator-metrics-apiserver-74487bb99f-4v22r   1/1     Running   0          2s


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;So that's it! That's how easy it is to get KEDA up and running in a EKS cluster.&lt;/p&gt;

&lt;p&gt;Watch out for the next part of KEDA in Amazon EKS mini-series. On the next part, we'll use KEDA to scale a workload based on the number of messages in a AWS SQS Queue.&lt;/p&gt;

&lt;p&gt;Pretty exciting right?!&lt;/p&gt;

&lt;p&gt;Cheers!&lt;/p&gt;

&lt;h4&gt;
  
  
  Reach out for a yarn
&lt;/h4&gt;

&lt;p&gt;If you have some questions, feedback or just want to reach out for a good ol' yarn, please connect and flick me a message at &lt;a href="https://www.linkedin.com/in/carlo-columna/" rel="noopener noreferrer"&gt;https://www.linkedin.com/in/carlo-columna/&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  References:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://keda.sh/" rel="noopener noreferrer"&gt;https://keda.sh/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.cncf.io/blog/2021/03/30/why-alibaba-cloud-uses-keda-for-application-autoscaling/" rel="noopener noreferrer"&gt;https://www.cncf.io/blog/2021/03/30/why-alibaba-cloud-uses-keda-for-application-autoscaling/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>kubernetes</category>
      <category>aws</category>
      <category>keda</category>
      <category>autoscaling</category>
    </item>
  </channel>
</rss>
