指標に基づいて Pod の自動スケーリングを最適化する


このチュートリアルでは、Cloud Monitoring から取得できる指標に基づいて Google Kubernetes Engine(GKE)ワークロードを自動的にスケーリングする方法を説明します。

このチュートリアルでは、次の 4 つのいずれかの指標に基づいて自動スケーリングを設定します。

CPU

CPU 使用率

ノード間の CPU 使用率に基づいてスケーリングします。これにより、費用対効果が向上し、CPU リソースの使用効率を最大限に高めることができます。ただし、CPU 使用率は最後の指標であるため、スケールアップ中にレイテンシが発生する場合があります。

Pub/Sub

Pub/Sub バックログ

Pub/Sub サブスクリプションに残っている未確認のメッセージ数に基づいてスケーリングします。問題が発生する前にレイテンシを効果的に削減できますが、CPU 使用率に基づく自動スケーリングよりも多くのリソースが使用される可能性があります。

カスタム指標

Cloud Monitoring カスタム指標

Cloud Monitoring クライアント ライブラリによってエクスポートされたカスタム ユーザー定義指標に基づいてスケーリングします。詳細については、Cloud Monitoring ドキュメントのカスタム指標の作成をご覧ください。

Prometheus カスタム指標

Prometheus カスタム指標

Prometheus 形式でエクスポートされたカスタム ユーザー定義指標に基づいてスケーリングします。Prometheus 指標のタイプは Gauge にする必要があります。custom.googleapis.com 接頭辞を含めることはできません。

基本的に、自動スケーリングとは費用とレイテンシの許容可能なバランスを見つけるプロセスです。これらの指標と他の指標を組み合わせて試し、最適なポリシーを探してください。

目標

このチュートリアルでは、次のタスクについて説明します。

  1. カスタム指標アダプタをデプロイする方法。
  2. アプリケーション コードから指標をエクスポートする方法。
  3. Cloud Monitoring インターフェースに指標を表示する方法。
  4. Cloud Monitoring の指標に基づいてアプリケーションをスケーリングするために HorizontalPodAutoscaler(HPA)リソースをデプロイする方法。

費用

このドキュメントでは、Google Cloud の次の課金対象のコンポーネントを使用します。

料金計算ツールを使うと、予想使用量に基づいて費用の見積もりを生成できます。 新しい Google Cloud ユーザーは無料トライアルをご利用いただける場合があります。

このドキュメントに記載されているタスクの完了後、作成したリソースを削除すると、それ以上の請求は発生しません。詳細については、クリーンアップをご覧ください。

始める前に

次の手順で Kubernetes Engine API を有効にします。
  1. Google Cloud コンソールの Kubernetes Engine ページにアクセスします。
  2. プロジェクトを作成または選択します。
  3. API と関連サービスが有効になるのを待ちます。 これには数分かかることがあります。
  4. Google Cloud プロジェクトで課金が有効になっていることを確認します

このチュートリアルで使用する gcloudkubectl のコマンドライン ツールとともにプリインストールされる Cloud Shell を使用して、このチュートリアルを進めることができます。Cloud Shell を使用する場合は、これらのコマンドライン ツールをワークステーションにインストールする必要はありません。

Cloud Shell を使用するには:

  1. Google Cloud コンソールに移動します。
  2. Google Cloud コンソール ウィンドウの上部にある「Cloud Shell をアクティブにする」ボタン Shell をアクティブにするボタン をクリックします。

    Google Cloud コンソールの一番下にある新しいフレームの中で Cloud Shell セッションが開き、コマンドライン プロンプトが表示されます。

    Cloud Shell セッション

環境設定

  1. Google Cloud CLI のデフォルト ゾーンを設定します。

    gcloud config set compute/zone zone
    

    次のように置き換えます。

  2. 環境変数 PROJECT_IDGoogle Cloud プロジェクト IDproject-id)を設定します。

    export PROJECT_ID=project-id
    
  3. Google Cloud CLI のデフォルト ゾーンを設定します。

    gcloud config set project $PROJECT_ID
    
  4. GKE クラスタを作成します。

    gcloud container clusters create metrics-autoscaling
    

カスタム指標アダプタのデプロイ

カスタム指標アダプタを使用すると、クラスタで Cloud Monitoring を使用して指標の送受信を行うことができます。

CPU

なし: 水平 Pod オートスケーラーは CPU 使用率に基づいてネイティブにスケーリングできるため、カスタム指標アダプタは必要ありません。

Pub/Sub

必要な認可ロールを作成する権限をユーザーに付与します。

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

クラスタに新しいリソースモデル アダプタをデプロイします。

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

カスタム指標

必要な認可ロールを作成する権限をユーザーに付与します。

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

クラスタにリソースモデル アダプタをデプロイします。

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

Prometheus カスタム指標

必要な認可ロールを作成する権限をユーザーに付与します。

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

クラスタにレガシー リソースモデル アダプタをデプロイします。

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

指標を使用したアプリケーションのデプロイ

このチュートリアルのアプリケーション コードを含むリポをダウンロードします。

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

カスタム指標

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

Prometheus カスタム指標

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

リポには、Cloud Monitoring に指標をエクスポートするコードが含まれています。

CPU

このアプリケーションは、ポート 8080 へのすべてのウェブ リクエストに対して「Hello、world!」というレスポンスを返します。Compute Engine の CPU 指標は 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

このアプリケーションは、Pub/Sub サブスクリプションをポーリングし、新しいメッセージの着信を確認します。Pub/Sub サブスクリプションの指標は 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)

カスタム指標

このアプリケーションは、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 カスタム指標

このアプリケーションは、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)

リポには、アプリケーションをクラスタにデプロイするための Kubernetes マニフェストも含まれています。

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

カスタム指標

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 カスタム指標

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

アプリケーションをクラスタにデプロイします。

CPU

kubectl apply -f manifests/helloweb-deployment.yaml

Pub/Sub

プロジェクトで Pub/Sub API を有効にします。

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

Pub/Sub トピックとサブスクリプションを作成します。

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

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"

サービス アカウント キー ファイルをダウンロードします。

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

サービス アカウント キーを Secret としてクラスタにインポートします。

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

アプリケーションをクラスタにデプロイします。

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

カスタム指標

kubectl apply -f custom-metrics-sd.yaml

Prometheus カスタム指標

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

アプリケーションがデプロイされるまで待ちます。デプロイされると、すべての Pod が Ready 状態になります。

CPU

kubectl get pods

出力:

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

Pub/Sub

kubectl get pods

出力:

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

カスタム指標

kubectl get pods

出力:

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

Prometheus カスタム指標

kubectl get pods

出力:

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

Cloud Monitoring での指標の表示

アプリケーションを実行すると、Cloud Monitoring に指標が書き込まれます。

Metrics Explorer を使用してモニタリング対象リソースの指標を表示する方法は次のとおりです。

  1. Google Cloud コンソールのナビゲーション パネルで [Monitoring] を選択し、次に [ Metrics Explorer] を選択します。

    Metrics Explorer に移動

  2. [指標] 要素で [指標を選択] メニューを開き、リソースタイプと指標タイプを選択します。たとえば、仮想マシンの CPU 使用率をグラフ化する手順は次のとおりです。
    1. (省略可)メニューのオプションを減らすには、フィルタバーに指標名の一部を入力します。この例では、「utilization」と入力します。
    2. [有効なリソース] メニューで、[VM インスタンス] を選択します。
    3. [有効な指標カテゴリ] メニューで、[インスタンス] を選択します。
    4. [有効な指標] メニューで [CPU utilization] を選択し、[適用] をクリックします。
  3. 表示される時系列をフィルタするには、[フィルタ] 要素を使用します。

  4. 時系列を結合するには、[集計] 要素のメニューを使用します。たとえば、ゾーンに基づいて VM の CPU 使用率を表示するには、最初のメニューを [平均] に設定し、2 番目のメニューを [ゾーン] に設定します。

    [集計] 要素の最初のメニューが [未集計] になっている場合は、すべての時系列が表示されます。[集計] 要素のデフォルトは、選択した指標タイプによって決まります。

リソースタイプと指標は次のとおりです。

CPU

Metrics Explorer

Resource type: gce_instance

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

Pub/Sub

Metrics Explorer

Resource type: pubsub_subscription

Metric: pubsub.googleapis.com/subscription/num_undelivered_messages

カスタム指標

Metrics Explorer

Resource type: k8s_pod

Metric: custom.googleapis.com/custom-metric

Prometheus カスタム指標

Metrics Explorer

Resource type: gke_container

Metric: custom.googleapis.com/custom_prometheus

HorizontalPodAutoscaler オブジェクトの作成

Cloud Monitoring に指標が表示されたら、HorizontalPodAutoscaler をデプロイして、指標に基づいて Deployment のサイズを変更できます。

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

カスタム指標

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 カスタム指標

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

HorizontalPodAutoscaler をクラスタにデプロイします。

CPU

kubectl apply -f manifests/helloweb-hpa.yaml

Pub/Sub

kubectl apply -f deployment/pubsub-hpa.yaml

カスタム指標

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

Prometheus カスタム指標

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

負荷の生成

指標によっては、自動スケーリングを監視するために負荷の生成が必要になることがあります。

CPU

helloweb サーバーに対して 10,000 件のリクエストをシミュレートします。

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

Pub/Sub

Pub/Sub トピックに 200 件のメッセージを公開します。

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

カスタム指標

なし: このサンプルで使用されているコードは、カスタム指標の定数値 40 をエクスポートします。HorizontalPodAutoscaler が 20 のターゲット値で設定されているため、Deployment が自動的にスケールアップされます。

Prometheus カスタム指標

なし: このサンプルで使用されているコードは、カスタム指標の定数値 40 をエクスポートします。HorizontalPodAutoscaler が 20 のターゲット値で設定されているため、Deployment が自動的にスケールアップされます。

HorizontalPodAutoscaler のスケールアップの監視

Deployment の現在のレプリカの数を確認するには、次のコマンドを実行します。

kubectl get deployments

指標が反映されるまでしばらく待ってから、Deployment は 5 つの Pod を作成してバックログを処理します。

また、次のコマンドを実行することで、HorizontalPodAutoscaler の状態と最近のアクティビティを調べることもできます。

kubectl describe hpa

クリーンアップ

このチュートリアルで使用したリソースについて、Google Cloud アカウントに課金されないようにするには、リソースを含むプロジェクトを削除するか、プロジェクトを維持して個々のリソースを削除します。

CPU

GKE クラスタを削除します。

 gcloud container clusters delete metrics-autoscaling

Pub/Sub

  1. Pub/Sub サブスクリプションとトピックをクリーンアップします。

    gcloud pubsub subscriptions delete echo-read
    gcloud pubsub topics delete echo
    
  2. GKE クラスタを削除します。

    gcloud container clusters delete metrics-autoscaling
    

カスタム指標

GKE クラスタを削除します。

 gcloud container clusters delete metrics-autoscaling

Prometheus カスタム指標

GKE クラスタを削除します。

 gcloud container clusters delete metrics-autoscaling

次のステップ