1. はじめに
前回の記事「AWSコンソールでの構築経験をもとにTerraformを学ぶ【①ネットワーク編】
」では、Terraformの基本的な使い方を確認しながら、VPC・サブネット・インターネットゲートウェイ・ルートテーブル・セキュリティグループを構築しました。
今回はその続きとして、構築したネットワーク上にEC2・ALB・RDSをTerraformで構築します。
前回と同様、実務でAWSコンソールから構築した環境を一部簡略化したサンプル環境を使用し、コンソールで行っていた構築をTerraformではどのように表現するのかをまとめます。
2. EC2
今回は、Bastionサーバを1台、Webサーバを2台作成します。
Bastionはパブリックサブネット、Webサーバはそれぞれ異なるAZのプライベートサブネットに配置します。
■ AMIの取得
EC2で使用するAMIは、AMI IDを直接指定するのではなく、data を使ってAmazon Linux 2023のAMIを取得します。
data "aws_ami" "amazon_linux" {
most_recent = true
owners = ["amazon"]
filter {
name = "name"
values = ["al2023-ami-2023.*-x86_64"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
filter {
name = "root-device-type"
values = ["ebs"]
}
}
resource がTerraformで作成・管理するリソースであるのに対して、data は既存の情報を取得して利用するために使用します。
■ EC2の作成
取得したAMIは、EC2の ami から参照します。
resource "aws_instance" "web_1a" {
ami = data.aws_ami.amazon_linux.id
instance_type = "t3.micro"
subnet_id = aws_subnet.private_1a.id
vpc_security_group_ids = [aws_security_group.web.id]
associate_public_ip_address = false
tags = {
Name = "sample-web-1a"
}
}
Webサーバはプライベートサブネットに配置し、パブリックIPアドレスを付与しない構成としています。
一方、Bastionサーバはパブリックサブネットに配置し、外部からSSH接続できる構成とします。
3. ALB
2台のWebサーバへ通信を振り分けるため、ALBを作成します。
今回は外部公開用ではなく、VPC内部から利用するInternal ALBとして構築します。
■ ALBの作成
ALBは2つのプライベートサブネットに配置します。
resource "aws_lb" "web" {
name = "sample-web-alb"
internal = true
load_balancer_type = "application"
security_groups = [aws_security_group.alb.id]
subnets = [
aws_subnet.private_1a.id,
aws_subnet.private_1c.id
]
}
internal = true を指定することで、Internal ALBとして作成できます。
■ ターゲットグループとEC2の関連付け
ALBから通信を転送するためのターゲットグループを作成し、Webサーバを登録します。
resource "aws_lb_target_group_attachment" "web_1a" {
target_group_arn = aws_lb_target_group.web.arn
target_id = aws_instance.web_1a.id
port = 80
}
■ リスナーの設定
ALBが受け付けたHTTP通信をターゲットグループへ転送するよう、リスナーを設定します。
resource "aws_lb_listener" "http" {
load_balancer_arn = aws_lb.web.arn
port = 80
protocol = "HTTP"
default_action {
type = "forward"
target_group_arn = aws_lb_target_group.web.arn
}
}
ALBを構築する場合はALB本体だけでなく、ターゲットグループ・EC2の登録・リスナーなど、通信を転送するための設定もそれぞれTerraformで定義します。
4. RDS
最後に、Webサーバから接続するデータベースとして、RDS for PostgreSQLを作成します。
RDSはプライベートサブネットに配置し、インターネットから直接アクセスできない構成とします。
■ DBサブネットグループ
まず、RDSを配置するサブネットをDBサブネットグループとして定義します。
resource "aws_db_subnet_group" "main" {
name = "sample-db-subnet-group"
subnet_ids = [
aws_subnet.private_1a.id,
aws_subnet.private_1c.id
]
}
2つのAZのプライベートサブネットを指定しています。
■ RDSの作成
作成したDBサブネットグループとDB用セキュリティグループを指定して、RDSを作成します。
resource "aws_db_instance" "postgres" {
identifier = "sample-postgres"
engine = "postgres"
instance_class = "db.t3.micro"
allocated_storage = 20
storage_type = "gp3"
username = var.db_username
password = var.db_password
db_subnet_group_name = aws_db_subnet_group.main.name
vpc_security_group_ids = [aws_security_group.db.id]
publicly_accessible = false
multi_az = false
skip_final_snapshot = true
deletion_protection = false
}
DBのユーザー名とパスワードはコードへ直接記述せず、variable として定義しています。
variable "db_username" {
type = string
sensitive = true
}
variable "db_password" {
type = string
sensitive = true
}
値は terraform.tfvars から渡し、terraform.tfvars はGitの管理対象外としています。
■ Aurora PostgreSQLについて
当初は実務で使用したAurora PostgreSQLをTerraformで構築する予定でした。
Aurora PostgreSQLの場合、通常のRDSとは異なり、aws_rds_cluster でクラスターを作成し、aws_rds_cluster_instance でDBインスタンスを作成します。
resource "aws_rds_cluster" "aurora_postgres" {
cluster_identifier = "sample-aurora-postgres"
engine = "aurora-postgresql"
master_username = var.db_username
master_password = var.db_password
db_subnet_group_name = aws_db_subnet_group.main.name
vpc_security_group_ids = [aws_security_group.db.id]
skip_final_snapshot = true
deletion_protection = false
}
今回は使用しているAWSアカウントのFree Planのため、想定していた構成ではAuroraクラスターを作成できませんでした。
そのため、検証環境ではRDS for PostgreSQLを構築し、Aurora PostgreSQLのTerraformコードは参考実装としてリポジトリに残しています。
5. Terraformで再構築して学んだこと
今回、AWSコンソールで一度構築した環境をTerraformで再構築したことで、コンソール上で行っていた操作を、コードではどのように表現するのかを学ぶことができました。
特に、ルートテーブルとサブネットの関連付けや、ALB・ターゲットグループ・EC2の関連付けなど、コンソールでは画面上で設定していた内容が、Terraformではそれぞれのリソースの定義や参照として明示される点が印象に残りました。
また、fmt・validate・plan・apply といった基本的な実行の流れや、resource・data・variable の使い分けについても、実際に環境を構築することで理解を深めることができました。
今後は、今回対象外としたAWSリソースについてもTerraformでの構築を試し、IaCへの理解を深めていきたいと思います。