Redimensionamento automático horizontal de pods (HPA)

Este documento descreve como ativar o dimensionamento automático de pods horizontal (HPA) para o Google Cloud Managed Service for Prometheus. Pode ativar a HPA através de uma das seguintes ações:

Não pode usar o adaptador do Stackdriver e o adaptador do Prometheus em conjunto no mesmo cluster porque as respetivas definições de recursos sobrepõem-se, conforme descrito na Resolução de problemas. Recomendamos que escolha apenas uma solução para HPA.

Use o KEDA

O KEDA (Kubernetes Event-driven Autoscaling) é o escalador automático lançado mais recentemente que usa métricas do Prometheus e está a tornar-se uma solução preferencial na comunidade do Prometheus.

Para começar, consulte a documentação do KEDA para integração com o Google Cloud Managed Service for Prometheus.

Use o adaptador do Stackdriver de métricas personalizadas

O adaptador do Stackdriver de métricas personalizadas suporta a consulta de métricas do Managed Service for Prometheus a partir da versão v0.13.1 do adaptador.

Para configurar um exemplo de configuração do HPA com o adaptador do Stackdriver Custom Metrics, faça o seguinte:

  1. Configure a coleção gerida no seu cluster.
  2. Instale o adaptador do Stackdriver de métricas personalizadas no cluster.

    kubectl apply -f https://raw.githubusercontent.com/GoogleCloudPlatform/k8s-stackdriver/master/custom-metrics-stackdriver-adapter/deploy/production/adapter_new_resource_model.yaml
    
  3. Implemente um exportador de métricas do Prometheus de exemplo e um 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 uma aplicação de exportação que emite a métrica foo e um recurso HPA. O HPA aumenta a escala desta aplicação até 5 réplicas para alcançar o valor alvo da métrica foo.

  4. Se usar a Workload Identity Federation para o GKE, também tem de conceder a função Leitor do Monitoring à conta de serviço na qual o adaptador é executado. Ignore este passo se não tiver a Workload Identity Federation para o GKE ativada no seu cluster do 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. Defina um recurso PodMonitoring colocando a seguinte configuração num ficheiro com o nome 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. Implemente o novo recurso PodMonitoring:

    kubectl -n default apply -f podmonitoring.yaml
    

    Em alguns minutos, o Managed Service for Prometheus processa as métricas extraídas do exportador e armazena-as no Cloud Monitoring com um nome de formato longo. As métricas do Prometheus são armazenadas com as seguintes convenções:

    • O prefixo prometheus.googleapis.com.
    • Normalmente, este sufixo é um de gauge, counter, summary ou histogram, embora as métricas não tipadas possam ter o sufixo unknown ou unknown:counter. Para validar o sufixo, procure a métrica no Cloud Monitoring através do Explorador de métricas.
  7. Atualize o HPA implementado para consultar a métrica do Cloud Monitoring. A métrica foo é carregada como prometheus.googleapis.com/foo/gauge. Para tornar a métrica consultável pelo recurso HorizontalPodAutoscaler implementado, use o nome no formato longo no HPA implementado, mas tem de o modificar substituindo todas as barras invertidas (/) pelo caráter de barra vertical (|): prometheus.googleapis.com|foo|gauge. Para mais informações, consulte a secção Métricas disponíveis no Stackdriver do repositório do adaptador do Stackdriver de métricas personalizadas.

    1. Atualize o HPA implementado executando o seguinte comando:

      kubectl edit hpa custom-metric-prometheus-sd
      
    2. Altere o valor do campo pods.metric.name de foo para prometheus.googleapis.com|foo|gauge. A secção spec deve ter o seguinte aspeto:

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

    Neste exemplo, a configuração do HPA procura o valor médio da métrica prometheus.googleapis.com/foo/gauge para ser 20. Uma vez que a implementação define o valor da métrica como 40, o controlador HPA aumenta o número de pods até ao valor do campo maxReplicas (5) para tentar reduzir o valor médio da métrica em todos os pods para 20.

    A consulta HPA tem âmbito no espaço de nomes e no cluster em que o recurso HPA está instalado, pelo que as métricas idênticas noutros clusters e espaços de nomes não afetam o seu escalamento automático.

  8. Para ver o aumento da escala da carga de trabalho, execute o seguinte comando:

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

    O valor do campo REPLICAS é alterado de 1 para 5.

    NAME                          REFERENCE                                TARGETS        MINPODS   MAXPODS   REPLICAS   AGE
    custom-metric-prometheus-sd   Deployment/custom-metric-prometheus-sd   40/20          1         5         5          *
    
  9. Para reduzir a escala da implementação, atualize o valor da métrica alvo para um valor superior ao valor da métrica exportada. Neste exemplo, a implementação define o valor da métrica prometheus.googleapis.com/foo/gauge como 40. Se definir o valor alvo para um número superior a 40, a implementação é reduzida.

    Por exemplo, use kubectl edit para alterar o valor do campo pods.target.averageValue na configuração da HPA de 20 para 100.

    kubectl edit hpa custom-metric-prometheus-sd
    

    Modifique a secção de especificações para corresponder ao seguinte:

    spec:
      maxReplicas: 5
      metrics:
      - pods:
          metric:
            name: prometheus.googleapis.com|foo|gauge
          target:
            averageValue: "100"
            type: AverageValue
      type: Pods
      minReplicas: 1
    
  10. Para ver a redução da escala da carga de trabalho, execute o seguinte comando:

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

    O valor do campo REPLICAS é alterado de 5 para 1. Por design, isto acontece mais lentamente do que quando aumenta o 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 limpar o exemplo implementado, execute os seguintes 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 mais informações, consulte o exemplo do Prometheus no repositório do adaptador do Stackdriver de métricas personalizadas ou consulte o artigo Dimensionar uma aplicação.

Use o adaptador do Prometheus

As configurações do prometheus-adapter existentes podem ser usadas para o dimensionamento automático com apenas algumas alterações. A configuração do prometheus-adapter para dimensionar através do serviço gerido para Prometheus tem duas restrições adicionais em comparação com o dimensionamento através do Prometheus a montante:

  • As consultas têm de ser encaminhadas através do proxy da IU do frontend do Prometheus, tal como quando consulta o serviço gerido para Prometheus através da API ou da IU do Prometheus. Para o prometheus-adapter, tem de editar a prometheus-adapter implementação para alterar o prometheus-url valor da seguinte forma:

    --prometheus-url=http://frontend.NAMESPACE_NAME.svc:9090/
    

    onde NAMESPACE_NAME é o espaço de nomes onde o front-end está implementado.

  • Não pode usar um correspondente de expressões regulares (=~ ou !~) num nome de métrica no campo .seriesQuery da configuração das regras. Em alternativa, tem de especificar totalmente os nomes das métricas. Para algumas soluções alternativas, consulte o artigo Compatibilidade com PromQL.

Uma vez que os dados podem demorar um pouco mais a ficar disponíveis no Managed Service for Prometheus em comparação com o Prometheus a montante, a configuração de uma lógica de escala automática excessivamente ansiosa pode causar um comportamento indesejável. Embora não haja garantia de atualização dos dados, os dados estão normalmente disponíveis para consulta 3 a 7 segundos após serem enviados para o Managed Service for Prometheus, excluindo qualquer latência de rede.

Todas as consultas emitidas pelo prometheus-adapter têm âmbito global. Isto significa que, se tiver aplicações em dois espaços de nomes que emitem métricas com nomes idênticos, uma configuração do HPA que use essa métrica é dimensionada com base nos dados de ambas as aplicações. Recomendamos que use sempre filtros namespace ou cluster no seu PromQL para evitar a escalabilidade com dados incorretos.

Para configurar uma configuração de HPA de exemplo com o prometheus-adapter e a recolha gerida, siga estes passos:

  1. Configure a coleção gerida no seu cluster.
  2. Implemente o proxy da IU do frontend do Prometheus no seu cluster. Se usar a federação de identidades da carga de trabalho para o GKE, também tem de configurar e autorizar uma conta de serviço.
  3. Implemente os manifestos no diretório examples/hpa/ no repositório prometheus-engine:
    • example-app.yaml: uma implementação e um serviço de exemplo que emitem métricas.
    • pod-monitoring.yaml: um recurso que configura a obtenção de métricas de exemplo.
    • hpa.yaml: o recurso HPA que configura o dimensionamento para a sua carga de trabalho.
  4. Certifique-se de que o prometheus-adapter está instalado no seu cluster. Isto pode ser feito implementando o manifesto de instalação de exemplo no seu cluster. Este manifesto está configurado para:

    • Consultar um proxy de front-end implementado no espaço de nomes default.
    • Emita PromQL para calcular e devolver a métrica http_requests_per_second da implementação de exemplo.
  5. Execute os seguintes comandos, cada um numa sessão de terminal separada:

    1. Gere carga HTTP no serviço 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. Monitorize o redimensionador automático horizontal de pods:
      kubectl get hpa prometheus-example-app --watch
    3. Veja o aumento da carga de trabalho:
      kubectl get po -lapp.kubernetes.io/name=prometheus-example-app --watch
  6. Pare a geração de carga HTTP com Ctrl+C e veja a carga de trabalho diminuir.

Resolução de problemas

O adaptador do Stackdriver de métricas personalizadas usa definições de recursos com os mesmos nomes que os do adaptador do Prometheus, prometheus-adapter. Esta sobreposição de nomes significa que a execução de mais do que um adaptador no mesmo cluster causa erros.

A instalação do adaptador do Prometheus num cluster que tinha anteriormente o adaptador do Stackdriver de métricas personalizadas instalado pode gerar erros, como FailedGetObjectMetric, devido a nomes em conflito. Para resolver este problema, pode ter de eliminar os apiservices v1beta1.external.metrics.k8s.io, v1beta1.custom.metrics.k8s.io e v1beta2.custom.metrics.k8s.io registados anteriormente pelo adaptador de métricas personalizadas.

Sugestões de resolução de problemas:

  • Algumas métricas do sistema do Cloud Monitoring, como as métricas do Pub/Sub, têm um atraso de 60 segundos ou mais. Uma vez que o adaptador do Prometheus executa consultas com a data/hora atual, a consulta destas métricas com o adaptador do Prometheus pode resultar incorretamente na ausência de dados. Para consultar métricas com atraso, use o modificador offset no PromQL para alterar a compensação de tempo da sua consulta pela quantidade necessária.

  • Para verificar se o proxy da IU de front-end está a funcionar como previsto e não existem problemas com as autorizações, execute o seguinte comando num terminal:

    kubectl -n NAMESPACE_NAME port-forward svc/frontend 9090
    

    Em seguida, abra outro terminal e execute o seguinte comando:

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

    Quando o proxy da IU de front-end está a funcionar corretamente, a resposta no segundo terminal é semelhante à seguinte:

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

    Se receber um erro 403, significa que o proxy da IU de front-end não está configurado corretamente. Para ver informações sobre como resolver um erro 403, consulte o guia configure e autorize uma conta de serviço.

  • Para verificar se o apiserver de métricas personalizadas está disponível, execute o seguinte comando:

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

    Quando o apiserver está disponível, a resposta é semelhante à seguinte:

    $ 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 se o HPA está a funcionar conforme esperado, execute o seguinte 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
    

    Quando a resposta contém uma declaração como FailedGetPodsMetric, o HPA falha. A imagem seguinte ilustra uma resposta à chamada describe quando o HPA está a falhar:

    $ 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
    

    Quando o HPA falha, certifique-se de que está a gerar métricas com o load-generator. Pode verificar a API de métricas personalizadas diretamente com o comando:

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

    Um resultado bem-sucedido é semelhante ao seguinte:

    $ 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"
            ]
         }
      ]
      }
    

    Se não existirem métricas, não são apresentados dados em "resources" no resultado, por exemplo:

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