Масштабируем приложение в Kubernetes от метрики в Yandex Monitoring (или от любого http-эндпоинта)

Всем привет! Меня зовут Дмитрий Мамонтов, я DevOps-инженер с опытом работы более пяти лет, а также наставник на курсе «DevOps для эксплуатации и разработки» и один из авторов курса «Эксплуатация и разработка в Kubernetes» в Яндекс Практикуме.

Представим, что у нас есть приложение, которое шлёт свои метрики в Yandex Monitoring, и стоит задача: масштабировать это приложение с помощью HPA в кластере Kubernetes в зависимости от метрики.

Есть два популярных варианта решения этой задачи:

  1. Настроить интеграцию с prometheus-adapter и перекладывать метрики в Prometheus из Yandex Monitoring. Для этого метода нам необходим установленный Prometheus и prometheus-adapter. Prometheus будет опрашивать Yandex Monitoring, а адаптер будет отдавать метрику для работы HPA .

  2. Использовать Keda-operator. В данном случае опрашивать Yandex Monitoring будет Keda и сама управлять работой HPA. Воспользуемся этим вариантом.

Генерация тестовых метрик

Если приложения у нас нет под рукой, а попробовать хочется, то можно воспользоваться примером из документации или сгенерировать несколько значений метрик с помощью утилитыcurl.

  1. Логинимся в облако с помощью ycи выписываем токен.

yc init
yc iam create-token
  1. Отправим несколько метрик запросом вида, подставив значение folder и токена:

curl -X POST \
  'https://monitoring.api.cloud.yandex.net/monitoring/v2/data/write?folderId=ВАШФОЛДЕР&service=custom' \
  -H "Authorization: Bearer ВАШТОКЕН" \
  -H "Content-Type: application/json" \
  -d '{
    "metrics": [
        {
            "name": "trigger_metric",
            "labels": {
                "foo": "bar"
            },
            "value": 1000
        }
    ]
}'

Ответ будет вида:

{"writtenMetricsCount":1}

На графике будут видны метрики:

Метрик лучше сгенерировать несколько штук для наглядности

Метрик лучше сгенерировать несколько штук для наглядности

Приступаем к настройке масштабирования

Установку k8s (в облаке или вне его) я пропущу. В Яндексе можно поднять как через веб-консоль (или yc), так и с помощью terraform-провайдера.

Управлять масштабированием будем с помощью KEDA.
KEDA — это компонент для Kubernetes, который расширяет возможности горизонтального масштабирования (HPA). Он позволяет масштабировать приложения на основе событий или метрик от внешних источников, таких как очереди сообщений, базы данных, сервисы мониторинга и другие.

Для его настройки в нашем примере необходимо поставить оператор и настроить обращения в Yandex Monitoring.
Приступим:

Процесс установки оператора описан тут. Воспользуемся готовым helm-чартом.

# Добавим репозиторий чартов
$ helm repo add kedacore  https://kedacore.github.io/charts
$ helm repo update
# Установим оператора Keda:
# При желании, можно поправить\посмотреть параметры установки в values
$ helm show values kedacore/keda
$ helm install keda kedacore/keda --namespace keda --create-namespace

Итого получим установленный оператор:

$ kubectl get po -n keda
NAME                                              READY   STATUS    RESTARTS        AGE
keda-admission-webhooks-85bfd658f5-55zhf          1/1     Running   0               4h16m
keda-operator-c6f875576-465sm                     1/1     Running   1 (4h16m ago)   4h16m
keda-operator-metrics-apiserver-6d5b8869f-w6lz2   1/1     Running   0 

Приступаем к интеграции с Yandex Monitoring.

Сначала установим тестовое приложение, которое будем масштабировать (если у вас его ещё нет).

apiVersion: apps/v1
kind: Deployment
metadata:
  name: simple-app
  labels:
    app: nginx
spec:
  replicas: 3 # изначально будет 3 пода
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:latest

Запустим его в неймспейсе по-умолчанию:

kubectl apply -f deploy.yaml

Метрику будем забирать по http в формате prometheus.
Для наших целей такой способ отлично подойдет так, как нам для принятия решения о масштабировании нужно именно последнее значение.
Чтобы обратиться к API за метрикой нам необходим будет статический API KEY. Для этого необходимо создать сервис-аккаунт с привилегией monitoring.viewer и выписать ключ от его имени.

Создание ключа для обращения по API

Создание ключа для обращения по API

С помощью curl можно получить значение так (не забудьте подставить ключ и фолдер):

$ curl "https://monitoring.api.cloud.yandex.net/monitoring/v2/prometheusMetrics?folderId=ВАШФОЛДЕР&service=custom" \
  -H "Authorization: Bearer ВАШ APIKEY"
# TYPE trigger_metric gauge
trigger_metric{foo="bar"} 1000.0

Далее нам нужно описать сущность ScaledObject, настроить скейлер Metric-api и поместить токен для доступа к метрикам в секрет.
Вообще, Keda имеет очень много скейлеров, которые позволяют интегрировать её с почти любой базой данных или системой мониторинга.

Обратите внимание, что версия API в альфе и может поменяться в новой версии контроллера.

apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: my-scaler
spec:
  scaleTargetRef:
    name: simple-nginx
  pollingInterval: 5  # Как часто опрашивать. Default: 30 seconds
  cooldownPeriod:  10 # Сколько ждать после изменения. Default: 300 seconds
  minReplicaCount: 1  # Минимальное значение
  maxReplicaCount: 5  # Максимальное значение
  triggers:
    - type: metrics-api
      metadata:
        targetValue: "200.0" # После какого значения метрики начинаем увеличивать\уменьшать поды
        activationTargetValue: "50.0" # Можно установить значения для активации\деактивации ScaledObject
        format: "prometheus"
        url: "https://monitoring.api.cloud.yandex.net/monitoring/v2/prometheusMetrics?folderId=ВАШФОЛДЕР&service=custom" # урл, где искать метрику
        valueLocation: "trigger_metric{foo=\"bar\"}" # не забываем экранировать кавычки
        authMode: "bearer"
      authenticationRef:
        name: keda-metric-api-creds 
---
apiVersion: keda.sh/v1alpha1
kind: TriggerAuthentication
metadata:
  name: keda-metric-api-creds
spec:
  secretTargetRef:
    - parameter: token
      name: keda-metric-api-secret
      key: token
---
apiVersion: v1
kind: Secret
metadata:
  name: keda-metric-api-secret
data:
  token: "ТОКЕН APIKEY ЗАШИФРОВАННЫЙ В BASE64"

Деплоим вышеописанные манифесты:

$ kubectl apply -f scale.yml

Проверяем:

$ kubectl get scaledobject
NAME        SCALETARGETKIND      SCALETARGETNAME   MIN   MAX   TRIGGERS      AUTHENTICATION          READY   ACTIVE   FALLBACK   PAUSED    AGE
my-scaler   apps/v1.Deployment   simple-nginx      1     5     metrics-api   keda-metric-api-creds   True    True     False      Unknown   51m

$ kubectl get hpa
NAME                 REFERENCE                 TARGETS         MINPODS   MAXPODS   REPLICAS   AGE
keda-hpa-my-scaler   Deployment/simple-nginx   200/200 (avg)   1         5         5          52m

Видим, что создав сущность scaledobject контроллер создал еще и hpa, который будет управлять масштабированием приложения.
Отправим метрику со значением 100 и убедимся, что кол-во подов уменьшилось.

$ curl -X POST \
  'https://monitoring.api.cloud.yandex.net/monitoring/v2/data/write?folderId=ВАШФОЛДЕР&service=custom' \
  -H "Authorization: Bearer ВАШТОКЕН" \
  -H "Content-Type: application/json" \
  -d '{
    "metrics": [
        {
            "name": "trigger_metric",
            "labels": {
                "foo": "bar"
            },
            "value": 100
        }
    ]
}'

$ kubectl get po
NAME                            READY   STATUS    RESTARTS   AGE
simple-nginx-7bf8c77b5b-qth7k   1/1     Running   0          4h

Отправим значение 1000 и проверим увеличение.

$ curl -X POST \
  'https://monitoring.api.cloud.yandex.net/monitoring/v2/data/write?folderId=ВАШФОЛДЕР&service=custom' \
  -H "Authorization: Bearer ВАШТОКЕН" \
  -H "Content-Type: application/json" \
  -d '{
    "metrics": [
        {
            "name": "trigger_metric",
            "labels": {
                "foo": "bar"
            },
            "value": 1000
        }
    ]
}'
$ kubectl get po
NAME                            READY   STATUS    RESTARTS   AGE
simple-nginx-7bf8c77b5b-gb2ft   1/1     Running   0          44s
simple-nginx-7bf8c77b5b-j5df4   1/1     Running   0          44s
simple-nginx-7bf8c77b5b-jgfd8   1/1     Running   0          44s
simple-nginx-7bf8c77b5b-nhmwb   1/1     Running   0          29s
simple-nginx-7bf8c77b5b-qth7k   1/1     Running   0          4h1m

В описании hpa при изменении метрик видим события вида:

Normal   SuccessfulRescale             11m                    horizontal-pod-autoscaler  New size: 1; reason: All metrics below target
Normal   SuccessfulRescale             2m50s (x2 over 63m)    horizontal-pod-autoscaler  New size: 4; reason: external metric s0-metric-api-trigger_metric{foo="bar"}(&LabelSelector{MatchLabels:map[string]string{scaledobject.keda.sh/name: my-scaler,},MatchExpressions:[]LabelSelectorRequirement{},}) above target

Работает!

Выводы

Думаю, все уже поняли, что в данной интеграции вместо Yandex Monitoring может быть любой http-эндпоинт, который будет опрашиваться scaledobject с заданной периодичностью. Главное, чтобы метрики были в нужном формате: yaml, json, prometheus или xml. Это дает неограниченное количество интеграций масштабирования всего и вся. Например, приложение может отдавать метрику по http, hpa будет принимать решение о масштабировании.
При этом нам не нужно хранить метрику! Достаточно её оперативно и регулярно отдавать по http, чтобы контроллер принял решение о количестве запущенных подов.

© Habrahabr.ru