
EC2 で実行した k6 負荷テスト結果を Grafana OSS でリアルタイム可視化してみた
製造ビジネステクノロジー部の小林です。
最近、EC2 上で k6 を使用して負荷テストを実施しています。実行中に「今の VU 数はどれくらい?」「レイテンシーは問題ない?」とリアルタイムで確認したくなることがありませんか?
Grafana OSS(無料) を使えばダッシュボードでリアルタイム可視化できます。ただし、今回の環境では EC2 への接続が EC2 Instance Connect のみで、SSH 用のポートは開放できないという制約がありました。
この記事では、そんな制約のある環境でも k6 の負荷テスト結果を Grafana OSS で可視化する方法を解説します。pem キーなしの EC2 Instance Connect 環境でも大丈夫です!

前提条件
- k6 実行環境: Graviton EC2
- OS: Amazon Linux 2023(ARM)
- テスト対象:REST API(API Gateway + Lambda + DynamoDB)
- Lambda のタイムアウト:30 秒
- AWS クレデンシャル情報: aws-vault
以前、下記の記事で作成した環境を利用します。
利用ツール
- k6: 負荷テストツール
- InfluxDB: テスト結果(時系列データ)を保存
- Grafana OSS: データを可視化
Grafana OSS とは?
Grafana OSS(Open Source Software)は、オープンソースのデータ可視化・監視プラットフォームです。様々なデータソースからメトリクスを収集し、リアルタイムでダッシュボード表示できます。
特徴
- 豊富なデータソース対応: InfluxDB、Prometheus、MySQL、PostgreSQL、CloudWatch など 150 以上
- カスタマイズ可能なダッシュボード: ドラッグ&ドロップで直感的に作成
- アラート機能: 閾値を超えた際に通知
- 無料で利用可能: OSS 版は完全無料
- 活発なコミュニティ: 公式・コミュニティ製のダッシュボードテンプレートが豊富
Grafana のエディション比較
| エディション | 特徴 |
|---|---|
| Grafana OSS | 無料、セルフホスト、基本機能 |
| Grafana Enterprise | 有料、追加機能・サポートあり |
| Grafana Cloud | マネージドサービス、無料枠あり |
InfluxDB とは?
InfluxDB は時系列データに特化したオープンソースのデータベースです。
特徴
- 時系列データ(メトリクス、センサーデータ、ログなど)の保存
- 高速な書き込み・読み取り性能
- k6 が公式サポート(標準で連携可能)
- 完全無料で利用可能
k6 での選択肢
k6 の負荷テスト結果を保存する方法はいくつかあります。
| データストア | 特徴 |
|---|---|
| InfluxDB | k6 公式サポート、設定簡単、公式ダッシュボードあり |
| Prometheus | 汎用性高い、他の監視にも使える |
| JSON/CSV | 最もシンプル |
データフロー
k6(負荷テスト実行)
↓ --out influxdb=http://localhost:8086/k6
InfluxDB(データ保存)
↓ Data Source設定
Grafana(可視化)
InfluxDB 1.8 系を使う理由
1.8 系はセットアップが少なく、本記事のユースケースに適していました。2.x 系との詳細な比較は公式ドキュメントをご参照ください。
Grafana OSS だけじゃダメ?
Grafana は可視化ツールであって、データを保存する機能はありません。つまり、
グラフを描く = Grafana
データを保存する = InfluxDB(または他の DB)
という役割になります。
やってみた
InfluxDB のインストール
EC2 に Instance Connect でログインし、下記のコマンドを実行します。
# ダウンロード&解凍
wget https://dl.influxdata.com/influxdb/releases/influxdb-1.8.10_linux_amd64.tar.gz
tar xvfz influxdb-1.8.10_linux_amd64.tar.gz
# バックグラウンドで起動
cd influxdb-1.8.10-1/usr/bin
./influxd &
実行すると、以下のように influxdb が起動します。
ec2-user@ip-10-0-2-7 ~]$ cd influxdb-1.8.10-1/usr/bin
[ec2-user@ip-10-0-2-7 bin]$ cd influxdb-1.8.10-1/usr/bin
-bash: cd: influxdb-1.8.10-1/usr/bin: No such file or directory
[ec2-user@ip-10-0-2-7 bin]$ ./influxd &
[1] 30415
[ec2-user@ip-10-0-2-7 bin]$
8888888 .d888 888 8888888b. 888888b.
888 d88P" 888 888 "Y88b 888 "88b
888 888 888 888 888 888 .88P
888 88888b. 888888 888 888 888 888 888 888 888 8888888K.
888 888 "88b 888 888 888 888 Y8bd8P' 888 888 888 "Y88b
888 888 888 888 888 888 888 X88K 888 888 888 888
888 888 888 888 888 Y88b 888 .d8""8b. 888 .d88P 888 d88P
8888888 888 888 888 888 "Y88888 888 888 8888888P" 8888888P"
2026-01-29T15:22:01.751286Z info InfluxDB starting {"log_id": "10jaP0al000", "version": "1.8.10", "branch": "1.8", "commit": "688e697c51fd"}
2026-01-29T15:22:01.751314Z info Go runtime {"log_id": "10jaP0al000", "version": "go1.13.8", "maxprocs": 2}
2026-01-29T15:22:01.852530Z info Using data dir {"log_id": "10jaP0al000", "service": "store", "path": "/home/ec2-user/.influxdb/data"}
2026-01-29T15:22:01.852575Z info Compaction settings {"log_id": "10jaP0al000", "service": "store", "max_concurrent_compactions": 1, "throughput_bytes_per_second": 50331648, "throughput_bytes_per_second_burst": 50331648}
2026-01-29T15:22:01.852590Z info Open store (start) {"log_id": "10jaP0al000", "service": "store", "trace_id": "10jaP0~0000", "op_name": "tsdb_open", "op_event": "start"}
2026-01-29T15:22:01.856060Z info Reading file {"log_id": "10jaP0al000", "engine": "tsm1", "service": "cacheloader", "path": "/home/ec2-user/.influxdb/wal/_internal/monitor/1/_00001.wal", "size": 1088998}
2026-01-29T15:22:01.856153Z info Opened file {"log_id": "10jaP0al000", "engine": "tsm1", "service": "filestore", "path": "/home/ec2-user/.influxdb/data/k6/autogen/2/000000003-000000001.tsm", "id": 2, "duration": "0.059ms"}
2026-01-29T15:22:01.856265Z info Opened file {"log_id": "10jaP0al000", "engine": "tsm1", "service": "filestore", "path": "/home/ec2-user/.influxdb/data/k6/autogen/2/000000001-000000001.tsm", "id": 0, "duration": "0.041ms"}
2026-01-29T15:22:01.856337Z info Opened file {"log_id": "10jaP0al000", "engine": "tsm1", "service": "filestore", "path": "/home/ec2-user/.influxdb/data/k6/autogen/2/000000002-000000001.tsm", "id": 1, "duration": "0.039ms"}
2026-01-29T15:22:01.863294Z info Opened shard {"log_id": "10jaP0al000", "service": "store", "trace_id": "10jaP0~0000", "op_name": "tsdb_open", "index_version": "inmem", "path": "/home/ec2-user/.influxdb/data/k6/autogen/2", "duration": "7.752ms"}
2026-01-29T15:22:01.940814Z info Opened shard {"log_id": "10jaP0al000", "service": "store", "trace_id": "10jaP0~0000", "op_name": "tsdb_open", "index_version": "inmem", "path": "/home/ec2-user/.influxdb/data/_internal/monitor/1", "duration": "85.396ms"}
2026-01-29T15:22:01.940916Z info Open store (end) {"log_id": "10jaP0al000", "service": "store", "trace_id": "10jaP0~0000", "op_name": "tsdb_open", "op_event": "end", "op_elapsed": "88.326ms"}
2026-01-29T15:22:01.940944Z info Opened service {"log_id": "10jaP0al000", "service": "subscriber"}
2026-01-29T15:22:01.940951Z info Starting monitor service {"log_id": "10jaP0al000", "service": "monitor"}
2026-01-29T15:22:01.940957Z info Registered diagnostics client {"log_id": "10jaP0al000", "service": "monitor", "name": "build"}
2026-01-29T15:22:01.940966Z info Registered diagnostics client {"log_id": "10jaP0al000", "service": "monitor", "name": "runtime"}
2026-01-29T15:22:01.940973Z info Registered diagnostics client {"log_id": "10jaP0al000", "service": "monitor", "name": "network"}
2026-01-29T15:22:01.940985Z info Registered diagnostics client {"log_id": "10jaP0al000", "service": "monitor", "name": "system"}
2026-01-29T15:22:01.941008Z info Starting precreation service {"log_id": "10jaP0al000", "service": "shard-precreation", "check_interval": "10m", "advance_period": "30m"}
2026-01-29T15:22:01.941023Z info Starting snapshot service {"log_id": "10jaP0al000", "service": "snapshot"}
2026-01-29T15:22:01.941033Z info Starting continuous query service {"log_id": "10jaP0al000", "service": "continuous_querier"}
2026-01-29T15:22:01.941046Z info Starting HTTP service {"log_id": "10jaP0al000", "service": "httpd", "authentication": false}
2026-01-29T15:22:01.941055Z info opened HTTP access log {"log_id": "10jaP0al000", "service": "httpd", "path": "stderr"}
2026-01-29T15:22:01.941169Z info Listening on HTTP {"log_id": "10jaP0al000", "service": "httpd", "addr": "[::]:8086", "https": false}
2026-01-29T15:22:01.941192Z info Starting retention policy enforcement service {"log_id": "10jaP0al000", "service": "retention", "check_interval": "30m"}
2026-01-29T15:22:01.941178Z info Storing statistics {"log_id": "10jaP0al000", "service": "monitor", "db_instance": "_internal", "db_rp": "monitor", "interval": "10s"}
2026-01-29T15:22:01.941314Z info Listening for signals {"log_id": "10jaP0al000"}
2026-01-29T15:22:01.941560Z info Sending usage statistics to usage.influxdata.com {"log_id": "10jaP0al000"}
起動確認
[ec2-user@ip-10-0-2-7 bin]$ ps aux | grep influxd
ec2-user 30415 0.3 1.0 232528 41220 pts/0 Sl 15:22 0:00 ./influxd
Grafana OSS のインストール
Amazon Linux 2023 で Graviton(ARM)を使っている場合、以下の手順でインストールします。
# ARM版(aarch64)のGrafanaをダウンロード
wget https://dl.grafana.com/oss/release/grafana-11.4.0-1.aarch64.rpm
# インストール
sudo dnf install grafana-11.4.0-1.aarch64.rpm -y
# 起動&自動起動設定
sudo systemctl start grafana-server
sudo systemctl enable grafana-server
# 状態確認
sudo systemctl status grafana-server
systemctl enable について
C2 再起動時に Grafana を自動起動する設定です。これがないと再起動のたびに手動で sudo systemctl start grafana-server が必要になります。
EC2 の IAM ロールに必要なポリシーをアタッチ
EC2 インスタンスの IAM ロールに AmazonSSMManagedInstanceCore ポリシーが必要です。今回は AWS コンソールから設定しました。
IAM → ロール → EC2 インスタンスのロールを検索

許可を追加 → ポリシーをアタッチで、AmazonSSMManagedInstanceCore を検索してチェック。ポリシーをアタッチします。

SSM Agent を再起動(EC2 内で)
sudo systemctl restart amazon-ssm-agent
sudo systemctl status amazon-ssm-agent
SSM Agent を再起動できました。
[ec2-user@ip-10-0-2-7 ~]$ sudo systemctl status amazon-ssm-agent
● amazon-ssm-agent.service - amazon-ssm-agent
Loaded: loaded (/usr/lib/systemd/system/amazon-ssm-agent.service; enabled; preset: enabled)
Active: active (running) since Thu 2026-01-29 15:30:00 UTC; 1s ago
Main PID: 30648 (amazon-ssm-agen)
Tasks: 24 (limit: 4504)
Memory: 74.7M
CPU: 220ms
CGroup: /system.slice/amazon-ssm-agent.service
├─28030 /usr/bin/ssm-session-worker cm-kobayashi.shoma-5oj4qgonalcid36yb7catfktky
├─30648 /usr/bin/amazon-ssm-agent
└─30660 /usr/bin/ssm-agent-worker
ポートフォワーディングを開始(ローカルマシン)
# aws-vaultを使っている場合
aws-vault exec <プロファイル名> -- aws ssm start-session \
--target <instance-id> \
--document-name AWS-StartPortForwardingSession \
--parameters "portNumber=3000,localPortNumber=3000"
# 通常のAWS CLIの場合
aws ssm start-session \
--target <instance-id> \
--document-name AWS-StartPortForwardingSession \
--parameters "portNumber=3000,localPortNumber=3000"
実行すると下記のように表示されます。
serverless-backend %aws-vault exec my-profile -- aws ssm start-session --target i-00d734009aa8e8a25 --document-name AWS-StartPortForwardingSession --parameters "portNumber=3000,localPortNumber=3000"
Starting session with SessionId: cm-XXXXXXXX-XXXXXXXXX
Port 3000 opened for sessionId cm-XXXXXXXXX-XXXXXXXX
Waiting for connections...
この状態のまま、ブラウザで http://localhost:3000 にアクセスします。
トラブルシュート
ポートフォワーディング時に下記のようなエラーが発生することがあります。
An error occurred (TargetNotConnected) when calling the StartSession operation: i-00d734009aa8e8a25 is not connected.
serverless-backend %aws-vault exec my-profile -- aws ssm describe-instance-information --filters "Key=InstanceIds,Values=i-00d734009aa8e8a25"
これは、SSM Agent は登録されていますが、現在接続が切れている状態です。この場合下記のように再接続します。
# SSM Agentを再起動
sudo systemctl restart amazon-ssm-agent
# ステータス確認
sudo systemctl status amazon-ssm-agent
Grafana の画面が表示されました!
ログイン情報などを設定します。初回ログインではユーザー名、パスワード共にadminとなっています。


初回ログイン後、パスワード変更を求められます。

ログインが完了するとダッシュボードが表示されます!

データソース追加
左メニューから Connections → Data Sources を選択します。

検索メニューでInfluxDBを検索して選択します。

以下の情報を設定していきます。
- URL: http://localhost:8086
- Database: k6
その他はデフォルトのままです。


Save & Test をクリックします。

"Data source is working" と表示されれば OK です!
k6 ダッシュボードをインポート
左メニュー + → Import

Import via grafana.com に 2587 を入力します。2587 は k6 公式の Grafana ダッシュボードの ID です。

Load をクリックします。

InfluxDB データソースで先ほど作成したものを選択して Import をクリックします。

以上で設定完了です。ダッシュボードが表示されました!

k6 実行
それでは k6 を実行して実際にダッシュボードに表示されるか確認してみます。今回は前回の記事で使用したスクリプトを使用します。
まずテスト対象の URL を環境変数に設定します。
# テスト対象の URL を設定(自分の API エンドポイントに置き換えてください)
export BASE_URL=https://your-api-endpoint.com
# InfluxDB へメトリクスを送信しながらテストを実行
k6 run --out influxdb=http://localhost:8086/k6 dist/ramping-test.js
--out influxdb=... で InfluxDB にリアルタイムでメトリクスが送信されます。
トラブルシュート
コマンド実行時に下記のようなエラーが発生することがあります。
ERRO[0002] Couldn't write stats error="Post \"http://localhost:8086/write?consistency=&db=k6&precision=ns&rp=\": dial tcp 127.0.0.1:8086: connect: connection refused" output=InfluxDBv1
これは、InfluxDB が起動していないか、ポート 8086 がリッスンしていない状態です。下記のコマンドで起動しましょう。
# InfluxDBのディレクトリに移動
cd ~/influxdb-1.8.10-1/usr/bin
# バックグラウンドで起動
./influxd &
# Enterキーを押してプロンプトに戻る
起動確認
# プロセス確認
ps aux | grep influxd | grep -v grep
# ポート確認
netstat -tuln | grep 8086
tcp6 0 0 :::8086 :::* LISTEN と表示されれば OK です。
再度 k6 run コマンドを実行してみましょう。成功すると下記のように k6 が実行されます。

k6 実行後、先ほど作成した Grafana のダッシュボードを確認するとリアルタイムでメトリクスが確認できます。


ダッシュボードで見れるメトリクス
公式ダッシュボード(ID: 2587)では以下が可視化されます。
主要メトリクス
Virtual Users(仮想ユーザー数)
同時実行ユーザー数の推移をグラフで表示。段階的負荷テスト(Ramping Test)では、0 → 20 → 50 → 100 と段階的に増加する様子が確認できます。

Requests per Second(秒間リクエスト数)
Mean(平均): 平均的なスループット
Max(最大): ピーク時の処理能力
システムがどれだけのリクエストを処理できるかの指標です。

http_req_duration(レスポンスタイム)
mean(平均): 全リクエストの平均応答時間
med(中央値): 半数のリクエストがこの時間以内に完了
p90: 90%のリクエストがこの時間以内
p95: 95%のリクエストがこの時間以内
max(最大): 最も遅かったリクエスト
例: p95 が 500ms なら、95%のユーザーが 0.5 秒以内にレスポンスを得られる

Errors Per Second(エラー率)
エラーが発生しているかをリアルタイムで確認できます。今回のリクエストではエラーが発生しなかったため No data と表示されています。

Checks Per Second(テストチェックの成功率)
スクリプトで定義した check 関数の成功/失敗
例:「status is 200」のチェックが何回成功したかを確認できます。

おわりに
今回は Grafana OSS を利用して k6 の実行結果をリアルタイムで確認できるように可視化してみました。
セキュリティ要件で EC2 のインバウンドルールに穴を開けられない環境でも、EC2 Instance Connect のポートフォワーディングを活用することで、ローカル PC から localhost 経由でダッシュボードにアクセスできました。pem キー不要で手軽に実現できるのも嬉しいポイントです。
この記事がどなたかの参考になれば幸いです。








