はじめに
AWS ECS Fargate 上で FastAPI アプリを運用するためのタスク定義を作成した際の構成を整理しました。
Datadog のログ監視、初期化処理、マイグレーション実行などを複数のコンテナで分担する構成としています。
個人の備忘録程度の走り書きとなっておりますが、温かい目で見守っていただければ幸いです。
書こうと思ったきっかけ
受講しているITスクールのハッカソンの開発の一環で、Datadogやマイグレーションの構成に悩んだため、今後の再利用と共有のために記事としてまとめておくことにしました。
作成後の画面
作成したタスク定義JSON
{
"containerDefinitions": [
{
"name": "cws-instrumentation-init",
"image": "datadog/cws-instrumentation:latest",
"cpu": 0,
"portMappings": [],
"essential": false,
"command": [
"/cws-instrumentation",
"setup",
"--cws-volume-mount",
"/cws-instrumentation-volume"
],
"environment": [],
"mountPoints": [
{
"sourceVolume": "cws-instrumentation-volume",
"containerPath": "/cws-instrumentation-volume",
"readOnly": false
}
],
"volumesFrom": [],
"user": "0",
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/datadog",
"awslogs-region": "ap-northeast-1",
"awslogs-stream-prefix": "cws-instrumentation-init"
}
},
"systemControls": []
},
{
"name": "datadog-agent",
"image": "datadog/agent:latest",
"cpu": 0,
"portMappings": [],
"essential": true,
"environment": [
{
"name": "DD_CONTAINER_METRICS_ENABLED",
"value": "true"
},
{
"name": "DD_RUNTIME_SECURITY_CONFIG_ENABLED",
"value": "false"
},
{
"name": "DD_TRACE_ENABLED",
"value": "false"
},
{
"name": "DD_ORCHESTRATOR_EXPLORER_ENABLED",
"value": "false"
},
{
"name": "DD_SYSTEM_PROBE_ENABLED",
"value": "false"
},
{
"name": "DD_APM_ENABLED",
"value": "false"
},
{
"name": "DD_LOGS_ENABLED",
"value": "false"
},
{
"name": "DD_API_KEY",
"value": "xxx"
},
{
"name": "DD_SECURITY_AGENT_ENABLED",
"value": "false"
},
{
"name": "DD_SITE",
"value": "ap1.datadoghq.com"
},
{
"name": "DD_PROCESS_AGENT_ENABLED",
"value": "false"
},
{
"name": "ECS_FARGATE",
"value": "true"
},
{
"name": "DD_PROCESS_CONFIG_ENABLED",
"value": "false"
},
{
"name": "DD_RUNTIME_SECURITY_CONFIG_EBPFLESS_ENABLED",
"value": "false"
}
],
"mountPoints": [],
"volumesFrom": [],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/datadog",
"awslogs-region": "ap-northeast-1",
"awslogs-stream-prefix": "datadog-agent"
}
},
"healthCheck": {
"command": [
"CMD-SHELL",
"/probe.sh"
],
"interval": 30,
"timeout": 5,
"retries": 2,
"startPeriod": 60
},
"systemControls": []
},
{
"name": "my-app-repo",
"image": "xxx.dkr.ecr.ap-northeast-1.amazonaws.com/my-app-repo:latest",
"cpu": 0,
"portMappings": [
{
"containerPort": 8000,
"hostPort": 8000,
"protocol": "tcp"
}
],
"essential": true,
"entryPoint": [
"/bin/sh",
"-c"
],
"command": [
"uvicorn main:app --host 0.0.0.0 --port 8000 --log-level info"
],
"environment": [],
"mountPoints": [
{
"sourceVolume": "cws-instrumentation-volume",
"containerPath": "/cws-instrumentation-volume",
"readOnly": true
}
],
"volumesFrom": [],
"linuxParameters": {
"capabilities": {
"add": [
"SYS_PTRACE"
],
"drop": []
}
},
"dependsOn": [
{
"containerName": "datadog-agent",
"condition": "HEALTHY"
},
{
"containerName": "cws-instrumentation-init",
"condition": "SUCCESS"
},
{
"containerName": "migration-runner",
"condition": "SUCCESS"
}
],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/datadog",
"awslogs-region": "ap-northeast-1",
"awslogs-stream-prefix": "my-app-repo"
}
},
"systemControls": []
},
{
"name": "migration-runner",
"image": "xxx.dkr.ecr.ap-northeast-1.amazonaws.com/my-app-repo:latest",
"essential": false,
"entryPoint": [
"/bin/sh",
"-c"
],
"command": [
"cd /app && alembic upgrade head"
],
"environment": [
{
"name": "AWS_REGION",
"value": "ap-northeast-1"
},
{
"name": "MYSQL_PW",
"value": "xxx"
},
{
"name": "MYSQL_USER",
"value": "myuser"
},
{
"name": "MYSQL_HOST",
"value": "xxx.ap-northeast-1.rds.amazonaws.com"
},
{
"name": "MYSQL_DB",
"value": "mydatabase"
}
],
"dependsOn": [
{
"containerName": "datadog-agent",
"condition": "HEALTHY"
}
],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/datadog",
"awslogs-region": "ap-northeast-1",
"awslogs-stream-prefix": "migration-runner"
}
}
}
],
"family": "Datadog",
"taskRoleArn": "arn:aws:iam::xxx:role/ecs-task-execution-role",
"executionRoleArn": "arn:aws:iam::xxx:role/ecs-task-execution-role",
"networkMode": "awsvpc",
"volumes": [
{
"name": "cws-instrumentation-volume",
"host": {}
}
],
"requiresCompatibilities": [
"FARGATE"
],
"cpu": "256",
"memory": "512"
}
コンテナ構成
-
cws-instrumentation-init
: DatadogのCWS(Cloud Workload Security)初期化用コンテナ。セットアップスクリプトを実行して終了します。 -
datadog-agent
: Datadogエージェント本体。ログ収集やメトリクス監視を担当し、すべてのコンテナの起動に先立って健康状態チェックを提供します。 -
migration-runner
: Alembic を使ってマイグレーションを実行する補助的なコンテナ。alembic upgrade head
を実行後に終了します。 -
my-app-repo
: 本番アプリケーション(FastAPI)。依存関係がすべて整った後にuvicorn
によって起動されます。
特徴的な設定ポイント
-
my-app-repo
とmigration-runner
は同一イメージを使用しますが、起動時のcommand
によって動作が異なります。 -
migration-runner
はessential: false
に設定し、アプリ本体とは独立して動作します。 - すべてのログは Datadog のロググループ
/ecs/datadog
に送信され、awslogs-stream-prefix
によって分類されます。 -
dependsOn
を活用し、アプリの起動順序を制御しています。
まとめ
このように、Fargate 上で複数コンテナを使い分けることで、初期化処理・マイグレーション・監視・アプリ本体の機能を疎結合に保ちながら、安全にアプリを起動できます。
特に Alembic のマイグレーションを ECS タスク起動時に組み込むことで、デプロイ前後の整合性も確保できると思っています...!
今後は CI/CD パイプラインと連携し、この構成をより自動化していく予定です。