0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【第10回】AWS ECS Fargate 11回シリーズ|Phase 5-2|Secrets Manager & 全システム完成!

Last updated at Posted at 2026-01-23

この記事について

記事 タイトル 状態
第0回 全体ガイド ✅ 完了
第1回 構成図編 ✅ 完了
第2回 VPC & Subnet ✅ 完了
第3回 NAT Gateway & Route Table ✅ 完了
第4回 Security Group ✅ 完了
第5回 ECR & Docker Image ✅ 完了
第6回 Public ALB ✅ 完了
第7回 ECS Front & IAM ✅ 完了
第8回 ECS API & Internal ALB ✅ 完了
第9回 RDS MySQL ✅ 完了
第10回 Secrets Manager & 完成 📍今回

進捗: 100% (11/11記事) | Phase: 5-2 | 🎉マイルストーン5達成: 全システム完全動作!

📁 完全なコードはGitHubで公開GitHub: fargate-iac-02


1. はじめに

この記事は、Phase 5の後半として、Secrets ManagerでDBパスワードを管理し、全システムを完成させます。

📝 この記事の構成:

  • Terraformコードの全文を掲載し、1ファイルずつ詳しく解説します
  • 各コードブロックには初心者向けのコメントを充実させています
  • 完全なコードはGitHubでも公開しています(記事末尾のリンク参照)

1-1. 前回までの振り返り

Phase 5-1(第9回)で作成したもの:

  • ✅ RDS MySQL(Multi-AZ)
  • ✅ DB Subnet Group
  • ✅ 初期テーブルとデータ

今回(第10回: Phase 5-2)で作成するもの:

  • Secrets Manager(DBパスワード)
  • Secret Version
  • Phase 4の環境変数更新(Secrets Manager参照)
  • IAM Policy更新(Secrets Manager読み取り権限)

1-2. この記事で学べること

  • Secrets Managerの基本と役割
  • 環境変数でのSecrets参照方法
  • IAM Policyの更新
  • 全システムの統合

1-3. 対象読者

  • Phase 5-1(第9回)を完了した方
  • RDS MySQLを作成済みの方

1-4. 想定環境

  • Terraform: v1.9.x
  • Phase 1-5-1: 実行済み

2. Phase 5-2で作成するもの

2-1. リソース一覧

リソース 数量 用途 日額費用(24h) 月額費用
Secrets Manager 1 DB接続情報保存 $0.013 約$0.40
Secret Version 1 実際の値保存 無料 無料

合計: 2リソース
コスト: 約$0.013/日(約$0.40/月)

💰 Secrets Managerコスト詳細:

  • シークレット保存: $0.40/月 ÷ 30日 = $0.013/日
  • API呼び出し: $0.05/10,000回(従量課金)

Phase 5完成時点の累計コスト:

  • Phase 1-4: 約$5.74/日
  • Phase 5: 約$1.18/日
  • 合計: 約$6.92/日(約$208/月)

2-2. 構成図での位置づけ

API (ECS Fargate)
  ↓ IAM Task Role
Secrets Manager 👈 今回作成
  ├─ DB_PASSWORD
  ├─ DB_USERNAME
  └─ DB_SERVERNAME
  ↓ 環境変数として注入
API Container
  ↓ MySQL接続
RDS MySQL

3. Secrets Managerの役割

3-1. Secrets Managerとは

AWS Secrets Managerは、機密情報を安全に保存・管理するサービスです。

特徴:

  • パスワードを暗号化して保存
  • 自動ローテーション機能
  • IAMで細かくアクセス制御
  • 監査ログ(CloudTrail)

料理で例えると:

  • 環境変数にパスワード = レシピにパスワードを書く(誰でも見える)
  • Secrets Manager = 金庫にパスワードを保管(許可された人だけ見える)

3-2. なぜSecrets Managerが必要?

環境変数に直接書く問題点:

 悪い例: Task Definitionに直接書く
environment = [
  {
    name  = "DB_PASSWORD"
    value = "MySecurePass123!"  # 👈 平文で保存される
  }
]

問題点:
- terraform.tfstateに平文で保存される
- AWSコンソールで誰でも見える
- ログに出力される可能性

Secrets Managerを使う:

 良い例: Secrets Managerから取得
secrets = [
  {
    name      = "DB_PASSWORD"
    valueFrom = "arn:aws:secretsmanager:...:secret:db-password"  # 👈 参照のみ
  }
]

メリット:
- パスワードは暗号化して保存
- IAMで読み取り制限
- ローテーション可能

3-3. Secrets vs Environment

ECS Task Definitionでの設定方法:

方式 用途 セキュリティ
environment 一般的な設定値 低(平文)
secrets 機密情報 高(暗号化)

今回の使い分け:

  • environment: なし
  • secrets: DB_PASSWORD、DB_USERNAME、DB_SERVERNAME

4. ディレクトリ構成

4-1. Phase 5の完全なファイル構成

terraform/phase5-rds/
├── main.tf                ← 第9回で作成済み
├── variables.tf           ← 第9回で作成済み
├── terraform.tfvars       ← 第9回で作成済み
├── data.tf                ← 第9回で作成済み
├── rds.tf                 ← 第9回で作成済み
├── secrets.tf             👉 今回追加
└── outputs.tf             ← 更新

4-2. 追加ファイルの詳細

ファイル名 役割 行数
secrets.tf Secrets Manager、Secret Version 40

5. 追加ファイルの作成

5-1. ディレクトリ移動

Phase 5のディレクトリに移動します:

cd terraform/phase5-rds

5-2. 追加ファイルの作成

Secrets Manager用のファイルを作成します:

touch secrets.tf

作成されたファイル:

  • secrets.tf - Secrets Manager、Secret Version

確認:

ls -la
# main.tf, variables.tf, terraform.tfvars, data.tf, rds.tf, outputs.tf (第9回)
# secrets.tf (今回追加)

6. ファイル別コード解説

それでは、追加したファイルのコードを解説していきます。

6-1. secrets.tf - Secrets Manager

ファイルの役割:

  • Secrets Managerを作成
  • DB接続情報を保存

コード:

# ファイルパス: terraform/phase5-rds/secrets.tf

# ==========================================
# Secrets Manager Secret
# ==========================================
resource "aws_secretsmanager_secret" "db" {
  name        = "${var.project_name}-db-credentials"
  description = "Database credentials for ${var.project_name}"

  tags = {
    Name = "${var.project_name}-db-credentials"
  }
}

# ==========================================
# Secret Version
# ==========================================
resource "aws_secretsmanager_secret_version" "db" {
  secret_id = aws_secretsmanager_secret.db.id

  secret_string = jsonencode({
    DB_SERVERNAME = aws_db_instance.main.address
    DB_USERNAME   = var.db_username
    DB_PASSWORD   = var.db_password
  })
}

ポイント:

  1. secret_string: JSON形式で複数の値を保存
  2. DB_SERVERNAME: RDSのエンドポイント(自動取得)
  3. DB_USERNAME, DB_PASSWORD: variables.tfから取得

保存されるJSON:

{
  "DB_SERVERNAME": "myproject-mysql.xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com",
  "DB_USERNAME": "admin",
  "DB_PASSWORD": "MySecurePass123!"
}

6-2. outputs.tf - 出力の更新

既存のoutputs.tfに、Secrets Manager ARNを追加:

# ファイルパス: terraform/phase5-rds/outputs.tf

# ... 既存のRDS出力 ...

output "secrets_manager_arn" {
  description = "Secrets Manager ARN"
  value       = aws_secretsmanager_secret.db.arn
}

7. Phase 4の更新(ECS API)

7-1. Phase 4のiam.tfを更新

Task RoleにSecrets Manager読み取り権限を追加:

# ファイルパス: terraform/phase4-ecs-api/iam.tf

# ... 既存のTask Role定義 ...

# カスタムポリシー(Secrets Manager読み取り権限を追加)
resource "aws_iam_role_policy" "ecs_task" {
  name = "${var.project_name}-ecs-task-policy"
  role = aws_iam_role.ecs_task.id

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect = "Allow"
        Action = [
          "secretsmanager:GetSecretValue"
        ]
        Resource = [
          data.terraform_remote_state.rds.outputs.secrets_manager_arn
        ]
      }
    ]
  })
}

ポイント:

  • GetSecretValue: Secretの値を取得
  • Resource: Phase 5で作成したSecrets Manager ARN

7-2. Phase 4のdata.tfを更新

Phase 5の参照を追加:

# ファイルパス: terraform/phase4-ecs-api/data.tf

# ... 既存のdata, locals ...

# Phase 5の出力を参照(追加)
data "terraform_remote_state" "rds" {
  backend = "local"

  config = {
    path = "../phase5-rds/terraform.tfstate"
  }
}

# Phase 5から取得する値をローカル変数化(追加)
locals {
  secrets_manager_arn = data.terraform_remote_state.rds.outputs.secrets_manager_arn
}

7-3. Phase 4のecs_task_def.tfを更新

ファイルパス: terraform/phase4-ecs-api/ecs_task_def.tf

コード骨格:

🔄 変更点: environment → secrets

変更前 変更後
environment ブロック secrets ブロック
value = "直接値" valueFrom = "${SECRET_ARN}:KEY::"

secrets ブロックの設定:

name valueFrom 説明
DB_SERVERNAME ${local.secrets_manager_arn}:DB_SERVERNAME:: RDSエンドポイント
DB_USERNAME ${local.secrets_manager_arn}:DB_USERNAME:: DBユーザー名
DB_PASSWORD ${local.secrets_manager_arn}:DB_PASSWORD:: DBパスワード

valueFrom の形式:

arn:aws:secretsmanager:ap-northeast-1:XXXXXXXXXXX:secret:myproject-db-credentials-xxxxx:DB_PASSWORD::
                                                                                        ^^^^^^^^^^^^
                                                                                        JSONのキー

簡易コード(変更部分のみ):

# terraform/phase4-ecs-api/ecs_task_def.tf

container_definitions = jsonencode([
  {
    name      = "api"
    image     = "${local.ecr_api_url}:latest"
    essential = true

    # 🔄 変更前: environment(平文)
    # environment = [
    #   { name = "DB_USERNAME", value = "admin" }
    # ]

    # 🔄 変更後: secrets(Secrets Manager参照)
    secrets = [
      {
        name      = "DB_SERVERNAME"
        valueFrom = "${local.secrets_manager_arn}:DB_SERVERNAME::"
      },
      {
        name      = "DB_USERNAME"
        valueFrom = "${local.secrets_manager_arn}:DB_USERNAME::"
      },
      {
        name      = "DB_PASSWORD"
        valueFrom = "${local.secrets_manager_arn}:DB_PASSWORD::"
      }
    ]

    portMappings = [{ containerPort = 8080, protocol = "tcp" }]
    # ... logConfiguration, healthCheck 省略
  }
])

フルコード:

# terraform/phase4-ecs-api/ecs_task_def.tf

resource "aws_ecs_task_definition" "api" {
  family                   = "${var.project_name}-api"
  network_mode             = "awsvpc"
  requires_compatibilities = ["FARGATE"]
  cpu                      = "256"   # 0.25 vCPU
  memory                   = "512"   # 0.5 GB
  execution_role_arn       = local.ecs_task_execution_role_arn
  task_role_arn            = local.ecs_task_role_arn

  container_definitions = jsonencode([
    {
      name      = "api"
      image     = "${local.ecr_api_url}:latest"
      essential = true

      # 🔄 secrets: Secrets Managerから環境変数を取得
      secrets = [
        {
          name      = "DB_SERVERNAME"
          valueFrom = "${local.secrets_manager_arn}:DB_SERVERNAME::"
        },
        {
          name      = "DB_USERNAME"
          valueFrom = "${local.secrets_manager_arn}:DB_USERNAME::"
        },
        {
          name      = "DB_PASSWORD"
          valueFrom = "${local.secrets_manager_arn}:DB_PASSWORD::"
        }
      ]

      portMappings = [
        {
          containerPort = 8080
          protocol      = "tcp"
        }
      ]

      logConfiguration = {
        logDriver = "awslogs"
        options = {
          "awslogs-group"         = aws_cloudwatch_log_group.api.name
          "awslogs-region"        = var.region
          "awslogs-stream-prefix" = "ecs"
        }
      }

      healthCheck = {
        command     = ["CMD-SHELL", "curl -f http://localhost:8080/ || exit 1"]
        interval    = 30
        timeout     = 5
        retries     = 3
        startPeriod = 60
      }
    }
  ])

  tags = {
    Name = "${var.project_name}-api-task"
  }
}

ポイント解説:

項目 説明
family ${var.project_name}-api タスク定義のファミリー名
network_mode awsvpc Fargate必須。ENI割り当て
cpu / memory 256 / 512 0.25 vCPU / 0.5 GB
execution_role_arn Phase 3から参照 ECR pull、CloudWatch Logs書き込み用
task_role_arn Phase 3から参照 Secrets Manager読み取り用
secrets 3項目 environment の代わり。Secrets Managerから取得
valueFrom ARN:KEY:: 末尾の :: は必須(バージョン省略を意味)
containerPort 8080 APIがLISTENするポート
healthCheck curl で / コンテナの死活監視

📁 完全なコードは GitHub: fargate-iac-02 を参照


8. 実行手順

8-1. Phase 5にSecrets Managerを追加

cd terraform/phase5-rds

# secrets.tfを作成
# outputs.tfを更新

terraform apply:

terraform apply

実行結果:

Plan: 2 to add, 0 to change, 0 to destroy.

aws_secretsmanager_secret.db: Creating...
aws_secretsmanager_secret.db: Creation complete
aws_secretsmanager_secret_version.db: Creating...
aws_secretsmanager_secret_version.db: Creation complete

Apply complete! Resources: 2 added, 0 changed, 0 destroyed.

Outputs:

secrets_manager_arn = "arn:aws:secretsmanager:ap-northeast-1:XXXXXXXXXXX:secret:myproject-db-credentials-xxxxx"

8-2. Phase 4を更新

cd ../phase4-ecs-api

# iam.tfを更新(Secrets Manager読み取り権限)
# data.tfを更新(Phase 5参照)
# ecs_task_def.tfを更新(secrets設定)

terraform plan:

terraform plan

実行結果:

Plan: 1 to add, 1 to change, 1 to destroy.

  # aws_iam_role_policy.ecs_task will be updated in-place
  # aws_ecs_task_definition.api must be replaced
  # aws_ecs_service.api will be updated in-place

terraform apply:

terraform apply

確認メッセージ:

Enter a value: yes

実行結果:

aws_iam_role_policy.ecs_task: Modifying...
aws_iam_role_policy.ecs_task: Modifications complete
aws_ecs_task_definition.api: Creating...
aws_ecs_task_definition.api: Creation complete
aws_ecs_service.api: Modifying...
aws_ecs_service.api: Still modifying... [1m0s elapsed]
aws_ecs_service.api: Still modifying... [2m0s elapsed]
aws_ecs_service.api: Modifications complete after 2m30s

Apply complete! Resources: 1 added, 1 changed, 1 destroyed.

所要時間: 約3-4分(ECS Serviceの更新)


9. 動作確認

9-1. AWSコンソールで確認

Secrets Manager:

  1. AWSコンソール → Secrets Manager → Secrets
  2. 「myproject-db-credentials」が作成されていることを確認
  3. 「Retrieve secret value」をクリック
  4. JSON形式でDB接続情報が保存されていることを確認

ECS Task Definition:

  1. ECS → Task Definitions → myproject-api
  2. 最新のRevisionをクリック
  3. Container definitions → api → Environment
  4. Secrets セクションに3つの設定があることを確認

ECS Service:

  1. ECS → Clusters → myproject-cluster → Services
  2. api-service → Tasks タブ
  3. 新しいTaskがRunning状態

9-2. ブラウザでテスト

Public ALBのDNS名でアクセス:

cd ../phase3-ecs-front
terraform output public_alb_dns

ブラウザでアクセス:

http://myproject-public-alb-xxx.ap-northeast-1.elb.amazonaws.com/

テスト1: API Test

「API Test」ボタンをクリック:

結果:

API Response: API接続テストが成功しました

成功!


テスト2: Database Test 🎉

「Database Test」ボタンをクリック:

結果:

DB Response: データベース接続テストが成功しました(test_tableの件数:4)

成功!


10. トラブルシューティング

10-1. エラー1: Database Testが失敗する

現象:

  • 「Database Test」ボタンをクリックするとエラー

確認ポイント:

確認1: Secrets Managerから値が取得できているか

CloudWatch Logsでエラー確認:

  1. CloudWatch → Log groups → /ecs/myproject/api
  2. 最新のLog streamを開く
  3. エラーメッセージを確認

よくあるエラー:

Error: dial tcp: lookup xxx.rds.amazonaws.com: no such host

原因: DB_SERVERNAMEが間違っている

解決策:

cd ../../terraform/phase5-rds
terraform output db_endpoint_address

# 正しいエンドポイントを確認

確認2: IAM Roleの権限

Task RoleにSecrets Manager読み取り権限があるか:

cd ../phase4-ecs-api
terraform state show aws_iam_role_policy.ecs_task

# secretsmanager:GetSecretValue があることを確認

確認3: RDSに初期データが投入されているか

ECS Task経由でRDSに接続:

# 第9回の「7-2. 接続テスト」を参照

# test_tableにデータがあるか確認
SELECT COUNT(*) FROM test_table;

期待される結果:

+----------+
| COUNT(*) |
+----------+
|        4 |
+----------+

10-2. エラー2: Secrets Managerの値が更新されない

現象:

  • Secrets Managerの値を変更したが、ECS Taskに反映されない

原因:

  • ECS Taskは起動時にSecretsを取得するため、再起動が必要

解決策:

# ECS Serviceを強制更新
aws ecs update-service \
  --cluster myproject-cluster \
  --service myproject-api-service \
  --force-new-deployment \
  --region ap-northeast-1

11. 全システムのまとめ

11-1. 作成したリソース(全Phase)

Phase リソース数 主要リソース 日額費用(24h) 月額費用
Phase 1 16 VPC、NAT Gateway × 2 $3.00 $90
Phase 2 7 Security Group × 5、ECR × 2 無料 無料
Phase 3 11 Public ALB、ECS Front × 2 $0.77 $23
Phase 4 8 Internal ALB、ECS API × 2 $1.37 $43
Phase 5 8 RDS Multi-AZ、Secrets Manager $1.18 $35
合計 50 - 約$6.32/日 約$191/月

11-2. コスト詳細(日額・月額)

カテゴリ リソース 日額費用(24h) 月額費用 割合
ネットワーク NAT Gateway × 2 $3.00 $90 42%
負荷分散 ALB × 2 $1.34 $40 19%
コンピューティング ECS Fargate(4 Task) $1.36 $41 19%
データベース RDS MySQL Multi-AZ $1.17 $35 16%
その他 Logs、Secrets等 $0.20 $6 3%
合計 - 約$7.07/日 約$212/月 100%

💰 日額コスト内訳(詳細):

リソース 時間単価 計算式 日額
NAT Gateway × 2 $0.062/h × 2 $0.124 × 24h = $2.976 $3.00
Public ALB $0.028/h $0.028 × 24h = $0.672 $0.67
Internal ALB $0.028/h $0.028 × 24h = $0.672 $0.67
ECS Front × 2 $0.01420/h × 2 $0.02840 × 24h = $0.6816 $0.68
ECS API × 2 $0.01420/h × 2 $0.02840 × 24h = $0.6816 $0.68
RDS Multi-AZ $0.048/h $0.048 × 24h = $1.152 $1.17
Secrets Manager - $0.40/月 ÷ 30 = $0.0133 $0.013
CloudWatch Logs - 約$0.05/日 $0.05
合計 - - $6.95/日

月額換算: $6.95 × 30日 = $208.50/月


11-3. システム構成図(完成版)

インターネット
  ↓ HTTPS:443 / HTTP:80
Public ALB (internet-facing, Multi-AZ)
  ↓ HTTP:80
Frontend × 2 (ECS Fargate, Private Subnet)
  ├─ Nginx
  └─ CloudWatch Logs
  ↓ HTTP:80
Internal ALB (internal, Multi-AZ)
  ↓ HTTP:8080
API × 2 (ECS Fargate, Private Subnet)
  ├─ Go Application
  ├─ CloudWatch Logs
  └─ IAM Task Role
      ↓ Secrets Manager読み取り
      Secrets Manager
        ├─ DB_SERVERNAME
        ├─ DB_USERNAME
        └─ DB_PASSWORD
  ↓ MySQL:3306
RDS MySQL (Multi-AZ)
  ├─ Primary DB (AZ-1a)
  └─ Standby DB (AZ-1c)

第1回記事から再掲:


11-4. 達成したマイルストーン

🎯 マイルストーン1: Phase 1完了
   ✅ ネットワーク基盤(VPC、Subnet、NAT Gateway)

🎯 マイルストーン2: Phase 2完了
   ✅ セキュリティ基盤(Security Group × 5、ECR × 2)

🎯 マイルストーン3: Phase 3完了
   ✅ Frontend動作(ブラウザで画面表示成功!)

🎯 マイルストーン4: Phase 4完了
   ✅ API動作(「API Test」ボタン成功!)

🎯 マイルストーン5: Phase 5完了
   ✅ 全システム完成(「Database Test」ボタン成功!)

12. リソースのクリーンアップ

12-1. 全リソースの削除

逆順で削除:

# Phase 5
cd terraform/phase5-rds
terraform destroy

# Phase 4
cd ../phase4-ecs-api
terraform destroy

# Phase 3
cd ../phase3-ecs-front
terraform destroy

# Phase 2
cd ../phase2-security
terraform destroy

# Phase 1
cd ../phase1-network
terraform destroy

⚠️ 注意:

  • 削除保護が有効なリソースは先に無効化
  • RDSの最終スナップショットを確認
  • 重要データは必ずバックアップ

12-2. ECRイメージの削除

手動削除が必要:

# Frontend
aws ecr batch-delete-image \
  --repository-name myproject-front \
  --image-ids imageTag=latest \
  --region ap-northeast-1

# API
aws ecr batch-delete-image \
  --repository-name myproject-api \
  --image-ids imageTag=latest \
  --region ap-northeast-1

(シリーズ完結)

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?