2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【AWS】CloudFront→ALBで504エラー!原因はまさかの〇〇だった件

2
Posted at

発生した問題

初期状況

ALBには2つのセキュリティグループがアタッチされていました:

Before: 
  ALB → SG1: 0.0.0.0/0からの80/443を許可
     → SG2: CloudFrontプレフィックスからの443を許可
  結果: ✅正常動作

After:
  ALB → SG1: 削除(セキュリティ強化のため)
     → SG2: CloudFrontプレフィックスからの443を許可(これのみ残す)
  結果: ❌504エラー大量発生

セキュリティを強化するため、0.0.0.0/0を許可していたSGを削除し、CloudFrontからのアクセスのみを許可するSGだけを残したところ、504 Gateway Timeoutが発生。

Terraformでの管理における落とし穴

構成管理の状況

# 削除対象のSG1(dataブロックで管理)
data "aws_security_group" "alb_allow_all" {
  id = "sg-old-allow-all"  # 0.0.0.0/0を許可していたSG
}

# 残すSG2(resourceブロックで管理)
resource "aws_security_group" "alb_cloudfront_only" {
  name = "alb-cloudfront-only"
  
  ingress {
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    prefix_list_ids = ["pl-58a04531"] # CloudFront
  }
}

# ALBの設定
resource "aws_lb" "main" {
  security_groups = [
    data.aws_security_group.alb_allow_all.id,        # SG1: 削除予定
    aws_security_group.alb_cloudfront_only.id        # SG2: 残す
  ]
}

反省点:dataブロックで管理していたSG1を削除する際、インバウンドルールの変更に気を取られ、このSGがECSのインバウンドルールで参照されていることの確認が疎かになった

トラブルシューティングの迷走記

第1の仮説:CloudFrontのIPを個別に許可すればいい?

CloudFrontのアクセスログを確認:

2025-08-28 5:49:25 NRT20-P2 54.249.34.116 GET /company/30 504 OriginError
2025-08-28 5:49:34 SEA19-C2 157.55.39.7 GET /job/557 504 OriginError

「これらのIPをセキュリティグループに追加すれば...」

→ 大きな勘違い!これらはエンドユーザーのIPで、CloudFrontのエッジサーバーのIPではない

第2の仮説:ALBのインバウンドルールに80ポートが必要?

ターゲットグループのヘルスチェックを確認:

ヘルスチェック:
  プロトコル: HTTP
  ポート: 80

「CloudFrontは443で通信、でもヘルスチェックは80...だからALBの80ポートを開ける必要がある!」

→ これも違った!ヘルスチェックはALB→ECS間の通信で、ALBのインバウンドは無関係

第3の仮説:ヘルスチェックをHTTPSに変更?

「じゃあヘルスチェックもHTTPS:443にすれば統一できて...」

→ でも検証環境では80のままで動いているぞ?

衝撃の展開:検証環境での再現

同じ構成を検証環境で再現:

  • ALBのSG:CloudFrontから443のみ許可
  • ヘルスチェック:HTTP:80
  • 結果:正常に動作している!

「なぜ検証環境では動くのに、問題の環境では504エラー?」

根本原因の特定

Terraformで見落としていた依存関係

# ALBのセキュリティグループ(削除したSG1)
data "aws_security_group" "alb_allow_all" {
  id = "sg-old-allow-all"  # 0.0.0.0/0を許可
}

# ECSのセキュリティグループ(dataブロックで管理)
data "aws_security_group" "ecs" {
  id = "sg-ecs-existing"
}

# ECSのセキュリティグループルール(別のtfファイルで管理)
resource "aws_security_group_rule" "ecs_from_alb" {
  type                     = "ingress"
  from_port                = 80
  to_port                  = 80
  protocol                 = "tcp"
  source_security_group_id = data.aws_security_group.alb_allow_all.id  # 削除したSG1を参照!
  security_group_id        = data.aws_security_group.ecs.id
}

dataブロックの落とし穴:

  • ALBのSG1もECSのSGもdataブロックで管理
  • 既存リソースへの参照のため、依存関係が見えにくい
  • terraform planでは「SGを削除すると、ECSのインバウンドルールが無効になる」という警告が出ない

削除したセキュリティグループの呪い

削除したSG1 (sg-old-allow-all):
  用途: 0.0.0.0/0からの80/443を許可
  状態: 削除済み
  
残したSG2 (sg-cloudfront-only):
  用途: CloudFrontプレフィックスからの443のみ許可
  状態: 運用中
  
でも実は...
  
ECSのセキュリティグループ:
  インバウンドルール:
    - ソース: sg-old-allow-all(削除済み!) ← 幽霊参照
    - ポート: 80
    
  本来必要だったのは:
    - ソース: sg-cloudfront-only(残したSG)
    - ポート: 80

真の原因と解決方法

根本原因:ALBとECSのセキュリティグループの紐付けが切れた

通信経路とSGの関係

正常時の通信フロー:
CloudFront → ALB (SG1 + SG2) → ECS
              ↓                  ↑
         アウトバウンド      インバウンド
         (デフォルト全許可)   (SG1からの80を許可)

SG1削除後:
CloudFront → ALB (SG2のみ) → × → ECS
              ↓                    ↑
         アウトバウンド        インバウンド
         (デフォルト全許可)     (存在しないSG1を待機)

具体的な問題

  1. ALBのアウトバウンド: デフォルトで全許可なので問題なし
  2. ECSのインバウンド: 削除したSG1からの通信のみ許可 → ここが問題!
  3. ALB(SG2)からのトラフィックがECSに到達できない

解決策

# ECSのSGルールを修正
resource "aws_security_group_rule" "ecs_from_alb" {
  type                     = "ingress"
  from_port                = 80
  to_port                  = 80
  protocol                 = "tcp"
  source_security_group_id = aws_security_group.alb_cloudfront_only.id  # 残したSG2に更新!
  security_group_id        = data.aws_security_group.ecs.id
}

つまり、ECSのインバウンドルールが、削除したSG1からの通信を待っていたが、実際にはSG2から通信が来ていたため、通信がブロックされていました。

学んだ教訓

1. Terraformでdataブロックを使う際の注意点

# SGを削除する前に必ず実行
# dataブロックで管理しているSGの参照先を確認
aws ec2 describe-security-groups \
  --filters "Name=ip-permission.group-id,Values=[削除予定のSG-ID]" \
  --query 'SecurityGroups[*].[GroupId,GroupName]'

2. インバウンドだけでなくアウトバウンドの影響も確認

実際には、今回の問題は「ALBのアウトバウンド」ではなく「ECSのインバウンドで参照しているSG」の問題でした:

# SGを削除する前に、そのSGがどこで参照されているか確認
aws ec2 describe-security-groups \
  --filters "Name=ip-permission.group-id,Values=[削除予定のSG-ID]" \
  --query 'SecurityGroups[*].[GroupId,GroupName,IpPermissions[?UserIdGroupPairs[?GroupId==`削除予定のSG-ID`]]]'

3. 504エラーの切り分け方

  • CloudFrontログでOriginError+10秒タイムアウト = ALB→ターゲット間の問題
  • まずターゲットヘルスを確認
  • 次にターゲット側のSG設定を確認

4. 正しいSG設計パターン

ALB-SG:
  インバウンド: CloudFrontプレフィックスリストから443
  
ECS-SG:
  インバウンド: ALB-SGのIDを指定して80を許可(IPではなくSG-IDで)

まとめ

トラブルシューティングで遠回りしましたが、その過程で以下を学んだ:

  1. ALBのインバウンド80は不要(ヘルスチェックとは無関係)
  2. SGの参照は削除後も残る(幽霊参照に注意)
  3. Terraformのdataブロックは依存関係が見えにくい

最終的な原因は「削除したSGの幽霊参照」という落とし穴でした。特にTerraformでdataブロックを使っている場合、インバウンドルールだけでなく、そのSGがどこで参照されているかも必ず確認しましょう!

参考リンク

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?