paint-brush
Cómo usar el adaptador Prometheus para implementar métricas personalizadas de escalado automáticopor@sudip-sengupta
21,784 lecturas
21,784 lecturas

Cómo usar el adaptador Prometheus para implementar métricas personalizadas de escalado automático

por Sudip Sengupta14m2020/09/18
Read on Terminal Reader
Read this story w/o Javascript

Demasiado Largo; Para Leer

Prometheus es la herramienta estándar para monitorear las cargas de trabajo implementadas y el propio clúster de Kubernetes. Usaremos el adaptador de Prometheus para extraer métricas personalizadas de nuestra instalación de Prometheus y luego dejaremos que Horizontal Pod Autoscaler (HPA) lo use para escalar los pods hacia arriba o hacia abajo. El adaptador Prometheus nos ayuda a aprovechar las métricas recopiladas por Prometheus y usarlas para tomar decisiones de escalado. Se puede acceder a la aplicación mediante el servicio y también expone las métricas de nginx vts en el extremo de la aplicación.

Company Mentioned

Mention Thumbnail
featured image - Cómo usar el adaptador Prometheus para implementar métricas personalizadas de escalado automático
Sudip Sengupta HackerNoon profile picture

Introducción

Una de las principales ventajas de usar Kubernetes para la orquestación de contenedores es que hace que sea muy fácil escalar nuestra aplicación horizontalmente y tener en cuenta el aumento de la carga. De forma nativa, el escalado automático horizontal de pods puede escalar la implementación en función del uso de la CPU y la memoria, pero en escenarios más complejos nos gustaría tener en cuenta otras métricas antes de tomar decisiones de escalado.

Bienvenido adaptador Prometheus . Prometheus es la herramienta estándar para monitorear las cargas de trabajo implementadas y el propio clúster de Kubernetes. El adaptador Prometheus nos ayuda a aprovechar las métricas recopiladas por Prometheus y usarlas para tomar decisiones de escalado. Estas métricas están expuestas por un servicio API y pueden ser utilizadas fácilmente por nuestro objeto de escalado automático de pod horizontal .

Despliegue

Descripción general de la arquitectura

Usaremos el adaptador de Prometheus para extraer métricas personalizadas de nuestra instalación de Prometheus y luego dejaremos que Horizontal Pod Autoscaler (HPA) lo use para escalar los pods hacia arriba o hacia abajo.

Requisito previo

Conocimientos básicos sobre el escalado automático de pods horizontales Prometheus implementado en el clúster o accesible mediante un punto final.

Usaremos una implementación de alta disponibilidad de Prometheus-Thanos .

Implementación de la aplicación de muestra

Primero, implementemos una aplicación de muestra sobre la cual probaremos nuestro ajuste de escala automático de métricas de Prometheus. Podemos usar el manifiesto a continuación para hacerlo

 apiVersion: v1 kind: Namespace metadata: name: nginx --- apiVersion: extensions/v1beta1 kind: Deployment metadata: namespace: nginx name: nginx-deployment spec: replicas: 1 template: metadata: annotations: prometheus.io/path: "/status/format/prometheus" prometheus.io/scrape: "true" prometheus.io/port: "80" labels: app: nginx-server spec: affinity: podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 100 podAffinityTerm: labelSelector: matchExpressions: - key: app operator: In values: - nginx-server topologyKey: kubernetes.io/hostname containers: - name: nginx-demo image: vaibhavthakur/nginx-vts:1.0 imagePullPolicy: Always resources: limits: cpu: 2500m requests: cpu: 2000m ports: - containerPort: 80 name: http --- apiVersion: v1 kind: Service metadata: namespace: nginx name: nginx-service spec: ports: - port: 80 targetPort: 80 name: http selector: app: nginx-server type : LoadBalancer

Esto creará un espacio de nombres llamado nginx e implementará una aplicación Nginx de muestra en él. Se puede acceder a la aplicación mediante el servicio y también expone las métricas de nginx vts en el punto final

 /status/format/prometheus
sobre el puerto 80 . Por el bien de nuestra configuración, hemos creado una entrada de DNS para ExternalIP que se asigna a nginx.gotham.com

 root$ kubectl get deploy NAME READY UP-TO-DATE AVAILABLE AGE nginx-deployment 1/1 1 1 43d root$ kubectl get pods NAME READY STATUS RESTARTS AGE nginx-deployment-65d8df7488-c578v 1/1 Running 0 9h root$ kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE nginx-service ClusterIP 10.63.253.154 35.232.67.34 80/TCP 43d root$ kubectl describe deploy nginx-deployment Name: nginx-deployment Namespace: nginx CreationTimestamp: Tue, 08 Oct 2019 11:47:36 -0700 Labels: app=nginx-server Annotations: deployment.kubernetes.io/revision: 1 kubectl.kubernetes.io/last-applied-configuration: { "apiVersion" : "extensions/v1beta1" , "kind" : "Deployment" , "metadata" :{ "annotations" :{}, "name" : "nginx-deployment" , "namespace" : "nginx" }, "spec" :... Selector: app=nginx-server Replicas: 1 desired | 1 updated | 1 total | 1 available | 0 unavailable StrategyType: RollingUpdate MinReadySeconds: 0 RollingUpdateStrategy: 1 max unavailable, 1 max surge Pod Template: Labels: app=nginx-server Annotations: prometheus.io/path: /status/format/prometheus prometheus.io/port: 80 prometheus.io/scrape: true Containers: nginx-demo: Image: vaibhavthakur/nginx-vts:v1.0 Port: 80/TCP Host Port: 0/TCP Limits: cpu: 250m Requests: cpu: 200m Environment: <none> Mounts: <none> Volumes: <none> Conditions: Type Status Reason ---- ------ ------ Available True MinimumReplicasAvailable OldReplicaSets: <none> NewReplicaSet: nginx-deployment-65d8df7488 (1/1 replicas created) Events: <none> root$ curl nginx.gotham.com <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style> body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; } </style> </head> <body> <h1>Welcome to nginx!</h1> <p>If you see this page, the nginx web server is successfully installed and working. Further configuration is required.</p> <p>For online documentation and support please refer to <a href= "http://nginx.org/" >nginx.org</a>.<br/> Commercial support is available at <a href= "http://nginx.com/" >nginx.com</a>.</p> <p><em>Thank you for using nginx.</em></p> </body> </html>

Estas son todas las métricas expuestas actualmente por la aplicación

 $ curl nginx.gotham.com/status/format/prometheus # HELP nginx_vts_info Nginx info # TYPE nginx_vts_info gauge nginx_vts_info{hostname= "nginx-deployment-65d8df7488-c578v" ,version= "1.13.12" } 1 # HELP nginx_vts_start_time_seconds Nginx start time # TYPE nginx_vts_start_time_seconds gauge nginx_vts_start_time_seconds 1574283147.043 # HELP nginx_vts_main_connections Nginx connections # TYPE nginx_vts_main_connections gauge nginx_vts_main_connections{status= "accepted" } 215 nginx_vts_main_connections{status= "active" } 4 nginx_vts_main_connections{status= "handled" } 215 nginx_vts_main_connections{status= "reading" } 0 nginx_vts_main_connections{status= "requests" } 15577 nginx_vts_main_connections{status= "waiting" } 3 nginx_vts_main_connections{status= "writing" } 1 # HELP nginx_vts_main_shm_usage_bytes Shared memory [ngx_http_vhost_traffic_status] info # TYPE nginx_vts_main_shm_usage_bytes gauge nginx_vts_main_shm_usage_bytes{shared= "max_size" } 1048575 nginx_vts_main_shm_usage_bytes{shared= "used_size" } 3510 nginx_vts_main_shm_usage_bytes{shared= "used_node" } 1 # HELP nginx_vts_server_bytes_total The request/response bytes # TYPE nginx_vts_server_bytes_total counter # HELP nginx_vts_server_requests_total The requests counter # TYPE nginx_vts_server_requests_total counter # HELP nginx_vts_server_request_seconds_total The request processing time in seconds # TYPE nginx_vts_server_request_seconds_total counter # HELP nginx_vts_server_request_seconds The average of request processing times in seconds # TYPE nginx_vts_server_request_seconds gauge # HELP nginx_vts_server_request_duration_seconds The histogram of request processing time # TYPE nginx_vts_server_request_duration_seconds histogram # HELP nginx_vts_server_cache_total The requests cache counter # TYPE nginx_vts_server_cache_total counter nginx_vts_server_bytes_total{host= "_" ,direction= "in" } 3303449 nginx_vts_server_bytes_total{host= "_" ,direction= "out" } 61641572 nginx_vts_server_requests_total{host= "_" ,code= "1xx" } 0 nginx_vts_server_requests_total{host= "_" ,code= "2xx" } 15574 nginx_vts_server_requests_total{host= "_" ,code= "3xx" } 0 nginx_vts_server_requests_total{host= "_" ,code= "4xx" } 2 nginx_vts_server_requests_total{host= "_" ,code= "5xx" } 0 nginx_vts_server_requests_total{host= "_" ,code= "total" } 15576 nginx_vts_server_request_seconds_total{host= "_" } 0.000 nginx_vts_server_request_seconds{host= "_" } 0.000 nginx_vts_server_cache_total{host= "_" ,status= "miss" } 0 nginx_vts_server_cache_total{host= "_" ,status= "bypass" } 0 nginx_vts_server_cache_total{host= "_" ,status= "expired" } 0 nginx_vts_server_cache_total{host= "_" ,status= "stale" } 0 nginx_vts_server_cache_total{host= "_" ,status= "updating" } 0 nginx_vts_server_cache_total{host= "_" ,status= "revalidated" } 0 nginx_vts_server_cache_total{host= "_" ,status= "hit" } 0 nginx_vts_server_cache_total{host= "_" ,status= "scarce" } 0 nginx_vts_server_bytes_total{host= "*" ,direction= "in" } 3303449 nginx_vts_server_bytes_total{host= "*" ,direction= "out" } 61641572 nginx_vts_server_requests_total{host= "*" ,code= "1xx" } 0 nginx_vts_server_requests_total{host= "*" ,code= "2xx" } 15574 nginx_vts_server_requests_total{host= "*" ,code= "3xx" } 0 nginx_vts_server_requests_total{host= "*" ,code= "4xx" } 2 nginx_vts_server_requests_total{host= "*" ,code= "5xx" } 0 nginx_vts_server_requests_total{host= "*" ,code= "total" } 15576 nginx_vts_server_request_seconds_total{host= "*" } 0.000 nginx_vts_server_request_seconds{host= "*" } 0.000 nginx_vts_server_cache_total{host= "*" ,status= "miss" } 0 nginx_vts_server_cache_total{host= "*" ,status= "bypass" } 0 nginx_vts_server_cache_total{host= "*" ,status= "expired" } 0 nginx_vts_server_cache_total{host= "*" ,status= "stale" } 0 nginx_vts_server_cache_total{host= "*" ,status= "updating" } 0 nginx_vts_server_cache_total{host= "*" ,status= "revalidated" } 0 nginx_vts_server_cache_total{host= "*" ,status= "hit" } 0 nginx_vts_server_cache_total{host= "*" ,status= "scarce" } 0

Entre estos estamos particularmente interesados en

 nginx_vts_server_requests_total
 . Usaremos el valor de esta métrica para determinar si escalar o no nuestra implementación de Nginx.

Crear mapa de configuración del adaptador de Prometheus

Use el manifiesto a continuación para crear el mapa de configuración del adaptador Prometheus

 apiVersion: v1 kind: ConfigMap metadata: name: adapter-config namespace: monitoring data: config.yaml: | rules: - seriesQuery: 'nginx_vts_server_requests_total' resources: overrides: kubernetes_namespace: resource: namespace kubernetes_pod_name: resource: pod name: matches: "^(.*)_total" as: " ${1} _per_second" metricsQuery: (sum(rate(<<.Series>>{<<.LabelMatchers>>}[1m])) by (<<.GroupBy>>))

Este mapa de configuración solo especifica una única métrica. Sin embargo, siempre podemos agregar más métricas.

Crear implementación de adaptador de Prometheus

Use el siguiente manifiesto para implementar Prometheus Adapter

 apiVersion: apps/v1 kind: Deployment metadata: labels: app: custom-metrics-apiserver name: custom-metrics-apiserver namespace: monitoring spec: replicas: 1 selector: matchLabels: app: custom-metrics-apiserver template: metadata: labels: app: custom-metrics-apiserver name: custom-metrics-apiserver spec: serviceAccountName: monitoring containers: - name: custom-metrics-apiserver image: quay.io/coreos/k8s-prometheus-adapter-amd64:v0.4.1 args: - /adapter - --secure-port=6443 - --tls-cert-file=/var/run/serving-cert/serving.crt - --tls-private-key-file=/var/run/serving-cert/serving.key - --logtostderr= true - --prometheus-url=http://thanos-querier.monitoring:9090/ - --metrics-relist-interval=30s - --v=10 - --config=/etc/adapter/config.yaml ports: - containerPort: 6443 volumeMounts: - mountPath: /var/run/serving-cert name: volume-serving-cert readOnly: true - mountPath: /etc/adapter/ name: config readOnly: true volumes: - name: volume-serving-cert secret: secretName: cm-adapter-serving-certs - name: config configMap: name: adapter-config

Esto creará nuestra implementación que generará el módulo del adaptador de Prometheus para extraer métricas de Prometheus. Cabe señalar que hemos establecido el argumento

 --prometheus-url=
 http://thanos-querier.monitoring:9090/
.  Esto se debe a que implementamos un clúster de Prometheus-Thanos en el espacio de nombres de monitoreo en el mismo clúster de Kubernetes que el adaptador de Prometheus. Puede cambiar este argumento para que apunte a su implementación de Prometheus.

Si observa los registros de este contenedor, puede ver que está obteniendo la métrica definida en el archivo de configuración

 I1122 00:26:53.228394 1 api.go:74] GET http://thanos-querier.monitoring:9090/api/v1/series?match%5B%5D=nginx_vts_server_requests_total&start=1574381213.217 200 OK I1122 00:26:53.234234 1 api.go:93] Response Body: { "status" : "success" , "data" :[{ "__name__" : "nginx_vts_server_requests_total" , "app" : "nginx-server" , "cluster" : "prometheus-ha" , "code" : "1xx" , "host" : "*" , "instance" : "10.60.64.39:80" , "job" : "kubernetes-pods" , "kubernetes_namespace" : "nginx" , "kubernetes_pod_name" : "nginx-deployment-65d8df7488-sbp95" , "pod_template_hash" : "65d8df7488" },{ "__name__" : "nginx_vts_server_requests_total" , "app" : "nginx-server" , "cluster" : "prometheus-ha" , "code" : "1xx" , "host" : "*" , "instance" : "10.60.64.8:80" , "job" : "kubernetes-pods" , "kubernetes_namespace" : "nginx" , "kubernetes_pod_name" : "nginx-deployment-65d8df7488-mwzxg" , "pod_template_hash" : "65d8df7488" }

Creación del servicio API del adaptador de Prometheus

El manifiesto a continuación creará un servicio de API para que la API de Kubernetes pueda acceder a nuestro adaptador Prometheus y, por lo tanto, nuestro escalador automático horizontal de pods pueda obtener las métricas.

 apiVersion: v1 kind: Service metadata: name: custom-metrics-apiserver namespace: monitoring spec: ports: - port: 443 targetPort: 6443 selector: app: custom-metrics-apiserver --- apiVersion: apiregistration.k8s.io/v1beta1 kind: APIService metadata: name: v1beta1.custom.metrics.k8s.io spec: service: name: custom-metrics-apiserver namespace: monitoring group: custom.metrics.k8s.io version: v1beta1 insecureSkipTLSVerify: true groupPriorityMinimum: 100 versionPriority: 100

Probar la configuración

Veamos qué métricas personalizadas están disponibles

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

Podemos ver eso

 nginx_vts_server_requests_per_second
La métrica está disponible. Ahora, verifiquemos el valor actual de esta métrica

 root$ kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1/namespaces/nginx/pods/*/nginx_vts_server_requests_per_second" | jq . { "kind" : "MetricValueList" , "apiVersion" : "custom.metrics.k8s.io/v1beta1" , "metadata" : { "selfLink" : "/apis/custom.metrics.k8s.io/v1beta1/namespaces/nginx/pods/%2A/nginx_vts_server_requests_per_second" }, "items" : [ { "describedObject" : { "kind" : "Pod" , "namespace" : "nginx" , "name" : "nginx-deployment-65d8df7488-v575j" , "apiVersion" : "/v1" }, "metricName" : "nginx_vts_server_requests_per_second" , "timestamp" : "2019-11-19T18:38:21Z" , "value" : "1236m" } ] }

Cree un HPA que utilizará estas métricas. Podemos usar el manifiesto a continuación para hacerlo.

 apiVersion: autoscaling/v2beta1 kind: HorizontalPodAutoscaler metadata: name: nginx-custom-hpa namespace: nginx spec: scaleTargetRef: apiVersion: extensions/v1beta1 kind: Deployment name: nginx-deployment minReplicas: 2 maxReplicas: 10 metrics: - type : Pods pods: metricName: nginx_vts_server_requests_per_second targetAverageValue: 4000m

Una vez que haya aplicado este manifiesto, puede verificar el estado actual de HPA de la siguiente manera:

 root$ kubectl describe hpa Name: nginx-custom-hpa Namespace: nginx Labels: <none> Annotations: autoscaling.alpha.kubernetes.io/metrics: [{ "type" : "Pods" , "pods" :{ "metricName" : "nginx_vts_server_requests_per_second" , "targetAverageValue" : "4" }}] kubectl.kubernetes.io/last-applied-configuration: { "apiVersion" : "autoscaling/v2beta1" , "kind" : "HorizontalPodAutoscaler" , "metadata" :{ "annotations" :{}, "name" : "nginx-custom-hpa" , "namespace" : "n... CreationTimestamp: Thu, 21 Nov 2019 11:11:05 -0800 Reference: Deployment/nginx-deployment Min replicas: 2 Max replicas: 10 Deployment pods: 0 current / 0 desired Events: <none>

Ahora, generemos algo de carga en nuestro servicio. Usaremos una utilidad llamada Vegeta para esto.

En una terminal separada, ejecute el siguiente comando

 echo "GET http://nginx.gotham.com/" | vegeta attack -rate=5 -duration=0 | vegeta report

Mientras tanto, controle los pods de nginx y el escalador automático de pods horizontales y debería ver algo como esto

 root$ kubectl get -w pods NAME READY STATUS RESTARTS AGE nginx-deployment-65d8df7488-mwzxg 1/1 Running 0 9h nginx-deployment-65d8df7488-sbp95 1/1 Running 0 4m9s NAME AGE nginx-deployment-65d8df7488-pwjzm 0s nginx-deployment-65d8df7488-pwjzm 0s nginx-deployment-65d8df7488-pwjzm 0s nginx-deployment-65d8df7488-pwjzm 2s nginx-deployment-65d8df7488-pwjzm 4s nginx-deployment-65d8df7488-jvbvp 0s nginx-deployment-65d8df7488-jvbvp 0s nginx-deployment-65d8df7488-jvbvp 1s nginx-deployment-65d8df7488-jvbvp 4s nginx-deployment-65d8df7488-jvbvp 7s nginx-deployment-65d8df7488-skjkm 0s nginx-deployment-65d8df7488-skjkm 0s nginx-deployment-65d8df7488-jh5vw 0s nginx-deployment-65d8df7488-skjkm 0s nginx-deployment-65d8df7488-jh5vw 0s nginx-deployment-65d8df7488-jh5vw 1s nginx-deployment-65d8df7488-skjkm 2s nginx-deployment-65d8df7488-jh5vw 2s nginx-deployment-65d8df7488-skjkm 3s nginx-deployment-65d8df7488-jh5vw 4s root$ kubectl get hpa NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE nginx-custom-hpa Deployment/nginx-deployment 5223m/4 2 10 3 5m5s

Se puede ver claramente que la HPA amplió nuestras cápsulas según el requisito, y cuando interrumpimos el comando de Vegeta, recibimos el informe de Vegeta. Muestra claramente que todas nuestras solicitudes fueron atendidas por la aplicación.

 root$ echo "GET http://nginx.gotham.com/" | vegeta attack -rate=5 -duration=0 | vegeta report ^CRequests [total, rate, throughput] 224, 5.02, 5.02 Duration [total, attack, wait ] 44.663806863s, 44.601823883s, 61.98298ms Latencies [mean, 50, 95, 99, max] 63.3879ms, 60.867241ms, 79.414139ms, 111.981619ms, 229.310088ms Bytes In [total, mean] 137088, 612.00 Bytes Out [total, mean] 0, 0.00 Success [ratio] 100.00% Status Codes [code:count] 200:224 Error Set:

Conclusión

Esta configuración demuestra cómo podemos usar el adaptador de Prometheus para escalar automáticamente las implementaciones en función de algunas métricas personalizadas. En aras de la simplicidad, solo hemos obtenido una métrica de nuestro servidor Prometheus. Sin embargo, el mapa de configuración del adaptador se puede ampliar para obtener algunas o todas las métricas disponibles y usarlas para el ajuste de escala automático.

Si la instalación de Prometheus está fuera de nuestro clúster de Kubernetes, solo debemos asegurarnos de que se pueda acceder al punto final de la consulta desde el clúster y actualizarlo en el manifiesto de implementación del adaptador. Con escenarios más complejos, se pueden obtener múltiples métricas y usarlas en combinación para tomar decisiones de escalado.

No dude en comunicarse si tiene alguna pregunta sobre la configuración y estaremos encantados de ayudarle.

Publicado anteriormente en https://appfleet.com/blog/prometheus-metrics-based-autoscaling-in-kubernetes/ .