# HPA Autoscaler

Источник: https://ealebed.github.io/posts/2019/знакомство-с-kubernetes-часть-19-horizontalpodautoscaler/

 `HorizontalPodAutoscaler` - объектов, предназначенных для автоматического масштабирования количества подов ([Pods](../4.3.6.1%20Orchestrators.md)) в [Replication Controller](../4.3.6.1%20Orchestrators.md), [Replica Set](../4.3.6.1%20Orchestrators.md) или [Deployment](../4.3.6.1%20Orchestrators.md), основываясь на использовании [CPU](/2%20ComputerScience/2.0%20Linux/2.0.2%20Processor(CPU).md) (или, при поддержке custom metrics, на других метриках приложения).

 > Cтоит отметить, что HorizontalPodAutoscaler не может быть применен к объектам, которые не предназначены для масштабирования, например [DaemonSets](../4.3.6.1%20Orchestrators.md). Horizontal Pod Autoscaler состоит из [Kubernetes](../4.3.6.2%20Kubernetes(k8s).md) ресурса (объекта) и контроллера, поведение которого описывается ресурсом.

 C периодичностью 15 секунд (можно изменить с помощью параметра `--horizontal-pod-autoscaler-sync-period`), контроллер собирает данные по использованию метрик, определенных в манифесте ресурса `HorizontalPodAutoscaler`. Метрики собираются или с resource metrics API (метрики использования ресурсов подами) или с `custom metrics API` (остальные метрики, например, метрики приложения).

 Для каждого подконтрольного пода, контроллер собирает метрики (например, использования [CPU](/2%20ComputerScience/2.0%20Linux/2.0.2%20Processor(CPU).md)) с resource metrics API (`metrics.k8s.io`, предоставляется metrics-server). Далее, происходит вычисление текущего значения использования [CPU](/2%20ComputerScience/2.0%20Linux/2.0.2%20Processor(CPU).md) в процентах от запрошенных ресурсов (resource request) контейнерами каждого пода, после чего это значение сравнивается с “целевым” (target) значением - порогом, после которого количество подов должно быть увеличено.

 ## Пример

 Создадим файл `test-hpa.yaml` с описанием ресурса `HorizontalPodAutoscaler` такого содержания:

 ```yaml
apiVersion: autoscaling/v2beta1
kind: HorizontalPodAutoscaler
metadata:
  name: test-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: test-api-deploy
  minReplicas: 10
  maxReplicas: 29
  metrics:
  - type: Resource
    resource:
      name: cpu
      targetAverageUtilization: 80
 ```

Создадим данный объект в кластере [Kubernetes](../4.3.6.2%20Kubernetes(k8s).md):

`kubectl create -f test-hpa.yaml`

Проверим наличие объекта:

```k8s
-> kubectl get horizontalpodautoscaler
NAME          REFERENCE                        TARGETS         MINPODS   MAXPODS   REPLICAS   AGE
test-hpa      Deployment/test-api-deploy       <unknown>/80%   10        29        0          7s
```

Спустя некоторое время, вместо `<unknown>`, мы должны увидеть текущее использование [CPU](/2%20ComputerScience/2.0%20Linux/2.0.2%20Processor(CPU).md) подами в деплойменте `test-api-deploy`, однако в моем случае этого не произошло. Начинаем разбираться - для начала, убедимся, что `metrics.k8s.io` доступно:

```yaml
-> kubectl get --raw "/apis/metrics.k8s.io/" | jq
{
  "kind": "APIGroup",
  "apiVersion": "v1",
  "name": "metrics.k8s.io",
  "versions": [
    {
      "groupVersion": "metrics.k8s.io/v1beta1",
      "version": "v1beta1"
    }
  ],
  "preferredVersion": {
    "groupVersion": "metrics.k8s.io/v1beta1",
    "version": "v1beta1"
  }
}
```

Проверим, что метрики использования [CPU](/2%20ComputerScience/2.0%20Linux/2.0.2%20Processor(CPU).md) доступны. Первый вариант:

```k8s
-> kubectl top pod | grep test-api-deploy
test-api-deploy-5f77b79896-2t9x9                        738m         43931Mi
test-api-deploy-5f77b79896-fhr7b                        643m         43999Mi
```

Второй вариант (метрики только одного конкретного пода):


<details><summary>Раскрыть</summary>
<p>

```k8s
-> kubectl get --raw /apis/metrics.k8s.io/v1beta1/namespaces/default/pods/test-api-deploy-5f77b79896-xhpbx | jq
{
  "kind": "PodMetrics",
  "apiVersion": "metrics.k8s.io/v1beta1",
  "metadata": {
    "name": "test-api-deploy-5f77b79896-xhpbx",
    "namespace": "default",
    "selfLink": "/apis/metrics.k8s.io/v1beta1/namespaces/default/pods/test-api-deploy-5f77b79896-xhpbx",
    "creationTimestamp": "2019-06-11T13:50:00Z"
  },
  "timestamp": "2019-06-11T13:49:41Z",
  "window": "30s",
  "containers": [
    {
      "name": "envoy",
      "usage": {
        "cpu": "489151208n",
        "memory": "45692Ki"
      }
    },
    {
      "name": "test",
      "usage": {
        "cpu": "7125240328n",
        "memory": "45515856Ki"
      }
    }
  ]
}
```

</p>
</details>

Как видим, метрики доступны. Получим детальное описание нашего `HorizontalPodAutoscaler`:

<details><summary>Раскрыть</summary>
<p>

-> kubectl describe hpa test-hpa
Name:                                                  test-hpa
Namespace:                                             default
Labels:                                                app.kubernetes.io/managed-by=spinnaker
                                                       app.kubernetes.io/name=test
Annotations:                                           artifact.spinnaker.io/location: default
                                                       artifact.spinnaker.io/name: test-hpa
                                                       artifact.spinnaker.io/type: kubernetes/horizontalpodautoscaler
                                                       kubectl.kubernetes.io/last-applied-configuration:
                                                         {"apiVersion":"autoscaling/v2beta1","kind":"HorizontalPodAutoscaler","metadata":{"annotations":{"artifact.spinnaker.io/location":"default"...
                                                       moniker.spinnaker.io/application: test
                                                       moniker.spinnaker.io/cluster: horizontalpodautoscaler test-hpa
CreationTimestamp:                                     Tue, 11 Jun 2019 11:21:03 +0300
Reference:                                             Deployment/test-api-deploy
Metrics:                                               ( current / target )
  resource cpu on pods  (as a percentage of request):  <unknown> / 80%
Min replicas:                                          10
Max replicas:                                          29
Deployment pods:                                       10 current / 10 desired
Conditions:
  Type           Status  Reason                   Message
  ----           ------  ------                   -------
  AbleToScale    True    SucceededGetScale        the HPA controller was able to get the target's current scale
  ScalingActive  False   FailedGetResourceMetric  the HPA was unable to compute the replica count: missing request for cpu
Events:
  Type     Reason                        Age                    From                       Message
  ----     ------                        ----                   ----                       -------
  Normal   SuccessfulRescale             7m17s                  horizontal-pod-autoscaler  New size: 10; reason: Current number of replicas below Spec.MinReplicas
  Warning  FailedComputeMetricsReplicas  4m15s (x12 over 7m2s)  horizontal-pod-autoscaler  failed to get cpu utilization: missing request for cpu
  Warning  FailedGetResourceMetric       2m15s (x20 over 7m2s)  horizontal-pod-autoscaler  missing request for cpu
</p>
</details>

Здесь самое важное - сообщение `the HPA was unable to compute the replica count: missing request for cpu`. И действительно, в манифесте развертывания (`Deployment`) не указаны resource requests для одного из контейнеров (с именем envoy):

<details><summary>Раскрыть</summary>
<p>

```k8s
apiVersion: apps/v1
kind: Deployment
metadata:
  annotations:
# From https://www.spinnaker.io/reference/providers/kubernetes-v2/#strategy  
    strategy.spinnaker.io/use-source-capacity: "true"
  name: test-api-deploy
spec:
#  replicas: 15
  selector:
    matchLabels:
      deployment: test-api-deploy
  strategy:
    rollingUpdate:
      maxSurge: 0
    type: RollingUpdate
  template:
    metadata:
      labels:
        deployment: test-api-deploy
    spec:
      containers:
      - image: envoyproxy/envoy:v1.10.0
        name: envoy
        ports:
        - containerPort: 8080
          name: http
        volumeMounts:
        - mountPath: /etc/envoy
          name: envoy-config
      - env:
        - name: JAVA_OPTS
          value: -Xms40g -Xmx40g
        image: index.docker.io/ealebed/test:v1
        name: test
        resources:
          limits:
            memory: 55Gi
          requests:
            cpu: "10"
            memory: 55Gi
      volumes:
      - configMap:
          name: envoy-config
        name: envoy-config
```

</p>
</details>

**Важно!** Если не указаны resource request хотя бы для одного из контейнеров в `Replication Controller`, `Replica Set` или `Deployment`, то текущее значение использование [CPU](/2%20ComputerScience/2.0%20Linux/2.0.2%20Processor(CPU).md) подами не может быть корректно определено, и, в результате, `HorizontalPodAutoscaler` не будет предпринимать никаких действий по масштабированию.

После исправления этой досадной ошибки, `HorizontalPodAutoscaler`, базируясь на полученных метриках, начинает масштабировать поды в развертывании:

```k8s
kubectl get horizontalpodautoscaler                                                                                                              
NAME          REFERENCE                       TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
test-hpa      Deployment/test-api-deploy      86%/80%   10        29        29         9m10
```

Формула, по которой HorizontalPodAutoscaler вычисляет требуемое количество реплик выглядит так:

`desiredReplicas = ceil[currentReplicas * ( currentMetricValue / desiredMetricValue )]`

Например, если текущее значение метрики (currentMetricValue) равно `200m`, а ожидаемое (desiredMetricValue) установлено в `100m`, то количество реплик будет удвоено (`200.0 / 100.0 == 2.0`). Если же текущее значение метрики равно всего лишь `50m`, то количество реплик должно быть уменьшено вдвое (`50.0 / 100.0 == 0.5`). Если соотношение текущего значения метрики к ожидаемому значению достаточно близко к 1, то никаких действий не будет предпринято.

Так как мы указали `targetAverageUtilization` при описании ресурса `HorizontalPodAutoscaler`, то текущее значение метрики (`currentMetricValue`) использования [CPU](/2%20ComputerScience/2.0%20Linux/2.0.2%20Processor(CPU).md) рассчитывается как среднее значение этой метрики для всех подов, контролируемых данным автоскейлером.

После того, как текущее значение использования [CPU](/2%20ComputerScience/2.0%20Linux/2.0.2%20Processor(CPU).md) снизилось и оставалось низким в течении 5 минут (устанавливается с помощью параметра `--horizontal-pod-autoscaler-downscale-stabilization`), количество реплик было автоматически уменьшено:

```k8s
-> kubectl get horizontalpodautoscaler                                                                                                              
NAME          REFERENCE                       TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
test-hpa      Deployment/test-api-deploy      70%/80%   20        29        23         1h
```

----------

[4.3.6.6.3 k8s Thread And Heap Dump Theme](./4.3.6.6.3%20k8sThreadAndHeapDump.md) | [Back To iOSWiki Contents](https://github.com/eldaroid/iOSWiki) | [4.3.6.6.5 Vertical Pod Autoscaler Theme](./4.3.6.6.5%20VerticalPodAutoscaler.md)