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

Сегодня микросервисы требуют постоянного стремления к автоматизации и оптимизации. В этой статье рассмотрим такой инструмент в 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 и количество запросов в секунду при масштабировании.


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

Комментарии (0)