Amazon Web Services ブログ

Locust を用いた Amazon EKS 上で動作するワークロードの負荷テスト

 序章

多くの AWS 利用者は、自身のワークロードを実行するために Amazon Elastic Kubernetes Service ( Amazon EKS ) を利用しています。そのため、EKS クラスターをテストするプロセスを用意して、ワークロードを公開する前に弱点を特定し、クラスターを最適化することが不可欠です。負荷テストは、実世界のトラフィックを模倣する人工的な負荷を発生させることによって、ワークロードの性能や信頼性に焦点を当てます。特に、EKS の高い弾力性を期待する場合には有効です。 Locust は、リアルタイムダッシュボード 及び プログラマブルなテストシナリオを備えたオープンソースの負荷試験ツールです。

このブログでは、2 つの Amazon EKS クラスターを構築する手順を説明します。片方の Amazon EKS では Locust を用いて負荷を発生させ、もう片方でサンプルのワークロードを実行します。このブログの内容は、Amazon EKS の性能 及び 信頼性をテストする際の任意の状況に適用できます。

このブログで利用する全てのソースコードとリソースは、こちらの GitHub リポジトリにあります。

ソリューションの概要

下記の図は、このブログ全体で利用するアーキテクチャを示しています。アプリケーションは、EKS クラスター ( ターゲットクラスター ) 内の ポッドグループで実行され、Application Load Balancer を通してパブリックに公開されています。その間、Locust は別の EKS クラスター ( Locust クラスター ) で動作します。今回、ノード数とポッド数をそれぞれスケールアウトさせる必要があるため、Cluster Autoscaler ( CA ) 及び Horizonal Pod Autoscaler ( HPA ) は、両方のクラスターで設定されます。

加えて、Application Load Balancer を作成し、Ingress を介して Kubernetes サービスへ接続するために、AWS Load Balancer Controller は両方のクラスターにインストールします。

注意:今回用意するリソースにかかる料金は、AWS の無料枠でカバーできません。

下準備

本手順では、2 つの EKS クラスターが必要です。負荷を発生させる Locust クラスター と テスト対象のワークロードを動作させる もう一方のクラスターです。下記のガイドを使って、クラスターをセットアップします。

前提条件

CLIツールのインストールと設定

git clone https://github.com/aws-samples/Load-testing-your-workload-running-on-Amazon-EKS-with-Locust.git

既にクラスターの用意があるならば、これらの手順をスキップできます。

VPC を 2つ作成可能か確認するために、AWS アカウントのリミットをチェックしてください。もし、当該 AWS アカウントでクォータ以上の数の VPC を作成しようとした場合、クラスターの作成は失敗します。

AWS コンソールで結果を確認してください。

ベーシックアドオンチャートのインストール

EKS で負荷テストをするために、2, 3 の Kubernetes アドオンを追加します。これらのアドオンは、YAML/JSON で記載された Kubernetes マニフェストファイル または Helm チャートを介してインストールできます。今回は、管理が簡単でよく利用される Helm チャートを使います。awsblog-loadtest-locustawsblog-loadtest-workload ( または、他のターゲットクラスター ) の準備ができているならば、下記のチャートをインストールするために、こちらを参照してください。

  • メトリクスサーバーのデプロイ ( YAML )
  • HPA のインストール手順のスキップ
  • Cluster Autoscaler のインストール ( チャート )
  • AWS Load Balancer Controller のインストール ( チャート)
  • Container Insights のインストール ( YAML )

Helm チャートを使ったサンプルアプリケーションのインストール

下準備の最後の手順として、テスト用のサンプルアプリケーションをデプロイする必要があります。これにも Helm を利用します。 ( テスト用のアプリケーションが既に存在するならば、この手順はスキップし、自身のアプリケーションを利用できます )。

サンプルアプリケーションが正常にインストールされると、Helm チャートによって作成された Ingress に関連づけられた Application Load Balancer の DNS 名にアクセスすることで、サンプルアプリケーションからのウェルカムメッセージを確認できます。

ウォークスルー

Kubernetes の準備が整いました。しかし、アプリケーションのテストをする前に設定することが まだいくつか残っています。

ステップ 1  Locust のインストール

Locust クラスターに対してコマンドを発行するため、Kubernetes のコンテキストを切り替えてください。

前のセクションで、ワークロードクラスターにサンプルアプリケーションをインストールしました。アプリケーションをテストするためワークロードクラスターから Locust クラスターへコンテキストを切り替える必要があります。

# Unset context - Optional
kubectl config unset current-context

# Set context of locust cluster
export LOCUST_CLUSTER_NAME="awsblog-loadtest-locust"
export LOCUST_CONTEXT=$(kubectl config get-contexts | grep "${LOCUST_CLUSTER_NAME}" | awk -F" " '{print $1}')
kubectl config use-context ${LOCUST_CONTEXT}

# Check
kubectl config current-context

Delivery Hero 公開チャートリポジトリの追加

# Helm chart repo setting
helm repo add deliveryhero "https://charts.deliveryhero.io/"
helm repo update deliveryhero

# Check
helm repo list | egrep "NAME|deliveryhero"

locustfile.py の作成

下記コードのファイルを作成し、locustfile.pyとして保存してください。テスト先のエンドポイントを変更したい場合は、下記ファイルの “/” から始まる行を変更してください ( 下準備でデプロイした、 /load-gen/loop?count=50&range=1000 のように )。locustfile.py の書き方詳細は、こちらを参照ください。

# Execute in root of repository, and maybe this file has existed.
cat <<EOF > locustfile.py && cat locustfile.py
from locust import HttpUser, task, between
default_headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36'}
class WebsiteUser(HttpUser):
    wait_time = between(1, 5)
    @task(1)
    def get_index(self):
        self.client.get("/", headers=default_headers)
EOF

locustfile.py を用いた Locust のインストールと設定

下記のコマンドを実行すると、前のセクションで作成した locustfile.py を使って Locust をインストールできます。この時、HPA は有効化されており、5 つのポッドで Locust が起動します。これは、負荷量の上昇に伴って自動的にスケールする負荷テストの良い開始ポイント提供します。

# Create ConfigMap 'eks-loadtest-locustfile' from the locustfile.py created above
kubectl create configmap eks-loadtest-locustfile --from-file ./locustfile.py

# Install Locust Helm Chart
helm upgrade --install eks-loadtest-locust deliveryhero/locust \
 --set loadtest.name=eks-loadtest \
 --set loadtest.locust_locustfile_configmap=eks-loadtest-locustfile \
 --set loadtest.locust_locustfile=locustfile.py \
 --set worker.hpa.enabled=true \
 --set worker.hpa.minReplicas=5

ステップ 2 ALB を介して Locust を公開

Locust のインストールが成功した後、Locust のダッシュボードを公開するために Ingress を作成すると、ブラウザからアクセスできます。

# Move to the root of directory
# Create Ingress using ALB
kubectl apply -f walkthrough/alb-ingress.yaml

ステップ 3 Locust のダッシュボードのチェック

ステップ 2 で Ingress を作成した後、下記のコマンドを実行することで Application Load Balancer の URL を取得できます。おそらく、Application Load Balancer のエンドポイントを取得するために、少し時間 ( 2 分程度 ) が必要です。

# Check
kubectl get ingress ingress-locust-dashboard

# Copy the exposed hostname to clipboard (only mac osx)
kubectl get ingress ingress-locust-dashboard -o jsonpath='{.status.loadBalancer.ingress[0].hostname}' | pbcopy

AWS コンソールからも同様の情報を得ることができます。Amazon EC2 のコンソール内のロードバランサーのページに移動し、目的のロードバランサーを選択します。URL は、“Description” タブに “DNS name” として記載があります。

ブラウザからロードバランサーの URL にアクセスします。

もしくは、Locust サービスをローカルポートにポートフォワーディングすることで、Ingress リソースを作成せずに Locust のダッシュボードにアクセスすることもできます。

ブラウザで http://localhost:8089 へ接続してください。

# Port forwarding from local to locust-service resource
kubectl port-forward svc/locust 8089:8089

ステップ 4 テストを実行

先程デプロイしたアプリケーションの URL を入力しましょう。“Number of total users to simulate” に 100 を、“Spawn rate” に 1 を入力しましょう。さらに、先程作成したワークロードクラスターの URL エンドポイントを “Host” に入力します。

数分間実行しましょう。実行中に Statistics タブと Charts タブを切り替えて、どのようにテストが展開されているか確認しましょう。

statistics タブでは、負荷テストの結果のサマリーを確認できます。また、上の図の赤のライン Failure/s に目立ったスパイクが発生していない点は重要です。

ワークロードクラスターのベーシックメトリクスの断片を確認するために、CloudWatch Container Insights ダッシュボードを参照します。さらに、Prometheus/Grafana ダッシュボードをセットアップすることで EKS コントロールプレーンを含む様々なメトリクスを可視化することができます。EKS Kubernetes コントロールプレーンのパフォーマンスを監視する方法について、こちらのベストプラクティスガイドを参照してください。

EKS で実行されているターゲットワークロードは、100 ユーザからのリクエストを妥当な応答時間以内で処理していることが負荷試験の結果から分かります。また、CloudWatch Container Insights でクラスターのパフォーマンスメトリクスの詳細を見ることができます。

ここで、もう少しだけ負荷を与えます。負荷テストを止め、ユーザ数を増やします。“Number of total users to simulate” の値を 1,000 に、“Spawn rate” の値を 10 にしましょう。そして先ほどのグラフと比べてみましょう。

前回の負荷テストと同様のグラフになりました。ワークロードクラスターは、これらの負荷をカバーできるようです。

ステップ 5 セカンドテスト

ここで、ユーザー数を 100 万人にして、負荷試験時間を長くしてみましょう。“Number of total users to simulate” の値を 1,000,000 に、“Spawn rate” の値を 100 にしましょう。ワークロードクラスター 及び Locust クラスターはこのトラフィックをカバーするための十分なリソースがあります。グラフは、緩やかな右肩上がりとなっています。クラスターのインスタンスが小さかったり、Locust の CPU 負荷が高かったりすると、ノードを追加するために、Cluster Autoscaler が実行されます。グラフにそれが反映されています。

負荷が高くクラスターがスケールを必要とするとき、レスポンスタイムが遅くなり、50X HTTP レスポンスが返答されることもあります。なので、応答性と耐障害性を高めるため、クラスターに対していくつかの手法が必要です。自身で負荷テストをする際の役立つ Tips をここにいくつか記載します。

  • 新たにスケールしたポッドの待機時間を最小化するために、最適なポッドの Readiness probe を見つけましょう。
  • 許容時間内に Autoscaler によってスケールアウトするようにワークロードに合わせ HPA/CA の設定の微調整しましょう。
  • HPA の応答時間 + CA の応答時間 + ノードのプロビジョニングに時間がかかり、ノードのプロビジョニングには通常 最も時間がかかります。それゆえ、Amazon EKS optimized Amazon Linux AMIs をベースとしたより軽量な AMI を利用することや、より高い集積性を確保するために、コンテナのリソースに制限をかけることが有効です。
  • ボトルネックがあるかどうか確認するため、スケールしたポッドのライフサイクルを確認しましょう。例えば、メトリクスの抽出遅延、ポッドのスケールアウトのための HPA のトリガー、コンテナイメージのダウンロード、アプリケーションの読み込み時間、 Readiness probe などです。
  • オーバープロビジョニングする際は、負の優先度がついた一時的なポッドを採用し、クラスターに十分なリソースを確保しましょう。これにより、ノードのプロビジョニング時間が劇的に削減できます。なぜなら、スケールしたポッドがホストできるノードが既に存在するからです。
  • 負荷テスト中にスケールダウンが発生するなら、それを止めましょう。スケールダウンによるノードの排除についての詳細は、こちらを参照ください。
  • パフォーマンスとコストのトレードオフを理解しましょう。クラスターに最適なノードタイプ 及び ノード数、スポットインスタンスの割合、オートスケーリングの閾値 及び アルゴリズム等を見つけ、それに合わせるために、定期的に負荷テストをしましょう。
  • EKS におけるオートスケーリングについての詳細は、こちらを参照ください。

クリーンアップ

負荷テストが完了したら、テストで利用したリソースを削除します。

 Setting to clean up target EKS cluster: Workload / Locust

# Set optional environment variables
export AWS_PROFILE="YOUR_PROFILE" # If not, use 'default' profile
export AWS_REGION="YOUR_REGION"   # ex. ap-northeast-2

# Set common environment variables
export TARGET_GROUP_NAME="workload"
export TARGET_CLUSTER_NAME="awsblog-loadtest-${TARGET_GROUP_NAME}"

# Switch Context to executes commands on the target cluster: Workload / Locust
# Unset context
kubectl config unset current-context
# Set context of workload cluster
export TARGET_CONTEXT=$(kubectl config get-contexts | grep "${TARGET_CLUSTER_NAME}" | awk -F" " '{print $1}')
kubectl config use-context ${TARGET_CONTEXT}

# Check if current context set to run on workload cluster
kubectl config current-context
# Clean up target EKS cluster: Workload / Locust

# Uninstall Helm Charts
helm list -A |grep -v NAME |awk -F" " '{print "helm -n "$2" uninstall "$1}' |xargs -I {} sh -c '{}'

# Check Empty Helm List
helm list -A

# Delete IRSA setting (must run before delete the cluster!)
eksctl delete iamserviceaccount --cluster "${TARGET_CLUSTER_NAME}" \
  --namespace "kube-system" --name "${CA:-cluster-autoscaler}"
eksctl delete iamserviceaccount --cluster "${TARGET_CLUSTER_NAME}" \
  --namespace "kube-system" --name "${AWS_LB_CNTL:-aws-load-balancer-controller}"

# Delete target EKS cluster: Workload / Locust
eksctl delete cluster --name="${TARGET_CLUSTER_NAME}"

完了です。注意点として、上記のスクリプトは、awsblog-loadtest-locust クラスターを削除するだけです。TARGET_GROUP_NAME に workload をセットして、awsblog-loadtest-workload クラスターを削除するために、必ず上記のプロセスを再度繰り返してください。

さいごに

このブログでは、EKS 上で実行されたワークロードの負荷テストをするために、2 つの EKS クラスターとツールをセットアップしました。Cluster Autoscaler が動作していると、予想通り、Locust を用いて発生させた高い負荷はスムーズに処理されたことが分かりました。Locust のダッシュボード 及び CloudWatch Container Insights は、クラスターが耐えられる最大の負荷を特定するのに役立ちます。

実環境に合わせた様々なテストシナリオに沿ってlocustfile.py をカスタマイズすることで、さらに負荷試験を実施できます。次のブログでは、EKS クラスターが負荷テスト時の高負荷やスパイクした負荷に対処する様々なテクニックを紹介しようと思います。

翻訳はソリューションアーキテクト祖父江が担当しました。原文はこちらです。