EC2上に構築したGatlingサーバで、APIサーバからVPCピアリング接続でTiDB Cloudにクエリを実行してみた
こんにちは、ゲームソリューション部のsoraです。
今回は、EC2上に構築したGatlingサーバで、APIサーバからVPCピアリング接続でTiDB Cloudにクエリを実行してみたことについて書いていきます。
構築する構成
構成は以下です。
GatlingなどのインストールのためにNAT Gatewayを一時的に配置しています。
テストの結果を見るために、CloudWatch Logsへのログ出力をしています。
APIサーバで動作するソースコードやGatlingで使用するシナリオファイルをS3から取得するために、S3への接続ルートも作っています。
今回、GatlingサーバやAPIサーバの構築・設定、TiDB Cloudへのクエリ実行がメインのため、AWSリソースの構築自体は完了している状態から説明します。
TiDB Cloudの構築・設定
Dedicatedクラスタ作成
TiDB CloudにてDedicatedクラスタを作成します。
今回はテストのため、最小構成で構築します。
VPCピアリング接続設定
VPCピアリングで接続するため、TiDB Cloudにてピアリング接続を作成します。
今回は割愛しますが、詳細は以下ブログの中で記載しております。
APIサーバの構築・設定
Node.jsのインストール
ユーザデータを使用してインストールします。
ベースは以下ブログに記載した通りですが、今回はnvmは不要なため少し変えています。
#!/bin/bash
# Update the package list and install necessary packages
dnf update -y
# タイムゾーンの設定
timedatectl set-timezone Asia/Tokyo
# rsyslogのインストール(システムログがjournalで保存され文字化けするため)
dnf install -y rsyslog
systemctl enable rsyslog
systemctl start rsyslog
# CloudWatch Agentのインストール
dnf install -y amazon-cloudwatch-agent
# Create the CloudWatch Agent configuration file for logging
bash -c 'cat <<EOL > /opt/aws/amazon-cloudwatch-agent/bin/config.json
{
"logs": {
"logs_collected": {
"files": {
"collect_list": [
{
"file_path": "/var/log/messages",
"log_group_name": "/api-server",
"log_stream_name": "{instance_id}",
"retention_in_days": 7
},
{
"file_path": "/var/log/api-app/*.log",
"log_group_name": "/api-server-app",
"log_stream_name": "{instance_id}",
"retention_in_days": 7
}
]
}
}
}
}
EOL'
# Start the CloudWatch Agent
/opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -c file:/opt/aws/amazon-cloudwatch-agent/bin/config.json -s
# デフォルトでenableだが念のため明示的にenableしておく
systemctl enable amazon-cloudwatch-agent
# ログファイル用のディレクトリ作成
mkdir /var/log/api-app
chown -R ec2-user:ec2-user /var/log/api-app
# nodejsのインストールの準備
dnf install -y gcc-c++ make
# マルチパート形式
Content-Type: multipart/mixed; boundary="//"
MIME-Version: 1.0
--//
Content-Type: text/cloud-config; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment; filename="cloud-config.txt"
#cloud-config
cloud-init directives
--//
Content-Type: text/x-shellscript; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment; filename="userdata.txt"
#!/bin/bash
if ! su - ec2-user -c "command -v node" > /dev/null 2>&1; then
su - ec2-user -c "curl -sL https://rpm.nodesource.com/setup_20.x | sudo -E bash -"
fi
--//--
# nodejsのインストール
dnf install -y nodejs
# ディレクトリ作成
mkdir /home/ec2-user/tidb-connection-api
chown -R ec2-user:ec2-user /home/ec2-user/tidb-connection-api
ソースコード
ソースコードは以下です。
フレームワークとしてExpressを使用し、ロガーにはpinoを使用します。
クエリが実行できることが見たいだけなので、TiDB Cloudクラスタ構築時のデフォルトのデータだけで実行可能なクエリにしています。
import express from 'express';
import mysql from 'mysql2/promise';
import dotenv from 'dotenv';
import fs from 'fs';
import pino from 'pino';
// ログファイルを指定
const logFile = fs.createWriteStream('/var/log/api-app/app.log', { flags: 'a' });
const logger = pino(logFile);
const app = express();
dotenv.config();
// データベース接続設定
const dbConfig = {
host: process.env.TIDB_HOST,
port: process.env.TIDB_PORT,
user: process.env.TIDB_USER,
password: process.env.TIDB_PASSWORD,
database: process.env.TIDB_DATABASE,
ssl: process.env.TIDB_ENABLE_SSL === 'true' ? {
// SSLの設定
minVersion: 'TLSv1.2',
ca: fs.readFileSync(process.env.TIDB_CA_PATH)
} : null,
};
// Gatlingから受けるポート
const PORT = 3000;
app.get('/read', async (req, res) => {
let connection;
try {
// データベースに接続
connection = await mysql.createConnection(dbConfig);
logger.info('Connection has been established successfully.');
// クエリを実行
const [results, fields] = await connection.execute('SHOW DATABASES');
// 結果を返す
res.json({
results: results,
fields: fields
});
logger.info('Read Query OK');
} catch (err) {
logger.error('Error executing query:', err.message);
res.status(500).json({ error: 'Database query failed' });
} finally {
if (connection) {
// 接続を閉じる
await connection.end();
}
}
});
app.use((req, res, next) => {
logger.error('Not Found:', err.message);
res.status(404).json({ message: 'Not Found' });
});
app.listen(PORT, () => {
logger.info(`Server is running on port ${PORT}`);
});
TiDB Cloud接続用の.env
ファイルは以下です。
ホストやパスワードなど、環境に合わせて設定してください。
また、TiDB CloudのDedicatedクラスタ構築後にダウンロードしたca.cer
も、S3経由で渡してjsファイルと同じ場所に配置しておきます。
TIDB_HOST=tidb.xxxxxxxxx.clusters.tidb-cloud.com
TIDB_PORT=4000
TIDB_USER=root
TIDB_PASSWORD=xxxxxxxx
TIDB_DATABASE=test
TIDB_ENABLE_SSL=true
TIDB_CA_PATH=ca.cer
Gatlingサーバの構築・設定
Gatlingのインストール
ユーザデータにてインストールします。
ユーザデータを使用しない場合のインストール方法は以下ブログに記載しています。
上記ブログではGatling 3.9.5
を使用していましたが、今回はGatling 3.11.5
を使用します。
(テスト実行時に記載しますが、3.11以降で実行コマンドなどが若干異なっていたため注意が必要です。)
#!/bin/bash
# Update the package list and install necessary packages
dnf update -y
# タイムゾーンの設定
timedatectl set-timezone Asia/Tokyo
# JDKのインストール
dnf install -y java-21-amazon-corretto-devel
# Download and extract Gatling
GATLING_VERSION="3.11.5"
wget https://repo1.maven.org/maven2/io/gatling/highcharts/gatling-charts-highcharts-bundle/${GATLING_VERSION}/gatling-charts-highcharts-bundle-${GATLING_VERSION}.zip
unzip gatling-charts-highcharts-bundle-${GATLING_VERSION}.zip
cp -r gatling-charts-highcharts-bundle-${GATLING_VERSION} /home/ec2-user/gatling
chown -R ec2-user:ec2-user /home/ec2-user/gatling
テストシナリオ
テストシナリオは以下です。
テストシナリオはgatling/src/test/java/
配下に配置します。
以下のシナリオでは10ユーザが接続するシナリオになっています。
{api-server-private-subnet}
の部分は、APIサーバのプライベートサブネットのIPアドレスを指定します。
package simulation;
import io.gatling.javaapi.core.CoreDsl.*;
import io.gatling.javaapi.http.HttpDsl.*;
import io.gatling.javaapi.core.*;
import io.gatling.javaapi.http.*;
import java.time.Duration;
import static io.gatling.javaapi.core.CoreDsl.*;
import static io.gatling.javaapi.http.HttpDsl.*;
public class ApiSimulation extends Simulation {
HttpProtocolBuilder httpProtocol = http
.baseUrl("http://{api-server-private-subnet}:3000")
.acceptHeader("application/json");
ScenarioBuilder scn = scenario("TiDB Request Scenario")
.exec(
http("Request to API")
.get("/read")
.check(status().is(200))
);
{
setUp(
scn.injectOpen(atOnceUsers(10))
).protocols(httpProtocol);
}
}
テスト実行
準備ができたためテストを実行します。
まずはAPIサーバを起動します。
node index.mjs
次にGatlingサーバにて準備したテストシナリオを実行します。
cd gatling
./mvnw gatling:test -Dgatling.simulationClass=simulations.ApiSimulation
ちなみにコマンドの説明は以下です。
./mvnw
:Maven Wrapperスクリプトを実行するコマンド(Windowsの場合はmvnw.cmd)gatling:test
:Gatlingのテスト実行-Dgatling
:JavaのシステムプロパティでGatlingを指定して実行するsimulationClass=simulations.ApiSimulation
:シミュレーションクラスの指定
gatling:test
の部分については、他には以下のようなオプションもあります。
gatling:test
:実行(Mavenのテストの一部として実行)- CI/CDや定期的なテスト実行向き
gatling:execute
:実行(testより柔軟性が高い、Mavenに依存しない)- 開発中のデバッグや特定のシミュレーション実行向き
gatling:compile
:コンパイルgatling:clean
:実行結果のクリーンアップgatling:package
:シミュレーションのパッケージングgatling:verify
:シミュレーション結果を検証
実行結果の確認
CloudWatch Logsを見てみると、APIサーバ側で指定した情報が取得できており、実行できていることが確認できました。
TiDB Cloud側でメトリクスを確認してみると、クエリの実行ができていることが確認できました。
参考
最後に
今回は、EC2上に構築したGatlingサーバで、APIサーバからVPCピアリング接続でTiDB Cloudにクエリを実行してみたことを記事にしました。
どなたかの参考になると幸いです。