0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AWS 3層アーキテクチャをTerraformで構築してGitHubに安全に公開するまでの手順

0
Last updated at Posted at 2026-05-30

はじめに

本記事は、AWS上に「Web・AP・DB」の3層レイヤーインフラストラクチャをTerraform(IaC)で自動構築し、セキュリティに配慮した状態でGitHubへ公開するまでの手順をまとめた備忘録です。

後から同じ構成を完全に再現できるように、ソースコード、検証時のエラー対策、Gitのトラブルシューティングを網羅しています。

1. 成果物のインフラ構成

Gemini_Generated_Image_jjxtdajjxtdajjxt.png

個人開発環境における検証コスト(無料枠の適用)を最小限に抑えるため、あえてシングルAZ構成を採用しています。

  • Web層: インターネットからのHTTP(80)アクセスを受付(Ubuntu / Apache)
  • AP層: Web層からのみ通信を許可する隔離された領域(EC2 Ubuntu)
  • DB層: AP層からのみ接続を許可するデータベース領域(RDS MySQL)
  • 運用・保守: SSH秘密鍵の管理を不要にするため、AWS Systems Manager (Session Manager) を導入しブラウザから安全にリモートログイン可能。

2. ソースコードの準備

プロジェクトのルートディレクトリに、以下の3つのファイルを作成します。

main.tf

データベースのパスワードを直書きせず、variable を用いて外部から安全に注入する設計にしています。

# 1. プロバイダーの設定
provider "aws" {
  region = "ap-northeast-1"
}

# 2. VPCの定義
resource "aws_vpc" "main" {
  cidr_block           = "10.0.0.0/16"
  enable_dns_hostnames = true
  enable_dns_support   = true
  tags = { Name = "3tier-vpc" }
}

# 3. インターネットゲートウェイの定義
resource "aws_internet_gateway" "igw" {
  vpc_id = aws_vpc.main.id
  tags = { Name = "3tier-igw" }
}

# 4. Web層用サブネット(パブリック)
resource "aws_subnet" "web_1a" {
  vpc_id                  = aws_vpc.main.id
  cidr_block              = "10.0.1.0/24"
  availability_zone       = "ap-northeast-1a"
  map_public_ip_on_launch = true
  tags = { Name = "3tier-web-subnet-1a" }
}

# 5. AP層用サブネット(プライベート)
resource "aws_subnet" "app_1a" {
  vpc_id            = aws_vpc.main.id
  cidr_block        = "10.0.2.0/24"
  availability_zone = "ap-northeast-1a"
  tags = { Name = "3tier-app-subnet-1a" }
}

# 6. DB層用サブネット(プライベート)
resource "aws_subnet" "db_1a" {
  vpc_id            = aws_vpc.main.id
  cidr_block        = "10.0.3.0/24"
  availability_zone = "ap-northeast-1a"
  tags = { Name = "3tier-db-subnet-1a" }
}

# 7. ルートテーブルの定義(パブリック用)
resource "aws_route_table" "public" {
  vpc_id = aws_vpc.main.id
  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.igw.id
  }
  tags = { Name = "3tier-public-rt" }
}

# 8. ルートテーブルとWeb層サブネットの紐付け
resource "aws_route_table_association" "web_1a" {
  subnet_id      = aws_subnet.web_1a.id
  route_table_id = aws_route_table.public.id
}

# 9. Webサーバー用セキュリティグループ
resource "aws_security_group" "web_sg" {
  name        = "3tier-web-sg"
  description = "Allow HTTP traffic from internet"
  vpc_id      = aws_vpc.main.id

  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
  tags = { Name = "3tier-web-sg" }
}

# 10. APサーバー用セキュリティグループ
resource "aws_security_group" "app_sg" {
  name        = "3tier-app-sg"
  description = "Allow traffic from Web SG"
  vpc_id      = aws_vpc.main.id

  ingress {
    from_port       = 3000
    to_port         = 3000
    protocol        = "tcp"
    security_groups = [aws_security_group.web_sg.id]
  }
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
  tags = { Name = "3tier-app-sg" }
}

# 11. DB(RDS)用セキュリティグループ
resource "aws_security_group" "db_sg" {
  name        = "3tier-db-sg"
  description = "Allow traffic from App SG"
  vpc_id      = aws_vpc.main.id

  ingress {
    from_port       = 3306
    to_port         = 3306
    protocol        = "tcp"
    security_groups = [aws_security_group.app_sg.id]
  }
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
  tags = { Name = "3tier-db-sg" }
}

# 12. Webサーバー(EC2インスタンス)
resource "aws_instance" "web" {
  ami                    = "ami-0d52744d6551d851e" 
  instance_type          = "t3.micro"
  subnet_id              = aws_subnet.web_1a.id
  vpc_security_group_ids = [aws_security_group.web_sg.id]
  iam_instance_profile   = aws_iam_instance_profile.ec2_profile.name

  user_data = <<-EOF
              #!/bin/bash
              apt update -y
              apt install -y apache2
              systemctl start apache2
              systemctl enable apache2
              echo "<h1>Hello from AWS 3-Tier Architecture (Web Layer)</h1>" > /var/www/html/index.html
              EOF

  tags = { Name = "3tier-web-server" }
}

# 13. APサーバー(EC2インスタンス)
resource "aws_instance" "app" {
  ami                    = "ami-0d52744d6551d851e"
  instance_type          = "t3.micro"
  subnet_id              = aws_subnet.app_1a.id
  vpc_security_group_ids = [aws_security_group.app_sg.id]
  iam_instance_profile   = aws_iam_instance_profile.ec2_profile.name
  tags = { Name = "3tier-app-server" }
}

# 14. SSM用のIAMロールとプロファイル
resource "aws_iam_role" "ec2_ssm" {
  name = "3tier-ec2-ssm-role"
  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action = "sts:AssumeRole"
        Effect = "Allow"
        Principal = { Service = "ec2.amazonaws.com" }
      }
    ]
  })
}

resource "aws_iam_role_policy_attachment" "ssm_attach" {
  role       = aws_iam_role.ec2_ssm.name
  policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
}

resource "aws_iam_instance_profile" "ec2_profile" {
  name = "3tier-ec2-ssm-profile"
  role = aws_iam_role.ec2_ssm.name
}

# 15. RDS配置用の追加サブネット(マルチAZ用要件を満たすため定義)
resource "aws_subnet" "db_1c" {
  vpc_id            = aws_vpc.main.id
  cidr_block        = "10.0.4.0/24"
  availability_zone = "ap-northeast-1c"
  tags = { Name = "3tier-db-1c" }
}

# 16. DBサブネットグループ
resource "aws_db_subnet_group" "db_subnet_group" {
  name       = "dbsg"
  subnet_ids = [aws_subnet.db_1a.id, aws_subnet.db_1c.id]
  tags = { Name = "3tier-db-subnet-group" }
}

# 17. データベース(RDS MySQL)インスタンス
resource "aws_db_instance" "db" {
  allocated_storage     = 20
  max_allocated_storage = 20
  engine                = "mysql"
  engine_version        = "8.0"
  instance_class        = "db.t3.micro"
  db_name               = "mydb"
  username              = "admin"
  password              = var.db_password
  db_subnet_group_name  = aws_db_subnet_group.db_subnet_group.name
  vpc_security_group_ids = [aws_security_group.db_sg.id]
  skip_final_snapshot   = true # 検証環境のためtrue
  tags = { Name = "3tier-db-instance" }
}

# 18. 変数定義(パスワードマスク用)
variable "db_password" {
  description = "RDS root password"
  type        = string
  sensitive   = true
}

.gitignore

パスワードを含むメタデータ(tfstate)や、巨大なプロバイダバイナリ(.terraform/)をGit管理から確実に除外するためのファイル。ファイル名の先頭にドットが必要。

.terraform/
*.tfstate
*.tfstate.backup
.terraform.lock.hcl

README.md

ポートフォリオとしての見栄え、設計思想を言語化したドキュメント。

# AWS 3-Tier Architecture with Terraform

Terraformを使用してAWS上に3層インフラストラクチャをコード化したリポジトリです。本環境は検証コストの最適化を優先して構成しています。

## 可用性とコスト最適化のトレードオフ
本インフラは個人検証用のため、あえて「シングルAZ」の最小構成を採用しており、単一障害点(SPOF)が存在します。
実務における本番環境への移行時は、以下の高可用性(HA)拡張を行えるよう設計しています。
1. **Web/AP層の冗長化**: マルチAZ配置(1a/1c)への拡張、および前段へのALB(Application Load Balancer)の配置。
2. **DB層の高可用性化**: 定義済みのサブネットグループ(1a/1c)を活かし、パラメータ `multi_az = true` を有効化することで即座にマルチAZ同期レプリケーション構成へ拡張可能です。


3. デプロイとエラーへの対処法

初期化と適用

terraform init
terraform apply

実行時に var.db_password を求められるので入力する。

🚨 遭遇したエラーと対応策

現象:RDSインスタンス作成時に以下のエラーが発生

api error InvalidParameterValue: The parameter MasterUserPassword is not a valid password because it is shorter than 8 characters.

  • 原因: AWS側のセキュリティルールにより、MySQLのマスターパスワードは「8文字以上」である必要がある。プロンプトへの入力が不足していた。
  • 対策: terraform apply を再実行し、英数字を含む8文字以上の強力なパスワード(例: SuperSecure123)を入力することで解決。

4. GitHubへの安全な公開手順(トラブル事例含む)

Git管理を開始してGitHubへアップロードする。

git init
git add .

🚨 遭遇したエラーと対応策(大容量ファイルの拒否)

現象:git push 時にGitHub側から100MB制限で拒否される

remote: error: File .terraform/providers/.../terraform-provider-aws... is 857.66 MB; this exceeds GitHub's file size limit of 100.00 MB

  • 原因: .gitignore ファイルの作成漏れ(またはファイル名不備)により、Terraformが自動ダウンロードした800MB超のAWSプロバイダ本体ファイルまでGitが追跡してしまい、コミット履歴に刻まれてしまった。
  • 対策: Gitの歴史を一度リセットし、.gitignore を正しく配置してやり直した。
# 1. 汚染されたGit履歴を一度完全に削除
rm -rf .git

# 2. .gitignore が正しく存在することを確認(なければ上記の内容で新規作成)
ls -a

# 3. 再度Gitを初期化して安全なファイルだけを登録
git init
git add .

# 4. 軽いテキストファイル(main.tf, README, .gitignore)だけが対象であることを確認
git status

# 5. コミットとGitHubへの送信
git commit -m "feat: AWS 3-tier architecture with Terraform"
git branch -M main
git remote add origin https://github.com/あなたのユーザー名/リポジトリ名.git
git push -u origin main


5. リソースのクリーンアップ(掃除)

検証が終了したら、AWSの不要な課金を防ぐためにリソースを即座に削除します。

terraform destroy

確認プロンプトが出たら、yes と入力して実行します。約5〜10分でAWS上のすべてのインフラ(VPC、EC2、RDSなど)が自動解体されます。

まとめ

Terraformを利用することで、複雑な3層インフラをコード1本で安全に管理・デプロイ・破壊できるサイクルを構築できました。
GitHubへの公開時は、インフラの実行キャッシュや機密情報を巻き込まないよう、最初に適切な .gitignore を配置することが実務上極めて重要です。

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?