Ottimizza la scalabilità automatica dei pod in base alle metriche


Questo tutorial mostra come scalare automaticamente i carichi di lavoro Google Kubernetes Engine (GKE) in base alle metriche disponibili in Cloud Monitoring.

In questo tutorial puoi configurare la scalabilità automatica in base a una delle quattro metriche diverse:

CPU

Utilizzo CPU

Scala in base all'utilizzo percentuale delle CPU nei nodi. Ciò può essere conveniente, consentendoti di massimizzare l'utilizzo delle risorse della CPU. Poiché l'utilizzo della CPU è una metrica finale, tuttavia, gli utenti potrebbero riscontrare una latenza mentre è in corso uno scale up.

Pub/Sub

Backlog Pub/Sub

Scala in base al numero di messaggi non confermati rimanenti in una sottoscrizione Pub/Sub. Questo può ridurre in modo efficace la latenza prima che diventi un problema, ma potrebbe utilizzare risorse relativamente più elevate rispetto alla scalabilità automatica basata sull'utilizzo della CPU.

Metrica personalizzata

Metrica di Cloud Monitoring personalizzata

Scala in base a una metrica personalizzata definita dall'utente esportata dalle librerie client di Cloud Monitoring. Per saperne di più, consulta Creazione di metriche personalizzate nella documentazione di Cloud Monitoring.

Prometheus personalizzato

Metrica Prometheus personalizzata

Scala in base a una metrica personalizzata definita dall'utente esportata nel formato Prometheus. La metrica Prometheus deve essere di tipo Gauge e non deve contenere il prefisso custom.googleapis.com.

La scalabilità automatica consente fondamentalmente di trovare un equilibrio accettabile tra costo e latenza. Per trovare una norma adatta alle tue esigenze, ti consigliamo di sperimentare una combinazione di queste metriche e altre.

Obiettivi

Questo tutorial riguarda le attività seguenti:

  1. Come eseguire il deployment dell'adattatore delle metriche personalizzate.
  2. Come esportare le metriche dal codice dell'applicazione.
  3. Come visualizzare le metriche nell'interfaccia di Cloud Monitoring.
  4. Come eseguire il deployment di una risorsa HorizontalPodAutoscaler (HPA) per scalare la tua applicazione in base alle metriche di Cloud Monitoring.

Costi

In questo documento vengono utilizzati i seguenti componenti fatturabili di Google Cloud:

Per generare una stima dei costi in base all'utilizzo previsto, utilizza il Calcolatore prezzi. I nuovi utenti di Google Cloud possono essere idonei a una prova senza costi aggiuntivi.

Una volta completate le attività descritte in questo documento, puoi evitare la fatturazione continua eliminando le risorse che hai creato. Per ulteriori informazioni, consulta la pagina Pulizia.

Prima di iniziare

Segui questi passaggi per abilitare l'API Kubernetes Engine:
  1. Visita la pagina Kubernetes Engine nella console Google Cloud.
  2. Crea o seleziona un progetto.
  3. Attendi che l'API e i servizi correlati siano abilitati. Questa operazione può richiedere diversi minuti.
  4. Assicurati che la fatturazione sia attivata per il tuo progetto Google Cloud.

Puoi seguire questo tutorial utilizzando Cloud Shell, in cui sono preinstallati gli strumenti a riga di comando gcloud e kubectl utilizzati in questo tutorial. Se utilizzi Cloud Shell, non è necessario installare questi strumenti a riga di comando sulla workstation.

Per utilizzare Cloud Shell:

  1. Vai alla console Google Cloud.
  2. Fai clic sul pulsante Attiva Cloud Shell Pulsante Attiva shell nella parte superiore della finestra della console Google Cloud.

    All'interno di un nuovo frame nella parte inferiore della console Google Cloud si apre una sessione di Cloud Shell che mostra un prompt della riga di comando.

    Sessione di Cloud Shell

Configurazione dell'ambiente

  1. Imposta la zona predefinita per Google Cloud CLI:

    gcloud config set compute/zone zone
    

    Sostituisci quanto segue:

    • zone: scegli la zona più vicina a te. Per saperne di più, consulta Regioni e zone.
  2. Imposta la variabile di ambiente PROJECT_ID sul tuo ID progetto Google Cloud (project-id):

    export PROJECT_ID=project-id
    
  3. Imposta la zona predefinita per Google Cloud CLI:

    gcloud config set project $PROJECT_ID
    
  4. Crea un cluster GKE

    gcloud container clusters create metrics-autoscaling
    

Deployment dell'adattatore per le metriche personalizzate

L'adattatore delle metriche personalizzate consente al cluster di inviare e ricevere metriche con Cloud Monitoring.

CPU

Non applicabile: gli Horizontal Pod Autoscaler possono scalare in modo nativo in base all'utilizzo della CPU, quindi non è necessario l'adattatore per le metriche personalizzate.

Pub/Sub

Concedi all'utente la possibilità di creare ruoli di autorizzazione richiesti:

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

Esegui il deployment dell'adattatore del nuovo modello di risorsa sul cluster:

kubectl apply -f https://raw.githubusercontent.com/GoogleCloudPlatform/k8s-stackdriver/master/custom-metrics-stackdriver-adapter/deploy/production/adapter_new_resource_model.yaml

Metrica personalizzata

Concedi all'utente la possibilità di creare ruoli di autorizzazione richiesti:

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

Esegui il deployment dell'adattatore per modello di risorsa sul cluster:

kubectl apply -f https://raw.githubusercontent.com/GoogleCloudPlatform/k8s-stackdriver/master/custom-metrics-stackdriver-adapter/deploy/production/adapter_new_resource_model.yaml

Prometheus personalizzato

Concedi all'utente la possibilità di creare ruoli di autorizzazione richiesti:

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

Esegui il deployment dell'adattatore per il modello di risorse legacy sul cluster:

kubectl apply -f https://raw.githubusercontent.com/GoogleCloudPlatform/k8s-stackdriver/master/custom-metrics-stackdriver-adapter/deploy/production/adapter.yaml
dell'adattatore.

Deployment di un'applicazione con le metriche

Scarica il repository contenente il codice dell'applicazione per questo tutorial:

CPU

git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples.git
cd kubernetes-engine-samples/quickstarts/hello-app

Pub/Sub

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

Metrica personalizzata

git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples.git
cd kubernetes-engine-samples/observability/custom-metrics-autoscaling/direct-to-sd

Prometheus personalizzato

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

Il repository contiene codice che esporta le metriche in Cloud Monitoring:

CPU

Questa applicazione risponde "Hello World!" a qualsiasi richiesta web sulla porta 8080. Le metriche della CPU di Compute Engine vengono raccolte automaticamente da Cloud Monitoring.

package main

import (
	"fmt"
	"log"
	"net/http"
	"os"
)

func main() {
	// register hello function to handle all requests
	mux := http.NewServeMux()
	mux.HandleFunc("/", hello)

	// use PORT environment variable, or default to 8080
	port := os.Getenv("PORT")
	if port == "" {
		port = "8080"
	}

	// start the web server on port and accept requests
	log.Printf("Server listening on port %s", port)
	log.Fatal(http.ListenAndServe(":"+port, mux))
}

// hello responds to the request with a plain-text "Hello, world" message.
func hello(w http.ResponseWriter, r *http.Request) {
	log.Printf("Serving request: %s", r.URL.Path)
	host, _ := os.Hostname()
	fmt.Fprintf(w, "Hello, world!\n")
	fmt.Fprintf(w, "Version: 1.0.0\n")
	fmt.Fprintf(w, "Hostname: %s\n", host)
}

Pub/Sub

Questa applicazione esegue il polling di una sottoscrizione Pub/Sub per i nuovi messaggi, confermandoli all'arrivo. Le metriche relative all'abbonamento Pub/Sub vengono raccolte automaticamente da 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)

Metrica personalizzata

Questa applicazione esporta una metrica del valore costante utilizzando le librerie client di Cloud Monitoring.

func exportMetric(stackdriverService *monitoring.Service, metricName string,
	metricValue int64, metricLabels map[string]string, monitoredResource string, resourceLabels map[string]string) error {
	dataPoint := &monitoring.Point{
		Interval: &monitoring.TimeInterval{
			EndTime: time.Now().Format(time.RFC3339),
		},
		Value: &monitoring.TypedValue{
			Int64Value: &metricValue,
		},
	}
	// Write time series data.
	request := &monitoring.CreateTimeSeriesRequest{
		TimeSeries: []*monitoring.TimeSeries{
			{
				Metric: &monitoring.Metric{
					Type:   "custom.googleapis.com/" + metricName,
					Labels: metricLabels,
				},
				Resource: &monitoring.MonitoredResource{
					Type:   monitoredResource,
					Labels: resourceLabels,
				},
				Points: []*monitoring.Point{
					dataPoint,
				},
			},
		},
	}
	projectName := fmt.Sprintf("projects/%s", resourceLabels["project_id"])
	_, err := stackdriverService.Projects.TimeSeries.Create(projectName, request).Do()
	return err
}

Prometheus personalizzato

Questa applicazione esporta una metrica del valore costante utilizzando il formato 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)

Il repository contiene anche un manifest Kubernetes per il deployment dell'applicazione nel cluster:

CPU

apiVersion: apps/v1
kind: Deployment
metadata:
  name: helloweb
  labels:
    app: hello
spec:
  selector:
    matchLabels:
      app: hello
      tier: web
  template:
    metadata:
      labels:
        app: hello
        tier: web
    spec:
      containers:
      - name: hello-app
        image: us-docker.pkg.dev/google-samples/containers/gke/hello-app:1.0
        ports:
        - containerPort: 8080
        resources:
          requests:
            cpu: 200m

Pub/Sub

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

Metrica personalizzata

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    run: custom-metric-sd
  name: custom-metric-sd
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      run: custom-metric-sd
  template:
    metadata:
      labels:
        run: custom-metric-sd
    spec:
      containers:
      - command: ["./sd-dummy-exporter"]
        args:
        - --use-new-resource-model=true
        - --use-old-resource-model=false
        - --metric-name=custom-metric
        - --metric-value=40
        - --pod-name=$(POD_NAME)
        - --namespace=$(NAMESPACE)
        image: us-docker.pkg.dev/google-samples/containers/gke/sd-dummy-exporter:v0.3.0
        name: sd-dummy-exporter
        resources:
          requests:
            cpu: 100m
        env:
        # save Kubernetes metadata as environment variables for use in metrics
        - name: POD_NAME
          valueFrom:
            fieldRef:
              apiVersion: v1
              fieldPath: metadata.name
        - name: NAMESPACE
          valueFrom:
            fieldRef:
              apiVersion: v1
              fieldPath: metadata.namespace

Prometheus personalizzato

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    run: custom-metric-prometheus-sd
  name: custom-metric-prometheus-sd
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      run: custom-metric-prometheus-sd
  template:
    metadata:
      labels:
        run: custom-metric-prometheus-sd
    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
      # pre-built 'prometheus-to-sd' sidecar container to export prometheus
      # metrics to Stackdriver
      - name: prometheus-to-sd
        image: gcr.io/google-containers/prometheus-to-sd:v0.5.0
        command: ["/monitor"]
        args:
        - --source=:http://localhost:8080
        - --stackdriver-prefix=custom.googleapis.com
        - --pod-id=$(POD_ID)
        - --namespace-id=$(POD_NAMESPACE)
        env:
        # save Kubernetes metadata as environment variables for use in metrics
        - name: POD_ID
          valueFrom:
            fieldRef:
              apiVersion: v1
              fieldPath: metadata.uid
        - name: POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace

Esegui il deployment dell'applicazione nel tuo cluster:

CPU

kubectl apply -f manifests/helloweb-deployment.yaml

Pub/Sub

Abilita l'API Pub/Sub sul tuo progetto:

gcloud services enable cloudresourcemanager.googleapis.com pubsub.googleapis.com

Crea un argomento e una sottoscrizione Pub/Sub:

gcloud pubsub topics create echo
gcloud pubsub subscriptions create echo-read --topic=echo

Crea un account di servizio con accesso a 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"

Scarica il file della chiave dell'account di servizio:

gcloud iam service-accounts keys create key.json \
  --iam-account autoscaling-pubsub-sa@$PROJECT_ID.iam.gserviceaccount.com

Importa la chiave dell'account di servizio nel cluster come Secret:

kubectl create secret generic pubsub-key --from-file=key.json=./key.json

Esegui il deployment dell'applicazione nel tuo cluster:

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

Metrica personalizzata

kubectl apply -f custom-metrics-sd.yaml

Prometheus personalizzato

kubectl apply -f custom-metrics-prometheus-sd.yaml

Dopo aver atteso il deployment dell'applicazione, tutti i pod raggiungono lo stato Ready:

CPU

kubectl get pods

Output:

NAME                        READY   STATUS    RESTARTS   AGE
helloweb-7f7f7474fc-hzcdq   1/1     Running   0          10s

Pub/Sub

kubectl get pods

Output:

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

Metrica personalizzata

kubectl get pods

Output:

NAME                                READY   STATUS    RESTARTS   AGE
custom-metric-sd-58dbf4ffc5-tm62v   1/1     Running   0          33s

Prometheus personalizzato

kubectl get pods

Output:

NAME                                           READY   STATUS    RESTARTS   AGE
custom-metric-prometheus-sd-697bf7c7d7-ns76p   2/2     Running   0          49s

Visualizzazione delle metriche su Cloud Monitoring

Durante l'esecuzione, l'applicazione scrive le metriche in Cloud Monitoring.

Per visualizzare le metriche per una risorsa monitorata utilizzando Metrics Explorer, procedi come segue:

  1. Nel pannello di navigazione della console Google Cloud, seleziona Monitoring e poi  Metrics Explorer:

    Vai a Metrics Explorer

  2. Nell'elemento Metrica, espandi il menu Seleziona una metrica, quindi seleziona un tipo di risorsa e un tipo di metrica. Ad esempio, per tracciare un grafico dell'utilizzo della CPU di una macchina virtuale:
    1. (Facoltativo) Per ridurre le opzioni del menu, inserisci parte del nome della metrica nella barra dei filtri. Per questo esempio, inserisci utilization.
    2. Nel menu Risorse attive, seleziona Istanza VM.
    3. Nel menu Categorie di metriche attive, seleziona Istanza.
    4. Nel menu Metriche attive, seleziona Utilizzo CPU e fai clic su Applica.
  3. Per filtrare le serie temporali visualizzate, utilizza l'elemento Filter.

  4. Per combinare le serie temporali, utilizza i menu dell'elemento Aggregazione. Ad esempio, per visualizzare l'utilizzo della CPU per le VM, in base alla loro zona, imposta il primo menu su Media e il secondo su zone.

    Tutte le serie temporali vengono visualizzate quando il primo menu dell'elemento Aggregation è impostato su Unaggregated. Le impostazioni predefinite per l'elemento Aggregazione sono determinate dal tipo di metrica selezionato.

Il tipo di risorsa e le metriche sono i seguenti:

CPU

Metrics Explorer

Tipo di risorsa: gce_instance

Metrica: compute.googleapis.com/instance/cpu/utilization

Pub/Sub

Metrics Explorer

Tipo di risorsa: pubsub_subscription

Metrica: pubsub.googleapis.com/subscription/num_undelivered_messages

Metrica personalizzata

Metrics Explorer

Tipo di risorsa: k8s_pod

Metrica: custom.googleapis.com/custom-metric

Prometheus personalizzato

Metrics Explorer

Tipo di risorsa: gke_container

Metrica: custom.googleapis.com/custom_prometheus

Creazione di un oggetto HorizontalPodAutoscaler

Quando vedi la metrica in Cloud Monitoring, puoi eseguire il deployment di un HorizontalPodAutoscaler per ridimensionare il deployment in base alla metrica.

CPU

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: cpu
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: helloweb
  minReplicas: 1
  maxReplicas: 5
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 30

Pub/Sub

apiVersion: autoscaling/v2beta2
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

Metrica personalizzata

apiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
metadata:
  name: custom-metric-sd
  namespace: default
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: custom-metric-sd
  minReplicas: 1
  maxReplicas: 5
  metrics:
  - type: Pods
    pods:
      metric:
        name: custom-metric
      target:
        type: AverageValue
        averageValue: 20

Prometheus personalizzato

apiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
metadata:
  name: custom-prometheus-hpa
  namespace: default
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: custom-metric-prometheus-sd
  minReplicas: 1
  maxReplicas: 5
  metrics:
  - type: Pods
    pods:
      metric:
        name: custom_prometheus
      target:
        type: AverageValue
        averageValue: 20

Esegui il deployment di HorizontalPodAutoscaler nel cluster:

CPU

kubectl apply -f manifests/helloweb-hpa.yaml

Pub/Sub

kubectl apply -f deployment/pubsub-hpa.yaml

Metrica personalizzata

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

Prometheus personalizzato

kubectl apply -f custom-metrics-prometheus-sd-hpa.yaml

Generazione del carico in corso...

Per alcune metriche, potrebbe essere necessario generare carico per osservare la scalabilità automatica:

CPU

Simula 10.000 richieste al server helloweb:

 kubectl exec -it deployments/helloweb -- /bin/sh -c \
     "for i in $(seq -s' ' 1 10000); do wget -q -O- localhost:8080; done"

Pub/Sub

Pubblica 200 messaggi nell'argomento Pub/Sub:

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

Metrica personalizzata

Non applicabile: il codice utilizzato in questo esempio esporta un valore costante di 40 per la metrica personalizzata. HorizontalPodAutoscaler è impostato con un valore target pari a 20, quindi tenta di fare automaticamente lo scale up del deployment.

Prometheus personalizzato

Non applicabile: il codice utilizzato in questo esempio esporta un valore costante di 40 per la metrica personalizzata. HorizontalPodAutoscaler è impostato con un valore target pari a 20, quindi tenta di fare automaticamente lo scale up del deployment.

Osservazione dello scale up di HorizontalPodAutoscaler

Puoi controllare il numero attuale di repliche del tuo deployment eseguendo:

kubectl get deployments

Dopo aver atteso un po' di tempo per la propagazione della metrica, il deployment crea cinque pod per gestire il backlog.

Puoi anche controllare lo stato e l'attività recente di HorizontalPodAutoscaler eseguendo:

kubectl describe hpa

Esegui la pulizia

Per evitare che al tuo Account Google Cloud vengano addebitati costi relativi alle risorse utilizzate in questo tutorial, elimina il progetto che contiene le risorse oppure mantieni il progetto ed elimina le singole risorse.

CPU

Elimina il cluster GKE:

 gcloud container clusters delete metrics-autoscaling

Pub/Sub

  1. Esegui la pulizia della sottoscrizione Pub/Sub e dell'argomento:

    gcloud pubsub subscriptions delete echo-read
    gcloud pubsub topics delete echo
    
  2. Elimina il cluster GKE:

    gcloud container clusters delete metrics-autoscaling
    

Metrica personalizzata

Elimina il cluster GKE:

 gcloud container clusters delete metrics-autoscaling

Prometheus personalizzato

Elimina il cluster GKE:

 gcloud container clusters delete metrics-autoscaling

Passaggi successivi