Optimiser l'autoscaling des pods en fonction des métriques


Ce tutoriel explique comment procéder à l'autoscaling de vos charges de travail Google Kubernetes Engine (GKE) en fonction de métriques disponibles dans Cloud Monitoring.

Dans ce tutoriel, vous pouvez configurer l'autoscaling en fonction de l'une des quatre métriques suivantes :

Processeur

Utilisation du processeur

Scaling basé sur le pourcentage d'utilisation des processeurs sur les nœuds. Cela peut être rentable, ce qui vous permet d'optimiser l'utilisation des ressources du processeur. Cependant, comme l'utilisation du processeur est une métrique de fin, vos utilisateurs peuvent rencontrer une latence pendant le scaling.

Pub/Sub

Tâches en attente Pub/Sub

Scaling basé sur le nombre de messages non confirmés restants dans un abonnement Pub/Sub. Cela peut véritablement réduire la latence avant que cela ne devienne un problème, mais cela peut utiliser relativement plus de ressources que l'autocaling basé sur l'utilisation du processeur.

Métrique personnalisée

Métrique Cloud Monitoring personnalisée

Scaling basé sur une métrique personnalisée définie par l'utilisateur exportée par les bibliothèques clientes Cloud Monitoring. Pour en savoir plus, consultez la section Créer des métriques personnalisées dans la documentation Cloud Monitoring.

Prometheus personnalisé

Métrique Prometheus personnalisée

Scaling basé sur une métrique personnalisée définie par l'utilisateur exportée au format Prometheus. Votre métrique Prometheus doit être de type Gauge et ne doit pas contenir le préfixe custom.googleapis.com.

L'autoscaling consiste essentiellement à trouver un juste équilibre entre coût et latence. Vous pouvez tester une combinaison de ces statistiques et d'autres pour trouver une stratégie adaptée à vos besoins.

Objectifs

Ce tutoriel couvre les tâches suivantes :

  1. Découvrez comment déployer l'adaptateur de métriques personnalisées.
  2. Comment exporter des métriques à partir du code de votre application.
  3. Découvrez comment afficher vos métriques dans l'interface Cloud Monitoring.
  4. Comment déployer une ressource HorizontalPodAutoscaler (HPA) pour faire évoluer votre application en fonction des métriques Cloud Monitoring.

Coûts

Dans ce document, vous utilisez les composants facturables suivants de Google Cloud :

Obtenez une estimation des coûts en fonction de votre utilisation prévue à l'aide du simulateur de coût. Les nouveaux utilisateurs de Google Cloud peuvent bénéficier d'un essai gratuit.

Une fois que vous avez terminé les tâches décrites dans ce document, vous pouvez éviter de continuer à payer des frais en supprimant les ressources que vous avez créées. Pour en savoir plus, consultez la section Effectuer un nettoyage.

Avant de commencer

Pour activer l'API Kubernetes Engine, procédez comme suit :
  1. Accédez à la page Kubernetes Engine dans la console Google Cloud.
  2. Créer ou sélectionner un projet
  3. Patientez le temps de l'activation de l'API et des services associés. Cette opération peut prendre plusieurs minutes.
  4. Vérifiez que la facturation est activée pour votre projet Google Cloud.

Vous pouvez suivre ce tutoriel avec Cloud Shell, où sont préinstallés les outils de ligne de commande gcloud et kubectl utilisés ici. Si vous utilisez Cloud Shell, vous n'avez pas besoin d'installer ces outils de ligne de commande sur votre poste de travail.

Pour utiliser Cloud Shell, procédez comme suit :

  1. Accédez à Google Cloud Console.
  2. Cliquez sur le bouton Activer Cloud Shell en haut de la fenêtre de la console Google Cloud.Bouton d'activation de Cloud Shell

    Une session Cloud Shell s'ouvre dans un nouveau cadre en bas de la console Google Cloud et affiche une invite de ligne de commande.

    Session Cloud Shell

Configurer votre environnement

  1. Définissez la zone par défaut pour Google Cloud CLI :

    gcloud config set compute/zone zone
    

    Remplacez les éléments suivants :

    • zone : choisissez la zone la plus proche de vous. Pour en savoir plus, consultez la page Régions et zones.
  2. Définissez la variable d'environnement PROJECT_ID sur l'ID de votre projet Google Cloud (project-id) :

    export PROJECT_ID=project-id
    
  3. Définissez la zone par défaut pour Google Cloud CLI :

    gcloud config set project $PROJECT_ID
    
  4. Créez un cluster GKE.

    gcloud container clusters create metrics-autoscaling
    

Déployer l'adaptateur de métriques personnalisées

L'adaptateur de métriques personnalisées permet à votre cluster d'envoyer et de recevoir des métriques avec Cloud Monitoring.

Processeur

Non applicable : les autoscalers horizontaux de pods peuvent s'adapter en fonction de l'utilisation du processeur en mode natif. Ainsi, l'adaptateur de métriques personnalisées n'est pas requis.

Pub/Sub

Accordez à votre utilisateur la possibilité de créer les rôles d'autorisation requis :

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

Déployez l'adaptateur du nouveau modèle de ressource sur votre cluster :

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

Métrique personnalisée

Accordez à votre utilisateur la possibilité de créer les rôles d'autorisation requis :

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

Déployez l'adaptateur du modèle de ressource sur votre cluster :

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

Prometheus personnalisé

Accordez à votre utilisateur la possibilité de créer les rôles d'autorisation requis :

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

Déployez l'adaptateur de l'ancien modèle sur votre cluster :

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

Déployer une application avec des métriques

Téléchargez le dépôt contenant le code de l'application pour ce tutoriel :

Processeur

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

Métrique personnalisée

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

Prometheus personnalisé

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

Le dépôt contient du code qui exporte les métriques vers Cloud Monitoring :

Processeur

Cette application répond "Hello, world!" à toutes les requêtes Web sur le port 8080. Les métriques du processeur Compute Engine sont automatiquement collectées par 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

Cette application interroge un abonnement Pub/Sub pour rechercher de nouveaux messages, en les confirmant à leur arrivée. Les métriques d'abonnement Pub/Sub sont automatiquement collectées par 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étrique personnalisée

Cette application exporte une métrique de valeur constante à l'aide des bibliothèques clientes 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 personnalisé

Cette application exporte une métrique de valeur constante à l'aide du format 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)

Le dépôt contient également un fichier manifeste Kubernetes pour déployer l'application sur votre 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

Métrique personnalisée

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 personnalisé

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

Déployez l'application sur votre cluster :

Processeur

kubectl apply -f manifests/helloweb-deployment.yaml

Pub/Sub

Activez l'API Pub/Sub sur votre projet :

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

Créez un sujet et un abonnement Pub/Sub :

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

Créez un compte de service avec accès à 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"

Téléchargez le fichier de clé du compte de service :

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

Importez la clé du compte de service dans votre cluster en tant que secret :

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

Déployez l'application sur votre cluster :

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

Métrique personnalisée

kubectl apply -f custom-metrics-sd.yaml

Prometheus personnalisé

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

Après avoir attendu que l'application soit déployée, tous les pods atteignent l'état Ready :

Processeur

kubectl get pods

Sortie :

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

Pub/Sub

kubectl get pods

Sortie :

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

Métrique personnalisée

kubectl get pods

Sortie :

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

Prometheus personnalisé

kubectl get pods

Sortie :

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

Afficher les métriques dans Cloud Monitoring

À mesure que votre application s'exécute, elle écrit vos métriques dans Cloud Monitoring.

Pour afficher les métriques d'une ressource surveillée à l'aide de l'explorateur de métriques, procédez comme suit :

  1. Dans le panneau de navigation de la console Google Cloud, sélectionnez Monitoring, puis  Explorateur de métriques :

    Accéder à l'explorateur de métriques

  2. Dans l'élément Métrique, développez le menu Sélectionner une métrique, puis sélectionnez un type de ressource et un type de métrique. Par exemple, pour représenter graphiquement l'utilisation du processeur d'une machine virtuelle, procédez comme suit :
    1. (Facultatif) Pour réduire les options du menu, saisissez une partie du nom de la métrique dans la barre de filtre. Pour cet exemple, saisissez utilization.
    2. Dans le menu Ressources actives, sélectionnez Instance de VM.
    3. Dans le menu Catégories de métriques actives, sélectionnez Instance.
    4. Dans le menu Métriques actives, sélectionnez Utilisation du processeur, puis cliquez sur Appliquer.
  3. Pour filtrer les séries temporelles affichées, utilisez l'élément Filtre.

  4. Pour combiner des séries temporelles, utilisez les menus de l'élément Agrégation. Par exemple, pour afficher l'utilisation du processeur pour vos VM, en fonction de leur zone, définissez le premier menu sur Moyenne et le second sur zone.

    Toutes les séries temporelles sont affichées lorsque le premier menu de l'élément Agrégation est défini sur Non agrégé. Les paramètres par défaut de l'élément Aggregation (Agrégation) sont déterminés par le type de métrique que vous avez sélectionné.

Le type de ressource et les métriques sont les suivants :

Processeur

Explorateur de métriques

Type de ressource : gce_instance

Métrique : compute.googleapis.com/instance/cpu/utilization

Pub/Sub

Explorateur de métriques

Type de ressource : pubsub_subscription

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

Métrique personnalisée

Explorateur de métriques

Type de ressource : k8s_pod

Métrique : custom.googleapis.com/custom-metric

Prometheus personnalisé

Explorateur de métriques

Type de ressource : gke_container

Métrique : custom.googleapis.com/custom_prometheus

Créer un objet HorizontalPodAutoscaler

Une fois que voyez votre métrique dans Cloud Monitoring, vous pouvez déployer un objet HorizontalPodAutoscaler pour redimensionner votre déploiement en fonction de votre métrique.

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

Métrique personnalisée

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 personnalisé

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

Déployez HorizontalPodAutoscaler sur votre cluster :

Processeur

kubectl apply -f manifests/helloweb-hpa.yaml

Pub/Sub

kubectl apply -f deployment/pubsub-hpa.yaml

Métrique personnalisée

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

Prometheus personnalisé

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

Générer la charge

Pour certaines métriques, vous devrez peut-être générer une charge pour surveiller l'autoscaling :

Processeur

Simulez 10 000 requêtes envoyées au serveur 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

Publiez 200 messages dans le sujet Pub/Sub :

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

Métrique personnalisée

Non applicable : le code utilisé dans cet exemple exporte une valeur constante de 40 pour la métrique personnalisée. L'objet HorizontalPodAutoscaler est défini avec une valeur cible de 20. Il tente donc de faire évoluer automatiquement le déploiement à la hausse.

Prometheus personnalisé

Non applicable : le code utilisé dans cet exemple exporte une valeur constante de 40 pour la métrique personnalisée. L'objet HorizontalPodAutoscaler est défini avec une valeur cible de 20. Il tente donc de faire évoluer automatiquement le déploiement à la hausse.

Observer le scaling à la hausse de HorizontalPodAutoscaler

Vous pouvez vérifier le nombre actuel d'instances dupliquées de votre déploiement en exécutant la commande suivante :

kubectl get deployments

Après avoir laissé le temps à la métrique de se propager, le déploiement crée cinq pods pour gérer le traitement en attente.

Vous pouvez également inspecter l'état et l'activité récente de l'outil HorizontalPodAutoscaler en exécutant la commande suivante :

kubectl describe hpa

Nettoyer

Pour éviter que les ressources utilisées lors de ce tutoriel soient facturées sur votre compte Google Cloud, supprimez le projet contenant les ressources, ou conservez le projet et supprimez les ressources individuelles.

Processeur

Supprimez le cluster GKE :

 gcloud container clusters delete metrics-autoscaling

Pub/Sub

  1. Nettoyez l'abonnement et le sujet Pub/Sub :

    gcloud pubsub subscriptions delete echo-read
    gcloud pubsub topics delete echo
    
  2. Supprimez le cluster GKE :

    gcloud container clusters delete metrics-autoscaling
    

Métrique personnalisée

Supprimez le cluster GKE :

 gcloud container clusters delete metrics-autoscaling

Prometheus personnalisé

Supprimez le cluster GKE :

 gcloud container clusters delete metrics-autoscaling

Étape suivante