CLOUD PRATICA / AWS 実践コース

EventBridge Scheduler × ECS バッチ
今回の講座群を一本につなぐ

Slack のデータを定期同期する題材を通して、コード、Docker、ECR、ECS、Scheduler、IAM、RDS がどう受け渡しするかを俯瞰します。手順の暗記ではなく、どこで何が起きているかを後から説明できる形に整理しました。

TL;DR

  1. Scheduler は時刻を管理して ECS タスクを起動し、同期処理そのものは Go アプリが担当する。
  2. MODE=batch はタスク定義、TYPE は実行時 override、共通設定は S3、秘密情報は Secrets Manager に分ける。
  3. local → AWS CLI → Scheduler の順に検証し、CloudWatch Logs と DB 更新の両方で成功を判断する。

01全体俯瞰 — 1回の同期が通る道

Scheduler は司令塔、ECS は実行場所、Go アプリが処理本体です。

EventBridge SchedulerからECSタスクを起動しSlackデータをRDSへ同期する全体図
Scheduler は処理を実行せず、ECS タスクを起動する。同期の本体はコンテナ内にある。

決まった時刻になると EventBridge Scheduler が Scheduler 用 IAM ロールを使い、ECS RunTask を呼びます。ECS はタスク定義に従い、ECR のイメージから Fargate タスクを1個起動します。

起動した slack-metrics コンテナは Slack API からユーザー、公開チャンネル、メッセージを取得し、RDS PostgreSQL に保存します。標準出力は CloudWatch Logs に送られます。

覚え方: Scheduler = いつ、IAM = 何を許す、ECS = どこで動かす、Go アプリ = 何をする、RDS = 何を残す。

02コードを追う — main.go からDB更新まで

処理の入口と分岐を押さえると、インフラに必要な設定が見えてきます。

sync-workspacesのGoコード処理フロー
ロックの内側で実行可否を再確認し、成功後だけ最終同期時刻を更新する。
段階コード上の役割起きること
1main.go設定と依存先を組み立て、MODE=batch で CLI へ
2Command.RunTYPE=sync-workspaces を選択
3SyncWorkspaces全 workspace を取得し、Bot Token と期間を設定
4WorkspaceUsecase.Syncトランザクションと workspace 行ロック
5WorkspaceSyncJob最終同期から30分以上か判定
6WorkspaceService.Syncusers → channels → messages の順で同期
7UpdateLastSynced成功後に workspace_sync_jobs を保存・更新

Command は workspace ごとのエラーをログに出して次へ進むため、プロセスの exit code 0 だけでは全 workspace の同期成功を証明できません。CloudWatch のエラー本文も確認します。

GOCONF="$PWD/config/config.toml.tmpl" \
+SLACK_USE_MOCK=false \
+MODE=batch TYPE=sync-workspaces \
+go run main.go

0330分制御 — レート制限と排他は別の役目

時間の制御と同時実行の防止を組み合わせています。

仕組み守るもの実装
30分判定短時間の再同期を避けるnow > updated_at + 30分
行ロック同じ workspace の同時同期を防ぐトランザクション内の SELECT FOR UPDATE
最終同期更新次回判定の基準を残す同期成功後に workspace_sync_jobs を upsert

検証中に30分待たず再実行したい場合は、対象 workspace の workspace_sync_jobs 行を削除するか、updated_at を30分より前に更新します。これはデータ側だけの変更なので、コード修正・Docker build・デプロイは不要です。

stg の対象 workspace だけを変更し、他の受講生や本番データには触れないこと。実行直後に元の同期記録が再作成される点も把握しておきます。

04環境変数 — 置き場所が設計を表す

固定する役割、実行ごとの選択、環境共通設定、秘密情報を分けます。

ECSバッチにおける環境変数の配置図
MODE はタスクの役割、TYPE は今回の仕事。共通 env と秘密情報は別管理にする。
置き場所代表値理由
ECSバッチ用タスク定義MODE=batchこのタスク定義の役割として固定
Scheduler / CLI overrideTYPE=sync-workspaces実行するバッチごとに変更
S3 env fileGOCONF、USE_MOCK、LOG_LEVEL、SSL_MODE環境単位で共有する非機密設定
Secrets ManagerDB host/user/password認証情報を平文envファイルから分離

MODE=batch を API と共通の S3 env に置くと、API タスクまで batch 分岐へ入り、HTTP サーバーが起動しなくなります。

USE_MOCK=false は本物の外部サービスへ接続する合図です。まだ構築していない SES や SQS まで false にすると初期化で失敗し得るため、今回必要な Slack と PostgreSQL だけを実接続にします。

05検証戦略 — 小さなセーブポイントを置く

待ち時間と原因候補を同時に減らす順番です。

ECSバッチを段階的に検証する3つのセーブポイント
最終経路を一気に試さず、失敗箇所を限定しながらAWS要素を一段ずつ足す。
段階増える確認範囲成功証跡
1. ローカルGoアプリ、Slack API、ローカルPostgreSQLログ + TablePlusのusers/channels/messages
2. AWS CLIECR、タスク定義、Fargate、network、S3 env、Secrets、RDSECS STOPPED/exitCode + CloudWatch + stg DB
3. Scheduler時刻、Scheduler IAM、RunTask/PassRole指定時刻のECSタスク + CloudWatch + stg DB
AWS_PROFILE=cp-terraform-stg \
+make run-batch ENV=stg TYPE=sync-workspaces \
+  PRIVATE_SUBNET_ID=subnet-... \
+  SECURITY_GROUP_ID=sg-...

CloudWatch Logs は処理の説明、TablePlus は保存結果の証拠。片方だけでなく両方を見ると、外部APIエラーを正常終了と取り違えません。

06IAM — RunTask と PassRole を分けて考える

起動する許可と、実行時ロールを渡す許可は別物です。

EventBridge SchedulerからECSを起動するIAM権限の関係図
Scheduler ロールは ECS を起動し、タスク定義に指定された2つのロールを ECS へ渡す。
IAM要素役割
信頼ポリシーscheduler.amazonaws.com が Scheduler用ロールを AssumeRole できる
ecs:RunTaskECS タスクを起動できる
iam:PassRoletask role / execution role を ECS サービスへ渡せる
task role起動後のアプリがAWS APIへアクセスする権限
execution roleECSがイメージ取得、ログ送信、env/secrets取得を行う権限

講座では自動生成ロールを動作確認に使った後、意味のある固定名のロールと再利用可能なポリシーへ置き換えました。似た Scheduler を増やしてもランダム名のロールが乱立しません。

教材の Resource=* は学習用に広い設定です。実運用ではタスク定義や渡せるロールを絞り、最小権限へ寄せます。

07今回の到達点と次の一手

構築済みと検証済みを混ぜずに整理します。

項目状態証拠・残課題
ECRイメージ完了最新コードをbuildしてstg ECRへpush
ECSバッチタスク定義完了ARM64 / Fargate / MODE=batch / readonly root
CLIからRunTask起動確認済みタスクはSTOPPED・exitCode 0
Slackデータ同期未完Slack APIが invalid_auth / not_authed
Scheduler構築済みsync-workspaces-stg、TYPE override設定済み
Scheduler IAM整理完了固定名ロールへ交換し自動生成物を削除
最終動作確認待ち有効なBot TokenでCloudWatchとDBを再確認

exitCode 0 はコンテナが落ちなかった証拠であって、全 workspace の同期成功とは限りません。今回のコードは workspace ごとの失敗をログに残して処理を継続します。

完成の判断は「AWSリソースを作った」ではなく、「指定時刻に起動し、外部APIから取得し、DBへ保存されたことを証明できた」です。

結論

  1. EventBridge Scheduler、IAM、ECS、アプリ、外部サービス、DBを別々の責務として捉える。
  2. MODE、TYPE、共通env、秘密情報を、それぞれ意味に合う場所へ置く。
  3. local → CLI → Scheduler と段階を上げ、ログとDB更新の両方が揃って初めて完了とする。