
本番デプロイ当日の記録 — Canary SG作成・ECS SG更新・Pulumiデプロイまでの一部始終
前回の記事では、Canary 用のセキュリティグループ(SG)管理を Pulumi から Terraform へ段階的に移行する設計について書きました。
今回はその続きで、実際に本番環境へデプロイした当日の記録です。
設計フェーズとは違い、当日はいくつか予期しない細かいハマりもありました。特に .yml vs .yaml の拡張子まわりは、知っていて損のない話だったので残しておきます。
前提:作業の全体像
この作業はいくつかのフェーズに分かれています。
Phase 1: Canary用SGを手動作成(ingress無し、egress 80/443のみ)
Phase 2: 複数のECS SGにCanary SGからのingressルールを追加
Phase 3: Pulumi.prod.ymlのcanarySecurityGroupIdを実IDに差し替え → commit & push
Phase 4: PRをdevにマージ
Phase 5: Jenkinsでpreview → deploy
Phase 6: Canary動作確認(RUNNING/PASSED確認、SNSサブスクリプション承認)
Phase 7: snsEnabled: true化、その他後処理(別PR)今回の記録は主に Phase 1〜3 と、途中で発生したいくつかのやり直しについてです。
作業開始前:読み取りだけでシミュレーションする
実際に操作を始める前に、読み取り専用のコマンドだけで事前確認を行いました。
これは「本番でやってはじめて気づく」を減らすためのステップです。
確認したこと:
- AWS プロファイルで正しいアカウントにログインできているか
- VPC が存在するか
- Canary 用 SG がすでに存在していないか(重複作成のリスク)
- Phase 2 で操作対象となる ECS SG が全件存在するか
- Git ブランチの状態
Pulumi.prod.ymlのプレースホルダーがまだ書き換えられていないか
# プロファイル確認
aws sts get-caller-identity --profile <prod-profile>
# VPC存在確認
aws ec2 describe-vpcs --vpc-ids <vpc-id> --profile <prod-profile> --region us-west-2
# Canary SG重複チェック
aws ec2 describe-security-groups \
--filters "Name=group-name,Values=<canary-sg-name>" \
"Name=vpc-id,Values=<vpc-id>" \
--profile <prod-profile> --region us-west-2
# ECS SG一覧取得
for NAME in <ecs-sg-name-1> <ecs-sg-name-2> ...; do
SG_ID=$(aws ec2 describe-security-groups \
--filters "Name=group-name,Values=$NAME" \
--query 'SecurityGroups[0].GroupId' --output text \
--profile <prod-profile> --region us-west-2)
echo "$NAME = $SG_ID"
done結果として「Canary SG は未作成、ECS SG は全件存在、Pulumi.prod.yml はまだプレースホルダー」を確認できたので、安心して Phase 1 に進めました。
シミュレーション自体は 5 分もかからないのですが、「実行していいよ」と言える状態を作るという意味で、心理的な効果が大きいです。
Phase 1: Canary SG作成
# SGを作成
MSYS_NO_PATHCONV=1 aws ec2 create-security-group \
--group-name <canary-sg-name> \
--description "Security group for Synthetics Canary" \
--vpc-id <vpc-id> \
--tag-specifications 'ResourceType=security-group,Tags=[{Key=Name,Value=<canary-sg-name>}]' \
--profile <prod-profile> --region us-west-2
# デフォルトのegress(全開放)を削除
MSYS_NO_PATHCONV=1 aws ec2 revoke-security-group-egress \
--group-id <canary-sg-id> \
--ip-permissions '[{"IpProtocol":"-1","IpRanges":[{"CidrIp":"0.0.0.0/0"}]}]' \
--profile <prod-profile> --region us-west-2
# HTTP(80)を許可
MSYS_NO_PATHCONV=1 aws ec2 authorize-security-group-egress \
--group-id <canary-sg-id> \
--ip-permissions '[{"IpProtocol":"tcp","FromPort":80,"ToPort":80,"IpRanges":[{"CidrIp":"0.0.0.0/0"}]}]' \
--profile <prod-profile> --region us-west-2
# HTTPS(443)を許可
MSYS_NO_PATHCONV=1 aws ec2 authorize-security-group-egress \
--group-id <canary-sg-id> \
--ip-permissions '[{"IpProtocol":"tcp","FromPort":443,"ToPort":443,"IpRanges":[{"CidrIp":"0.0.0.0/0"}]}]' \
--profile <prod-profile> --region us-west-2MSYS_NO_PATHCONV=1 が必須なのは Git Bash 特有の問題です。
Git Bash は Windows のパス変換を自動でやろうとするので、0.0.0.0/0 のような文字列も変換対象になってしまいます。この環境変数を設定することで変換を無効化できます。
作成後は ingress が空、egress が HTTP/HTTPS のみ — 意図通りの状態を確認して次へ。
Phase 2: 既存ECS SGへのingressルール追加
Canary SG の ID が確定したので、監視対象となる複数の ECS SG に対して、「Canary SG からの TCP/80」 ingress ルールを追加します。
MSYS_NO_PATHCONV=1 aws ec2 authorize-security-group-ingress \
--group-id <ecs-sg-id> \
--ip-permissions '[{
"IpProtocol":"tcp",
"FromPort":80,
"ToPort":80,
"UserIdGroupPairs":[{
"GroupId":"<canary-sg-id>",
"Description":"Allow from Synthetics Canary SG"
}]
}]' \
--profile <prod-profile> --region us-west-2これを対象の ECS SG 分だけ繰り返しました(今回は 6 件)。
最後に逆引きで確認。「Canary SG からの ingress を許可している SG を検索」するフィルターが便利です。
MSYS_NO_PATHCONV=1 aws ec2 describe-security-groups \
--filters "Name=ip-permission.group-id,Values=<canary-sg-id>" \
--profile <prod-profile> --region us-west-2 \
--query 'SecurityGroups[*].GroupName'6 件全件ヒットを確認して Phase 3 へ。
Phase 3: Pulumi.prod.ymlの更新とcommit
Pulumi.prod.yml のプレースホルダーを、Phase 1 で作成した Canary SG の ID に書き換えます。
# 変更前
cm-iac-monitoring:canarySecurityGroupId: sg-xxxxxxxxxxxxxxxxx
# 変更後
cm-iac-monitoring:canarySecurityGroupId: <作成したCanary SG ID>commit → push して、PR を Ready for review に変更。
ここで発生したやり直し:.yml vs .yaml 問題
Phase 5 の Jenkins preview 実行後、こんなエラーが出ました。
error: Missing required configuration variable 'cm-iac-monitoring:vpcSubnetIds'
please set a value using the command `pulumi config set cm-iac-monitoring:vpcSubnetIds <value>`「あれ、vpcSubnetIds は Pulumi.prod.yml に書いてあるはず…」と思ったら、原因はファイルのリネームでした。
作業の途中で「.yml よりも .yaml の方が正式では?」という話になり、Pulumi.prod.yml を Pulumi.prod.yaml にリネームしていたのですが、それがエラーの原因でした。
Pulumiのスタック設定ファイル探索ルール
Pulumi はプロジェクトファイル(Pulumi.yml)と同じ拡張子でスタック設定ファイルを探します。
✅ Pulumi.yml + Pulumi.prod.yml → 動作する
✅ Pulumi.yaml + Pulumi.prod.yaml → 動作する
❌ Pulumi.yml + Pulumi.prod.yaml → config が読まれずエラーこのリポジトリのプロジェクトファイルは Pulumi.yml(.yml)だったので、スタックファイルも .yml でなければなりませんでした。.yaml にリネームしたことで Pulumi が設定を見失い、「vpcSubnetIds が無い」という形でエラーになりました。
ステートに保存されているのはリソース情報であって、config の値ではありません。そのため「ステートに入っているから大丈夫」という判断は機能しない、というのも確認になりました。
.yml と .yaml — なぜ両方あるのか
ついでに、.yml と .yaml が両方存在する背景も整理しておきます。
.yaml が YAML 仕様(yaml.org)の推奨です。一方 .yml は、かつての Windows の 8.3 ファイル名制約(FAT16 時代、拡張子は最大 3 文字)の名残です。.htm vs .html、.jpg vs .jpeg と同じ事情です。
8.3 制約はとっくに廃止されていますが、普及済みのプロジェクトや慣習が残り、両方が共存し続けています。今後新規に作るなら .yaml を使う方が仕様的には正しいのですが、既存プロジェクトを変えるコストは高い。今回はそれを実感しました。
後半の追加作業:snsEnabled の有効化
デプロイ後、各エンドポイントで snsEnabled: false になっていた設定を true に変更しました。初回デプロイ時は意図的に false にしており(まず動作確認を優先)、問題ないことを確認してから有効化するという手順です。
# 変更前
- name: <endpoint-name>
...
snsEnabled: false
# 変更後
- name: <endpoint-name>
...
snsEnabled: true全エンドポイントに対して変更して commit & push。
振り返り
当日の作業を通じてあらためて感じたこと。
事前シミュレーションは省略しない方がいい。 読み取りだけで 5〜10 分かけてでも、「実行前に全件確認できた」という状態は精神的に全然違います。本番操作中に「そういえばこの SG 存在するっけ」となるのを防げます。
Pulumi のスタック設定ファイルは拡張子を揃える。 これは知らないとハマります。エラーメッセージも「config が無い」という形で出るので、原因がファイル名だとすぐには気づきにくい。
命名規則の変更は、影響範囲を正しく見積もってから。 .yml → .yaml の変更は「単なるリネーム」に見えますが、Pulumi のように拡張子の一致を前提とするツールでは動作に影響します。今回は monitoring 配下のみでも、他にも同じパターンがあるなら一括で変えないとかえって混乱します。
今回は一部作業で Claude Code を使って操作しました。AWS の操作手順を事前に読み込んでもらい、フェーズごとに確認しながら進める形です。コマンドを手で打つよりも、「今この操作は何をしているか」「何を確認すべきか」をセットで確認しながら進められるのが良かったです。
次のステップは、今回まだ手動で行った SG 管理を Terraform に移行することです。それはまた別の記事で。
参考
AWS の運用手順やトラブル対処を体系的に学びたい方には、以下の書籍が参考になります。本記事のような事前確認や CLI 操作の作法を整理するのに役立ちます。
この記事は役に立ちましたか?

この記事が、何かの整理につながったら
コーヒー1杯分の応援をもらえると嬉しいです。
あなたへのおすすめ
