Slack のデータを定期同期する題材を通して、コード、Docker、ECR、ECS、Scheduler、IAM、RDS がどう受け渡しするかを俯瞰します。手順の暗記ではなく、どこで何が起きているかを後から説明できる形に整理しました。
Scheduler は司令塔、ECS は実行場所、Go アプリが処理本体です。
決まった時刻になると EventBridge Scheduler が Scheduler 用 IAM ロールを使い、ECS RunTask を呼びます。ECS はタスク定義に従い、ECR のイメージから Fargate タスクを1個起動します。
起動した slack-metrics コンテナは Slack API からユーザー、公開チャンネル、メッセージを取得し、RDS PostgreSQL に保存します。標準出力は CloudWatch Logs に送られます。
覚え方: Scheduler = いつ、IAM = 何を許す、ECS = どこで動かす、Go アプリ = 何をする、RDS = 何を残す。
処理の入口と分岐を押さえると、インフラに必要な設定が見えてきます。
| 段階 | コード上の役割 | 起きること |
|---|---|---|
| 1 | main.go | 設定と依存先を組み立て、MODE=batch で CLI へ |
| 2 | Command.Run | TYPE=sync-workspaces を選択 |
| 3 | SyncWorkspaces | 全 workspace を取得し、Bot Token と期間を設定 |
| 4 | WorkspaceUsecase.Sync | トランザクションと workspace 行ロック |
| 5 | WorkspaceSyncJob | 最終同期から30分以上か判定 |
| 6 | WorkspaceService.Sync | users → channels → messages の順で同期 |
| 7 | UpdateLastSynced | 成功後に 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
時間の制御と同時実行の防止を組み合わせています。
| 仕組み | 守るもの | 実装 |
|---|---|---|
| 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 だけを変更し、他の受講生や本番データには触れないこと。実行直後に元の同期記録が再作成される点も把握しておきます。
固定する役割、実行ごとの選択、環境共通設定、秘密情報を分けます。
| 置き場所 | 代表値 | 理由 |
|---|---|---|
| ECSバッチ用タスク定義 | MODE=batch | このタスク定義の役割として固定 |
| Scheduler / CLI override | TYPE=sync-workspaces | 実行するバッチごとに変更 |
| S3 env file | GOCONF、USE_MOCK、LOG_LEVEL、SSL_MODE | 環境単位で共有する非機密設定 |
| Secrets Manager | DB host/user/password | 認証情報を平文envファイルから分離 |
MODE=batch を API と共通の S3 env に置くと、API タスクまで batch 分岐へ入り、HTTP サーバーが起動しなくなります。
USE_MOCK=false は本物の外部サービスへ接続する合図です。まだ構築していない SES や SQS まで false にすると初期化で失敗し得るため、今回必要な Slack と PostgreSQL だけを実接続にします。
待ち時間と原因候補を同時に減らす順番です。
| 段階 | 増える確認範囲 | 成功証跡 |
|---|---|---|
| 1. ローカル | Goアプリ、Slack API、ローカルPostgreSQL | ログ + TablePlusのusers/channels/messages |
| 2. AWS CLI | ECR、タスク定義、Fargate、network、S3 env、Secrets、RDS | ECS 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エラーを正常終了と取り違えません。
起動する許可と、実行時ロールを渡す許可は別物です。
| IAM要素 | 役割 |
|---|---|
| 信頼ポリシー | scheduler.amazonaws.com が Scheduler用ロールを AssumeRole できる |
| ecs:RunTask | ECS タスクを起動できる |
| iam:PassRole | task role / execution role を ECS サービスへ渡せる |
| task role | 起動後のアプリがAWS APIへアクセスする権限 |
| execution role | ECSがイメージ取得、ログ送信、env/secrets取得を行う権限 |
講座では自動生成ロールを動作確認に使った後、意味のある固定名のロールと再利用可能なポリシーへ置き換えました。似た Scheduler を増やしてもランダム名のロールが乱立しません。
教材の Resource=* は学習用に広い設定です。実運用ではタスク定義や渡せるロールを絞り、最小権限へ寄せます。
構築済みと検証済みを混ぜずに整理します。
| 項目 | 状態 | 証拠・残課題 |
|---|---|---|
| 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 ごとの失敗をログに残して処理を継続します。