Привет, Хабр!

Сегодня микросервисы требуют постоянного стремления к автоматизации и оптимизации. В этой статье рассмотрим такой инструмент в Kubernetes, как Horizontal Pod Autoscaler или сокращенно HPA.

Развертывание микросервиса

Развернем простое приложение в Kubernetes. Будем использовать стандартный подход через Deployment и Service:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-microservice
spec:
  replicas: 1  # Начинаем с одного пода
  selector:
    matchLabels:
      app: my-microservice
  template:
    metadata:
      labels:
        app: my-microservice
    spec:
      containers:
      - name: my-microservice
        image: nginx:latest  # Используем NGINX для простоты
        resources:
          requests:
            cpu: 100m  # Ресурсы по умолчанию
            memory: 256Mi
          limits:
            cpu: 200m
            memory: 512Mi
        ports:
        - containerPort: 80

---
apiVersion: v1
kind: Service
metadata:
  name: my-microservice-service
spec:
  selector:
    app: my-microservice
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80
  type: ClusterIP

Манифест описывает простое приложение NGINX, которое будем использовать для тестирования HPA. Создаем Deployment с одним подом и устанавливаем базовые требования к ресурсам.

kubectl apply -f my-microservice.yaml

Теперь есть базовый микросервис, готовый к автоскейлингу.

Теперь настроим HPA. Он автоматически увеличивает или уменьшает количество подов в зависимости от нагрузки на CPU.

kubectl autoscale deployment my-microservice --cpu-percent=50 --min=1 --max=10

Команда создаёт HPA, который будет отслеживать загрузку CPU и масштабировать Deployment в пределах от 1 до 10 подов, пытаясь поддерживать среднюю загрузку CPU на уровне 50%.

Что здесь важно:

  • --cpu-percent=50: цель автоскейлинга — поддерживать загрузку CPU на уровне 50%.

  • --min=1 и --max=10: минимум один под, максимум десять. Эти параметры зависят от приложения и ресурсов кластера.

Для более детальной настройки HPA можно использовать YAML-манифест:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: my-microservice-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: my-microservice
  minReplicas: 1
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 50  # Цель по CPU

Этот манифест можно применить командой:

kubectl apply -f my-microservice-hpa.yaml

После настройки HPA убедимся, что он работает корректно. Для этого можно использовать несколько команд, например:

kubectl get hpa

Можно увидеть что-то вроде:

NAME                  REFERENCE                    TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
my-microservice-hpa   Deployment/my-microservice   30%/50%   1         10        2          5m

Этот вывод показывает:

  • TARGETS: текущая загрузка CPU (в нашем случае 30%) относительно цели (50%).

  • REPLICAS: Текущее количество подов (в данном примере 2).

Чтобы протестировать, как работает автоскейлер, можно сгенерировать нагрузку на приложение. Один из способов — запустить контейнер, который будет постоянно отправлять запросы на микросервис:

kubectl run -i --tty load-generator --image=busybox --restart=Never -- /bin/sh
while true; do wget -q -O- http://my-microservice-service; done

Через некоторое время, можно снова проверить статус HPA:

kubectl get hpa

Можно увидеть, как количество подов увеличивается по мере роста загрузки CPU.

Для остановки нагрузки, можно нажать Ctrl + C в терминале, где запущен load-generator.

Кастомные метрики

Для настройки ��астомных метрик потребуется интеграция с системой мониторинга, например, Prometheus, которая будет собирать данные метрик и предоставлять их для HPA через API.

Допустим, нужно масштабировать приложение на основе количества HTTP-запросов в секунду. Для этого сначала необходимо экспортировать соответствующие метрики из приложения в Prometheus. Пример метрики:

httpRequestsTotal := prometheus.NewCounter(
    prometheus.CounterOpts{
        Name: "http_requests_total",
        Help: "Total number of HTTP requests processed.",
    },
)

Код на Go экспортирует метрику http_requests_total, которую можно собрать и использовать для автоскейлинга.

Теперь нужно настроить Prometheus Adapter для работы с этой метрикой. Например, можно добавить следующую конфигурацию в values.yaml для Prometheus Adapter:

rules:
  custom:
    - seriesQuery: 'http_requests_total{namespace="default"}'
      resources:
        overrides:
          namespace:
            resource: namespace
          pod:
            resource: pod
      name:
        matches: "^(.*)_total"
        as: "${1}_per_second"
      metricsQuery: 'rate(http_requests_total{<<.LabelMatchers>>}[2m])'

Этот конфиг преобразует метрику http_requests_total в http_requests_per_second, которую можно использовать для автоскейлинга.

Теперь создадим манифест HPA, который будет масштабировать поды на основе количества запросов в секунду:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: my-microservice-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: my-microservice
  minReplicas: 2
  maxReplicas: 10
  metrics:
  - type: Pods
    pods:
      metric:
        name: http_requests_per_second
      target:
        type: AverageValue
        averageValue: "100"

Этот манифест говорит Kubernetes масштабировать микросервис, если среднее количество HTTP-запросов превышает 100 запросов в секунду на под.

Настройка HPA с кастомными метриками — это тонкий процесс. Вот несколько советов, которые помогут вам избежать подводных камней и оптимизировать производительность автоскейлера.

Избегание частого масштабирования

Thrashing — это ситуация, когда автоскейлер часто меняет количество подов из-за слишком агрессивных или неправильно настроенных порогов. Чтобы этого избежать:

  • Установите адекватные пороговые значения и периоды ожидания. Например, можно настроить задержку между скейлингом с помощью параметра --horizontal-pod-autoscaler-sync-period.

  • Еще можно использовать плавные метрики, такие как rate() для запросов, которые вычисляют средние значения за определённый интервал времени, чтобы избежать резких изменений.

HPA по умолч��нию синхронизируется каждые 15 секунд. Этот интервал можно изменить, чтобы снизить нагрузку на систему или ускорить реакцию на изменения нагрузки:

httpRequestsTotal := prometheus.NewCounter(
    prometheus.CounterOpts{
        Name: "http_requests_total",
        Help: "Total number of HTTP requests processed.",
    },
)

Увеличение периода синхронизации может снизить частоту изменений, но может задерживать реакцию на резкие скачки нагрузки.

Для более точного контроля за автоскейлингом можно использовать комбинацию метрик, например, CPU и кастомные метрики одновременно:

metrics:
- type: Resource
  resource:
    name: cpu
    target:
      type: Utilization
      averageUtilization: 60
- type: Pods
  pods:
    metric:
      name: http_requests_per_second
    target:
      type: AverageValue
      averageValue: "100"

Здесь HPA будет одновременно учитывать загрузку CPU и количество запросов в секунду при масштабировании.


Больше актуальных навыков по инфраструктуре, архитектуре и разработке приложений вы можете получить в рамках практических онлайн-курсов от экспертов отрасли.