<p>In a recent experiment, I tried to autoscale my K8s cluster using two mechanisms: KEDA and HPA (see below). I wanted to use HPA OOB resource metrics to scale my cluster based on pod resource utilization (memory and CPU) and KEDA to autoscale based on custom-metrics.</p>
<p>Even though my deployment succeeds and the cluster was healthy and functional. When autoscaling kicked in, the cluster went haywire! Pods were constantly being provisioned and then de-provisioned, this state continued on even after I stopped the traffic against the cluster. I had to wait for the cool-down periods before it went sane again.</p>
<p>I didn't find any official documentation on this topic, thus, asking here.</p>
<p>My questions:</p>
<ul>
<li>Can a k8s cluster be configured to autoscale using multiple mechanisms?</li>
<li>If so, what did I do wrong?</li>
</ul>
<p>This was on K8s version 1.15.11 and KEDA 1.4.1</p>
<pre class="lang-yaml prettyprint-override"><code>apiVersion: keda.k8s.io/v1alpha1
kind: ScaledObject
metadata:
  name: {{ $fullName }}
  labels:
    deploymentName: {{ $fullName }}
    {{- include &quot;deployment.labels&quot; . | nindent 4 }}
spec:
  scaleTargetRef:
    deploymentName: {{ $fullName }}
  pollingInterval: {{ .Values.scaleobject.pollingInterval }}
  cooldownPeriod:  {{ .Values.scaleobject.cooldownPeriod }}
  minReplicaCount: {{ .Values.scaleobject.minReplicaCount }}
  maxReplicaCount: {{ .Values.scaleobject.maxReplicaCount }}   
  triggers:
  - type: prometheus
    metadata:
      serverAddress: {{ tpl .Values.scaleobject.serverAddress . | quote }}  
      metricName: access_frequency
      threshold: &quot;{{ .Values.scaleobject.threshold }}&quot;
      query: {{ tpl .Values.scaleobject.query . | quote  }}
---
apiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
metadata:
  name: resource-utilization-scaling
  namespace: default
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: {{ $fullName }}
  minReplicas: {{ .Values.scaleobject.minReplicaCount }}
  maxReplicas: {{ .Values.scaleobject.maxReplicaCount }}
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: {{ .Values.scaleobject.cpuUtilization }}
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: {{ .Values.scaleobject.memUtilization }}

</code></pre>

<p><a href="https://keda.sh/" rel="nofollow noreferrer">KEDA</a> doesn't have <a href="https://github.com/kedacore/keda/issues/637" rel="nofollow noreferrer">direct cluster autoscaler support yet</a> so you will have some unpredictability. In essence, you have two pieces of information that are not being shared that of KEDA and that of the cluster autoscaler and some of those may not agree at a particular time.</p>
<p>Best in my opinion to slow down your autoscaling overall of everything so that it allows all the autoscaler to catch up with any discrepancy. For example, you can make use of things like <a href="https://docs.aws.amazon.com/autoscaling/ec2/userguide/Cooldown.html#cooldown-default" rel="nofollow noreferrer">cooldown</a> in an autoscaling group to avoid some resource starvation.</p>
<p>✌️</p>
