Autoescalado horizontal de pods (HPA)

En este documento se describe cómo habilitar el escalado automático horizontal de pods (HPA) en Google Cloud Managed Service para Prometheus. Para habilitar HPA, puedes hacer lo siguiente:

No puedes usar el adaptador de Stackdriver y el de Prometheus al mismo tiempo en el mismo clúster porque sus definiciones de recursos se solapan, tal como se describe en la sección Solución de problemas. Recomendamos elegir solo una solución para HPA.

Usar KEDA

KEDA (Kubernetes Event-driven Autoscaling) es el autoescalador más reciente que usa métricas de Prometheus y se está convirtiendo en una solución popular en la comunidad de Prometheus.

Para empezar, consulta la documentación de KEDA sobre la integración con Google Cloud Managed Service para Prometheus.

Usar el adaptador de Stackdriver de métricas personalizadas

El adaptador de Stackdriver de métricas personalizadas admite consultas de métricas de Managed Service para Prometheus a partir de la versión v0.13.1 del adaptador.

Para configurar un ejemplo de HPA con el adaptador de métricas personalizadas de Stackdriver, siga estos pasos:

  1. Configure la recogida gestionada en su clúster.
  2. Instala el adaptador de Stackdriver de métricas personalizadas en tu clúster.

    kubectl apply -f https://raw.githubusercontent.com/GoogleCloudPlatform/k8s-stackdriver/master/custom-metrics-stackdriver-adapter/deploy/production/adapter_new_resource_model.yaml
    
  3. Despliega un exportador de métricas de Prometheus de ejemplo y un recurso HPA:

    kubectl apply -f https://raw.githubusercontent.com/GoogleCloudPlatform/k8s-stackdriver/master/custom-metrics-stackdriver-adapter/examples/prometheus-to-sd/custom-metrics-prometheus-sd.yaml
    

    Este comando implementa una aplicación de exportador que emite la métrica foo y un recurso HPA. El HPA escala esta aplicación hasta 5 réplicas para alcanzar el valor objetivo de la métrica foo.

  4. Si usas Workload Identity Federation para GKE, también debes asignar el rol Lector de Monitoring a la cuenta de servicio con la que se ejecuta el adaptador. Sáltate este paso si no tienes habilitada la federación de Workload Identity para GKE en tu clúster de Kubernetes.

    export PROJECT_NUMBER=$(gcloud projects describe PROJECT_ID --format 'get(projectNumber)')
    gcloud projects add-iam-policy-binding projects/PROJECT_ID \
      --role roles/monitoring.viewer \
      --member=principal://iam.googleapis.com/projects/$PROJECT_NUMBER/locations/global/workloadIdentityPools/PROJECT_ID.svc.id.goog/subject/ns/custom-metrics/sa/custom-metrics-stackdriver-adapter
    
  5. Define un recurso de PodMonitoring colocando la siguiente configuración en un archivo llamado podmonitoring.yaml.

    apiVersion: monitoring.googleapis.com/v1
    kind: PodMonitoring
    metadata:
      name: prom-example
    spec:
      selector:
        matchLabels:
          run: custom-metric-prometheus-sd
      endpoints:
      - port: 8080
        interval: 30s
    
  6. Despliega el nuevo recurso de PodMonitoring:

    kubectl -n default apply -f podmonitoring.yaml
    

    En un par de minutos, Managed Service para Prometheus procesa las métricas recogidas del exportador y las almacena en Cloud Monitoring con un nombre largo. Las métricas de Prometheus se almacenan con las siguientes convenciones:

    • El prefijo prometheus.googleapis.com.
    • Este sufijo suele ser gauge, counter, summary o histogram, aunque las métricas sin tipo pueden tener el sufijo unknown o unknown:counter. Para verificar el sufijo, busca la métrica en Cloud Monitoring con el explorador de métricas.
  7. Actualiza el HPA desplegado para consultar la métrica de Cloud Monitoring. La métrica foo se ingiere como prometheus.googleapis.com/foo/gauge. Para que el recurso HorizontalPodAutoscaler implementado pueda consultar la métrica, debes usar el nombre completo en el HPA implementado, pero tienes que modificarlo sustituyendo todas las barras diagonales (/) por el carácter de barra vertical (|): prometheus.googleapis.com|foo|gauge. Para obtener más información, consulta la sección Métricas disponibles en Stackdriver del repositorio del adaptador de Stackdriver de métricas personalizadas.

    1. Para actualizar el HPA desplegado, ejecuta el siguiente comando:

      kubectl edit hpa custom-metric-prometheus-sd
      
    2. Cambia el valor del campo pods.metric.name de foo a prometheus.googleapis.com|foo|gauge. La sección spec debería tener este aspecto:

      spec:
         maxReplicas: 5
         metrics:
         - pods:
             metric:
               name: prometheus.googleapis.com|foo|gauge
             target:
               averageValue: "20"
               type: AverageValue
           type: Pods
         minReplicas: 1
      

    En este ejemplo, la configuración de HPA busca que el valor medio de la métrica prometheus.googleapis.com/foo/gauge sea 20. Como el valor de la métrica de Deployment es 40, el controlador de HPA aumenta el número de pods hasta el valor del campo maxReplicas (5) para intentar reducir el valor medio de la métrica en todos los pods a 20.

    La consulta de HPA se limita al espacio de nombres y al clúster en los que se instala el recurso de HPA, por lo que las métricas idénticas de otros clústeres y espacios de nombres no afectan al autoescalado.

  8. Para ver cómo se escala la carga de trabajo, ejecuta el siguiente comando:

    kubectl get hpa custom-metric-prometheus-sd --watch
    

    El valor del campo REPLICAS cambia de 1 a 5.

    NAME                          REFERENCE                                TARGETS        MINPODS   MAXPODS   REPLICAS   AGE
    custom-metric-prometheus-sd   Deployment/custom-metric-prometheus-sd   40/20          1         5         5          *
    
  9. Para reducir la escala de la implementación, actualice el valor de la métrica objetivo para que sea superior al valor de la métrica exportada. En este ejemplo, Deployment define el valor de la métrica prometheus.googleapis.com/foo/gauge como 40. Si asignas al valor objetivo un número superior a 40, la implementación se reducirá.

    Por ejemplo, usa kubectl edit para cambiar el valor del campo pods.target.averageValue en la configuración de HPA de 20 a 100.

    kubectl edit hpa custom-metric-prometheus-sd
    

    Modifica la sección de especificaciones para que coincida con lo siguiente:

    spec:
      maxReplicas: 5
      metrics:
      - pods:
          metric:
            name: prometheus.googleapis.com|foo|gauge
          target:
            averageValue: "100"
            type: AverageValue
      type: Pods
      minReplicas: 1
    
  10. Para ver cómo se reduce la carga de trabajo, ejecuta el siguiente comando:

    kubectl get hpa custom-metric-prometheus-sd --watch
    

    El valor del campo REPLICAS cambia de 5 a 1. Por diseño, esto ocurre más lentamente que cuando se aumenta el número de pods:

    NAME                          REFERENCE                                TARGETS        MINPODS   MAXPODS   REPLICAS   AGE
    custom-metric-prometheus-sd   Deployment/custom-metric-prometheus-sd   40/100          1         5         1          *
    
  11. Para limpiar el ejemplo desplegado, ejecuta los siguientes comandos:

    kubectl delete -f https://raw.githubusercontent.com/GoogleCloudPlatform/k8s-stackdriver/master/custom-metrics-stackdriver-adapter/deploy/production/adapter_new_resource_model.yaml
    kubectl delete -f https://raw.githubusercontent.com/GoogleCloudPlatform/k8s-stackdriver/master/custom-metrics-stackdriver-adapter/examples/prometheus-to-sd/custom-metrics-prometheus-sd.yaml
    kubectl delete podmonitoring/prom-example
    

Para obtener más información, consulta el ejemplo de Prometheus en el repositorio del adaptador de Stackdriver de métricas personalizadas o consulta Escalar una aplicación.

Usar el adaptador de Prometheus

Las configuraciones de prometheus-adapter se pueden usar para autoescalar con solo unos pocos cambios. Configurar prometheus-adapter para escalar con Managed Service para Prometheus tiene dos restricciones adicionales en comparación con el escalado con Prometheus upstream:

Como los datos pueden tardar un poco más en estar disponibles en Managed Service para Prometheus en comparación con Prometheus upstream, configurar una lógica de autoescalado demasiado impaciente puede provocar un comportamiento no deseado. Aunque no se garantiza la actualización de los datos, estos suelen estar disponibles para consultarse entre 3 y 7 segundos después de enviarse a Managed Service para Prometheus, sin incluir la latencia de la red.

Todas las consultas emitidas por prometheus-adapter son de ámbito global. Esto significa que, si tienes aplicaciones en dos espacios de nombres que emiten métricas con el mismo nombre, una configuración de HPA que use esa métrica se escalará usando datos de ambas aplicaciones. Te recomendamos que siempre uses filtros namespace o cluster en tu PromQL para evitar el escalado con datos incorrectos.

Para configurar un ejemplo de HPA con prometheus-adapter y la recogida gestionada, sigue estos pasos:

  1. Configure la recogida gestionada en su clúster.
  2. Despliega el proxy de la interfaz de usuario de frontend de Prometheus en tu clúster. Si usas Workload Identity Federation para GKE, también debes configurar y autorizar una cuenta de servicio.
  3. Despliega los manifiestos en el directorio examples/hpa/ del repositorio prometheus-engine:
    • example-app.yaml: una implementación y un servicio de ejemplo que emiten métricas.
    • pod-monitoring.yaml: un recurso que configura el raspado de las métricas de ejemplo.
    • hpa.yaml: el recurso HPA que configura el escalado de tu carga de trabajo.
  4. Asegúrate de que prometheus-adapter esté instalado en tu clúster. Para ello, puedes implementar el manifiesto de instalación de ejemplo en tu clúster. Este archivo de manifiesto está configurado para lo siguiente:

    • Consulta un proxy de frontend implementado en el espacio de nombres default.
    • Ejecuta PromQL para calcular y devolver la métrica http_requests_per_second de la implementación de ejemplo.
  5. Ejecuta los siguientes comandos, cada uno en una sesión de terminal independiente:

    1. Genera carga HTTP en el servicio prometheus-example-app:
      kubectl run -i --tty load-generator --rm --image=busybox:1.28 --restart=Never -- /bin/sh -c "while sleep 0.01; do wget -q -O- http://prometheus-example-app; done"
    2. Consulta la herramienta de adaptación dinámica de pods horizontal:
      kubectl get hpa prometheus-example-app --watch
    3. Observa cómo se escala la carga de trabajo:
      kubectl get po -lapp.kubernetes.io/name=prometheus-example-app --watch
  6. Detén la generación de carga HTTP con Ctrl+C y observa cómo se reduce la carga de trabajo.

Solución de problemas

El adaptador de Stackdriver de métricas personalizadas usa definiciones de recursos con los mismos nombres que los del adaptador de Prometheus, prometheus-adapter. Esta coincidencia de nombres significa que, si se ejecuta más de un adaptador en el mismo clúster, se producirán errores.

Si instalas el adaptador de Prometheus en un clúster que ya tenía instalado el adaptador de Stackdriver Custom Metrics, es posible que se produzcan errores como FailedGetObjectMetric debido a que los nombres coinciden. Para solucionar este problema, es posible que tengas que eliminar las APIs v1beta1.external.metrics.k8s.io, v1beta1.custom.metrics.k8s.io y v1beta2.custom.metrics.k8s.io apiservices que haya registrado anteriormente el adaptador de métricas personalizadas.

Consejos para solucionar problemas:

  • Algunas métricas del sistema de Cloud Monitoring, como las métricas de Pub/Sub, tienen un retraso de 60 segundos o más. Como Prometheus Adapter ejecuta consultas con la marca de tiempo actual, es posible que al consultar estas métricas con Prometheus Adapter no se obtengan datos. Para consultar métricas con retraso, usa el modificador offset en PromQL para cambiar el desfase de tiempo de tu consulta en la cantidad necesaria.

  • Para verificar que el proxy de la interfaz de usuario frontend funciona correctamente y que no hay problemas con los permisos, ejecuta el siguiente comando en un terminal:

    kubectl -n NAMESPACE_NAME port-forward svc/frontend 9090
    

    A continuación, abre otro terminal y ejecuta el siguiente comando:

    curl --silent 'localhost:9090/api/v1/series?match%5B%5D=up'
    

    Si el proxy de la interfaz de usuario frontend funciona correctamente, la respuesta en la segunda terminal será similar a la siguiente:

    curl --silent 'localhost:9090/api/v1/series?match%5B%5D=up' | jq .
    {
      "status": "success",
      "data": [
         ...
      ]
    }
    

    Si recibes un error 403, significa que el proxy de la interfaz de usuario frontend no está configurado correctamente. Para obtener información sobre cómo resolver un error 403, consulta la guía Configurar y autorizar una cuenta de servicio.

  • Para verificar que el apiserver de métricas personalizadas está disponible, ejecuta el siguiente comando:

    kubectl get apiservices.apiregistration.k8s.io v1beta1.custom.metrics.k8s.io
    

    Cuando el apiserver está disponible, la respuesta es similar a la siguiente:

    $ kubectl get apiservices.apiregistration.k8s.io v1beta1.custom.metrics.k8s.io
    NAME                            SERVICE                         AVAILABLE   AGE
    v1beta1.custom.metrics.k8s.io   monitoring/prometheus-adapter   True        33m
    
  • Para verificar que tu HPA funciona correctamente, ejecuta el siguiente comando:

    $ kubectl describe hpa prometheus-example-app
    Name:                                  prometheus-example-app
    Namespace:                             default
    Labels:                                
    Annotations:                           
    Reference:                             Deployment/prometheus-example-app
    Metrics:                               ( current / target )
    "http_requests_per_second" on pods:  11500m / 10
    Min replicas:                          1
    Max replicas:                          10
    Deployment pods:                       2 current / 2 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 http_requests_per_second
    ScalingLimited  False   DesiredWithinRange  the desired count is within the acceptable range
    Events:
    Type     Reason               Age                   From                       Message
    ----     ------               ----                  ----                       -------
    Normal   SuccessfulRescale    47s                   horizontal-pod-autoscaler  New size: 2; reason: pods metric http_requests_per_second above target
    

    Cuando la respuesta contiene una instrucción como FailedGetPodsMetric, significa que el HPA falla. A continuación se muestra una respuesta a la llamada describe cuando el HPA falla:

    $ kubectl describe hpa prometheus-example-app
    Name:                                  prometheus-example-app
    Namespace:                             default
    Reference:                             Deployment/prometheus-example-app
    Metrics:                               ( current / target )
      "http_requests_per_second" on pods:   / 10
    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   False   FailedGetPodsMetric  the HPA was unable to compute the replica count: unable to get metric http_requests_per_second: unable to fetch metrics from custom metrics API: the server could not find the metric http_requests_per_second for pods
      ScalingLimited  False   DesiredWithinRange   the desired count is within the acceptable range
    Events:
      Type     Reason               Age                   From                       Message
      ----     ------               ----                  ----                       -------
      Warning  FailedGetPodsMetric  104s (x11 over 16m)   horizontal-pod-autoscaler  unable to get metric http_requests_per_second: unable to fetch metrics from custom metrics API: the server could not find the metric http_requests_per_second for pods
    

    Si el HPA falla, asegúrate de generar métricas con load-generator. Puede consultar la API de métricas personalizadas directamente con el comando:

    kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1/" | jq .
    

    El resultado correcto es similar al siguiente:

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

    Si no hay métricas, no habrá datos en "resources" en el resultado. Por ejemplo:

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