Масштабируем приложение в Kubernetes от метрики в Yandex Monitoring (или от любого http-эндпоинта)
Всем привет! Меня зовут Дмитрий Мамонтов, я DevOps-инженер с опытом работы более пяти лет, а также наставник на курсе «DevOps для эксплуатации и разработки» и один из авторов курса «Эксплуатация и разработка в Kubernetes» в Яндекс Практикуме.
Представим, что у нас есть приложение, которое шлёт свои метрики в Yandex Monitoring, и стоит задача: масштабировать это приложение с помощью HPA в кластере Kubernetes в зависимости от метрики.
Есть два популярных варианта решения этой задачи:
Настроить интеграцию с prometheus-adapter и перекладывать метрики в Prometheus из Yandex Monitoring. Для этого метода нам необходим установленный Prometheus и prometheus-adapter. Prometheus будет опрашивать Yandex Monitoring, а адаптер будет отдавать метрику для работы HPA .
Использовать Keda-operator. В данном случае опрашивать Yandex Monitoring будет Keda и сама управлять работой HPA. Воспользуемся этим вариантом.
Генерация тестовых метрик
Если приложения у нас нет под рукой, а попробовать хочется, то можно воспользоваться примером из документации или сгенерировать несколько значений метрик с помощью утилитыcurl
.
Логинимся в облако с помощью ycи выписываем токен.
yc init
yc iam create-token
Отправим несколько метрик запросом вида, подставив значение 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
С помощью 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, чтобы контроллер принял решение о количестве запущенных подов.