Ниже рассматривается только HPA (Horizontal Pod Autoscaling) с apiVersion: autoscaling/v2, чья поддержка появилась начиная с Kubernetes v1.12.
Для настройки HPA требуется:
- определить, что именно масштабируется (
.spec.scaleTargetRef
); - определить диапазон масштабирования (
.spec.minReplicas
,.scale.maxReplicas
); - зарегистрировать в API Kubernetes и определить метрики, на основе которых производится масштабирование (
.spec.metrics
).
Метрики с точки зрения HPA бывают трех видов:
- классические — с типом (
.spec.metrics[].type
) «Resource», используются для простейшего масштабирования по потреблению процессора и памяти; - кастомные — с типами (
.spec.metrics[].type
) «Pods» или «Object»; - внешние — с типом (
.spec.metrics[].type
) «External».
Важно! По умолчанию HPA использует разные подходы при масштабировании:
- Если метрики указывают на требование масштабировать вверх, это происходит незамедлительно (
spec.behavior.scaleUp.stabilizationWindowSeconds
= 0). Единственное ограничение — скорость прироста: за 15 секунд поды могут удвоиться, но если подов меньше 4, добавятся 4 новых пода. - Если метрики указывают на то, что требуется масштабировать вниз, это происходит в течение 5 минут (
spec.behavior.scaleUp.stabilizationWindowSeconds
= 300): собираются предложения о новом количестве реплик, в результате чего выбирается самое большое значение. Нет ограничений на количество удаляемых подов за один раз.
Если имеются проблемы с колебаниями метрик и происходит резкое увеличение ненужных реплик приложения, применяются следующие подходы:
- Оборачивание метрики агрегирующей функцией (например,
avg_over_time()
), если метрика определена PromQL-запросом. Подробнее см. пример. - Увеличение времени стабилизации (параметр
spec.behavior.scaleUp.stabilizationWindowSeconds
) в ресурсе HorizontalPodAutoscaler. В течение обозначенного периода будут собираться предложения об увеличении количества реплик, в результате чего будет выбрано самое скромное предложение. Это решение тождественно применению агрегирующей функцииmin_over_time(<stabilizationWindowSeconds>)
, но только в том случае, если метрика растет и требуется масштабирование вверх. Для масштабирования вниз, как правило, достаточно стандартных настроек. Подробнее см. пример. - Ограничение скорости прироста новых реплик с помощью политик
spec.behavior.scaleUp.policies
.
Типы масштабирования
Используйте следующие метрики для масштабирования приложений:
- Классического типа.
- Кастомные namespace-scoped-метрики. При условии, если у вас одно приложение, источник метрик находится внутри namespace и связан с одним из объектов.
- Кастомные cluster-wide-метрики. При условии, если у вас много приложений используют одинаковую метрику, источник которой находится в namespace приложения, и она связана с одним из объектов. Подобные метрики предусмотрены на случай необходимости выделения общих инфраструктурных компонентов в отдельный деплой («infra»).
- Если источник метрики не привязан к namespace приложения, используйте внешние метрики. Например, метрики облачного провайдера или внешнего SaaS-сервиса.
Важно! Рекомендуется использовать вариант 1 (классические метрики), или вариант 2 (кастомные метрики, определяемые в Namespace). В этом случае, рекомендуется определить конфигурацию приложения, включающую его автоматическое масштабирование, в репозиторий самого приложения. Следует рассматривать варианты 3 и 4 только в том случае, если у вас имеется большая коллекция идентичных микросервисов.
Классическое масштабирование по потреблению ресурсов
Пример HPA для масштабирования по базовым метрикам из metrics.k8s.io
: CPU и памяти подов. Особое внимание на averageUtulization
— это значение отражает целевой процент ресурсов, который был реквестирован.
1apiVersion: autoscaling/v2
2kind: HorizontalPodAutoscaler
3metadata:
4 name: app-hpa
5 namespace: app-prod
6spec:
7 # Указывается контроллер, который нужно масштабировать (ссылка на deployment или statefulset).
8 scaleTargetRef:
9 apiVersion: apps/v1
10 kind: Deployment
11 name: app
12 # Границы масштабирования контроллера.
13 minReplicas: 1
14 maxReplicas: 10
15 # Если для приложения характерны кратковременные скачки потребления CPU,
16 # можно отложить принятие решения о масштабировании, чтобы убедиться, что оно необходимо.
17 # По умолчанию масштабирование вверх происходит немедленно.
18 behavior:
19 scaleUp:
20 stabilizationWindowSeconds: 300
21 metrics:
22 # Масштабирование по CPU и памяти.
23 - type: Resource
24 resource:
25 name: cpu
26 target:
27 # Масштабирование, когда среднее использование CPU всех подов в scaleTargetRef превышает заданное значение.
28 # Для метрики с type: Resource доступен только type: Utilization.
29 type: Utilization
30 # Масштабирование, если для всех подов из Deployment запрошено по 1 ядру и в среднем уже используется более 700m.
31 averageUtilization: 70
32 - type: Resource
33 resource:
34 name: memory
35 target:
36 # Пример масштабирования, когда среднее использование памяти всех подов в scaleTargetRef превышает заданное значение.
37 type: Utilization
38 # Масштабирование, если для подов запрошено по 1 ГБ памяти и в среднем использовано уже более 800 МБ.
39 averageUtilization: 80
Масштабирование по кастомным метрикам
Регистрация кастомных метрик в Kubernetes API
Кастомные метрики необходимо регистрировать в API /apis/custom.metrics.k8s.io/
, эту регистрацию производит prometheus-metrics-adapter (и он же реализует API). На эти метрики можно будет ссылаться из объекта HorizontalPodAutoscaler. Настройка ванильного prometheus-metrics-adapter — трудоемкий процесс, мы его упростили, определив набор Custom Resources с разным Scope:
- Namespaced:
ServiceMetric
;IngressMetric
;PodMetric
;DeploymentMetric
;StatefulsetMetric
;NamespaceMetric
;DaemonSetMetric
(недоступен пользователям).
- Cluster:
ClusterServiceMetric
(недоступен пользователям);ClusterIngressMetric
(недоступен пользователям);ClusterPodMetric
(недоступен пользователям);ClusterDeploymentMetric
(недоступен пользователям);ClusterStatefulsetMetric
(недоступен пользователям);ClusterDaemonSetMetric
(недоступен пользователям).
С помощью cluster-wide-ресурса можно задать глобальное определение метрики, а с помощью Namespace можно переопределить её локально. Формат для всех custom resource — одинаковый.
Применяем кастомные метрики в HPA
После регистрации кастомной метрики на нее можно ссылаться. С точки зрения HPA, кастомные метрики бывают двух видов — Pods
и Object
.
Object
— отсылает к объекту в кластере, который имеет в Prometheus метрики с соответствующими лейблами (namespace=XXX,ingress=YYY
). Эти лейблы будут подставляться вместо <<.LabelMatchers>>
в вашем кастомном запросе.
1apiVersion: deckhouse.io/v1beta1
2kind: IngressMetric
3metadata:
4 name: mymetric
5 namespace: mynamespace
6spec:
7 query: sum(rate(ingress_nginx_detail_requests_total{<<.LabelMatchers>>}[2m])) by (<<.GroupBy>>) OR on() vector(0)
8---
9kind: HorizontalPodAutoscaler
10apiVersion: autoscaling/v2
11metadata:
12 name: myhpa
13 namespace: mynamespace
14spec:
15 # Указывается контроллер, который нужно масштабировать (ссылка на deployment или statefulset).
16 scaleTargetRef:
17 apiVersion: apps/v1
18 kind: Deployment
19 name: myapp
20 minReplicas: 1
21 maxReplicas: 2
22 # Метрики, используемые для масштабирования.
23 # Пример использования кастомных метрик.
24 metrics:
25 - type: Object
26 object:
27 # Объект, который обладает метриками в Prometheus.
28 describedObject:
29 apiVersion: networking.k8s.io/v1
30 kind: Ingress
31 name: myingress
32 metric:
33 # Метрика, зарегистрированная с помощью custom resource IngressMetric или ClusterIngressMetric.
34 # Можно использовать rps_1m, rps_5m или rps_15m которые поставляются с модулем prometheus-metrics-adapter.
35 name: mymetric
36 target:
37 # Для метрик типа Object можно использовать `Value` или `AverageValue`.
38 type: AverageValue
39 # Масштабирование происходит, если среднее значение кастомной метрики для всех подов в Deployment сильно отличается от 10.
40 averageValue: 10
Pods
— из ресурса, которым управляет HPA, будут выбраны все поды и для каждого пода будут собраны метрики с соответствующими лейблами (namespace=XXX
, pod=YYY-sadiq
, namespace=XXX
, pod=YYY-e3adf
, и т. д.). Из этих показателей HPA рассчитает среднее значение и использует для масштабирования.
Пример использования кастомных метрик с размером очереди RabbitMQ
В представленном примере рассматривается очередь send_forum_message
в RabbitMQ, для которого зарегистрирован сервис rmq
. Если количество сообщений в этой очереди превышает 42, выполняется масштабирование.
1apiVersion: deckhouse.io/v1beta1
2kind: ServiceMetric
3metadata:
4 name: rmq-queue-forum-messages
5 namespace: mynamespace
6spec:
7 query: sum (rabbitmq_queue_messages{<<.LabelMatchers>>,queue=~"send_forum_message",vhost="/"}) by (<<.GroupBy>>)
8---
9kind: HorizontalPodAutoscaler
10apiVersion: autoscaling/v2
11metadata:
12 name: myhpa
13 namespace: mynamespace
14spec:
15 # Указывается контроллер, который нужно масштабировать (ссылка на deployment или statefulset).
16 scaleTargetRef:
17 apiVersion: apps/v1
18 kind: Deployment
19 name: myconsumer
20 minReplicas: 1
21 maxReplicas: 5
22 metrics:
23 - type: Object
24 object:
25 describedObject:
26 apiVersion: v1
27 kind: Service
28 name: rmq
29 metric:
30 name: rmq-queue-forum-messages
31 target:
32 type: Value
33 value: 42
Пример использования нестабильной кастомной метрики
Улучшение предыдущего примера.
В представленном примере рассматривается очередь send_forum_message
в RabbitMQ, для которого зарегистрирован сервис rmq
. Если количество сообщений в этой очереди превышает 42, выполняется масштабирование. Мы не хотим реагировать на краткосрочные всплески, поэтому используется MQL-функцию avg_over_time()
, чтобы усреднить метрику.
1apiVersion: deckhouse.io/v1beta1
2kind: ServiceMetric
3metadata:
4 name: rmq-queue-forum-messages
5 namespace: mynamespace
6spec:
7 query: sum (avg_over_time(rabbitmq_queue_messages{<<.LabelMatchers>>,queue=~"send_forum_message",vhost="/"}[5m])) by (<<.GroupBy>>)
8---
9kind: HorizontalPodAutoscaler
10apiVersion: autoscaling/v2
11metadata:
12 name: myhpa
13 namespace: mynamespace
14spec:
15 # Указывается контроллер, который нужно масштабировать (ссылка на deployment или statefulset).
16 scaleTargetRef:
17 apiVersion: apps/v1
18 kind: Deployment
19 name: myconsumer
20 minReplicas: 1
21 maxReplicas: 5
22 metrics:
23 - type: Object
24 object:
25 describedObject:
26 apiVersion: v1
27 kind: Service
28 name: rmq
29 metric:
30 name: rmq-queue-forum-messages
31 target:
32 type: Value
33 value: 42
Примеры с использованием кастомных метрик типа Pods
Пример масштабирования воркеров по процентному количеству активных php-fpm-воркеров.
В представленом примере среднее количество php-fpm-воркеров в Deployment mybackend
не больше 5.
1apiVersion: deckhouse.io/v1beta1
2kind: PodMetric
3metadata:
4 name: php-fpm-active-workers
5spec:
6 query: sum (phpfpm_processes_total{state="active",<<.LabelMatchers>>}) by (<<.GroupBy>>)
7---
8kind: HorizontalPodAutoscaler
9apiVersion: autoscaling/v2
10metadata:
11 name: myhpa
12 namespace: mynamespace
13spec:
14 # Указывается контроллер, который нужно масштабировать (ссылка на deployment или statefulset).
15 scaleTargetRef:
16 apiVersion: apps/v1
17 kind: Deployment
18 name: mybackend
19 minReplicas: 1
20 maxReplicas: 5
21 metrics:
22 # Указание HPA обойти все поды Deployment'а и собрать с них метрики.
23 - type: Pods
24 # Указывать describedObject в отличие от type: Object не надо.
25 pods:
26 metric:
27 # Кастомная метрика, зарегистрированная с помощью custom resource PodMetric.
28 name: php-fpm-active-workers
29 target:
30 # Для метрик с type: Pods можно использовать только AverageValue.
31 type: AverageValue
32 # Масштабирование, если среднее значение метрики у всех подов Deployment'а больше 5.
33 averageValue: 5
Масштабируется Deployment по процентному количеству активных php-fpm-воркеров.
1---
2apiVersion: deckhouse.io/v1beta1
3kind: PodMetric
4metadata:
5 name: php-fpm-active-worker
6spec:
7 # Процент активных php-fpm-воркеров. Функция round() для того, чтобы не смущаться от миллипроцентов в HPA.
8 query: round(sum by(<<.GroupBy>>) (phpfpm_processes_total{state="active",<<.LabelMatchers>>}) / sum by(<<.GroupBy>>) (phpfpm_processes_total{<<.LabelMatchers>>}) * 100)
9---
10kind: HorizontalPodAutoscaler
11apiVersion: autoscaling/v2
12metadata:
13 name: {{ .Chart.Name }}-hpa
14spec:
15 # Указывается контроллер, который нужно масштабировать (ссылка на deployment или statefulset).
16 scaleTargetRef:
17 apiVersion: apps/v1beta1
18 kind: Deployment
19 name: {{ .Chart.Name }}
20 minReplicas: 4
21 maxReplicas: 8
22 metrics:
23 - type: Pods
24 pods:
25 metric:
26 name: php-fpm-active-worker
27 target:
28 type: AverageValue
29 # Масштабирование, если в среднем по Deployment 80% воркеров заняты.
30 averageValue: 80
Регистрация внешних метрик в Kubernetes API
Модуль prometheus-metrics-adapter
поддерживает механизм externalRules
, с помощью которого можно определять кастомные PromQL-запросы и регистрировать их как метрики.
В примерах инсталляций добавлено универсальное правило, которое позволяет создавать собственные метрики без настроек в prometheus-metrics-adapter
, — «любая метрика в Prometheus с именем kube_adapter_metric_<name>
будет зарегистрирована в API под именем <name>
». После чего, остается написать экспортер (exporter), который будет экспортировать подобную метрику, или создать правило recording rule в Prometheus, которое будет агрегировать вашу метрику на основе других метрик.
Пример CustomPrometheusRules:
В примере представлены пользовательские правила Prometheus для метрики mymetric
.
В примере представлены пользовательские правила Prometheus для метрики mymetric
.
1apiVersion: deckhouse.io/v1
2kind: CustomPrometheusRules
3metadata:
4 # Рекомендованный шаблон для названия ваших CustomPrometheusRules.
5 name: prometheus-metrics-adapter-mymetric
6spec:
7 groups:
8 # Рекомендованный шаблон.
9 - name: prometheus-metrics-adapter.mymetric
10 rules:
11 # Название вашей новой метрики.
12 # Важно! Префикс 'kube_adapter_metric_' обязателен.
13 - record: kube_adapter_metric_mymetric
14 # Запрос, результаты которого попадут в итоговую метрику, нет смысла тащить в нее лишние лейблы.
15 expr: sum(ingress_nginx_detail_sent_bytes_sum) by (namespace,ingress)
Применение внешних метрик в HPA
После регистрации внешней метрики на нее можно сослаться.
1kind: HorizontalPodAutoscaler
2apiVersion: autoscaling/v2
3metadata:
4 name: myhpa
5 namespace: mynamespace
6spec:
7 # Указывается контроллер, который нужно масштабировать (ссылка на deployment или statefulset).
8 scaleTargetRef:
9 apiVersion: apps/v1
10 kind: Deployment
11 name: myapp
12 minReplicas: 1
13 maxReplicas: 2
14 metrics:
15 # Используем внешние метрики для масштабирования.
16 - type: External
17 external:
18 metric:
19 # Метрика, которую мы зарегистрировали с помощью создания метрики в Prometheus kube_adapter_metric_mymetric, но без префикса 'kube_adapter_metric_'.
20 name: mymetric
21 selector:
22 # Для внешних метрик можно и нужно уточнять запрос с помощью лейблов.
23 matchLabels:
24 namespace: mynamespace
25 ingress: myingress
26 target:
27 # Для метрик типа External можно использовать только `type: Value`.
28 type: Value
29 # Масштабирование, если значение нашей метрики больше 10.
30 value: 10
Пример с размером очереди в Amazon SQS
Чтобы установить экспортер для интеграции с SQS:
- Cоздайте отдельный “служебный” репозиторий Git (или, к примеру, можно использовать “инфраструктурный” репозиторий).
- Pазместите в нем инсталляцию экспортера и сценарий для создания требуемого CustomPrometheusRules.
Готово, вы объединили кластер. Если необходимо настроить автомасштабирование только для одного приложения (в одном пространстве имен), лучше ставить экспортер вместе с этим приложением и воспользоваться NamespaceMetrics
.
Ниже приведен пример экспортера (например, sqs-exporter) для получения метрик из Amazon SQS, если:
- в Amazon SQS работает очередь
send_forum_message
; - выполняется масштабирование при количестве сообщений в этой очереди больше 42.
1apiVersion: deckhouse.io/v1
2kind: CustomPrometheusRules
3metadata:
4 # Рекомендованное название — prometheus-metrics-adapter-<metric name>.
5 name: prometheus-metrics-adapter-sqs-messages-visible
6spec:
7 groups:
8 # Рекомендованный шаблон названия.
9 - name: prometheus-metrics-adapter.sqs_messages_visible
10 rules:
11 # Важно! Префикс 'kube_adapter_metric_' обязателен.
12 - record: kube_adapter_metric_sqs_messages_visible
13 expr: sum (sqs_messages_visible) by (queue)
14---
15kind: HorizontalPodAutoscaler
16apiVersion: autoscaling/v2
17metadata:
18 name: myhpa
19 namespace: mynamespace
20spec:
21 # Указывается контроллер, который нужно масштабировать (ссылка на deployment или statefulset).
22 scaleTargetRef:
23 apiVersion: apps/v1
24 kind: Deployment
25 name: myconsumer
26 minReplicas: 1
27 maxReplicas: 5
28 metrics:
29 - type: External
30 external:
31 metric:
32 # name должен совпадать с CustomPrometheusRules record без префикса 'kube_adapter_metric_'.
33 name: sqs_messages_visible
34 selector:
35 matchLabels:
36 queue: send_forum_messages
37 target:
38 type: Value
39 value: 42
Способы отладки
Как получить список кастомных метрик?
1kubectl get --raw /apis/custom.metrics.k8s.io/v1beta1/
Как получить значение метрики, привязанной к объекту?
1kubectl get --raw /apis/custom.metrics.k8s.io/v1beta1/namespaces/my-namespace/services/*/my-service-metric
2kubectl get --raw /apis/custom.metrics.k8s.io/v1beta1/namespaces/my-namespace/ingresses/*/rps_1m
3kubectl get --raw /apis/custom.metrics.k8s.io/v1beta1/namespaces/my-namespace/ingresses/*/mymetric
Как получить значение метрики, созданной через NamespaceMetric
?
1kubectl get --raw /apis/custom.metrics.k8s.io/v1beta1/namespaces/my-namespace/metrics/my-ns-metric
Как получить external-метрики?
1kubectl get --raw /apis/external.metrics.k8s.io/v1beta1
2kubectl get --raw /apis/external.metrics.k8s.io/v1beta1/namespaces/d8-ingress-nginx/d8_ingress_nginx_ds_cpu_utilization