運用ランブック¶
前提: AWS CLI プロファイルの設定が必要です。AWS CLI セットアップ を参照してください。
以降のコマンドは事前に
aws sso login済みでAWS_PROFILEが設定されている前提です。# dev環境 export AWS_PROFILE=rikako-development-sso # shared環境(ECR操作時) export AWS_PROFILE=rikako-shared-sso
環境情報¶
| 項目 | dev環境 | prod環境 |
|---|---|---|
| Lambda (公開API) | rikako-api-development |
rikako-api-production |
| Lambda (管理API) | rikako-admin-api-development |
rikako-admin-api-production |
| Lambda (Slack 通知) | rikako-slack-notifier-development |
rikako-slack-notifier-production |
| 公開API エンドポイント | https://api.dev.rikako.org (API Gateway) |
https://api.rikako.org (API Gateway) |
| 管理画面 | https://admin.dev.rikako.org (Basic Auth) |
https://admin.rikako.org (Basic Auth) |
| 管理API | https://admin.dev.rikako.org/api |
https://admin.rikako.org/api |
| 画像CDN | https://image.dev.rikako.org/ |
https://image.rikako.org/ |
| コンテンツCDN | https://content.dev.rikako.org/ |
https://content.rikako.org/ |
| 画像S3 | rikako-images-development |
rikako-images-production |
| コンテンツS3 | rikako-content-development |
rikako-content-production |
| 管理画面S3 | rikako-admin-development |
rikako-admin-production |
| Neon プロジェクト ID | muddy-tree-64549662 (ap-southeast-1) |
fragrant-poetry-87067174 (ap-southeast-1) |
| Neon エンドポイント | ep-raspy-lab-a1wo0g6n |
ep-misty-unit-aoxkoz1d |
| ECR (shared) | 579039992557.dkr.ecr.ap-northeast-1.amazonaws.com/rikako-api |
同左 |
| CloudWatch Dashboard | rikako-dev |
rikako-prod |
| AWSアカウント | 197865631794 | 211125415945 |
| AWS Profile | rikako-development-sso |
rikako-production-sso |
Shared 環境(ECR を共有): AWS アカウント 579039992557 / プロファイル rikako-shared-sso。
1. デプロイ手順¶
公開API / 管理API¶
Dev: mainブランチへのマージで自動デプロイ(GitHub Actions)。
# 手動トリガー(Dev)
gh workflow run "Deploy API Dev" --repo takoikatakotako/rikako --ref main
gh workflow run "Deploy Admin API Dev" --repo takoikatakotako/rikako --ref main
# 状況確認
gh run list --repo takoikatakotako/rikako --limit 5
Prod: 手動 dispatch のみ(自動デプロイなし)。
gh workflow run "Deploy API Prod" --repo takoikatakotako/rikako --ref main
gh workflow run "Deploy Admin API Prod" --repo takoikatakotako/rikako --ref main
デプロイフロー¶
- Dockerイメージをビルド(
app/Dockerfile.lambda/app/Dockerfile.admin) - ECRにプッシュ(タグ:
dev/prod) aws lambda update-function-codeで Lambda 関数のイメージを更新aws lambda wait function-updatedで更新完了まで待機/healthエンドポイントでヘルスチェック
管理画面フロントエンド¶
mainブランチへのマージで admin/ 配下に変更がある場合に自動デプロイ。
# 手動デプロイ
gh workflow run "Deploy Admin Frontend Dev" --repo takoikatakotako/rikako --ref main
デプロイフロー¶
npm run buildでビルド- S3にsync(静的アセット: 1年キャッシュ、HTML: キャッシュなし)
- CloudFrontキャッシュを無効化
2. ロールバック手順¶
Lambda API(公開・管理共通)¶
ECRのイメージタグ dev は上書き可能。直前のイメージに戻すには:
# 直近のイメージダイジェストを確認
aws ecr describe-images \
--registry-id 579039992557 \
--repository-name rikako-api \
--query 'imageDetails | sort_by(@, &imagePushedAt) | [-5:].[imagePushedAt,imageDigest]' \
--output table
# 特定のダイジェストに戻す(公開API)
aws lambda update-function-code \
--function-name rikako-api-development \
--image-uri 579039992557.dkr.ecr.ap-northeast-1.amazonaws.com/rikako-api@sha256:<digest>
# 管理APIも同様
aws lambda update-function-code \
--function-name rikako-admin-api-development \
--image-uri 579039992557.dkr.ecr.ap-northeast-1.amazonaws.com/rikako-admin-api@sha256:<digest>
注意: ECRのライフサイクルポリシーにより最新5イメージのみ保持。古いイメージは自動削除される。
管理画面フロントエンド¶
gitで前のコミットに戻してデプロイワークフローを再実行する。
# 直前のコミットでデプロイ
gh workflow run "Deploy Admin Frontend Dev" --repo takoikatakotako/rikako --ref <commit-sha>
2.5 シークレット管理(SSM Parameter Store)¶
Lambda が読むシークレット(OPENAI_API_KEY / SLACK_WEBHOOK_URL / DATABASE_URL 等)は、Lambda 環境変数には ssm:/path 形式の参照のみを保存し、起動時に app/internal/secrets.Resolve または slack_notifier の _resolve_ssm が実値を取得する仕組み(Issue #199)。
管理対象パラメータ¶
| パス | 内容 | 登録方法 |
|---|---|---|
/rikako/<env>/openai-api-key |
OpenAI API キー | 手動 put-parameter |
/rikako/<env>/slack-contact-webhook-url |
お問い合わせ通知用 Slack Webhook | 手動 put-parameter |
/rikako/<env>/slack-alert-webhook-url |
CloudWatch アラート用 Slack Webhook | 手動 put-parameter |
/rikako/<env>/database-url |
Neon 接続文字列 | Terraform が neon_project.default.connection_uri を初期値として SecureString 登録。lifecycle.ignore_changes = [value] 指定のため以後の値はローテで上書き可(ローテ手順参照) |
/rikako/neon-api-key |
Neon API キー(Terraform Provider 用) | 手動 put-parameter |
初回登録¶
# 例: dev に OpenAI API key を登録
AWS_PROFILE=rikako-development-sso aws ssm put-parameter \
--name /rikako/development/openai-api-key \
--value 'sk-...' \
--type SecureString \
--region ap-northeast-1
ローテーション¶
SSM 値を put-parameter --overwrite で更新すれば、次回 Lambda cold start で新しい値が反映される(Lambda 関数の再デプロイ不要)。即時反映が必要な場合は Lambda コンソールから「最新バージョンを発行」または update-function-code で warm container を破棄する。
AWS_PROFILE=rikako-development-sso aws ssm put-parameter \
--name /rikako/development/openai-api-key \
--value 'sk-new-value...' \
--type SecureString \
--overwrite \
--region ap-northeast-1
確認¶
aws lambda get-function-configuration --query 'Environment.Variables' で シークレット実値が出ず ssm:/... リテラルだけが返ることを確認する。実値が露出していたら漏洩リスクがあるので即対応。
Neon DB パスワードのローテーション {#neon-db}¶
/rikako/<env>/database-url の Neon パスワードをローテする手順。
重要な前提 - Neon の role は DB を所有しているため、
terraform apply -replace=neon_role.defaultでの drop/recreate は HTTP 422ROLE_OWNS_OBJECTSで失敗する。ローテは必ず Neon API のreset_password(role を残しパスワードのみ再生成)で行う。 - SSMdatabase-urlはlifecycle.ignore_changes = [value]指定のため、put-parameter --overwriteした値を terraform が巻き戻さない。 - 環境ごとにアプリが使う role/DB が異なるので注意: - dev: rolerikako_owner/ DBrikako- prod: roleneondb_owner/ DBneondb(Neon デフォルト) - リセット直後から旧パスワードは無効になり、warm Lambda は cold start まで DB 接続に失敗する。
# 例: dev(prod の場合は PROFILE/PROJECT/BRANCH/ROLE/DB/SSM 名を読み替え)
export AWS_PROFILE=rikako-development-sso
API_KEY=$(aws ssm get-parameter --name /rikako/neon-api-key --with-decryption --query Parameter.Value --output text)
PROJECT_ID=muddy-tree-64549662
BRANCH_ID=br-calm-rice-a1123e1l
ROLE=rikako_owner
DB=rikako
HOST=$(cd terraform/environments/dev && terraform state show neon_project.default | grep 'database_host ' | sed -E 's/.*= "(.*)"/\1/')
# 1. パスワードを reset(新パスワードを取得、出力しない)
NEWPASS=$(curl -s -X POST \
"https://console.neon.tech/api/v2/projects/${PROJECT_ID}/branches/${BRANCH_ID}/roles/${ROLE}/reset_password" \
-H "Authorization: Bearer ${API_KEY}" -H "Accept: application/json" | jq -r '.role.password')
# 2. SSM を上書き
aws ssm put-parameter --name /rikako/development/database-url --type SecureString --overwrite \
--value "postgres://${ROLE}:${NEWPASS}@${HOST}/${DB}?sslmode=require"
# 3. Lambda を cold start(warm container に旧パスが残るため)
TS=$(date +%s)
for fn in rikako-api-development rikako-admin-api-development; do
aws lambda update-function-configuration --function-name "$fn" --description "db rotation $TS" >/dev/null
aws lambda wait function-updated --function-name "$fn"
done
# 4. 確認(DB を叩くエンドポイントが 200 を返すこと)
curl -s -o /dev/null -w "%{http_code}\n" https://api.dev.rikako.org/workbooks
prod は次のように読み替える: AWS_PROFILE=rikako-production-sso、PROJECT_ID は cd terraform/environments/prod && terraform state show neon_project.default の id、BRANCH_ID は同 default_branch_id、ROLE=neondb_owner、DB=neondb、SSM 名 /rikako/production/database-url、関数は rikako-api-production / rikako-admin-api-production。
3. マイグレーション実行手順¶
GitHub Actions のワークフローで実行する。
# GitHub上で手動実行(推奨)
gh workflow run "Run Database Migration" \
--repo takoikatakotako/rikako \
-f environment=dev \
-f direction=up \
-f steps=all
パラメータ¶
| パラメータ | 説明 |
|---|---|
| environment | dev または prod |
| direction | up(適用)または down(ロールバック) |
| steps | all(全て)または数値(ステップ数) |
マイグレーションロールバック¶
# 1ステップ戻す
gh workflow run "Run Database Migration" \
--repo takoikatakotako/rikako \
-f environment=dev \
-f direction=down \
-f steps=1
ローカルでのマイグレーション¶
docker compose up -d postgres
docker run --rm -v $(pwd)/migrations:/migrations \
migrate/migrate -path=/migrations \
-database "postgres://rikako:password@host.docker.internal:5432/rikako?sslmode=disable" up
4. データインポート・同期手順¶
datasync(推奨)¶
YAMLデータを正としてDBと差分同期する。
cd app
# ローカルDB
go run ./cmd/datasync -data ../data plan # 差分確認
go run ./cmd/datasync -data ../data apply # 反映
# dev環境
go run ./cmd/datasync -data ../data -env dev plan
go run ./cmd/datasync -data ../data -env dev apply
詳細は データ同期 (datasync) を参照。
画像のS3アップロード¶
# アップロード
aws s3 sync data/images/ s3://rikako-images-development/ --exclude ".DS_Store"
# 確認
aws s3 ls s3://rikako-images-development/ | wc -l
importer(全件再投入)¶
全データを削除して再投入する場合に使用。
cd app && go run ./cmd/importer -data ../data
5. 障害対応フロー¶
Lambda障害¶
症状: APIがエラーを返す、タイムアウトする
# 1. ヘルスチェック(prod は api.rikako.org に置き換え)
curl -s https://api.dev.rikako.org/health
# 2. CloudWatchダッシュボードを確認
# https://ap-northeast-1.console.aws.amazon.com/cloudwatch/home?region=ap-northeast-1#dashboards/dashboard/rikako-dev
# 3. Lambda最新ログを確認
aws logs tail /aws/lambda/rikako-api-development --since 30m
# 4. エラーのみ抽出
aws logs filter-log-events \
--log-group-name /aws/lambda/rikako-api-development \
--start-time $(date -v-1H +%s000) \
--filter-pattern "ERROR"
# 5. 必要に応じてロールバック(→ 2. ロールバック手順)
DB障害(Neon)¶
症状: API が 500 エラー、connection refused
# 1. Neonダッシュボードを確認
# https://console.neon.tech/
# 2. 接続テスト
go run ./cmd/datasync -data ../data -env dev plan
# 3. Neonのステータスページを確認
# https://neonstatus.com/
Neonが応答しない場合: - Neonのステータスページでリージョン障害を確認 - 障害が長引く場合はNeonサポートに連絡
画像配信障害¶
症状: 画像が表示されない、403/404エラー
# 1. CloudFront経由でアクセス確認(prod は image.rikako.org に置き換え)
curl -I https://image.dev.rikako.org/1.png
# 2. S3直接確認
aws s3 ls s3://rikako-images-development/1.png
# 3. CloudFrontキャッシュが古い場合は無効化
aws cloudfront create-invalidation \
--distribution-id E1LVBAGQ7YS8CR \
--paths "/*"
6. ログ調査手順¶
CloudWatch Logs¶
# 直近30分のログ(公開API)
aws logs tail /aws/lambda/rikako-api-development --since 30m --follow
# 直近30分のログ(管理API)
aws logs tail /aws/lambda/rikako-admin-api-development --since 30m --follow
# 特定パターンで検索
aws logs filter-log-events \
--log-group-name /aws/lambda/rikako-api-development \
--start-time $(date -v-1H +%s000) \
--filter-pattern "ERROR"
# 特定リクエストの調査(パスで絞り込み)
aws logs filter-log-events \
--log-group-name /aws/lambda/rikako-api-development \
--start-time $(date -v-1H +%s000) \
--filter-pattern "/questions"
CloudWatch Dashboard¶
ブラウザで確認:
https://ap-northeast-1.console.aws.amazon.com/cloudwatch/home?region=ap-northeast-1#dashboards/dashboard/rikako-dev
確認できるメトリクス: - Invocations(呼び出し回数) - Duration(平均・p99レイテンシ) - Errors(エラー数) - Throttles(スロットリング数) - ConcurrentExecutions(同時実行数)
注意: ログ保持期間は7日間。それ以前のログは自動削除される。
7. スケーリング手順¶
Neon CU(コンピューティングユニット)変更¶
現在: dev は 0.25〜2 CU、prod は 0.25〜4 CU(auto-scaling、auto-suspend 無効)。
cd terraform/environments/dev # prod は environments/prod
# terraform で変更(neon.tf の default_endpoint_settings を編集)
terraform plan
terraform apply
neon.tf の該当箇所(neon_project の default_endpoint_settings ブロック):
resource "neon_project" "default" {
# ...
default_endpoint_settings {
autoscaling_limit_min_cu = 0.25 # 最小CU
autoscaling_limit_max_cu = 2 # 最大CU(prod は 4)
suspend_timeout_seconds = 0 # 常時稼働(auto-suspend 無効)
}
}
Lambda設定変更¶
メモリ・タイムアウトの変更は terraform/environments/dev/main.tf を編集:
module "lambda" {
memory_size = 512 # MB(現在値)
timeout = 30 # 秒(現在値)
}
cd terraform/environments/dev
terraform plan
terraform apply
Lambda同時実行数の制限¶
現在は制限なし(AWSアカウントデフォルト: 1000)。制限が必要な場合:
# 一時的に制限(Terraform外)
aws lambda put-function-concurrency \
--function-name rikako-api-development \
--reserved-concurrent-executions 100
# 制限解除
aws lambda delete-function-concurrency \
--function-name rikako-api-development
8. Terraform操作¶
# Plan/Apply(dev)
cd terraform/environments/dev
terraform plan
terraform apply
# Plan/Apply(shared)
cd terraform/environments/shared
terraform plan
terraform apply
注意: PRで
terraform/配下を変更するとGitHub Actionsで自動的にplanが実行され、結果がPRにコメントされる。
9. よくある操作¶
新しい問題を追加する¶
data/questions/{id}.ymlを作成- 必要なら
data/images/{id}.pngを追加 data/workbooks/*.ymlのquestionsに追加datasync planで確認 →datasync applyで反映- 画像があれば
aws s3 syncでS3にアップロード
Cognito ユーザー作成¶
aws cognito-idp admin-create-user \
--user-pool-id ap-northeast-1_DvsZzCoJw \
--username <email> \
--user-attributes Name=email,Value=<email>
CloudFrontキャッシュクリア¶
# 画像CDN
aws cloudfront create-invalidation \
--distribution-id E1LVBAGQ7YS8CR \
--paths "/*"
# 管理画面
aws cloudfront create-invalidation \
--distribution-id EIA13UUJ41NKO \
--paths "/*"