<p>I have the below Horizontal Pod Autoscaller configuration on Google Kubernetes Engine to scale a deployment by a custom metric - <code>RabbitMQ messages ready count</code> for a specific queue: <code>foo-queue</code>.</p>
<p>It picks up the metric value correctly.</p>
<p>When inserting 2 messages it scales the deployment to the maximum 10 replicas.
I expect it to scale to 2 replicas since the targetValue is 1 and there are 2 messages ready.</p>
<p>Why does it scale so aggressively?</p>
<p>HPA configuration:</p>
<pre><code>apiVersion: autoscaling/v2beta1
kind: HorizontalPodAutoscaler
metadata:
  name: foo-hpa
  namespace: development
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: foo
  minReplicas: 1
  maxReplicas: 10
  metrics:
  - type: External
    external:
      metricName: &quot;custom.googleapis.com|rabbitmq_queue_messages_ready&quot;
      metricSelector:
        matchLabels:
          metric.labels.queue: foo-queue
      targetValue: 1
</code></pre>

<p>I think you did a great job <a href="https://stackoverflow.com/a/57889681/868533">explaining how <code>targetValue</code> works</a> with HorizontalPodAutoscalers. However, based on your question, I think you're looking for <code>targetAverageValue</code> instead of <code>targetValue</code>. </p>

<p>In <a href="https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/#algorithm-details" rel="nofollow noreferrer">the Kubernetes docs on HPAs</a>, it mentions that using <code>targetAverageValue</code> instructs Kubernetes to scale pods based on the average metric exposed by all Pods under the autoscaler. While the docs aren't explicit about it, an external metric (like the number of jobs waiting in a message queue) counts as a single data point. By scaling on an external metric with <code>targetAverageValue</code>, you can create an autoscaler that scales the number of Pods to match a ratio of Pods to jobs.</p>

<p>Back to your example:</p>

<pre><code>apiVersion: autoscaling/v2beta1
kind: HorizontalPodAutoscaler
metadata:
  name: foo-hpa
  namespace: development
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: foo
  minReplicas: 1
  maxReplicas: 10
  metrics:
  - type: External
    external:
      metricName: "custom.googleapis.com|rabbitmq_queue_messages_ready"
      metricSelector:
        matchLabels:
          metric.labels.queue: foo-queue
      # Aim for one Pod per message in the queue
      targetAverageValue: 1
</code></pre>

<p>will cause the HPA to try keeping one Pod around for every message in your queue (with a max of 10 pods).</p>

<p>As an aside, targeting one Pod per message is probably going to cause you to start and stop Pods constantly. If you end up starting a ton of Pods and process all of the messages in the queue, Kubernetes will scale your Pods down to 1. Depending on how long it takes to start your Pods and how long it takes to process your messages, you may have lower average message latency by specifying a higher <code>targetAverageValue</code>. Ideally, given a constant amount of traffic, you should aim to have a constant number of Pods processing messages (which requires you to process messages at about the same rate that they are enqueued).</p>
