はじめに
Terraformで構築したAWS環境に対して、
- GitHub Actions
- OIDC
- SSM Run Command
- Ansible
を利用した自動デプロイ環境を構築しました。
最終的には問題なく動作するようになりましたが、構築中にはいくつかハマったポイントがありました。
この記事では、実際に遭遇したエラーや調査方法、解決までの流れをまとめます。
同じような構成を構築している方の参考になれば幸いです。
構成
GitHub Actions
↓
OIDC認証
↓
IAM Role
↓
SSM Run Command
↓
EC2
↓
Ansible
↓
Spring Boot
ハマったポイント①
SSMコマンドが成功なのに何も起きない
最初に遭遇した問題です。
GitHub Actions上では正常終了しているにも関わらず、EC2側では何も実行されていませんでした。
実際の状況
GitHub Actions
↓
Success
しかし、
EC2
↓
何も実行されていない
という状態でした。
調査
まずはSSMの実行履歴を確認しました。
Systems Manager
↓
Run Command
↓
Command History
すると、コマンド自体は正常に送信されていることが確認できました。
原因
GitHub Actionsでは以下を実行していました。
aws ssm send-command
しかし、このコマンドは処理完了まで待ってくれるわけではありません。
実際には
コマンド送信
↓
Command ID返却
↓
終了
という非同期処理でした。
つまり、
GitHub Actions Success
≠
EC2上の処理成功
だったのです。
解決
Command IDを取得し、実行完了までポーリングするようにしました。
STATUS=$(aws ssm get-command-invocation \
--command-id "${COMMAND_ID}" \
--instance-id "${EC2_INSTANCE_ID}" \
--query "Status" \
--output text)
さらに、
if [ "${STATUS}" != "Success" ]; then
exit 1
fi
を追加し、SSM実行失敗時はWorkflowも失敗するようにしました。
ハマったポイント②
SSMのログをどこで見ればいいのかわからない
SSM実行結果を確認したかったのですが、
最初はどこを見ればいいのかわかりませんでした。
解決
以下の画面から確認できます。
Systems Manager
↓
Run Command
↓
Command History
ここを見ると、
- 実行結果
- エラー内容
- 実行時間
などを確認できます。
トラブルシューティングの第一歩はここでした。
ハマったポイント③
CloudWatch Logsにログが出力されない
SSM実行結果を詳しく確認したくてCloudWatch Logs出力を設定しました。
しかし、ロググループには何も出力されません。
調査①
まずCloudWatch Logsを確認すると、
ロググループ自体が存在していませんでした。
そのためTerraformでロググループを作成しました。
resource "aws_cloudwatch_log_group" "ssm_run_command" {
name = "SSMRunCommandLogs"
retention_in_days = 7
}
しかしまだ出力されない
ロググループ作成後もログは出力されませんでした。
調査②
さらに調査すると、EC2ロール側にCloudWatch Logsへの書き込み権限が不足していました。
不足していた権限は以下です。
logs:CreateLogStream
logs:DescribeLogStreams
logs:PutLogEvents
解決
EC2ロールへ以下の権限を追加しました。
{
"Effect": "Allow",
"Action": [
"logs:CreateLogStream",
"logs:DescribeLogStreams",
"logs:PutLogEvents"
]
}
追加後は正常にログが出力されるようになりました。
ハマったポイント④
IAM権限不足の調査とResourceの絞り込み
Terraform Plan実行時に以下のエラーが発生しました。
AccessDenied:
iam:GetInstanceProfile
最初は、
- Terraformの設定ミス?
- OIDCの設定ミス?
- IAMロールの設定ミス?
と原因が分かりませんでした。
調査
CloudTrailのイベント履歴を確認しました。
CloudTrail
↓
Event History
↓
エラーイベントを確認
すると、
Event Source
↓
iam.amazonaws.com
Event Name
↓
GetInstanceProfile
Error Code
↓
AccessDenied
となっていました。
これにより、GitHub Actions用IAMロールが
iam:GetInstanceProfile
を実行しようとして失敗していたことが分かりました。
解決
GitHub Actions用ロールへ以下の権限を追加しました。
iam:GetInstanceProfile
追加後はTerraform Planが正常に実行できるようになりました。
Resourceの絞り込みにも苦戦した
今回の構築では、IAMポリシーを必要最小限にすることも意識していました。
最初は動作確認を優先し、
{
"Resource": "*"
}
のような広い権限で検証していました。
しかし、そのままでは権限が広すぎるため、実際に使用するリソースのARNへ絞り込むことにしました。
すると今度は別の権限エラーが発生するようになりました。
そのたびにCloudTrailを確認し、
- 誰が
- どのAPIを
- どのリソースに対して
- なぜ失敗したのか
を調査しながら権限を修正しました。
学んだこと
IAMポリシーは最初から完璧に設計するのは難しいと感じました。
まずは動作確認を行い、その後CloudTrailを利用して実際に使用されているAPIやリソースを確認しながら、少しずつ権限を絞っていく方が効率的でした。
また、IAM権限エラーが発生した場合は、
CloudTrail
↓
Event History
を確認することで、
- 誰が
- どのAPIを
- なぜ失敗したのか
を把握できるため、トラブルシューティングに非常に役立ちました。
ハマったポイント⑤
Terraform Stateが実環境とずれた
学習中、AWSコンソールから直接リソースを削除したことがありました。
その後Terraformを実行すると、エラーが発生するようになりました。
実際の状況
Terraformは、
Terraform State
↓
EC2が存在する
と認識しています。
しかし実際のAWS環境では、
AWS環境
↓
EC2は削除済み
という状態になっていました。
つまり、
Terraform State
↓
存在する
実環境
↓
存在しない
という不整合が発生していました。
なぜ起きたのか
TerraformはStateファイルを基準にリソースを管理しています。
そのため、
Terraform
↓
Stateを参照
↓
変更差分を計算
という流れで動作します。
AWSコンソールから直接リソースを削除しても、TerraformのStateは自動では更新されません。
結果として、
Terraformの認識
↓
まだ存在する
実際
↓
既に削除済み
となり、エラーが発生します。
解決方法
状況に応じて以下を利用しました。
Stateから削除する場合
terraform state rm <resource>
Terraformの管理対象からリソースを外します。
既存リソースを取り込む場合
terraform import
既に存在するAWSリソースをTerraform管理下へ取り込みます。
学んだこと
学習中はAWSコンソールから直接操作したくなることがあります。
しかしTerraform管理下のリソースについては、
Terraformで作る
↓
Terraformで変更する
↓
Terraformで削除する
を徹底した方がよいと感じました。
特にチーム開発では、コンソールからの直接変更はStateとの不整合を引き起こす原因になります。
今回の経験を通じて、
「Terraformの本当の管理対象はAWSではなくStateファイルである」
ということを強く実感しました。
まとめ
GitHub Actions + SSM + Ansibleによる自動デプロイ環境は便利ですが、
構築中にはさまざまなトラブルに遭遇しました。
今回紹介した内容はすべて実際に発生したものです。
今回の構築を通して特に学びになったのは以下の3点です。
- SSMは非同期で動作するため、実行結果の確認が必要
- CloudWatch LogsやSSM Command Historyなどのログ確認がトラブルシューティングの第一歩になる
- IAM権限不足の調査にはCloudTrailが非常に有効
また、Terraformを利用する場合は、AWSコンソールから直接変更せず、
Terraformで作成
↓
Terraformで変更
↓
Terraformで削除
を徹底することの重要性も実感しました。
今回の経験を通じて、
「AWSのトラブルシューティングはまずログを見る」
という習慣の大切さを学びました。
同じような構成を構築する際の参考になれば幸いです。