はじめに
webアプリケーションを作っていると、高負荷が予想される処理の対応には工夫が必要となります。
今回、ECSを使用している場合にタスクに対する負荷を抑える手法の例を紹介します。
ケース例
こちら自分が実際に業務で遭遇したケースですが、phpコンテナとnginxコンテナを含んだECSのwebアプリケーションを開発していた際に、ユーザーが顧客データ(数万件)が入っているcsvファイルをアップロードする場面がありました。このデータをDBに格納する際、アプリケーションに対して高負荷が予想されます。
実際のCPU使用率のメトリクス:
csvファイルがアップロードされたタイミングでcpu使用率が急上昇しており、webサーバに対して負荷がかかっています。
解決策の例
ECSでcsvファイルのデータをDBに格納する処理のみ別のタスクを起動させて行うようにしました。これによりECSのwebサーバに対する高負荷を抑制することが期待できます。
実際のやり方について
詳細に説明すると長くなってしまうので重要な点のみ抜粋します。
S3の設定
バケットを作成した後、以下のようにEventBridgeへの通知をオンにしておきます。
S3のバケットに対してファイルのアップロード等が行われることをトリガーとしてEventBridgeにイベントを送信します。
EventBridgeの設定
ルールの詳細: イベントパターンを持つルール
として、イベントパターンを以下のようなJSONとします。
'prefix'で指定したパスに含まれるファイルのイベントをEventBridgeで検知します。
{
"source": ["aws.s3"],
"detail-type": ["Object Created"],
"detail": {
"bucket": {
"name": ["バケット名"]
},
"object": {
"key": [{
"prefix": "ファイルのパス"
}]
}
}
}
ターゲットは、後ほど作成するLambda関数を指定します。
ECSのタスクをターゲットとしていないのは、EventBridgeの送信するイベントのデータをLambda関数において適切に処理してからECSのタスクに渡したいためです。
Lambda関数の設定
S3のファイルデータをECSのタスクに渡す場合、バケット名とキー(ファイル名を含んだパス)をLambda関数で取り出して環境変数として送ることになります。
Lambda関数はPythonの場合以下のように記述します。
import json
import boto3
import os
ecs_client = boto3.client("ecs")
# 環境変数
ECS_CLUSTER = os.environ["ECS_CLUSTER"]
SUBNET_ID = os.environ["SUBNET_ID"]
TASK_DEFINITION = os.environ["TASK_DEFINITION"]
def lambda_handler(event, context):
# s3の情報を取得
bucket_name = event['detail']['bucket']['name']
key = event['detail']['object']['key']
# 新しいタスクを起動
ecs_client.run_task(
cluster=ECS_CLUSTER,
taskDefinition=TASK_DEFINITION,
launchType="FARGATE",
networkConfiguration={
"awsvpcConfiguration": {
# ECSのタスクが存在するサブネット
"subnets": [SUBNET_ID],
"assignPublicIp": "ENABLED",
}
},
overrides={
"containerOverrides": [
{
"name": "コンテナ名",
# ECSタスクに環境変数として渡す
"environment": [
{"name": "S3_BUCKET", "value": bucket_name},
{"name": "S3_KEY", "value": key},
],
},
],
},
)
Laravelを使用している場合の例
Dockerコンテナが起動した際に実行されるシェルスクリプトに以下の内容を記載しました。
#!/bin/bash
# 環境変数からバケット名とキーを取得
BUCKET_NAME=$S3_BUCKET
OBJECT_KEY=$S3_KEY
# 環境変数が渡されているか確認
echo "デバッグ"
echo $BUCKET_NAME
echo $OBJECT_KEY
# S3からファイルをダウンロード
aws s3 cp s3://$BUCKET_NAME/$OBJECT_KEY /tmp/data.csv
# Artisanコマンドを実行
php /var/www/html/app/artisan import:csv /tmp/data.csv
このシェルスクリプトがコンテナの起動とともに実行され、PHPの関数にS3上のCSVファイルの情報を渡して呼び出すことで高負荷を要する処理のみ別タスクを起動させて処理を行わせることができます。
タスクは実行の終了後に停止されるのでAWSの使用料金を抑えることもでき、一石二鳥ですね。
おわりに
負荷対策の手法には様々なやり方が考えられるので自分自身勉強を重ねていきたいです。