🌱
AWS&TerraformでWebサーバとALBを立ててみる
想定読者
- Terraformの構文は勉強したけど、リソース作成はこれからの方
- 構成例を元に手を動かしてみたい方
- AWSを軽く触ったことがある方
はじめに
Terraform初学者の人がAWS上で実際に手を動かして作れる構成を考えてみました。
構成図は以下の通りです。
全てのコードは以下のリポジトリに格納されています。
各リソースのパラメータについては公式ドキュメントをご確認ください。
事前準備
terraformを利用するために、main.tf
を用意します。
また利用したいAWSのregionはここで設定します(今回はap-northeasw-1
(東京)を指定)
main.tf
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.9"
}
}
}
variable "aws_access_key" {}
variable "aws_secret_access_key" {}
provider "aws" {
region = "ap-northeast-1"
access_key = var.aws_access_key
secret_key = var.aws_secret_access_key
}
AWSのCredentialを設定しておく必要があります。
今回はサクッと使いたいので、terraform.tfvars
を利用することとします。
terraform.tfvars
aws_access_key = "YOUR_AWS_ACCESS_KEY"
aws_secret_access_key = "YOUR_AWS_SECRET_ACCESS_KEY"
上記を用意後、CLI上でterraform init
を叩き、Terraform has been successfully initialized!
と表示されることを確認しましょう。
解説
- VPCを作成する
まずはVPCを作成していきます。CIDRなどはお好きなもので大丈夫です。
Nameタグは自分が今回作ったリソースだとわかるようなものに変更してください。
(その他のリソースも同様ですが、ここでは例のためexampleとしています)
network.tf
resource "aws_vpc" "example" {
cidr_block = "10.100.0.0/16"
tags = {
Name = "example"
}
}
試しにここで terraform plan
を叩き、以下の表示が出ることを確認しましょう。
# aws_vpc.example will be created
+ resource "aws_vpc" "example" {
+ arn = (known after apply)
+ cidr_block = "10.100.0.0/16"
+ default_network_acl_id = (known after apply)
+ default_route_table_id = (known after apply)
+ default_security_group_id = (known after apply)
+ dhcp_options_id = (known after apply)
+ enable_classiclink = (known after apply)
+ enable_classiclink_dns_support = (known after apply)
+ enable_dns_hostnames = (known after apply)
+ enable_dns_support = true
+ id = (known after apply)
+ instance_tenancy = "default"
+ ipv6_association_id = (known after apply)
+ ipv6_cidr_block = (known after apply)
+ ipv6_cidr_block_network_border_group = (known after apply)
+ main_route_table_id = (known after apply)
+ owner_id = (known after apply)
+ tags = {
+ "Name" = "example"
}
+ tags_all = {
+ "Name" = "example"
}
}
Plan: 1 to add, 0 to change, 0 to destroy.
では実際にterraform apply
を実行し、リソースを作成してみましょう。
Enter a value
と聞かれるので、yes
を入力します。
以下の表示が出れば作成できているので、GUI上でも確認してみましょう。
aws_vpc.example: Creating...
aws_vpc.example: Creation complete after 1s [id=vpc-02c546f38de3782ac]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
これ以降のステップでも都度terraform plan
及びterraform apply
を行って、実際にリソースができているか確認していきましょう。
- サブネットを作成する
次にサブネットを作成していきます。CIDRなどはお好きなもので大丈夫です。
今回public subnetを2つ作成していますが、これはALBにサブネットを登録する際に2つ以上のAZにまたがっている必要があるためです。
実際に今回利用するのは1aのみです(そのためprivateは1aのみ作成しています)
network.tf
# public
resource "aws_subnet" "example_public_1a" {
vpc_id = aws_vpc.example.id
cidr_block = "10.100.0.0/24"
availability_zone = "ap-northeast-1a"
tags = {
Name = "example_public_1a"
}
}
resource "aws_subnet" "example_public_1c" {
vpc_id = aws_vpc.example.id
cidr_block = "10.100.1.0/24"
availability_zone = "ap-northeast-1c"
tags = {
Name = "example_public_1c"
}
}
# private
resource "aws_subnet" "example_private_1a" {
vpc_id = aws_vpc.example.id
cidr_block = "10.100.100.0/24"
availability_zone = "ap-northeast-1a"
tags = {
Name = "example_private_1a"
}
}
- IGW/NAT GWを作成する
インターネットからとの接続に必要なInternet GatewayとNAT Gatewayを作成します。
network.tf
# Internet GW
resource "aws_internet_gateway" "example" {
vpc_id = aws_vpc.example.id
tags = {
Name = "example"
}
}
# NAT GW
resource "aws_nat_gateway" "example" {
allocation_id = aws_eip.nat_gw.id
subnet_id = aws_subnet.example_public_1a.id
depends_on = [aws_internet_gateway.example]
tags = {
Name = "example"
}
}
# EIP(NATGW)
resource "aws_eip" "nat_gw" {
vpc = true
}
- IGW/NATGW用のルートテーブルを作成する
ルートテーブルにて、各サブネットとInternet Gateway及びNAT Gatewayとの経路を用意します。
network.tf
# RouteTable/Public
resource "aws_route_table" "example_internet_gw" {
vpc_id = aws_vpc.example.id
tags = {
Name = "example_route_internet_gw"
}
}
resource "aws_route" "example_internet_gw_default" {
route_table_id = aws_route_table.example_internet_gw.id
destination_cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.example.id
}
resource "aws_route_table_association" "example_internet_gw_1a" {
subnet_id = aws_subnet.example_public_1a.id
route_table_id = aws_route_table.example_internet_gw.id
}
resource "aws_route_table_association" "example_internet_gw_1c" {
subnet_id = aws_subnet.example_public_1c.id
route_table_id = aws_route_table.example_internet_gw.id
}
# RouteTable/Private
resource "aws_route_table" "example_private" {
vpc_id = aws_vpc.example.id
tags = {
Name = "example_route_private"
}
}
resource "aws_route" "example_private_default" {
route_table_id = aws_route_table.example_private.id
destination_cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.example.id
}
resource "aws_route_table_association" "example_private_1" {
subnet_id = aws_subnet.example_private_1a.id
route_table_id = aws_route_table.example_private.id
}
- EC2を作成する
Private SubnetにEC2を作成します。合わせてEC2に付与するSGも作成していきます。
server.tf
# EC2
resource "aws_instance" "example" {
ami = "ami-0992fc94ca0f1415a"
instance_type = "t2.micro"
vpc_security_group_ids = [aws_security_group.example_for_instance.id]
subnet_id = aws_subnet.example_private_1a.id
user_data = file("install_apache.sh")
tags = {
Name = "example"
}
}
# SG(ALB Allow)
resource "aws_security_group" "example_for_instance" {
name = "example_instance"
vpc_id = aws_vpc.example.id
tags = {
Name = "example_for_instance"
}
}
resource "aws_security_group_rule" "sg_ingress_instance" {
type = "ingress"
from_port = 80
to_port = 80
protocol = "tcp"
source_security_group_id = aws_security_group.example_for_alb.id
security_group_id = aws_security_group.example_for_instance.id
}
resource "aws_security_group_rule" "sg_egress_instance" {
type = "egress"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
security_group_id = aws_security_group.example_for_instance.id
}
今回はuser_dataも作成と合わせて読み込みたいので、user_data用にinstall_apache.sh
も作成します。
install_apache.sh
#! /bin/bash
yum update -y
yum install -y httpd
systemctl start httpd
systemctl enable httpd
- ALBを作成する
EC2の前面にALBを配置したいので、Public SubnetにALBを作成します。
合わせてHTTPのみ許可するSGを作成します。
server.tf
# ALB
resource "aws_lb" "example" {
name = "example"
load_balancer_type = "application"
internal = false
security_groups = [aws_security_group.example_for_alb.id]
subnets = [
aws_subnet.example_public_1a.id,
aws_subnet.example_public_1c.id
]
tags = {
Name = "example"
}
}
# SG
resource "aws_security_group" "example_for_alb" {
name = "example_http"
vpc_id = aws_vpc.example.id
tags = {
Name = "example_for_alb"
}
}
resource "aws_security_group_rule" "sg_ingress" {
type = "ingress"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
security_group_id = aws_security_group.example_for_alb.id
}
resource "aws_security_group_rule" "sg_egress" {
type = "egress"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
security_group_id = aws_security_group.example_for_alb.id
}
- ALB用のListener/TGを作成する
先ほど作成したEC2をALBと紐付けしたいので、ListenerとTarget Groupを作成します。
また、今回Apacheのデフォルト画面を表示させる関係上、health_checkのmatcherを403にしています。
(Apacheのデフォルト画面のステータスコードが403であるため)
service.tf
# LB - Listener
resource "aws_lb_listener" "example" {
load_balancer_arn = aws_lb.example.arn
port = 80
protocol = "HTTP"
default_action {
type = "forward"
target_group_arn = aws_lb_target_group.example.arn
}
}
# LB - TargetGroup
resource "aws_lb_target_group" "example" {
name = "example"
port = 80
protocol = "HTTP"
vpc_id = aws_vpc.example.id
health_check {
path = "/"
matcher = 403
port = 80
protocol = "HTTP"
}
depends_on = [aws_lb.example]
}
resource "aws_lb_target_group_attachment" "example" {
target_group_arn = aws_lb_target_group.example.arn
target_id = aws_instance.example.id
port = 80
}
- 確認する
terraform apply
を行い、各リソースをapplyし終えたら、ALBのDNS名からアクセスしてみます。
Apacheのデフォルト画面が表示されればOKです。
まとめ
AWS&TerraformでALB-EC2の構成を簡単に作ってみました。
この記事を参考にリソースを作ってみて、少しでもTerraform&AWSの理解の助けになれば幸いです。
Discussion