External Scalers Click here for latest

Warning

You are currently viewing v"2.10" of the documentation and it is not the latest. For the most recent documentation, kindly click here.

While KEDA ships with a set of built-in scalers, users can also extend KEDA through a GRPC service that implements the same interface as the built-in scalers.

Built-in scalers run in the KEDA process/pod, while external scalers require an externally managed GRPC server that’s accessible from KEDA with optional TLS authentication. KEDA itself acts as a GRPC client and it exposes similar service interface for the built-in scalers, so external scalers can fully replace built-in ones.

This document describes the external scaler interfaces and how to implement them in Go, Node, and .NET; however for more details on GRPC refer to the official GRPC documentation

Want to learn about existing external scalers? Explore our external scaler community.

Overview

Built-in scalers interface

Since external scalers mirror the interface of built-in scalers, it’s worth becoming familiar with the Go interface that the built-in scalers implement:

// Scaler interface
type Scaler interface {
	// GetMetricsAndActivity returns the metric values and activity for a metric Name
	GetMetricsAndActivity(ctx context.Context, metricName string) ([]external_metrics.ExternalMetricValue, bool, error)
	// GetMetricSpecForScaling returns the metrics based on which this scaler determines that the ScaleTarget scales. This is used to construct the HPA spec that is created for
	// this scaled object. The labels used should match the selectors used in GetMetrics
	GetMetricSpecForScaling(ctx context.Context) []v2.MetricSpec
	// Close any resources that need disposing when scaler is no longer used or destroyed
	Close(ctx context.Context) error
}

// PushScaler interface
type PushScaler interface {
	Scaler

	// Run is the only writer to the active channel and must close it once done.
	Run(ctx context.Context, active chan<- bool)
}

The Scaler interface defines 3 methods:

  • Close is called to allow the scaler to clean up connections or other resources.
  • GetMetricSpecForScaling returns the target value for the HPA definition for the scaler. For more details refer to Implementing GetMetricSpec.
  • GetMetricsAndActivity is called on pollingInterval and. When activity returns true, KEDA will scale to what is returned by the metric limited by maxReplicaCount on the ScaledObject/ScaledJob. When false is returned, KEDA will scale to minReplicaCount or optionally idleReplicaCount. More details around the defaults and how these options work together can be found on the ScaledObjectSpec.

    Refer to the HPA docs for how HPA calculates replicaCount based on metric value and target value. KEDA supports both AverageValue and Value metric target types for external metrics. When AverageValue (the default metric type) is used, the metric value returned by the external scaler will be divided by the number of replicas.

The PushScaler interface adds a Run method. This method receives a push channel (active), on which the scaler can send true at any time. The purpose of this mechanism is to initiate a scaling operation independently from pollingInterval.

External Scaler GRPC interface

KEDA comes with 2 external scalers external and external-push.

The configuration in the ScaledObject points to a GRPC service endpoint that implements the externalscaler.proto GRPC contract:

service ExternalScaler {
    rpc IsActive(ScaledObjectRef) returns (IsActiveResponse) {}
    rpc StreamIsActive(ScaledObjectRef) returns (stream IsActiveResponse) {}
    rpc GetMetricSpec(ScaledObjectRef) returns (GetMetricSpecResponse) {}
    rpc GetMetrics(GetMetricsRequest) returns (GetMetricsResponse) {}
}

Much of this contract is similar to the built-in scalers:

  • GetMetricsSpec mirrors its counterpart in the Scaler interface for creating HPA definition.
  • IsActive and GetMetrics map to the GetMetricsAndActivity method on the Scaler interface.
  • StreamIsActive maps to the Run method on the PushScaler interface.

There are, however, some notable differences:

  • There is no Close method. The scaler is expected to be functional throughout its lifetime.
  • IsActive, StreamIsActive, and GetMetricsSpec are called with a ScaledObjectRef that contains the scaledObject name/namespace as well as the content of metadata defined in the trigger.

Example

Given the following ScaledObject:

apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: scaledobject-name
  namespace: scaledobject-namespace
spec:
  scaleTargetRef:
    name: deployment-name
  triggers:
    - type: external-push
      metadata:
        scalerAddress: service-address.svc.local:9090
        key1: value1
        key2: value2

KEDA will attempt a GRPC connection to service-address.svc.local:9090 immediately after reconciling the ScaledObject. It will then make the following RPC calls:

  • IsActive - KEDA does an initial call to IsActive followed by one call on each pollingInterval
  • StreamIsActive - KEDA does an initial call and the scaler is expected to maintain a long-lived connection (called a stream in GRPC terminology). The external push scaler can then send an IsActive event back to KEDA at any time. KEDA will only attempt another call to StreamIsActive if it needs to re-connect
  • GetMetricsSpec - KEDA will do an initial call with the following data in the incoming ScaledObjectRef parameter:
  • GetMetrics - KEDA will call this method every pollingInterval to get the point-in-time metric values for the names returned by GetMetricsSpec.
{
  "name": "scaledobject-name",
  "namespace": "scaledobject-namespace",
  "scalerMetadata": {
    "scalerAddress": "service-address.svc.local:9090",
    "key1": "value1",
    "key2": "value2"
  }
}

Note: KEDA will issue all of the above RPC calls except StreamIsActive if spec.triggers.type is external. It must be external-push for StreamIsActive to be called.

Implementing KEDA external scaler GRPC interface

Implementing an external scaler

1. Download externalscaler.proto

2. Prepare project

Golang

C#

Javascript

3. Implementing IsActive

Just like IsActive(ctx context.Context) (bool, error) in the go interface, the IsActive method in the GRPC interface is called every pollingInterval with a ScaledObjectRef object that contains the scaledObject name, namespace, and scaler metadata.

This section implements an external scaler that queries earthquakes from earthquake.usgs.gov and scales the deployment if there has been more than 2 earthquakes with magnitude > 1.0 around a particular longitude/latitude in the previous day.

Submit the following ScaledObject to tell KEDA to start making RPC calls to your external scaler (modifying appropriate fields as necessary):

apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: scaledobject-name
  namespace: scaledobject-namespace
spec:
  scaleTargetRef:
    name: deployment-name
  triggers:
    - type: external
      metadata:
        scalerAddress: earthquake-scaler:9090
        longitude: "-122.335167"
        latitude: "47.608013"

Golang

C#

Javascript

4. Implementing StreamIsActive

Unlike IsActive, StreamIsActive is called once when KEDA reconciles the ScaledObject, and expects the external scaler to maintain a long-lived connection and push IsActiveResponse objects whenever the scaler needs KEDA to activate the deployment.

This implementation creates a timer and queries USGS APIs on that timer, effectively ignoring pollingInterval set in the scaledObject. Alternatively any other asynchronous event can be used instead of a timer, like an HTTP request, or a network connection.

Golang

C#

Javascript

5. Implementing GetMetricSpec

GetMetricSpec returns the target value for the HPA definition for the scaler. This scaler will define a static target of 10, but the threshold value is often specified in the metadata for other scalers.

Golang

C#

Javascript

6. Implementing GetMetrics

GetMetrics returns the value of the metric referred to from GetMetricSpec, in this example it’s earthquakeThreshold.

Golang

C#

Javascript