Kubernetes Autoscaling with Custom Metrics


Background

One of the most amazing things about Kubernetes is its ability to automatically scale up and down. In current (autoscaling/v1) releases, the only metric which could be used as a scaling target is CPU usage, as measured by heapster.

With the introduction of the custom metrics api, deployments will be able to scale using one or more application-specific metrics, as well as CPU.

Disclaimer

I spent way too much time trying to figure out how to do this today. These are my findings, but I could not get it to work. I look forward to being able to use this in the future when there are more examples to follow and when it is not hidden behind feature flags.

Exposing Metrics

A few months ago, the metrics repo was created for the purpose of storing type definitions and client code for publishing and gathering metrics.

There was not a clear path forward from there, but luckily I discovered that a boilerplate and a Prometheus adapter have been created.

The boilerplate defines a CustomMetricsProvider interface here, which looks like the following:

type CustomMetricsProvider interface {

	GetRootScopedMetricByName(groupResource schema.GroupResource, name string, metricName string) (*custom_metrics.MetricValue, error)

	GetRootScopedMetricBySelector(groupResource schema.GroupResource, selector labels.Selector, metricName string) (*custom_metrics.MetricValueList, error)

	GetNamespacedMetricByName(groupResource schema.GroupResource, namespace string, name string, metricName string) (*custom_metrics.MetricValue, error)

	GetNamespacedMetricBySelector(groupResource schema.GroupResource, namespace string, selector labels.Selector, metricName string) (*custom_metrics.MetricValueList, error)

	ListAllMetrics() []MetricInfo
}

This interface is the core of what is necessary to create a custom metrics server.

The boilerplate project inherits from k8s.io/apiserver, which creates APIs that are registered with the API registration API (because, of course that’s a thing).

Fun with Flags

The Horizontal Pod Autoscaler documentation says that to enable this feature, two flags must be enabled while deploying the cluster. These flags are --horizontal-pod-autoscaler-use-rest-clients and --apiserver. I assume that in future k8s releases this will not be necessary, as it will become standard.

The first flag switches the autoscaling controller to use a REST client, rather than a client that calls heapster directly.

The second flag enables kube-aggregator, which provides the previously mentioned API registration API.

Ship It

From what I can tell, this is what the HorizontalPodAutoscaler configuration will look like.

apiVersion: autoscaling/v2alpha1
kind: HorizontalPodAutoscaler
metadata:
  name: my-deployment
  namespace: default
spec:
  scaleTargetRef:
    apiVersion: apps/v1beta1
    kind: Deployment
    name: my-deployment
  minReplicas: 1
  maxReplicas: 10
  metrics:
  - type: Object
    object:
      target:
        kind: Service
        name: name-of-metrics-app
      metricName: my-custom-metric
      targetValue: 9001