はじめに
まず、大前提として、ここから先は現在キャッチアップしているTerraformを使いながら、さまざまなコードを自分用に書いていきます。
※補足事項
そのため、内容は完全に自分向けとなっています。
また、このブログ投稿サイトのプラットフォームについても、自分自身の備忘録としてメモを残す場として活用していきたいと考えています。このプラットフォーム上に記録を積み重ねていく予定です。
あらかじめ、その点をご理解いただければ幸いです。
実際にコードで行っていること
前回の続きです。
-
AWSプロバイダー設定
- 東京リージョンでリソースを構築する設定。
-
VPC作成
- CIDRブロック範囲
10.0.0.0/16
のVPCを作成。 - DNSサポートとDNSホスト名を有効化。
- CIDRブロック範囲
-
パブリックサブネット作成
- 2つのパブリックサブネットを異なるアベイラビリティゾーンで作成。
- インターネットゲートウェイを通じてインターネットにアクセス可能。
-
プライベートサブネット作成
- 4つのプライベートサブネットを異なるCIDRブロックとアベイラビリティゾーンで作成。
- NATゲートウェイを経由してインターネットにアクセス可能。
-
インターネットゲートウェイ設定
- パブリックサブネットのインターネットアクセスを提供。
-
ルートテーブルの設定
- パブリックサブネット用のルートテーブルを作成し、インターネットゲートウェイをルートに設定。
- プライベートサブネット用のルートテーブルを作成し、NATゲートウェイをルートに設定。
-
NATゲートウェイ設定
- Elastic IPを割り当てたNATゲートウェイを作成。
- プライベートサブネットのアウトバウンド通信をサポート。
-
サブネットとルートテーブルの関連付け
- 各サブネットを対応するルートテーブルに関連付け。
-
セキュリティグループ設定
- SSH(22番)とALB(443番)のインバウンド通信を許可。
- 全アウトバウンドトラフィックを許可。
-
ALBとターゲットグループの設定
- 外部公開用のALBを作成し、HTTPリクエストを処理するターゲットグループを設定(ヘルスチェックとスティッキーセッションを有効化)。
-
ECSインスタンスの登録
- プライベートサブネット内のECSインスタンスをターゲットグループに登録し、ALBを介してリクエストを転送。
-
ECSクラスターとタスク実行ロールの作成
-
ECSクラスター:
aws_ecs_cluster.main
ECSサービスを実行するためのクラスターを作成。 -
タスク実行ロール:
aws_iam_role.ecs_task_execution_role
ECRアクセスやログ送信権限を付与するIAMロール。
-
ECSクラスター:
-
ECSタスク定義の作成
-
リソース名:
aws_ecs_task_definition.main
Fargate用のタスク(コンテナ)設定を定義。- CPU: 256
- メモリ: 512
- コンテナ: ECRイメージを使用し、ポート1323を開放。
-
リソース名:
-
ECSサービスの作成
-
リソース名:
aws_ecs_service.main
ECSクラスター内でタスクを実行するサービスを作成。- ALBとターゲットグループを使用してアクセスを管理。
- タスクをFargateで1つ実行(
desired_count = 1
)。
-
リソース名:
-
ACMによるHTTPS通信の有効化
-
リソース名:
acm_certificate_arn
ACM (AWS Certificate Manager) を利用して SSL/TLS 証明書を設定し、HTTPS 通信を暗号化。- ユーザーがアクセスするすべてのトラフィックが HTTPS によって保護。
-
リソース名:
-
RDSインスタンスの作成
-
リソース名:
aws_db_instance
RDSインスタンスを作成。- MySQL 8.0を使用したシングルAZのRDSインスタンスをプライベートサブネット内にデプロイ。
- プライベートサブネットを指定して、RDSインスタンスが配置可能なサブネットグループを作成
-
リソース名:
事前準備と補足事項
1. お名前.comで購入したドメインを使用してSSL証明書を取得する方法
AWS ACMとRoute53を利用してSSL証明書を取得する手順については、以下の記事をご参照ください:
2. ALBのAレコードをRoute53のホストゾーンに追加する方法
Terraformで自動デプロイ後、ALBのAレコードをRoute53に追加する手順については、以下の記事をご参照ください:
作ったコードについて
# AWSプロバイダーの設定
provider "aws" {
region = "ap-northeast-1" # 東京リージョン
}
# 変数の定義
variable "acm_certificate_arn" {
default = "arn:aws:acm:ap-northeast-1:xxx:certificate/e1c32c45-669b-45bf-b75b-6caf1f2c6e96"
description = "ARN of the ACM Certificate"
}
# VPCの作成
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16" # VPCのCIDRブロック
enable_dns_support = true
enable_dns_hostnames = true
tags = {
Name = "Terraform-vpc" # VPCの名前タグ
}
}
# パブリックサブネット1の作成
resource "aws_subnet" "public1" {
vpc_id = aws_vpc.main.id # 対象のVPC ID
cidr_block = "10.0.0.0/20" # サブネットのCIDRブロック
availability_zone = "ap-northeast-1a" # 対象のアベイラビリティゾーン
tags = {
Name = "Terraform-subnet-public1-ap-northeast-1a" # サブネットの名前タグ
}
}
# パブリックサブネット2の作成
resource "aws_subnet" "public2" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.16.0/20" # 別のCIDRブロックを使用
availability_zone = "ap-northeast-1c" # 別のアベイラビリティゾーンを指定
tags = {
Name = "Terraform-subnet-public2-ap-northeast-1c"
}
}
# プライベートサブネット1の作成
resource "aws_subnet" "private1" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.128.0/20" # プライベート用CIDRブロック
availability_zone = "ap-northeast-1a"
tags = {
Name = "Terraform-subnet-private1-ap-northeast-1a"
}
}
# プライベートサブネット2の作成
resource "aws_subnet" "private2" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.144.0/20" # 別のCIDRブロックを使用
availability_zone = "ap-northeast-1c"
tags = {
Name = "Terraform-subnet-private2-ap-northeast-1c"
}
}
# プライベートサブネット3の作成
resource "aws_subnet" "private3" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.160.0/20" # さらに別のCIDRブロック
availability_zone = "ap-northeast-1a"
tags = {
Name = "Terraform-subnet-private3-ap-northeast-1a"
}
}
# プライベートサブネット4の作成
resource "aws_subnet" "private4" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.176.0/20" # 最後のCIDRブロック
availability_zone = "ap-northeast-1c"
tags = {
Name = "Terraform-subnet-private4-ap-northeast-1c"
}
}
# インターネットゲートウェイの作成
resource "aws_internet_gateway" "main" {
vpc_id = aws_vpc.main.id # 対象のVPC ID
tags = {
Name = "Terraform-igw" # インターネットゲートウェイの名前タグ
}
}
# パブリックルートテーブルの作成
resource "aws_route_table" "public" {
vpc_id = aws_vpc.main.id # 対象のVPC ID
tags = {
Name = "Terraform-rtb-public" # ルートテーブルの名前タグ
}
}
# パブリックルートの設定
resource "aws_route" "public_internet_access" {
route_table_id = aws_route_table.public.id # パブリックルートテーブルのID
destination_cidr_block = "0.0.0.0/0" # 全トラフィックを許可
gateway_id = aws_internet_gateway.main.id # インターネットゲートウェイを指定
}
# パブリックサブネット1をルートテーブルに関連付け
resource "aws_route_table_association" "public1" {
subnet_id = aws_subnet.public1.id # パブリックサブネット1のID
route_table_id = aws_route_table.public.id # パブリックルートテーブルのID
}
# パブリックサブネット2をルートテーブルに関連付け
resource "aws_route_table_association" "public2" {
subnet_id = aws_subnet.public2.id # パブリックサブネット2のID
route_table_id = aws_route_table.public.id
}
# NATゲートウェイ用のEIP(Elastic IP)を作成
resource "aws_eip" "nat" {
tags = {
Name = "Terraform-eip-ap-northeast-1a" # Elastic IPの名前タグ
}
}
# NATゲートウェイの作成
resource "aws_nat_gateway" "public1" {
subnet_id = aws_subnet.public1.id # NATゲートウェイを配置するサブネット
allocation_id = aws_eip.nat.id # Elastic IPを割り当て
tags = {
Name = "Terraform-nat-public1-ap-northeast-1a" # NATゲートウェイの名前タグ
}
}
# プライベートルートテーブル1の作成
resource "aws_route_table" "private1" {
vpc_id = aws_vpc.main.id # 対象のVPC ID
tags = {
Name = "Terraform-rtb-private1-ap-northeast-1a" # プライベートルートテーブルの名前タグ
}
}
# プライベートルート1の設定(NATゲートウェイ経由)
resource "aws_route" "private1_nat_access" {
route_table_id = aws_route_table.private1.id # プライベートルートテーブル1のID
destination_cidr_block = "0.0.0.0/0" # 全トラフィックを許可
nat_gateway_id = aws_nat_gateway.public1.id # NATゲートウェイのID
}
# プライベートサブネット1をルートテーブル1に関連付け
resource "aws_route_table_association" "private1" {
subnet_id = aws_subnet.private1.id # プライベートサブネット1のID
route_table_id = aws_route_table.private1.id # プライベートルートテーブル1のID
}
# プライベートルートテーブル2の作成
resource "aws_route_table" "private2" {
vpc_id = aws_vpc.main.id # 対象のVPC ID
tags = {
Name = "Terraform-rtb-private2-ap-northeast-1c" # プライベートルートテーブルの名前タグ
}
}
# プライベートルート2の設定(NATゲートウェイ経由)
resource "aws_route" "private2_nat_access" {
route_table_id = aws_route_table.private2.id # プライベートルートテーブル2のID
destination_cidr_block = "0.0.0.0/0" # 全トラフィックを許可
nat_gateway_id = aws_nat_gateway.public1.id # NATゲートウェイのID
}
# プライベートサブネット2をプライベートルートテーブル2に関連付け
resource "aws_route_table_association" "private2" {
subnet_id = aws_subnet.private2.id # プライベートサブネット2のID
route_table_id = aws_route_table.private2.id # プライベートルートテーブル2のID
}
# プライベートルートテーブル3の作成
resource "aws_route_table" "private3" {
vpc_id = aws_vpc.main.id # 対象のVPC ID
tags = {
Name = "Terraform-rtb-private3-ap-northeast-1a" # プライベートルートテーブル3の名前タグ
}
}
# プライベートルート3の設定(NATゲートウェイ経由)
resource "aws_route" "private3_nat_access" {
route_table_id = aws_route_table.private3.id # プライベートルートテーブル3のID
destination_cidr_block = "0.0.0.0/0" # 全トラフィックを許可
nat_gateway_id = aws_nat_gateway.public1.id # NATゲートウェイのID
}
# プライベートサブネット3をプライベートルートテーブル3に関連付け
resource "aws_route_table_association" "private3" {
subnet_id = aws_subnet.private3.id # プライベートサブネット3のID
route_table_id = aws_route_table.private3.id # プライベートルートテーブル3のID
}
# プライベートルートテーブル4の作成
resource "aws_route_table" "private4" {
vpc_id = aws_vpc.main.id # 対象のVPC ID
tags = {
Name = "Terraform-rtb-private4-ap-northeast-1c" # プライベートルートテーブル4の名前タグ
}
}
# プライベートルート4の設定(NATゲートウェイ経由)
resource "aws_route" "private4_nat_access" {
route_table_id = aws_route_table.private4.id # プライベートルートテーブル4のID
destination_cidr_block = "0.0.0.0/0" # 全トラフィックを許可
nat_gateway_id = aws_nat_gateway.public1.id # NATゲートウェイのID
}
# プライベートサブネット4をプライベートルートテーブル4に関連付け
resource "aws_route_table_association" "private4" {
subnet_id = aws_subnet.private4.id # プライベートサブネット4のID
route_table_id = aws_route_table.private4.id # プライベートルートテーブル4のID
}
# セキュリティグループの作成(ECS用)
resource "aws_security_group" "ecs_sg" {
vpc_id = aws_vpc.main.id
# ALBからのHTTPリクエスト(1323番ポート)を許可
ingress {
from_port = 1323
to_port = 1323
protocol = "tcp"
security_groups = [aws_security_group.public_sg.id] # ALBのセキュリティグループを指定
}
# 全トラフィックの送信を許可(アウトバウンド)
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "Terraform-private-instance-sg"
}
}
# ECSクラスターの作成
resource "aws_ecs_cluster" "main" {
name = "my-ecs-cluster"
}
# ECSタスク実行ロールの作成
resource "aws_iam_role" "ecs_task_execution_role" {
name = "ecs-task-execution-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Principal = { Service = "ecs-tasks.amazonaws.com" }
Action = "sts:AssumeRole"
}]
})
inline_policy {
name = "ecs-task-execution-policy"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"ecr:GetAuthorizationToken",
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage",
"logs:CreateLogStream",
"logs:PutLogEvents",
"s3:GetObject"
]
Resource = "*"
}
]
})
}
}
# ECSタスク定義の作成
resource "aws_ecs_task_definition" "main" {
family = "my-app-task"
network_mode = "awsvpc"
requires_compatibilities = ["FARGATE"]
cpu = "256"
memory = "512"
execution_role_arn = aws_iam_role.ecs_task_execution_role.arn
container_definitions = jsonencode([
{
name = "my-app-repo"
image = "xxx.dkr.ecr.ap-northeast-1.amazonaws.com/my-app-repo:latest"
portMappings = [
{
containerPort = 1323
hostPort = 1323
protocol = "tcp"
}
]
}
])
}
# ECSサービスの作成
resource "aws_ecs_service" "main" {
name = "my-ecs-service"
cluster = aws_ecs_cluster.main.id
task_definition = aws_ecs_task_definition.main.arn
desired_count = 1
launch_type = "FARGATE"
network_configuration {
subnets = [aws_subnet.public1.id]
security_groups = [aws_security_group.ecs_sg.id]
assign_public_ip = true
}
load_balancer {
target_group_arn = aws_lb_target_group.main_target_group.arn
container_name = "my-app-repo"
container_port = 1323
}
}
# パブリック(ALB)用セキュリティグループ作成
resource "aws_security_group" "public_sg" {
vpc_id = aws_vpc.main.id
ingress {
from_port = 443
to_port = 443
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 = "Terraform-public-sg"
}
}
# アプリケーションロードバランサー(ALB)作成
resource "aws_lb" "main_alb" {
name = "terraform-main-alb"
internal = false
load_balancer_type = "application"
security_groups = [aws_security_group.public_sg.id]
subnets = [aws_subnet.public1.id, aws_subnet.public2.id]
enable_deletion_protection = false
tags = {
Name = "terraform-main-alb"
}
}
# ターゲットグループ作成
resource "aws_lb_target_group" "main_target_group" {
name = "terraform-main-target-group"
port = 80
protocol = "HTTP"
vpc_id = aws_vpc.main.id
target_type = "ip" # ターゲットタイプを 'ip' に設定
health_check {
interval = 30
path = "/"
protocol = "HTTP"
timeout = 5
healthy_threshold = 3
unhealthy_threshold = 3
}
stickiness {
type = "lb_cookie"
enabled = true
cookie_duration = 86400
}
tags = {
Name = "terraform-main-target-group"
}
}
# ALBのリスナー作成
resource "aws_lb_listener" "http_listener" {
load_balancer_arn = aws_lb.main_alb.arn
port = 443
protocol = "HTTPS"
ssl_policy = "ELBSecurityPolicy-2016-08"
certificate_arn = var.acm_certificate_arn
default_action {
type = "forward"
target_group_arn = aws_lb_target_group.main_target_group.arn
}
}
# RDS用セキュリティグループ
resource "aws_security_group" "rds_sg" {
vpc_id = aws_vpc.main.id
ingress {
from_port = 3306
to_port = 3306
protocol = "tcp"
cidr_blocks = ["10.0.0.0/16"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "Terraform-rds-sg"
}
}
# RDS用サブネットグループ
resource "aws_db_subnet_group" "rds_subnet_group" {
name = "terraform-rds-subnet-group"
subnet_ids = [aws_subnet.private1.id, aws_subnet.private2.id]
tags = {
Name = "Terraform-rds-subnet-group"
}
}
# RDSインスタンス
resource "aws_db_instance" "rds_instance" {
allocated_storage = 20 # 無料利用枠に合わせたストレージ
storage_type = "gp2" # ストレージタイプ
engine = "mysql" # データベースエンジン
engine_version = "8.0.39" # MySQLバージョン
instance_class = "db.t4g.micro" # Graviton2プロセッサを使用したインスタンス
username = "admin" # 管理ユーザー名
password = "mypassword123" # 管理ユーザーパスワード
parameter_group_name = "default.mysql8.0" # デフォルトパラメータグループ
db_subnet_group_name = aws_db_subnet_group.rds_subnet_group.name # サブネットグループ
vpc_security_group_ids = [aws_security_group.rds_sg.id] # セキュリティグループ
publicly_accessible = false # プライベートサブネットで利用
multi_az = false # シングルAZ構成(無料利用枠対象)
tags = {
Name = "Terraform-rds-instance"
}
# 最終スナップショットをスキップ
skip_final_snapshot = true
}
まとめ
この記事は、自分用の備忘録としてまとめたものです。その点をご理解いただけると幸いです。
もし、この記事の内容が少しでも参考になれば嬉しく思います。
今後も同様の内容を継続して投稿していきますので、温かく見守っていただけるとありがたいです。
おまけ
お名前.comで取得したドメイン名を使用してアクセスしたところ、以下のようにブラウザで正常に表示されることを確認しました。コード化は大成功です!
また、今回作成したALBのDNS名でも、以下のように正常に表示されることを確認しています。
今回追加で実装したRDSインスタンスについても、マネジメントコンソール上で正しく作成されていることを確認しました。