構築概要
使用技術:Terraform, AWS
リージョン:東京(ap-northeast-1)
構成図
ディレクトリ構成例:
NETWORK/
├── .terraform/
│ └── modules/
│ └── network/
│ ├── ec2.tf
│ ├── iam.tf
│ ├── igw.tf
│ ├── output.tf
│ ├── route_table_association.tf
│ ├── route_table.tf
│ ├── security_group.tf
│ ├── subnet.tf
│ ├── variables.tf
│ ├── vpc.tf
├── main.tf
├── output.tf
実装のステップ
- SSH鍵の作成と登録
- VPC・サブネット・IGWの構築
- EC2インスタンスの起動
- SSH接続と通信確認
各ファイルの中身
CIDR構成の理由
VPC:10.0.0.0/16
VCP全体なので大きめのアドレス空間を確保(最大 65,536 IP)
万が一後からサブネットを追加しても平気なようにしている
Public Subnet:10.0.1.0/24
Public Subnetは 10.0.1.0/24 に固定することで、構成図やログ上でも識別しやすくしています(最大254 IP)
Private Subnet:10.0.101.0/24
Publicと明確に番号を分けることで混乱を防止
変数定義をここ(variables.tf)にまとめておく
// variables.tf
variable "vpc_cidr_block" {
type = string
}
variable "public_subnet_cidr" {
type = string
}
variable "private_subnet_cidr" {
type = string
}
VPCの作成
// vpc.tf
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr_block
// /main.tfで定義したvpc_cidr_blockを使用
tags = {
Name = "main-vpc"
}
}
Subnetの作成
// Subnet.tf
resource "aws_subnet" "public" {
vpc_id = aws_vpc.main.id
cidr_block = var.public_subnet_cidr
availability_zone = "ap-northeast-1a"
map_public_ip_on_launch = true
// 「 map_public_ip_on_launch = true」はサブネット内で起動するEC2インスタンスに自動的にパブリックIPを割り当てる
tags = {
Name = "public-subnet"
}
}
resource "aws_subnet" "private" {
vpc_id = aws_vpc.main.id
cidr_block = var.private_subnet_cidr
availability_zone = "ap-northeast-1a"
tags = {
Name = "private-subnet"
}
}
internet gatewayの作成
// igw.tf
resource "aws_internet_gateway" "igw" {
vpc_id = aws_vpc.main.id
tags = {
Name = "main-igw"
}
}
route tableの作成
// route_table.tf
resource "aws_route_table" "public_rt" {
vpc_id = aws_vpc.main.id
tags = {
Name = "public-rt"
}
}
# IGW経由のルート追加(0.0.0.0/0)
resource "aws_route" "public_internet" {
route_table_id = aws_route_table.public_rt.id
destination_cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.igw.id
}
resource "aws_route_table" "private_rt" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.nat.id
}
tags = {
Name = "private-rt"
}
}
サブネットにルートテーブルを紐づけ
// route_table_association.tf
resource "aws_route_table_association" "public_assoc" {
subnet_id = aws_subnet.public.id
route_table_id = aws_route_table.public_rt.id
}
resource "aws_route_table_association" "private_assoc" {
subnet_id = aws_subnet.private.id
route_table_id = aws_route_table.private_rt.id
}
セキュリティグループの作成
// security_group.tf
resource "aws_security_group" "web_sg" {
name = "dev-web-sg"
description = "Allow SSH and HTTP"
vpc_id = aws_vpc.main.id // アタッチするvpcを指定
// session manegerを使用する場合は22番ポートを開ける必要はないため削除する
ingress { //インバウンドのポート22(SSH)を許可
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress { //インバウンドのポート80(HTTP)を許可
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"]
}
}
cidr_blocksについて
このセキュリティグループでは、SSH(22)とHTTP(80)のインバウンド通信を許可し、アウトバウンドは全許可しています。cidr_blocks = ["0.0.0.0/0"] は全世界からのアクセスを意味するため、開発環境ではこれでもいいですが、本番環境ではIPを特定のものだけにすることが大事
EC2インスタンスの作成
resource "aws_instance" "web" {
ami = "ami-07faa35bbd2230d90" // 東京リージョンのAmazon Linux
instance_type = "t2.micro"
subnet_id = aws_subnet.public.id
security_groups = [aws_security_group.web_sg.id]
iam_instance_profile = aws_iam_instance_profile.ssm_profile.name
tags = {
Name = "dev-web-server"
}
}
resource "aws_instance" "private_web" {
ami = "ami-07faa35bbd2230d90" // 東京リージョンのAmazon Linux
instance_type = "t2.micro"
subnet_id = aws_subnet.private.id
associate_public_ip_address = false
security_groups = [aws_security_group.web_sg.id]
iam_instance_profile = aws_iam_instance_profile.ssm_profile.name
tags = {
Name = "private-web-server"
}
}
module内output.tf
output "ec2_public_ip" {
value = aws_instance.web.public_ip
}
main.tfの作成
// /main.tf
// awsを使用してリージョンは東京を使用
provider "aws" {
region = "ap-northeast-1"
}
module "network" {
source = "./modules/network"
vpc_cidr_block = "10.0.0.0/16"
public_subnet_cidr = "10.0.1.0/24"
private_subnet_cidr = "10.0.101.0/24"
}
ルート内のoutput.tf
output "ec2_public_ip" {
value = module.network.ec2_public_ip
}
2つのoutput.tf
・ ec2を作成する際に、自動でパブリックサブIPが割り当てられるようになっている ・ モジュール内のoutput.tfで先ほどのパブリックIPを外部に公開する ・ ルート内main.tfでモジュールを呼び出す ・ルート内output.tfでモジュールから受け取った値を再出力モジュール内とルートモジュールで output.tf を分けることで、構成の責務が明確になります。
コードの記述が完了したら、実際にAWS上に作成していきます
terraform init
// 最初の一回だけ初期化を行います
terraform plan
// 記述したものでどのようなリソースが作成されるか確認します
terraform apply
// 記述したものを実際に作成します
terraform destroy
// EC2やElastic IPなど、料金が発生するリソースも含まれるため、不要になったら terraform destroy で確実に削除することをおすすめします。
applyしてからの流れ
main.tf → modules/network/
├── variables.tf
├── vpc.tf
├── subnet.tf
├── igw.tf
├── route_table.tf
├── route_table_association.tf
├── key_pair.tf
├── security_group.tf
├── ec2.tf
└── output.tf
↓
output.tf(ルート)
- Terraformではまずmain.tfを読み込み、providerのaws の設定とmoduleのnetwork の呼び出します
- 次にsource = "./modules/network" により、モジュールディレクトリの読み込みが始まります
感想
まだまだ改善できる部分はあると思いますが、まずはこの構成でTerraformによるネットワークの基礎を押さえることができました。ここから先は、用途に応じて少しずつカスタマイズしていきたいと思います。
次は、private subnetからの外部通信(NAT Gateway構成)について行っていきたいと思います。