ReplicationController
Deployment
ReplicaSet
Кратко его рассматривали в посте Kubernetes: запуск metrics-server в AWS EKS для Kubernetes Pod AutoScaler, теперь разберёмся с доступными метриками.
Для HPA доступны три типа метрик:
metrics.k8s.io
: метрики использования ресурсов, как правило предоставляетсяmetrics-server
: метрики, предоставляемые адаптерами (контроллерами), работающими в самом кластере, такими какcustom.metrics.k8s.io
Microsoft Azure Adapter ,Google Stackdriver ,Prometheus Adapter (Prometheus Adapter используем в этом посте чуть позже), см. полный списоктут>>> : по сути тот же Custom Metircs API, но метрики поставляются внешней системой, например от AWS CloudWatchexternal.metrics.k8s.io
См.
Кроме HPA существует
Содержание
Создание HorizontalPodAutoscaler
Создадим простой НРА, который будет выполнять скейлинг по потреблению CPU:
apiVersion: autoscaling/v1 kind: HorizontalPodAutoscaler metadata: name: hpa-example spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: deployment-example minReplicas: 1 maxReplicas: 5 targetCPUUtilizationPercentage: 10
Тут:
apiVersion: autoscaling/v1
— API-группаautoscaling
, обращайте внимание на версию, т.к. в APIv1
доступен скейлинг только по CPU, а memory и custom metrics могут быть использованы в API `v2beta2` (вv1
доступны через аннотации), см.API Object .spec.scaleTargetRef
: указываем НРА, какой контроллер он будет скейлить (ReplicationController
,Deployment
илиReplicaSet
), в данном случае будет выполнен поиск объекта типаDeployment
с именем deployment-examplespec.minReplicas
,spec.maxReplicas
: минимальное и максимальное количество подов в контроллере, которое будет запущено этим HPAtargetCPUUtilizationPercentage
: % от использования CPU, при достижении которого НРА начнёт добавлять или удалять подыrequests
Создаём его:
Проверяем:
Сейчас у нас TARGETS
со значением <unknown>, т.к. нет подов, с которых НРА мог бы собрать метрики, хотя сами метрики в кластере доступны — проверяем:
Добавляем Deployment с именем deployment-example, файл hpa-deployment-example.yaml
:
apiVersion: apps/v1 kind: Deployment metadata: name: deployment-example spec: replicas: 1 strategy: type: RollingUpdate selector: matchLabels: application: deployment-example template: metadata: labels: application: deployment-example spec: containers: - name: deployment-example-pod image: nginx ports: - containerPort: 80 resources: requests: cpu: 100m memory: 100Mi
Тут мы описываем Deployment, который запустит один под с NINGX, которому задаст requests
в 100 millicores и 100
Создаём его:
Проверяем HPA теперь:
Наш НРА увидел деплоймент, и начал собирать с него метрики.
Проверим метрики с конкретного пода — находим его имя:
И выполняем API-запрос:
Потребление CPU — ноль, памяти — 2 мегабайта — глянем в top
:
«Ага, вот эти ребята!» (с)
Хорошо — метрики видим, НРА есть, деплоймент есть — попробуем посмотреть, как работает скейлинг.
Load testing HorizontalPodAutoscaler scaling
Используем
Запустим переадресацию портов с локальной машины к поду, что бы получить доступ к нашему NGINX, т.к. LoadBalancer в мир мы не создавали (см. Kubernetes: ClusterIP vs NodePort vs LoadBalancer, Services и Ingress — обзор, примеры):
Проверяем ресурсы ещё раз:
0% CPU использовано, запущен 1 под (REPLICAS
1).
Запускаем тест:
Тут:
- открываем 100 подключений, используя 600 потоков
- выполняем тест 5 минут
Проверяем под:
CPU уже 49mi, в реквестах мы задавали лимит в 10 milicpu — проверяем HPA:
TARGETS
49% из лимита в 10% — и НРА запустил новые поды — REPLICAS
4:
Multi-metrics scaling
Сейчас наш Deployment скейлится только на основании метрики потребления CPU.
Обновим НРА — добавим возможность скейлится по памяти:
apiVersion: autoscaling/v2beta1 kind: HorizontalPodAutoscaler metadata: name: hpa-example spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: deployment-example minReplicas: 1 maxReplicas: 5 metrics: - type: Resource resource: name: cpu targetAverageUtilization: 10 - type: Resource resource: name: memory targetAverageUtilization: 10
autoscaling API group versions
Давайте ещё раз вернёмся к версиям API.
В первом варианте мы использовали targetCPUUtilizationPercentage
.
Проверяем metrics
, которое явлется массивом external
, object
, pods
, resource
.
В свою очередь для resource
имеется объект targetAverageUtilization
и targetAverageValue
, которые мы и используем в mertics
вместо указания targetCPUUtilizationPercentage
.
Применяем изменения в НРА:
Проверяем:
Теперь в TARGETS
мы видим две метрики — и память, и CPU.
Нагрузить NGINX по памяти будет сложно, потому проверим, сколько он сейчас потребляет памяти:
2 мегабайта.
Изменим наш HPA, и зададим не % от реквеста, а явное ограничение в 1024Ki — 1 мегабайт.
Вместо targetAverageUtilization
используем targetAverageUtilization
:
... metrics: - type: Resource resource: name: cpu targetAverageUtilization: 10 - type: Resource resource: name: memory targetAverageValue: 1024Ki
Обновляем, проверяем:
Проверим значение из TARGETS
— переведём в килобайты:
И реальное потребление в поде:
2492 ~= 2496Ki, окей, с этим разобрались, скейлинг тоже работает.
Custom Metrics
Memory metrics scaling
Помимо использование метрик, которые нам дают API-сервер и cAdvisor
, мы можем использовать любые другие метрики, например — собираемые Prometheus.
Это могут быть метрики из Cloudwatch-експортёра, node_exporter
, или метрики непосредственно из приложения.
Документация
Так как у нас используется Prometehus (см. Kubernetes: мониторинг с Prometheus и Kubernetes: мониторинг кластера с Prometheus Operator) — то используем его адаптер, вместе всё будет выглядеть примерно так:
Если попробовать обратиться к external и custom API-ендпоинтам сейчас — то получим ошибку:
Устанавливаем адаптер из Helm-чарта:
Ждём пару минут, и проверяем API ещё раз:
Но почему в "resources":[]
пусто?
Посмотрим логи:
Вот наша ошибка:
dial tcp: lookup prometheus.default.svc on 172.20.0.10:53: no such host
Проверим адрес нашего Prometheus:
Окей — Prometheus доступен по адресу prometheus-prometheus-oper-prometheus.monitoring.svc.cluster.local:9090.
Открываем на редактирование деплоймент:
Обновляем prometheus-url
:
... spec: affinity: {} containers: - args: - /adapter - --secure-port=6443 - --cert-dir=/tmp/cert - --logtostderr=true - --prometheus-url=http://prometheus-prometheus-oper-prometheus.monitoring.svc.cluster.local:9090 ...
Обновляем, и через минуту проверяем:
Хорошо — метрики пошли, попробуем их применить в НРА.
Проверим наличие и значение memory_usage_bytes
:
Обновляем НРА:
apiVersion: autoscaling/v2beta1 kind: HorizontalPodAutoscaler metadata: name: hpa-example spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: deployment-example minReplicas: 1 maxReplicas: 5 metrics: - type: Pods pods: metricName: memory_usage_bytes targetAverageValue: 1024000
Проверим значения в HPA сейчас:
Применяем:
Проверяем ещё раз:
Реплика всё ещё 1, смотрим логи:
И реплики заскейлились:
И всё хорошо, пока мы используем готовые метрики, которые уже есть в кластере, например memory_usage_bytes
, собираемая cAdvisor со всех контейнеров.
Попробуем использовать более кастомную метрику, например — скейлить Gorush-сервер на основе его метрик, см. Kubernetes: запуск push-сервера Gorush в EKS за AWS LoadBalancer.
Application-based metrics scaling
У нас имеется Gorush-сервер, который выполняет отправку пуш-уведомлений на мобильные, у которого есть встроенный ендпонт /metrics
, по которому доступны стандартные time-series метрики, которые можно использовать в Prometheus.
Используем Service
, ConfigMap
и Deployment
:
apiVersion: v1 kind: Service metadata: name: gorush labels: app: gorush tier: frontend spec: selector: app: gorush tier: frontend type: ClusterIP ports: - name: gorush protocol: TCP port: 80 targetPort: 8088 --- apiVersion: v1 kind: ConfigMap metadata: name: gorush-config data: stat.engine: memory --- apiVersion: apps/v1 kind: Deployment metadata: name: gorush spec: replicas: 1 selector: matchLabels: app: gorush tier: frontend template: metadata: labels: app: gorush tier: frontend spec: containers: - image: appleboy/gorush name: gorush imagePullPolicy: Always ports: - containerPort: 8088 livenessProbe: httpGet: path: /healthz port: 8088 initialDelaySeconds: 3 periodSeconds: 3 env: - name: GORUSH_STAT_ENGINE valueFrom: configMapKeyRef: name: gorush-config key: stat.engine
Создаём отдельный неймспейс:
Создаём сервисы:
Проверяем поды:
Его сервис:
Пробросим порт к поду:
Проверяем метрики:
Либо другим образом: зная имя Service
, мы можем обратиться к нему напрямую.
Находим сервис:
Открываем прокси к API-серверу:
Обращаемся напрямую к сервису:
Kubernetes ServiceMonitor
Далее, нам надо добавить ServiceMonitor
в Kubernetes, см. Создание Kubernetes ServiceMonitor.
Проверим метрики сейчас — пробросим порт к нашему Prometheus:
Пробуем получить их:
"data":[]
пустая — метрики в Prometheus не собираются.
Описываем ServiceMonitor
:
apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor metadata: labels: serviceapp: gorush-servicemonitor release: prometheus name: gorush-servicemonitor namespace: monitoring spec: endpoints: - bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token interval: 15s port: gorush namespaceSelector: matchNames: - eks-dev-1-gorush selector: matchLabels: app: gorush
Прим: The Prometheus resource includes a field called serviceMonitorSelector
, which defines a selection of ServiceMonitors to be used. By default and before the version v0.19.0
, ServiceMonitors must be installed in the same namespace as the Prometheus instance. With the Prometheus Operator v0.19.0
and above, ServiceMonitors can be selected outside the Prometheus namespace via the serviceMonitorNamespaceSelector
field of the Prometheus resource.
См.
Создаём его:
Проверяем его в таргетах:
UP, отлично.
И через пару минут ещё раз метрики:
Либо так:
Хорошо — метрики пошли, добавим HorizontalPodAutoscaler
для этого деплоймента.
Проверяем, какие группы метрик доступны через Custom Metrics API:
Описываем HPA, который использует gorush_queue_usage
из группы Pods
:
apiVersion: autoscaling/v2beta1 kind: HorizontalPodAutoscaler metadata: name: gorush-hpa spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: gorush minReplicas: 1 maxReplicas: 5 metrics: - type: Pods pods: metricName: gorush_total_push_count targetAverageValue: 2
С таким НРА поды должны заскейлится, как только gorush_total_push_count
станет больше 2.
Создаём его:
Проверим значение метрики сейчас:
Проверяем НРА:
TARGETS
0/1, хорошо.
Шлём пуш:
Ещё раз проверяем метрику:
Один пуш отправлен.
Глянем НРА:
TARGETS
1/2, REPLICAS
всё ещё одна — шлём второй пуш, и проверяем события:
И сам НРА теперь:
Отлично — скейлинг по метрике gorush_total_push_count
работает.
Но есть нюанс: gorush_total_push_count
— метрика куммулятивная, т.е. на графике Production-окружения будет выглядеть так:
В таком случае — наш НРА будет скейлить вечно.
Prometheus Adapter ConfigMap
— seriesQuery
и metricsQuery
Что бы изменить это — создадим свою метрику.
Prometheus Adapter дня настроек использует ConfigMap
:
Который содержит файл config.yaml
, пример файла
Сформируем
rate(gorush_total_push_count{instance="push.server.com:80",job="push-server"}[5m])
Обновляем ConfigMap
, и добавляем свой запрос:
apiVersion: v1 data: config.yaml: | rules: - seriesQuery: '{__name__=~"gorush_total_push_count"}' seriesFilters: [] resources: overrides: namespace: resource: namespace pod: resource: pod name: matches: "" as: "gorush_push_per_second" metricsQuery: rate(<<.Series>>{<<.LabelMatchers>>}[5m])
Проверяем:
Пересоздаём под, что бы он обновил у себя ConfigMap
(см. Kubernetes: ConfigMap и Secrets — auto-reload данных в подах):
Проверяем:
Обновим НРА — используем gorush_push_per_second
:
apiVersion: autoscaling/v2beta1 kind: HorizontalPodAutoscaler metadata: name: gorush-hpa spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: gorush minReplicas: 1 maxReplicas: 5 metrics: - type: Pods pods: metricName: gorush_push_per_second targetAverageValue: 1m
Проверяем HPA:
Проверяем события:
И НРА теперь:
Готово.
Ссылки по теме
Kubernetes Autoscaling in Production: Best Practices for Cluster Autoscaler, HPA and VPA Ultimate Kubernetes Resource Planning Guide Horizontal Autoscaling in Kubernetes #2 – Custom Metrics Kubernetes HPA : ExternalMetrics+Prometheus Prometheus Custom Metrics Adapter Horizontal Pod Autoscaling (HPA) triggered by Kafka event Custom and external metrics for autoscaling workloads Prometheus Metrics Based Autoscaling in Kubernetes Kubernetes best practices: Resource requests and limits Знакомство с Kubernetes. Часть 19: HorizontalPodAutoscaler How to Use Kubernetes for Autoscaling Horizontal Pod Autoscaling by memory Autoscaling apps on Kubernetes with the Horizontal Pod Autoscaler Horizontally autoscale Kubernetes deployments on custom metrics Kubernetes pod autoscaler using custom metrics Kubernetes HPA Autoscaling with Custom and External Metrics Horizontal pod auto scaling by using custom metrics Horizontal Pod Autoscale with Custom Prometheus Metrics Kubernetes HPA using Custom Metrics Kubernetes HPA Autoscaling with Custom Metrics Building a K8s Autoscaler with Custom Metrics