Google Kubernetes Engine を使用した負荷分散テスト

このチュートリアルでは、Google Kubernetes Engine(GKE)を使用して、複数のコンテナを使ってシンプルな REST ベースの API のトラフィックを作成する負荷分散テスト フレームワークをデプロイする方法について説明します。ここでは、App Engine にデプロイされた、受信 HTTP POST リクエストに応答する REST スタイルのエンドポイントを公開するウェブ アプリケーションの負荷テストを行います。

同じパターンを使用して、メッセージング システム、データ ストリーム管理システム、データベース システムなど、さまざまなシナリオやアプリケーション用の負荷テスト フレームワークを作成できます。

目標

  • Deployment 構成を設定するための環境変数を定義する。
  • GKE クラスタを作成する。
  • 負荷テストを実行する。
  • 必要に応じて、ユーザー数をスケールアップするか、パターンを他のユースケースにも適用する。

費用

このチュートリアルでは、課金対象である次の Google Cloud コンポーネントを使用します。

  • App Engine
  • Artifact Registry
  • Cloud Build
  • Cloud Storage
  • GKE

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

準備

  1. Google Cloud アカウントにログインします。Google Cloud を初めて使用する場合は、アカウントを作成して、実際のシナリオでの Google プロダクトのパフォーマンスを評価してください。新規のお客様には、ワークロードの実行、テスト、デプロイができる無料クレジット $300 分を差し上げます。
  2. Google Cloud Console の [プロジェクト セレクタ] ページで、Google Cloud プロジェクトを選択または作成します。

    プロジェクト セレクタに移動

  3. Cloud プロジェクトに対して課金が有効になっていることを確認します。詳しくは、プロジェクトで課金が有効になっているかどうかを確認する方法をご覧ください。

  4. Cloud Build, Compute Engine, Google Kubernetes Engine, Container Analysis, and Artifact Registry API を有効にします。

    API を有効にする

  5. Google Cloud Console の [プロジェクト セレクタ] ページで、Google Cloud プロジェクトを選択または作成します。

    プロジェクト セレクタに移動

  6. Cloud プロジェクトに対して課金が有効になっていることを確認します。詳しくは、プロジェクトで課金が有効になっているかどうかを確認する方法をご覧ください。

  7. Cloud Build, Compute Engine, Google Kubernetes Engine, Container Analysis, and Artifact Registry API を有効にします。

    API を有効にする

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

ワークロード例

次の図は、リクエストがクライアントからアプリケーションに送信されるワークロード例を示しています。

クライアントからアプリケーションへのリクエスト。

このインタラクションをモデル化するために、Python ベースの分散型負荷テストツールである Locust を使用します。このツールでは複数のターゲットパスにリクエストを分散できます。たとえば、Locust は /login/metrics のターゲットパスにリクエストを分散できます。ワークロードは、Locust の一連のタスクとしてモデル化されます。

アーキテクチャ

このアーキテクチャには、次の 2 つの主要コンポーネントが含まれます。

  • Locust Docker コンテナ イメージ。
  • コンテナ オーケストレーションと管理メカニズム。

Locust Docker コンテナ イメージには Locust ソフトウェアが含まれています。Dockerfile はこのチュートリアルに付属する GitHub リポジトリのクローンを作成すると入手できます。Dockerfile はベースの Python イメージを使用し、Locust サービスを開始してタスクを実行するスクリプトを含んでいます。実際のクライアントに近づけるために、各 Locust タスクが重み付けされます。たとえば、クライアントの合計リクエスト 1,000 個ごとに登録が 1 回発生します。

GKE はコンテナのオーケストレーションと管理を提供します。GKE を使用すると、負荷テスト フレームワークの基盤となるコンテナノードの数を指定できます。負荷テストのワーカーをポッドに整理し、GKE が実行され続けるポッドの数を指定することもできます。

負荷テストのタスクをデプロイするには、次の手順を行います。

  1. 負荷テストのマスターをデプロイします。
  2. 負荷テストのワーカーのグループをデプロイします。この負荷テストのワーカーを使用して、テスト用の大量のトラフィックを作成できます。

次の図は、サンプル アプリケーションを使用して負荷テストを行うアーキテクチャを示しています。負荷テストの操作とモニタリングに使用されるウェブ インターフェースを提供するマスター Pod。ワーカー Pod は、テスト中のアプリケーションの REST リクエスト トラフィックを生成し、マスターに指標を送信します。

負荷テストの操作とモニタリングに使用されるウェブ インターフェースを提供するマスターポッド。テスト中のアプリケーションの REST リクエスト トラフィックを生成するワーカーポッド。

負荷テストのマスターについて

Locust マスターは、負荷テストのタスクを実行するためのエントリ ポイントです。Locust のマスター構成では、コンテナで使用される次のデフォルト ポートなど、いくつかの要素が指定されます。

  • 8089: ウェブ インターフェース用
  • 55575558: ワーカーとの通信用

この情報は、Locust ワーカーを構成するために後で使用されます。

サービスをデプロイして、クラスタ内の他のポッドが hostname:port を介して必要なポートにアクセスできるようにします。これらのポートは、わかりやすいポート名でも参照できます。

このサービスを使用すると、マスターが失敗して Deployment によって新しいポッドに置き換えられる場合でも、Locust ワーカーはマスターを簡単に検出して確実に通信できるようになります。

同じ VPC ネットワークを使用し、クラスタと同じ Google Cloud リージョンにあるクラスタ外のクライアントが Locust ウェブ アプリケーション サービスにアクセスできるようにする内部 TCP / UDP ロード バランシングを作成するために、必要なアノテーションを含む 2 番目のサービスがデプロイされます。

Locust マスターをデプロイした後、内部 TCP / UDP ロード バランシングによってプロビジョニングされたプライベート IP アドレスを使用してウェブ インターフェースを開くことができます。Locust ワーカーをデプロイした後、シミュレーションを開始して Locust ウェブ インターフェース経由で集計した統計情報を確認できます。

負荷テストのワーカーについて

Locust ワーカーは負荷テストのタスクを実行します。単一の Deployment を使って複数のポッドを作成します。ポッドは、Kubernetes クラスタ全体に分散されます。各ポッドは、環境変数を使用して、テスト対象システムのホスト名や Locust マスターのホスト名などの構成情報を制御します。

次の図は、Locust マスターと Locust ワーカー間の関係を示しています。

Locust マスターは階層の最上位に位置し、その下に複数のワーカーがある。

共通変数を初期化する

インフラストラクチャの要素がデプロイされる場所を制御する変数をいくつか定義する必要があります。

  1. Cloud Shell を開きます。

    Cloud Shell を開く

    このチュートリアルでは、Cloud Shell からすべての端末コマンドを実行します。

  2. カスタマイズが必要な環境変数を設定します。

    export GKE_CLUSTER=GKE_CLUSTER
    export AR_REPO=AR_REPO
    export REGION=REGION
    export ZONE=ZONE
    export SAMPLE_APP_LOCATION=SAMPLE_APP_LOCATION
    

    以下を置き換えます。

    • GKE_CLUSTER: GKE クラスタの名前
    • AR_REPO: Artifact Registry リポジトリの名前
    • REGION: GKE クラスタと Artifact Registry リポジトリが作成されるリージョン
    • ZONE: Compute Engine インスタンスを作成するリージョン内のゾーン
    • SAMPLE_APP_LOCATION: サンプル App Engine アプリケーションがデプロイされる(リージョン)ロケーション

    コマンドは次の例のようになります。

    export GKE_CLUSTER=gke-lt-cluster
    export AR_REPO=dist-lt-repo
    export REGION=us-central1
    export ZONE=us-central1-b
    export SAMPLE_APP_LOCATION=us-central
    
  3. 次の追加の環境変数を設定します。

    export GKE_VERSION=1.20
    export GKE_NODE_TYPE=e2-standard-4
    export GKE_SCOPE="https://www.googleapis.com/auth/cloud-platform"
    export PROJECT=$(gcloud config get-value project)
    export SAMPLE_APP_TARGET=${PROJECT}.appspot.com
    
  4. デフォルトのゾーンを設定します。これにより、以降のコマンドでこれらの値を指定する必要がなくなります。

    gcloud config set compute/zone ${ZONE}
    

GKE クラスタを作成する

  1. GKE クラスタを作成します。

    gcloud container clusters create ${GKE_CLUSTER} \
        --region ${REGION} \
        --cluster-version ${GKE_VERSION} \
        --machine-type ${GKE_NODE_TYPE} \
        --enable-autoscaling \
        --num-nodes 3 \
        --min-nodes 3 \
        --max-nodes 10 \
        --scopes "${GKE_SCOPE}"
    
  2. GKE クラスタに接続します。

    gcloud container clusters get-credentials ${GKE_CLUSTER} \
       --region ${REGION} \
       --project ${PROJECT}
    

環境を設定する

  1. GitHub からサンプル リポジトリのクローンを作成します。

    git clone https://github.com/GoogleCloudPlatform/distributed-load-testing-using-kubernetes
    
  2. 作業ディレクトリをクローニングしたリポジトリに変更します。

    cd distributed-load-testing-using-kubernetes
    

コンテナ イメージのビルド

  1. Artifact Registry リポジトリを作成する

    gcloud artifacts repositories create ${AR_REPO} \
        --repository-format=docker  \
        --location=${REGION} \
        --description="Distributed load testing with GKE and Locust"
    
  2. コンテナ イメージをビルドし、Artifact Registry リポジトリに保存します。

    export LOCUST_IMAGE_NAME=locust-tasks
    export LOCUST_IMAGE_TAG=latest
    gcloud builds submit \
        --tag ${REGION}-docker.pkg.dev/${PROJECT}/${AR_REPO}/${LOCUST_IMAGE_NAME}:${LOCUST_IMAGE_TAG} \
        docker-image
    

    付属の Locust Docker イメージには、サンプル アプリケーションの /login/metrics エンドポイントを呼び出すテストタスクが埋め込まれています。このサンプル タスクセットでは、これら 2 つのエンドポイントに送信されたリクエストのそれぞれの割合は 1999 になります。

    
    class MetricsTaskSet(TaskSet):
        _deviceid = None
    
        def on_start(self):
            self._deviceid = str(uuid.uuid4())
    
        @task(1)
        def login(self):
            self.client.post(
                '/login', {"deviceid": self._deviceid})
    
        @task(999)
        def post_metrics(self):
            self.client.post(
                "/metrics", {"deviceid": self._deviceid, "timestamp": datetime.now()})
    
    class MetricsLocust(FastHttpUser):
        tasks = {MetricsTaskSet}
    

  3. Docker イメージが Artifact Registry リポジトリにあることを確認します。

    gcloud artifacts docker images list ${REGION}-docker.pkg.dev/${PROJECT}/${AR_REPO} | \
        grep ${LOCUST_IMAGE_NAME}
    

    出力は次のようになります。

    Listing items under project PROJECT, location REGION, repository AR_REPO
    
    REGION-docker.pkg.dev/PROJECT/AR_REPO/locust-tasks  sha256:796d4be067eae7c82d41824791289045789182958913e57c0ef40e8d5ddcf283  2022-04-13T01:55:02  2022-04-13T01:55:02
    

サンプル アプリケーションをデプロイする

負荷テスト メカニズムを示すために使用されるサンプル アプリケーションは、App Engine にデプロイされます。

gcloud app create --region=${SAMPLE_APP_LOCATION}
gcloud app deploy sample-webapp/app.yaml \
  --project=${PROJECT}

出力は次のようになります。

File upload done.
Updating service [default]...done.
Setting traffic split for service [default]...done.
Deployed service [default] to [https://PROJECT.appspot.com]

サンプルの App Engine アプリケーションは、/login エンドポイントと /metrics エンドポイントを実装します。

@app.route('/login',  methods=['GET', 'POST'])
def login():
    deviceid = request.values.get('deviceid')
    return '/login - device: {}\n'.format(deviceid)

@app.route('/metrics',  methods=['GET', 'POST'])
def metrics():
    deviceid = request.values.get('deviceid')
    timestamp = request.values.get('timestamp')

    return '/metrics - device: {}, timestamp: {}\n'.format(deviceid, timestamp)

Locust のマスターポッドとワーカーポッドのデプロイ

  1. locust-master-controller.yaml ファイルと locust-worker-controller.yaml ファイルにあるターゲット ホスト、プロジェクト、イメージのパラメータの環境変数値を置き換え、Locust のマスター Deployment とワーカー Deployment を作成します。

    envsubst < kubernetes-config/locust-master-controller.yaml.tpl | kubectl apply -f -
    envsubst < kubernetes-config/locust-worker-controller.yaml.tpl | kubectl apply -f -
    envsubst < kubernetes-config/locust-master-service.yaml.tpl | kubectl apply -f -
    
  2. Locust の Deployment を確認します。

    kubectl get pods -o wide
    

    出力は次のようになります。

    NAME                             READY   STATUS    RESTARTS   AGE   IP           NODE
    locust-master-87f8ffd56-pxmsk    1/1     Running   0          1m    10.32.2.6    gke-gke-load-test-default-pool-96a3f394
    locust-worker-58879b475c-279q9   1/1     Running   0          1m    10.32.1.5    gke-gke-load-test-default-pool-96a3f394
    locust-worker-58879b475c-9frbw   1/1     Running   0          1m    10.32.2.8    gke-gke-load-test-default-pool-96a3f394
    locust-worker-58879b475c-dppmz   1/1     Running   0          1m    10.32.2.7    gke-gke-load-test-default-pool-96a3f394
    locust-worker-58879b475c-g8tzf   1/1     Running   0          1m    10.32.0.11   gke-gke-load-test-default-pool-96a3f394
    locust-worker-58879b475c-qcscq   1/1     Running   0          1m    10.32.1.4    gke-gke-load-test-default-pool-96a3f394
    
  3. サービスを確認します。

    kubectl get services
    

    出力は次のようになります。

    NAME                TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)             AGE
    kubernetes          ClusterIP      10.87.240.1     <none>        443/TCP             12m
    locust-master       ClusterIP      10.87.245.22    <none>        5557/TCP,5558/TCP    1m
    locust-master-web   LoadBalancer   10.87.246.225   <pending>     8089:31454/TCP       1m
    
  4. Locust マスターのウェブ アプリケーション サービスに内部 TCP / UDP ロード バランシング プライベート IP アドレス(GKE 外部 IP アドレス)がプロビジョニングされている間に watch ループを実行します。

    kubectl get svc locust-master-web --watch
    
  5. 外部 IP アドレスがプロビジョニングされたら、Ctrl+C を押して watch ループを終了します。

Locust のウェブ フロントエンドに接続する

Locust マスターのウェブ インターフェースを使用して、テスト対象システムに対して負荷テストのタスクを実行します。

  1. ウェブホスト サービスの内部ロードバランサの IP アドレスを取得します。

    export INTERNAL_LB_IP=$(kubectl get svc locust-master-web  \
                                   -o jsonpath="{.status.loadBalancer.ingress[0].ip}") && \
                                   echo $INTERNAL_LB_IP
    
  2. ネットワーク構成に応じて、プロビジョニングされた IP アドレスを使用して Locust ウェブ アプリケーションに接続する方法が 2 つあります。

    • ネットワーク ルーティング。ワークステーションからプロジェクト VPC ネットワークへのルーティングを許可するようにネットワークが構成されている場合、ワークステーションから内部 TCP / UDP ロード バランシング IP アドレスに直接アクセスできます。

    • プロキシと SSH トンネル。ワークステーションと VPC ネットワークの間にネットワーク ルートがない場合、nginx プロキシ、およびワークステーションとインスタンス間の SSH トンネルを使用して Compute Engine インスタンスを作成することで、内部 TCP / UDP ロード バランシング IP アドレスにトラフィックをルーティングできます。

ネットワーク ルーティング

ワークステーションと Google Cloud プロジェクトの VPC ネットワークの間にネットワーク トラフィックのルートがある場合は、ブラウザを開き、Locust マスターのウェブ インターフェースを開きます。次の URL の [INTERNAL_LB_IP] は、前の手順で確認した IP アドレスに置き換えます。http://[INTERNAL_LB_IP]:8089

プロキシと SSH トンネル。

  1. インスタンスの名前を使用して環境変数を設定します。

    export PROXY_VM=locust-nginx-proxy
    
  2. 内部 TCP / UDP ロード バランシングの Locust ウェブ アプリケーション ポート 8089 に対してプロキシ設定するように構成された ngnix Docker コンテナを含むインスタンスを起動します。

    gcloud compute instances create-with-container ${PROXY_VM} \
       --zone ${ZONE} \
       --container-image gcr.io/cloud-marketplace/google/nginx1:latest \
       --container-mount-host-path=host-path=/tmp/server.conf,mount-path=/etc/nginx/conf.d/default.conf \
       --metadata=startup-script="#! /bin/bash
         cat <<EOF  > /tmp/server.conf
         server {
             listen 8089;
             location / {
                 proxy_pass http://${INTERNAL_LB_IP}:8089;
             }
         }
    EOF"
    
  3. Cloud Shell からプロキシ インスタンスへの SSH トンネルを開きます。

    gcloud compute ssh --zone ${ZONE} ${PROXY_VM} \
                     -- -N -L 8089:localhost:8089
    
  4. ウェブでプレビュー アイコン(Cloud Shell のウェブでプレビュー アイコン)をクリックし、表示されたオプションから [ポートを変更] を選択します。

  5. [プレビュー ポートの変更] ダイアログで、[ポート番号] フィールドに「8089」と入力し、[変更してプレビュー] を選択します。

    しばらくすると、Locust ウェブ インターフェースがブラウザタブで開きます。

サンプル アプリケーションで基本的な負荷テストを実行する

  1. ブラウザで Locust のフロントエンドを開くと、新しい負荷テストを開始するために使用できるダイアログが表示されます。

    Locust マスターのウェブ インターフェースには、新しく swarm を開始し、ユーザーの数と生成速度を指定するためのダイアログがある。

  2. Number of users(ピーク時の同時実行)に合計ユーザー数として 10 を指定し、Spawn rate(ユーザーを生成する速度 / 秒)に 1 秒あたりに生成するユーザー数として 5 を指定します。

  3. [Start swarming] をクリックしてシミュレーションを開始します。

    リクエストによりスワーミングが開始されると、次の画像に示すように [Statistics] でリクエスト数や 1 秒あたりのリクエスト数などのシミュレーション指標の集計が始まります。

    Locust ウェブ インターフェースに統計情報の集計が開始されたことが示される。
  4. デプロイされたサービスやその他の指標を Google Cloud Console から表示します。

    App Engine ダッシュボードに、1 時間あたりのリクエスト数が種類別にグラフで表示される。
  5. テスト対象のアプリケーションの動作を確認したら、[Stop] をクリックしてテストを終了します。

ユーザー数をスケールアップする(省略可)

アプリケーションの負荷を増加してテストしたい場合は、シミュレーションするユーザー数を追加できます。シミュレーションするユーザー数を追加する前に、負荷の増加に対応できる十分なリソースがあることを確認してください。Google Cloud では、ポッドの数を増やすための基盤となる VM リソースがあれば、既存のポッドを再デプロイすることなく、Locust ワーカーポッドをデプロイに追加できます。最初の GKE クラスタは 3 ノードから始まり、最大 10 ノードまで自動スケーリングできます。

  • Locust ワーカーポッドのプールを 20 にスケールします。

    kubectl scale deployment/locust-worker --replicas=20
    

    新しいポッドのデプロイと起動には数分かかります。

Pod Unschedulable エラーが表示された場合は、クラスタにさらにロールを追加する必要があります。詳しくは、GKE クラスタのサイズ変更を参照してください。

ポッドが起動したら、Locust マスターのウェブ インターフェースに戻り、負荷テストを再開します。

パターンを拡張する

このパターンを拡張するために、新しい Locust タスクを作成することや、別の負荷テスト フレームワークに切り替えることができます。

収集する指標はカスタマイズできます。たとえば、1 秒あたりのリクエスト数の測定、負荷の増加に伴うレスポンスのレイテンシのモニタリング、レスポンスの失敗率とエラーのタイプの確認などが可能です。

詳細については、Cloud Monitoring のドキュメントをご覧ください。

クリーンアップ

チュートリアルが終了したら、GCP で作成したリソースについて料金が発生しないようにクリーンアップできます。

プロジェクトの削除

課金をなくす最も簡単な方法は、チュートリアル用に作成したプロジェクトを削除することです。

プロジェクトを削除するには:

  1. コンソールで [リソースの管理] ページに移動します。

    [リソースの管理] に移動

  2. プロジェクト リストで、削除するプロジェクトを選択し、[削除] をクリックします。
  3. ダイアログでプロジェクト ID を入力し、[シャットダウン] をクリックしてプロジェクトを削除します。

GKE クラスタの削除

プロジェクト全体を削除したくない場合は、次のコマンドを実行して GKE クラスタを削除します。

   gcloud container clusters delete ${GKE_CLUSTER} --region ${REGION}
   

次のステップ