はじめに
こんにちは、株式会社エモーションテック SREの岡崎です。今回はNew Relicと負荷試験ツールk6とGoogle Cloud のCloud Buildを組み合わせて負荷試験環境を構築した話についてご紹介します。
※この記事は New Relic Advent Calendar 2022の3日目およびエモーションテック Advent Calendar 2022 4日目の記事になります。
負荷試験における課題
まず、負荷試験を実施する際の課題として以下が気になっていました。
- 結果の評価に工数が掛りがち。例えば:
- 負荷をかける対象の環境についてサーバからデータベースまでの各種リソースを確認する必要がある
- エラーの発生有無も確認する必要がある
- 負荷発生サーバのリソース状況についても確認する必要がある(スペックが足りており想定通り負荷を掛けられているかをみるため)
- 結果の管理が大変。 時間が経つと、試験レポートやその時のメトリクスがどこに行ったかがわからなくなる
課題に対し、以下のように考えました。
結果の評価が大変。
⇨負荷試験結果(リクエスト送信数等の実績)、負荷発生サーバのメトリクス、試験対象環境のメトリクスを横断で確認できるようにしたい。 New Relicを利用すれば各種インフラのメトリクスからアプリケーションのMELT(Metrics, Events, Logs, Traces)まで手軽に確認できるのではないか。 負荷試験結果は、負荷試験ツールから何らかの仕組みで自動保存できるようにし、New Relicのメトリクスとセットで確認できれば良いのではないか。
結果の管理が大変。
⇨New Relicにメトリクスが集約されていれば、試験日時の記録があれば後からでも状況を振り返ることができるのではないか。
課題への対応として、以下の構成を組んでみましたのでご紹介します。
構成の紹介
以下を用いて負荷発生と試験時の各種情報収集を行う仕組みを構築しました。 - New Relic - 負荷試験ツール k6 - Google CloudのCloud Build
簡単な構成イメージです。
負荷試験ツールとしてk6を選定した理由は、弊社ブログのモノリスからマイクロサービスへ k6 導入準備中を参照ください。 ここでの決め手はNew Relicに試験時のメトリクスを簡単に送信できる点でした。
Cloud Buildを採用した理由は以下になります。
- 最小構成では細かいインフラの構築が不要で設定ファイルとgcloudコマンドだけで設定可能
- docker-composeを動かすコミュニティビルダーが提供されており、New Relic agentをバックグラウンドで動かしつつk6を実行することができる
実際に動かしてみた様子の紹介
k6の実行結果
k6の実行結果例です。
Cloud Buildのビルド履歴より結果のサマリーを確認可能です。 k6の標準出力をそのまま閲覧することができます。 (必要に応じてCloud Buildの処理の中でレポートをファイル出力し、Google Cloud Storageなど任意の場所にアップロードすることも可能です。)
New Relic
k6のVUs (Virtual Users)の推移や成功リクエスト数などを確認することが可能です。
Hosts画面からは、k6を稼働させたCloud Buildのサーバのリソースメトリクスを確認できます。 もしCPU使用率が高すぎるような場合はビルドサーバのサイズ変更をするといった判断が可能です。
Dashboardを作成してk6や試験対象環境のサーバやDBのリソースを一覧することもできます。
New Relicを活用することにより、以下を実現できます。
構築の仕方とコード紹介
Cloud Buildでk6を動かし、New Relicへメトリクスを送信する方法を示します。
準備
New Relicのメトリクスを収集するために以下のDockerイメージを利用します。
- newrelic/nri-statsd StatsD形式のデータをNew Relicに転送することが可能。 k6はデータをStatsD形式で出力できるので、こちらを経由してNew Relicにデータを送信する。
- newrelic/infrastructure New Relicのinfrastrucure agentで、負荷発生サーバ( Cloud Buildの仮想マシン) のCPU使用率等を取得するために利用する。
k6とNew Relicは両方ともDockerイメージとしても提供されているので、docker-composeで動かすことにします。 Cloud Buildでdocker-compose を使うには、「コミュニティ提供のビルダーとカスタム ビルダー」のdocker-composeが必要なので、ビルドして自身のGoogle Cloud ProjectのContainer Registryに保存します。 ただ、やることは以下のコマンドにてgit cloneとgcloudコマンドを実行するだけです。
git clone https://github.com/GoogleCloudPlatform/cloud-builders-community cd cloud-builders-community/docker-compose gcloud builds submit --substitutions=_DOCKER_COMPOSE_VERSION="2.12.2"
次にビルド時の処理を記述するcloudbuild.yamlを書きます。
例えば、hello worldと表示させるにはcloudbuild.yamlを以下のように記述します。
steps: - name: "ubuntu" args: ["bash", "-c", "echo hello world"]
以下のコマンドを実行します。
gcloud builds submit --region asia-northeast1 --config=cloudbuild.yaml .
ここまででCloud Buildで処理を動かすことができます。
New Relicを起動したあとにk6を実行するにはcloudbuild.yamlを以下のように記述します。
steps: - name: 'gcr.io/$PROJECT_ID/docker-compose' args: [ 'up', '-d', 'nr-statsd', 'nr-infra'] - name: 'gcr.io/$PROJECT_ID/docker-compose' args: ['run', '-e', 'RPS=$_RPS', '-e', 'VUS=$_VUS', '-e', 'DURATION=$_DURATION', 'k6', 'run', $_SCHENARIO_FILE ] # 最大 86400s timeout: 3600s
gcloud builds submit コマンドは--substitutionsオプションで変数を渡せます。 k6のシナリオファイル名やシナリオの実行時間などのパラメータを指定できるようにするには、以下のようにします。
コマンド例:
gcloud builds submit --region asia-northeast1 --config=cloudbuild.yaml . \ --substitutions _SCHENARIO_FILE=/scripts/test_health_check.js,_RPS=3,_VUS=1,_DURATION=120s
docker-compose.ymlを用意します。 以下のように記述して new relic statsd、new relic infra agent, k6を起動できるようにします。
services: k6: image: grafana/k6:latest ports: - "6565:6565" environment: - K6_OUT=statsd - K6_STATSD_ENABLE_TAGS=true - K6_STATSD_ADDR=nr-statsd:8125 volumes: - ./scripts:/scripts nr-statsd: image: newrelic/nri-statsd:latest ports: - "8125:8125/udp" environment: - NR_ACCOUNT_ID=<New RelicのアカウントIDを記述> - NR_API_KEY=<New RelicのAPIキーを記述> # see: # https://docs.newrelic.com/docs/infrastructure/install-infrastructure-agent/linux-installation/docker-container-infrastructure-monitoring/ nr-infra: environment: - NRIA_DISPLAY_NAME=cloudbuild-loadtest-attacker #好きな名前を指定 - NRIA_LICENSE_KEY=<New RelicのAPIキーを記述> image: newrelic/infrastructure:latest cap_add: - SYS_PTRACE network_mode: host pid: host privileged: true volumes: - /:/host:ro - /var/run/docker.sock:/var/run/docker.sock restart: unless-stopped
k6の実行
試しにk6で負荷を発生させてみます。
k6のシナリオファイルを以下の様に記述してみます。
import http from 'k6/http'; import { check, sleep } from 'k6'; export const options = { vus: `${__ENV.VUS}`, rps: `${__ENV.RPS}`, duration: `${__ENV.DURATION}` }; const res = http.get(`<リクエスト先のURLを記述>`); check(res, { 'status was 200': (r) => r.status === 200, }); }
Cloud Buildを実行します。
コマンド例:
gcloud builds submit --region asia-northeast1 --config=cloudbuild.yaml . \ --substitutions _SCHENARIO_FILE=/scripts/test_health_check.js,_RPS=3,_VUS=1,_DURATION=120s
コマンドを実行すると、そのままコンソール上にCloud Buildの仮想マシンやdocker-composeのセットアップ処理の様子が表示され、k6が実行されます。
コンソール表示の様子抜粋
...(略)... Step #2: Step #2: /\ |‾‾| /‾‾/ /‾‾/ Step #2: /\ / \ | |/ / / / Step #2: / \/ \ | ( / ‾‾\ Step #2: / \ | |\ \ | (‾) | Step #2: / __________ \ |__| \__\ \_____/ .io Step #2: Step #2: execution: local Step #2: script: /scripts/test_health_check.js Step #2: output: statsd (nr-statsd:8125) Step #2: scenarios: (100.00%) 1 scenario, 1 max VUs, 2m30s max duration (incl. graceful stop): Step #2: * default: 1 looping VUs for 2m0s (gracefulStop: 30s) Step #2: Step #2: Step #2: running (0m01.0s), 1/1 VUs, 0 complete and 0 interrupted iterations Step #2: default [ 1% ] 1 VUs 0m01.0s/2m0s Step #2: Step #2: running (0m02.0s), 1/1 VUs, 1 complete and 0 interrupted iterations Step #2: default [ 2% ] 1 VUs 0m02.0s/2m0s ...(略)... ---------------------------------------------------------------------- ID CREATE_TIME DURATION SOURCE IMAGES STATUS 1bd7e16f-9a2a-4419-b01d-b6f5bcd6ff72 2022-11-22T00:50:38+00:00 3M gs://your-project_cloudbuild/source/1669078223.977698-e733317c91b61ef0.tgz - SUCCESS
実行が完了したら、Cloud Buildの画面を見てみます。
k6はデフォルトで実行結果を標準出力に表示してくれるため、Cloud Buildのログ画面からも結果を見ることができます。 k6はtext, json, htmlなどさまざまなフォーマットでレポートをファイル出力できるため、ファイル出力を行いGCSにアップロードして閲覧できるようにしてもよいかもしれません。
New Relicの画面を見てみます。 冒頭で紹介したとおり、
- k6実行に関するメトリクスを確認できます
- infrastructure Host画面から攻撃サーバのリソース状況を確認できます
- 攻撃対象の環境、アプリケーションのMELTを見れます
以上が簡単な負荷試験を実行する流れです。 最低限必要なものは以下になります。
- Cloud Buildのコミュニティビルダーdocker-composeをビルドしてContainer Registryに登録
- cloudbuild.yaml
- docker-compose.yaml
- k6のシナリオファイル
Cloud Buildは細かいインフラの構築が不要で設定ファイルとgcloudコマンドだけで動かせます。 New Relicの方も各種メトリクスを見るためにはdocker-composeでAPIキーを設定するくらいで実現することができます。
docker-composeを利用しているので軽く動作確認をしたい場合はローカル環境でも実行可能です。
実運用でやりたいこと
実運用を想定して以下の対応について考えてみました。
- 負荷をかけた時間帯をNew Relicから確認できるようにする
- 定期的にシナリオを実行する
- 試験で不測の事態があればアラートをあげる
負荷をかけた時間帯をNew Relicから確認できるようにする
Deployment marker機能が利用できます。 これはNew Relic上に登録したアプリケーションに対し、デプロイ等が行われた時刻やアプリケーションの変更内容を記録して、グラフ上で参照できるようにする機能です。 デプロイ以外の任意の情報を付加できるので、k6を実行した際のCloud buildの実行IDやシナリオファイル名を送ることも可能です。 Deployment markerはcurlでNew Relicのエンドポイントにデータを送ることができます。
cloudbuild.yamlでDeployment markerを登録するためには以下のように記述します。
# New Relic Deployment markerの登録 - name: gcr.io/cloud-builders/curl args: [ "-X", "POST", "https://api.newrelic.com/v2/applications/<New Relic上のアプリケーションID>/deployments.json", "-H", "Api-Key: <New RelicのAPIキー>", "-i", "-H", "Content-Type: application/json", "-d", "{ \"deployment\": { \"revision\": \"cloudbuild: $BUILD_ID\", \"changelog\": \"$_SCHENARIO_FILE\", \"description\": \"run k6 test: $_SCHENARIO_FILE\", \"user\": \"<New Relicに登録されたユーザのメールアドレス>\" } }" ]
こちらの例では cloud buildで自動的に採番されるBUILD_IDや、--substitutions コマンドで渡したシナリオファイル名を送っています。
Cloud Buildを実行すると、APM画面でActivity streamというところに、curlで送信した情報が表示されます。 また、APMの各種グラフに、点線のバーが表示され、どの時刻でk6が実行されたかが分かるようになります。
Activity stream上の情報をクリックすると、詳細をみることができます。 curlで送信したシナリオファイル名だったり、実行前後のスループットやCPU使用率のような情報を閲覧することができます。
定期的にシナリオを実行する
gcloud builds submitコマンドを実行する環境と記事の前半で触れた各種設定ファイルを取得可能な環境があれば、 自動実行を実現可能です。
例えば以下のような構成を組むことができます。
試験で不測の事態があればアラートをあげる
負荷をかける側のサーバのスペックが足りなくて、思った様に負荷をかけられないという苦い思いをすることもあるかと思いますが、New Relicのinfra agentを入れることで、cpu使用率やメモリ使用率を監視し、アラートをあげることができます。
ドキュメント:New Relic: Introduction to alerts
おわりに
k6、New Relic、Cloud Buildで負荷試験中の環境の状況を可視化する方法をご紹介しました。 商用環境のObservability確保とともに開発工程についてもObservability確保のための取り組みをすることで、よりよいプロダクトを提供していけたらと考えます。
エモーションテックでは顧客体験、従業員体験の改善をサポートし、世の中の体験を変えるプロダクトを開発しています。 ぜひこの記事や他の記事を見て少しでも弊社に興味をもっていただけましたら、カジュアル面談も行っていますのでご応募お待ちしております。