Otimizar o escalonamento automático de pod com base em métricas

Neste tutorial, demonstramos como escalonar automaticamente as cargas de trabalho do Google Kubernetes Engine (GKE) com base nas métricas disponíveis no Cloud Monitoring.

Neste tutorial, é possível configurar o escalonamento automático com base em uma das seguintes métricas:

Pub/Sub

Backlog do Pub/Sub

Escalonar com base em uma métrica externa que informa o número de mensagens não reconhecidas restantes em uma assinatura do Pub/Sub. Isso pode reduzir a latência de maneira eficiente antes de se tornar um problema, mas talvez utilize relativamente mais recursos do que o escalonamento automático com base na utilização de CPU.

Métrica personalizada

Métrica personalizada do Prometheus

Escalone com base em uma métrica personalizada definida pelo usuário e exportada no formato Prometheus por meio do Google Managed Prometheus. Sua métrica do Prometheus precisa ser do tipo Gauge.

O escalonamento automático é basicamente sobre encontrar um equilíbrio aceitável entre custo e latência. Recomendamos fazer testes com uma combinação dessas e outras métricas para encontrar uma política que funcione para você.

Como implantar o adaptador de métricas personalizadas

O adaptador de métricas personalizadas permite que o cluster envie e receba métricas com o Cloud Monitoring.

Pub/Sub

O procedimento para instalar o adaptador de métricas personalizadas é diferente para clusters com ou sem a federação de identidade da carga de trabalho para GKE ativada. Selecione a opção que corresponde à configuração que você escolheu quando criou o cluster.

Identidade da carga de trabalho

Permita que o usuário crie os papéis de autorização necessários:

kubectl create clusterrolebinding cluster-admin-binding \
    --clusterrole cluster-admin --user "$(gcloud config get-value account)"

Implante o adaptador 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

O adaptador usa a conta de serviço custom-metrics-stackdriver-adapter do Kubernetes no namespace custom-metrics. Permita que esta conta de serviço leia as métricas do Cloud Monitoring atribuindo o papel de Leitor do Monitoring:

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

Autenticação legada

Permita que o usuário crie os papéis de autorização necessários:

kubectl create clusterrolebinding cluster-admin-binding \
    --clusterrole cluster-admin --user "$(gcloud config get-value account)"

Implante o adaptador 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

Métrica personalizada

O procedimento para instalar o adaptador de métricas personalizadas é diferente para clusters com ou sem a federação de identidade da carga de trabalho para GKE ativada. Selecione a opção que corresponde à configuração que você escolheu quando criou o cluster.

Identidade da carga de trabalho

Permita que o usuário crie os papéis de autorização necessários:

kubectl create clusterrolebinding cluster-admin-binding \
    --clusterrole cluster-admin --user "$(gcloud config get-value account)"

Implante o adaptador 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

O adaptador usa a conta de serviço custom-metrics-stackdriver-adapter do Kubernetes no namespace custom-metrics. Permita que esta conta de serviço leia as métricas do Cloud Monitoring atribuindo o papel de Leitor do Monitoring:

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

Autenticação legada

Permita que o usuário crie os papéis de autorização necessários:

kubectl create clusterrolebinding cluster-admin-binding \
    --clusterrole cluster-admin --user "$(gcloud config get-value account)"

Implante o adaptador 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

Como implantar um aplicativo com métricas

Faça o download do repositório que contém o código do aplicativo para este tutorial:

Pub/Sub

git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples.git
cd kubernetes-engine-samples/databases/cloud-pubsub

Métrica personalizada

git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples.git
cd kubernetes-engine-samples/observability/custom-metrics-autoscaling/google-managed-prometheus

O repositório contém um código que exporta métricas para o Cloud Monitoring:

Pub/Sub

Este aplicativo pesquisa uma inscrição do Pub/Sub para novas mensagens, reconhecendo-as assim que chegam. As métricas de inscrição do Pub/Sub são coletadas automaticamente pelo Cloud Monitoring.

from google import auth
from google.cloud import pubsub_v1


def main():
    """Continuously pull messages from subsciption"""

    # read default project ID
    _, project_id = auth.default()
    subscription_id = 'echo-read'

    subscriber = pubsub_v1.SubscriberClient()
    subscription_path = subscriber.subscription_path(
        project_id, subscription_id)

    def callback(message: pubsub_v1.subscriber.message.Message) -> None:
        """Process received message"""
        print(f"Received message: ID={message.message_id} Data={message.data}")
        print(f"[{datetime.datetime.now()}] Processing: {message.message_id}")
        time.sleep(3)
        print(f"[{datetime.datetime.now()}] Processed: {message.message_id}")
        message.ack()

    streaming_pull_future = subscriber.subscribe(
        subscription_path, callback=callback)
    print(f"Pulling messages from {subscription_path}...")

    with subscriber:
        try:
            streaming_pull_future.result()
        except Exception as e:
            print(e)

Métrica personalizada

O aplicativo responde a qualquer solicitação da Web para o caminho /metrics com uma métrica de valor constante usando o formato do Prometheus.

metric := prometheus.NewGauge(
	prometheus.GaugeOpts{
		Name: *metricName,
		Help: "Custom metric",
	},
)
prometheus.MustRegister(metric)
metric.Set(float64(*metricValue))

http.Handle("/metrics", promhttp.Handler())
log.Printf("Starting to listen on :%d", *port)
err := http.ListenAndServe(fmt.Sprintf(":%d", *port), nil)

O repositório também contém um manifesto do Kubernetes para implantar o aplicativo no cluster. Uma implantação é um objeto da API Kubernetes que permite executar várias réplicas de pods distribuídos entre os nós de um cluster:

Pub/Sub

O manifesto é diferente para clusters com ou sem a federação de identidade da carga de trabalho para o GKE ativada. Selecione a opção que corresponde à configuração escolhida quando você criou o cluster.

Identidade da carga de trabalho

apiVersion: apps/v1
kind: Deployment
metadata:
  name: pubsub
spec:
  selector:
    matchLabels:
      app: pubsub
  template:
    metadata:
      labels:
        app: pubsub
    spec:
      serviceAccountName: pubsub-sa
      containers:
      - name: subscriber
        image: us-docker.pkg.dev/google-samples/containers/gke/pubsub-sample:v2

Autenticação legada

apiVersion: apps/v1
kind: Deployment
metadata:
  name: pubsub
spec:
  selector:
    matchLabels:
      app: pubsub
  template:
    metadata:
      labels:
        app: pubsub
    spec:
      volumes:
      - name: google-cloud-key
        secret:
          secretName: pubsub-key
      containers:
      - name: subscriber
        image: us-docker.pkg.dev/google-samples/containers/gke/pubsub-sample:v2
        volumeMounts:
        - name: google-cloud-key
          mountPath: /var/secrets/google
        env:
        - name: GOOGLE_APPLICATION_CREDENTIALS
          value: /var/secrets/google/key.json

Métrica personalizada

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    run: custom-metrics-gmp
  name: custom-metrics-gmp
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      run: custom-metrics-gmp
  template:
    metadata:
      labels:
        run: custom-metrics-gmp
    spec:
      containers:
      # sample container generating custom metrics
      - name: prometheus-dummy-exporter
        image: us-docker.pkg.dev/google-samples/containers/gke/prometheus-dummy-exporter:v0.2.0
        command: ["./prometheus-dummy-exporter"]
        args:
        - --metric-name=custom_prometheus
        - --metric-value=40
        - --port=8080

Com o recurso PodMonitoring, o Google Cloud Managed Service para Prometheus exporta métricas do Prometheus para o Cloud Monitoring:

apiVersion: monitoring.googleapis.com/v1
kind: PodMonitoring
metadata:
  name: "custom-metrics-exporter"
spec:
  selector:
    matchLabels:
      run: custom-metrics-gmp
  endpoints:
  - port: 8080
    path: /metrics
    interval: 15s

A partir da versão 1.27 do GKE Standard ou da versão 1.25 do Autopilot do GKE, o Google Cloud Managed Service para Prometheus está ativado. Para ativar o Google Cloud Managed Service para Prometheus em clusters em versões anteriores, consulte Ative a coleta gerenciada.

Implante o aplicativo no cluster:

Pub/Sub

O procedimento para implantar o aplicativo é diferente para clusters com ou sem a federação de identidade da carga de trabalho para GKE ativada. Selecione a opção que corresponde à configuração que você escolheu quando criou o cluster.

Identidade da carga de trabalho

  1. Ative a API Pub/Sub no seu projeto:

    gcloud services enable cloudresourcemanager.googleapis.com pubsub.googleapis.com
    
  2. Crie um tópico e uma assinatura do Pub/Sub:

    gcloud pubsub topics create echo
    gcloud pubsub subscriptions create echo-read --topic=echo
    
  3. Implante o aplicativo no cluster:

    kubectl apply -f deployment/pubsub-with-workload-identity.yaml
    
  4. Este aplicativo define uma conta de serviço pubsub-sa do Kubernetes. Atribua à conta o papel de assinante do Pub/Sub para que o aplicativo possa publicar mensagens no tópico do Pub/Sub.

    gcloud projects add-iam-policy-binding projects/$PROJECT_ID \
      --role=roles/pubsub.subscriber \
      --member=principal://iam.googleapis.com/projects/$PROJECT_NUMBER/locations/global/workloadIdentityPools/$PROJECT_ID.svc.id.goog/subject/ns/default/sa/pubsub-sa
    

    O comando anterior usa um identificador principal, que permite que o IAM se refira diretamente a uma conta de serviço do Kubernetes.

    Prática recomendada:

    Use identificadores principais, mas considere a limitação na descrição de um método alternativo.

Autenticação legada

  1. Ative a API Pub/Sub no seu projeto:

    gcloud services enable cloudresourcemanager.googleapis.com pubsub.googleapis.com
    
  2. Crie um tópico e uma assinatura do Pub/Sub:

    gcloud pubsub topics create echo
    gcloud pubsub subscriptions create echo-read --topic=echo
    
  3. Crie uma conta de serviço com acesso ao Pub/Sub:

    gcloud iam service-accounts create autoscaling-pubsub-sa
    gcloud projects add-iam-policy-binding $PROJECT_ID \
      --member "serviceAccount:autoscaling-pubsub-sa@$PROJECT_ID.iam.gserviceaccount.com" \
      --role "roles/pubsub.subscriber"
    
  4. Faça o download do arquivo de chave da conta de serviço:

    gcloud iam service-accounts keys create key.json \
      --iam-account autoscaling-pubsub-sa@$PROJECT_ID.iam.gserviceaccount.com
    
  5. Importe a chave da conta de serviço para o cluster como um Secret:

    kubectl create secret generic pubsub-key --from-file=key.json=./key.json
    
  6. Implante o aplicativo no cluster:

    kubectl apply -f deployment/pubsub-with-secret.yaml
    

Métrica personalizada

kubectl apply -f custom-metrics-gmp.yaml

Depois de aguardar um momento pela implantação do aplicativo, todos os pods alcançam o estado Ready:

Pub/Sub

kubectl get pods

Saída:

NAME                     READY   STATUS    RESTARTS   AGE
pubsub-8cd995d7c-bdhqz   1/1     Running   0          58s

Métrica personalizada

kubectl get pods

Saída:

NAME                                  READY   STATUS    RESTARTS   AGE
custom-metrics-gmp-865dffdff9-x2cg9   1/1     Running   0          49s

Como ver métricas no Cloud Monitoring

Durante a execução do aplicativo, ele grava as métricas no Cloud Monitoring.

Para visualizar as métricas de um recurso monitorado usando o Metrics Explorer, faça o seguinte:

  1. No console Google Cloud , acesse a página do  Metrics explorer:

    Acesse o Metrics Explorer

    Se você usar a barra de pesquisa para encontrar essa página, selecione o resultado com o subtítulo Monitoramento.

  2. No elemento Métrica, expanda o menu Selecionar uma métrica e escolha um tipo de recurso e de métrica. Por exemplo, para gerar um gráfico do uso da CPU de uma máquina virtual, faça o seguinte:
    1. Opcional: para reduzir as opções do menu, insira parte do nome da métrica na Barra de filtros. Neste exemplo, digite utilization.
    2. No menu Recursos ativos, selecione Instância de VM.
    3. No menu Categorias de métrica ativas, selecione Instância.
    4. No menu Métricas ativas, selecione Utilização de CPU e clique em Aplicar.
  3. Para filtrar as séries temporais exibidas, use o elemento Filtro.

  4. Para combinar séries temporais, use os menus no elemento Agregação. Por exemplo, para exibir a utilização da CPU para suas VMs, com base na zona, defina o primeiro menu como Média e o segundo como zona.

    Todas as séries temporais são exibidas quando o primeiro menu do elemento Agregação está definido como Não agregado. As configurações padrão do elemento Agregação são determinadas pelo tipo de métrica selecionada.

O tipo de recurso e as métricas são os seguintes:

Pub/Sub

Metrics Explorer

Tipo de recurso: pubsub_subscription

Métrica: pubsub.googleapis.com/subscription/num_undelivered_messages

Métrica personalizada

Metrics Explorer

Tipo de recurso: prometheus_target

Métrica: prometheus.googleapis.com/custom_prometheus/gauge

Dependendo da métrica, talvez você ainda não veja muita atividade no Metrics Explorer do Cloud Monitoring. Não se surpreenda se a métrica não for atualizada.

Como criar um objeto HorizontalPodAutoscaler

Quando ver a métrica no Cloud Monitoring, será possível implantar um HorizontalPodAutoscaler para redimensionar a implantação com base na métrica.

Pub/Sub

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: pubsub
spec:
  minReplicas: 1
  maxReplicas: 5
  metrics:
  - external:
      metric:
       name: pubsub.googleapis.com|subscription|num_undelivered_messages
       selector:
         matchLabels:
           resource.labels.subscription_id: echo-read
      target:
        type: AverageValue
        averageValue: 2
    type: External
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: pubsub

Métrica personalizada

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: custom-metrics-gmp-hpa
  namespace: default
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: custom-metrics-gmp
  minReplicas: 1
  maxReplicas: 5
  metrics:
  - type: Pods
    pods:
      metric:
        name: prometheus.googleapis.com|custom_prometheus|gauge
      target:
        type: AverageValue
        averageValue: 20

Implante o HorizontalPodAutoscaler no cluster:

Pub/Sub

kubectl apply -f deployment/pubsub-hpa.yaml

Métrica personalizada

kubectl apply -f custom-metrics-gmp-hpa.yaml

Como gerar carga

Para algumas métricas, pode ser necessário gerar carga para monitorar o escalonamento automático:

Pub/Sub

Publique 200 mensagens no tópico do Pub/Sub:

for i in {1..200}; do gcloud pubsub topics publish echo --message="Autoscaling #${i}"; done

Métrica personalizada

Não aplicável: o código usado na amostra exporta um valor constante de 40 para a métrica personalizada. O HorizontalPodAutoscaler é definido com um valor de destino de 20. Portanto, ele tenta escalonar a implantação automaticamente e de forma vertical.

Talvez seja necessário aguardar alguns minutos para que o HorizontalPodAutoscaler responda às mudanças de métrica.

Como observar o escalonamento vertical do HorizontalPodAutoscaler

É possível verificar o número atual de réplicas da sua implantação executando:

kubectl get deployments

Após algum tempo para a métrica se propagar, a implantação cria cinco pods para lidar com o backlog.

Também é possível inspecionar o estado e a atividade recente do HorizontalPodAutoscaler, basta executar:

kubectl describe hpa