Kubernetes — Horizontal pod autoscaler на основе кастомных метрик

Спасибо Daniel Vaughan за данную статью.

Для горизонтального автоскалирования подов (HPA) на основе кастомных метрик, нам понадобятся:

Все это, мы будем устанавливать используя Helm. Установку Helm'а можно посмотреть тут.

 

Создадим директорию для хранения Helm values:

mkdir helm-values

 

Создадим values файл для Prometheus Operator'а:

cat <<EOF >helm-values/prometheus-operator-values.yaml
# This configuration means all ServiceMonitors in the namespace will be picked up
# Use with caution!
prometheus: 
  prometheusSpec:
    serviceMonitorSelectorNilUsesHelmValues: false
    serviceMonitorSelector: {}
EOF

 

Устанавливаем Prometheus Operator:

helm install stable/prometheus-operator -n prom \
    -f helm-values/prometheus-operator-values.yaml

 

 

Создадим values файл для Prometheus Adapter'а:

cat <<EOF >helm-values/prometheus-adapter-values.yaml
rules:
  default: false
  custom:
  - seriesQuery: 'demo_app_button_clicks_total'
    resources: { template: "<<.Resource>>" }
    name:
      matches: "^(.*)_total"
      as: "${1}_per_second"
    metricsQuery: "sum(rate(<<.Series>>{<<.LabelMatchers>>}[2m])) by (<<.GroupBy>>)"
prometheus:
  url: http://prom-prometheus-operator-prometheus.default
EOF

 

Тут мы указываем, что нам не нужны предустановленные кастомные метрики, и добавляем свою. И важно, чтобы URL сервиса "Prometheus Operator" был указан правильным

 

Устанавливаем Prometheus Adapter:

helm install stable/prometheus-adapter -n prom-adapter \
    -f helm-values/prometheus-adapter-values.yaml

 

Через минуту-две можно проверить, что API кастомных метрик работает:

kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1/" | jq .
{
  "kind": "APIResourceList",
  "apiVersion": "v1",
  "groupVersion": "custom.metrics.k8s.io/v1beta1",
  "resources": []
}

 

На данном этапе мы тут не видим нашу метрику "demo_app_button_clicks_total", так и должно быть, ведь мы только Prometheus Adapter'у указали как обрабатывать ее.

 

Создадим deployment и service для нашего тестового приложения:

cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo-app
  labels:
    app: demo-app
spec:
  selector:
    matchLabels:
      app: demo-app
  template:
    metadata:
      labels:
        app: demo-app
    spec:
      containers:
      - name: demo-app
        image: flipstone42/k8s-prometheus-custom-scaling:latest
        resources:
          limits:
            memory: "128Mi"
            cpu: "100m"
        ports:
        - containerPort: 8000
          name: http
---
apiVersion: v1
kind: Service
metadata:
  name: demo-app
  labels:
    app: demo-app
spec:
  type: ClusterIP
  selector:
    app: demo-app
  ports:
  - port: 8080
    targetPort: 8000
    name: web
EOF

 

Теперь создадим HPA для нашего deployment'а:

cat <<EOF | kubectl apply -f -
kind: HorizontalPodAutoscaler
apiVersion: autoscaling/v2beta1
metadata:
  name: demo-app
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: demo-app
  # autoscale between 1 and 10 replicas
  minReplicas: 1
  maxReplicas: 10
  metrics:
  # use a "Pods" metric, which takes the average of the
  # given metric across all pods controlled by the autoscaling target
  - type: Pods
    pods:
      metricName: demo_app_button_clicks_per_second
      targetAverageValue: 10m
EOF

 

Тут главное указать правильно имя Deployment'а и имя метрики, которое отдает наше приложение.

 

Создадим ServiceMonitor для отслеживания нашей метрики:

cat <<EOF | kubectl apply -f -
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: demo-app
spec:
  endpoints:
  - bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
    interval: 15s
    port: web
  selector:
    matchLabels:
      app: demo-app
EOF

 

Проверяем HPA. Если в столбце "TARGETS" вы видите "0/10m", а не "<unknown>/10m", значит метрика считывается.

kubectl get hpa demo-app 
NAME       REFERENCE             TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
demo-app   Deployment/demo-app   0/10m     1         10        1          1m

 

Выполним describe HPA чтобы окончательно убедится, что метрика считывается.

kubectl describe hpa demo-app
Name:                                           demo-app
Namespace:                                      default
Labels:                                         <none>
Annotations:                                    kubectl.kubernetes.io/last-applied-configuration:
                                                  {"apiVersion":"autoscaling/v2beta1","kind":"HorizontalPodAutoscaler","metadata":{"annotations":{},"name":"demo-app","namespace":"default"}...
CreationTimestamp:                              Tue, 26 Nov 2019 20:51:36 +0000
Reference:                                      Deployment/demo-app
Metrics:                                        ( current / target )
  "demo_app_button_clicks_per_second" on pods:  0 / 10m
Min replicas:                                   1
Max replicas:                                   10
Deployment pods:                                1 current / 1 desired
Conditions:
  Type            Status  Reason            Message
  ----            ------  ------            -------
  AbleToScale     True    ReadyForNewScale  recommended size matches current size
  ScalingActive   True    ValidMetricFound  the HPA was able to successfully calculate a replica count from pods metric demo_app_button_clicks_per_second
  ScalingLimited  True    TooFewReplicas    the desired replica count is less than the minimum replica count
Events:           <none>

 

Теперь можем видеть нашу метрику в Custom Metrics API:

kubectl get --raw /apis/custom.metrics.k8s.io/v1beta1 | jq .
{
  "kind": "APIResourceList",
  "apiVersion": "v1",
  "groupVersion": "custom.metrics.k8s.io/v1beta1",
  "resources": [
    {
      "name": "pods/demo_app_button_clicks_per_second",
      "singularName": "",
      "namespaced": true,
      "kind": "MetricValueList",
      "verbs": [
        "get"
      ]
    },
    {
      "name": "services/demo_app_button_clicks_per_second",
      "singularName": "",
      "namespaced": true,
      "kind": "MetricValueList",
      "verbs": [
        "get"
      ]
    },
    {
      "name": "jobs.batch/demo_app_button_clicks_per_second",
      "singularName": "",
      "namespaced": true,
      "kind": "MetricValueList",
      "verbs": [
        "get"
      ]
    },
    {
      "name": "namespaces/demo_app_button_clicks_per_second",
      "singularName": "",
      "namespaced": false,
      "kind": "MetricValueList",
      "verbs": [
        "get"
      ]
    }
  ]
}

 

Создадим для нашего приложения Ingress:

cat <<EOF | kubectl apply -f -
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: nginx
  name: demo
  namespace: default
spec:
  rules:
    - host: demo.artem.services
      http:
        paths:
          - backend:
              serviceName: demo-app
              servicePort: 8080
            path: /
EOF

 

Перейдем по URL нашего приложения:

 

Кликнем по кнопке "Hit me!" пять раз (чтобы сразу не скалировать поды в максимум)

 

Проверяем HPA:

kubectl get hpa demo-app
NAME       REFERENCE             TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
demo-app   Deployment/demo-app   38m/10m   1         10        1          10m

 

И смотрим количество Pod'ов:

kubectl get po -l app=demo-app
NAME                        READY   STATUS    RESTARTS   AGE
demo-app-78c7869955-9vzsr   1/1     Running   0          2m50s
demo-app-78c7869955-fgnsx   1/1     Running   0          2m50s
demo-app-78c7869955-sl9f8   1/1     Running   0          2m50s
demo-app-78c7869955-zxrtt   1/1     Running   0          13m

 

Через 10 минут, после того как значение метрики опустится, Pod'ы начнут скалироваться в обратную сторону.

 

Trableshooting

Если вы все сделали верно, но у вас не работают кастомные метрики, убедитесь, что у вас включен Aggregation API:

kubectl get pod kube-apiserver-master -n kube-system -o yaml

spec:
  containers:
  - command:
...
    - --requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.crt
    - --requestheader-allowed-names=front-proxy-client
    - --requestheader-extra-headers-prefix=X-Remote-Extra-
    - --requestheader-group-headers=X-Remote-Group
    - --requestheader-username-headers=X-Remote-User
    - --proxy-client-key-file=/etc/kubernetes/pki/sa.key
    - --proxy-client-cert-file=/etc/kubernetes/pki/ca.crt
...

 

Так же могут понадобится следующие флаги для Kubelet'а:

sudo cat /var/lib/kubelet/kubeadm-flags.env

... --anonymous-auth=false --authorization-mode=Webhook --authentication-token-webhook=true

 

После добавления флагов, не забудьте перезапустить Kubelet:

sudo systemctl restart kubelet
0 0 vote
Рейтинг статьи

Метки: Метки

Подписаться
Уведомление о
guest
0 комментариев
Inline Feedbacks
View all comments