Escalonamento automático horizontal de pods (HPA).

Neste documento, descrevemos como ativar o escalonamento automático de pods horizontal (HPA) para o Google Cloud Managed Service para Prometheus. É possível ativar o HPA de uma das seguintes maneiras:

É preciso escolher uma abordagem ou outra. Não é possível usar ambos porque as definições dos recursos se sobrepõem, conforme descrito em Solução de problemas.

Usar o adaptador de métricas personalizadas do Stackdriver

O adaptador de métricas personalizadas do Stackdriver dá suporte a métricas de consulta do Managed Service para Prometheus, começando com a versão v0.13.1 do adaptador.

Para definir um exemplo de configuração do HPA usando o adaptador de métricas personalizadas do Stackdriver, faça o seguinte:

  1. Configure a coleção gerenciada no cluster.
  2. Instale o adaptador de métricas personalizadas do Stackdriver 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. Implante um exportador de métricas do Prometheus e um recurso de 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
    

    Esse comando implanta um aplicativo exportador que emite a métrica foo e um recurso HPA. O HPA escalona esse aplicativo em até cinco réplicas para atingir o valor pretendido da métrica foo.

  4. Defina um recurso PodMonitoring colocando a seguinte configuração em um arquivo chamado 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
    
  5. Implante o novo recurso PodMonitoring:

    kubectl -n default apply -f podmonitoring.yaml
    

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

    • O prefixo prometheus.googleapis.com.
    • Esse sufixo geralmente é gauge, counter, summary ou histogram, embora as métricas sem tipo possam ter o sufixo unknown ou unknown:counter. Para verificar o sufixo, procure a métrica no Cloud Monitoring usando o Metrics Explorer.
  6. Atualizar o HPA implantado para consultar a métrica do Cloud Monitoring. A métrica foo é ingerida como prometheus.googleapis.com/foo/gauge. Para tornar a métrica consultável pelo recurso HorizontalPodAutoscaler implantado, use o nome de formato longo no HPA implantado, mas é necessário modificá-lo substituindo todas as barras (/) pelo caractere de barra vertical (|): prometheus.googleapis.com|foo|gauge. Para saber mais, consulte a seção Métricas disponíveis no Stackdriver do repositório de métricas personalizadas do Stackdriver Adapter.

    1. Atualize o HPA implantado executando o seguinte comando:

      kubectl edit hpa custom-metric-prometheus-sd
      
    2. Mude o valor do campo pods.metric.name de foo para prometheus.googleapis.com|foo|gauge. A seção spec será parecida com o exemplo a seguir:

      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 como 20. Como a implantação define o valor da métrica como 40, o controlador HPA aumenta o número de pods até o valor do campo maxReplicas (5) para tentar reduzir o valor médio da métrica em todos os pods para 20.

    O escopo da consulta HPA é do namespace e do cluster em que o recurso HPA está instalado. Portanto, métricas idênticas em outros clusters e namespaces não afetam o escalonamento automático.

  7. Para observar o aumento da carga de trabalho, execute o seguinte comando:

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

    O valor do campo REPLICAS muda 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          *
    
  8. Para reduzir a implantação, atualize o valor da métrica de destino para ser maior que o valor da métrica exportada. Neste exemplo, a implantação define o valor da métrica prometheus.googleapis.com/foo/gauge como 40. Se você definir o valor de destino como um número maior que 40, a implantação reduzirá a escala vertical.

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

    kubectl edit hpa custom-metric-prometheus-sd
    

    Modifique a seção de especificações para que corresponda ao seguinte:

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

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

    O valor do campo REPLICAS muda de 5 para 1. Por padrão, isso ocorre mais lentamente do que ao escalonar 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          *
    
  10. Para limpar o exemplo implantado, 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 ver mais informações, consulte o exemplo do Prometheus no repositório de adaptadores de métricas personalizadas do Stackdriver ou consulte Como escalonar um aplicativo.

Usar o adaptador do Prometheus

As configurações existentes do prometheus-adapter podem ser usadas para escalonamento automático com apenas algumas alterações. A configuração do prometheus-adapter para escalonamento usando o Managed Service para Prometheus tem duas restrições extras em comparação com o escalonamento usando o upstream do Prometheus:

  • As consultas precisam ser roteadas pelo proxy da interface do front-end do Prometheus, da mesma forma que acontece ao consultar o Managed Service para Prometheus usando a API ou a interface do Prometheus. Para o prometheus-adapter, é necessário editar a implantação do prometheus-adapter para alterar o valor do prometheus-url da seguinte maneira:

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

    em que NAMESPACE_NAME é o namespace em que o front-end é implantado.

  • Não é possível usar uma correspondência de regex em um nome de métrica no campo .seriesQuery da configuração de regras. Em vez disso, você precisa especificar totalmente os nomes das métricas.

Como os dados podem demorar um pouco mais para ficar disponíveis no Managed Service para Prometheus em comparação com o upstream do Prometheus, configurar uma lógica de escalonamento automático excessivamente ávida pode causar comportamentos indesejados. Não há garantia de atualização de dados, mas eles geralmente ficam disponíveis para consulta de três a sete segundos após o envio para o Managed Service para Prometheus, excluindo qualquer latência de rede.

Todas as consultas emitidas pelo adaptador de prometheus têm escopo global. Isso significa que, se você tiver aplicativos em dois namespaces que emitem métricas com nomes idênticos, uma configuração do HPA que usa essa métrica é escalonada usando dados dos dois aplicativos. Recomendamos sempre usar filtros namespace ou cluster na PromQL para evitar o escalonamento usando dados incorretos.

Para definir uma configuração de HPA de exemplo usando o prometheus-adapter e a coleção gerenciada, siga estas etapas:

  1. Configure a coleção gerenciada no cluster.
  2. Implante o proxy de IU do front-end do Prometheus no cluster. Se você usar a Identidade da carga de trabalho, também precisará configurar e autorizar uma conta de serviço.
  3. Implante os manifestos no diretório examples/hpa/ no repositório do prometheus-engine:
    • example-app.yaml: um exemplo de implantação e serviço que emite métricas.
    • pod-monitoring.yaml: um recurso que configura a raspagem de dados de métricas de exemplo.
    • hpa.yaml: o recurso HPA que configura o escalonamento da carga de trabalho.
  4. Verifique se prometheus-adapter está instalado no cluster. Para isso, implante o exemplo de manifesto de instalação no cluster. Esse manifesto é configurado para o seguinte:

    • Consultar um proxy de front-end implantado no namespace default.
    • Solicitar que o PromQL calcule e retorne a métrica http_requests_per_second da implantação de exemplo.
  5. Execute os seguintes comandos, cada um em uma sessão de terminal separada:

    1. Gere a 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. Observe o escalonador automático horizontal de pods:
      kubectl get hpa prometheus-example-app --watch
    3. Observe o escalonamento vertical da carga de trabalho:
      kubectl get po -lapp.kubernetes.io/name=prometheus-example-app --watch
  6. Para parar a geração de carga HTTP, use Ctrl+C e observe a redução da escala vertical da carga de trabalho.

Solução de problemas

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

A instalação do Adaptador do Prometheus em um cluster que tinha o Adaptador das métricas personalizadas do Stackdriver instalado pode gerar erros, como FailedGetObjectMetric devido a nomes de colisão. Para resolver esse problema, talvez seja necessário excluir os apiservices v1beta1.external.metrics.k8s.io, v1beta1.custom.metrics.k8s.io e v1beta2.custom.metrics.k8s.io registrados anteriormente pelo adaptador de métricas personalizadas.

Dicas para soluçã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. Como o adaptador do Prometheus executa consultas usando a marcação de tempo atual, a consulta dessas métricas com o adaptador do Prometheus pode não resultar em dados. Para consultar métricas atrasadas, use o modificador offset no PromQL para alterar o ajuste de horário da consulta pelo valor necessário.

  • Para verificar se o proxy de IU de front-end está funcionando como esperado e se não há problemas com permissões, execute o seguinte comando em um 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 de IU de front-end estiver funcionando corretamente, a resposta no segundo terminal será semelhante a esta:

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

    Se você receber um erro 403, significa que o proxy de IU de front-end não está configurado corretamente. Para informações sobre como resolver um erro 403, consulte o guia Configurar e autorizar 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 a esta:

    $ 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á funcionando conforme o 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 instrução como FailedGetPodsMetric, o HPA falha. Veja a seguir uma resposta à chamada describe quando o HPA 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 falhar, verifique se você está gerando métricas com o load-generator. Verifique a API de métricas personalizadas diretamente com o comando:

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

    Uma saída bem-sucedida será semelhante a esta:

    $ 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 houver métricas, não haverá dados em "resources" na saída, por exemplo:

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