{Terraform} モジュールサンプル(RDS/EC2/ALB)

 

S3
dynanodb
RDS
EC2
ALB

参考文献: Terraform Up & Running (Yevgeniy Brikman著) 2-4章

要件:
・tfstateファイルはS3に格納
・環境はdev/prodの2つで、環境ごとにディレクトリを分ける
・terraform_remote_stateでDB情報をEC2構築時に取得
・devとprodで共通する処理をモジュールとして分離
・モジュールのマジックナンバーをlocal valueとして定義
・module output変数でモジュール間の変数受け渡し
・path reference
・moduleをgitでバージョン管理

前提:
githubリポジトリ作成済

 

-- 1. 作業ディレクトリ作成

cd ~
mkdir test
cd test

mkdir -p live/global/s3
mkdir -p live/dev/services/web
mkdir -p live/dev/db/mysql
mkdir -p live/prod/services/web
mkdir -p live/prod/db/mysql

mkdir -p modules/services/web
mkdir -p modules/db/mysql


-- 2. ローカルにtfstateファイル作成

cd ~/test/live/global/s3

vim main.tf

provider "aws" {
  region = "ap-northeast-1"
}


resource "aws_s3_bucket" "terraform_state" {
  bucket = "terraformbucket123"

  # 空でなくても削除許可
  force_destroy = true

  lifecycle {
    prevent_destroy = false
  }
}


resource "aws_s3_bucket_server_side_encryption_configuration" "default" {
  bucket = aws_s3_bucket.terraform_state.id
  
  rule {
    apply_server_side_encryption_by_default {
      sse_algorithm = "AES256"
    }
  }
}

resource "aws_s3_bucket_public_access_block" "public_access" {
  bucket = aws_s3_bucket.terraform_state.id
  block_public_acls = true
  block_public_policy = true
  ignore_public_acls = true
  restrict_public_buckets = true
}


resource "aws_dynamodb_table" "terraform_locks" {
  name = "terraformtab123"
  billing_mode = "PAY_PER_REQUEST"
  hash_key = "LockID"
  attribute {
    name = "LockID"
    type = "S"
  }
}

 


vim outputs.tf

output "s3_bucket_arn" {
  value = aws_s3_bucket.terraform_state.arn
  description = "The ARN of the S3 bucket"
}

output "dynamodb_table_name" {
  value = aws_dynamodb_table.terraform_locks.name
  description = "The name of the DynamoDB table"
}


terraform init
terraform apply

 

-- 3. S3にtfstateファイル作成

vim main.tf

下記追加

terraform {
  backend "s3" {
    bucket = "terraformbucket123"
    key = "global/s3/terraform.tfstate"
    region = "ap-northeast-1"
    
    dynamodb_table = "terraformtab123"
    encrypt = true
  }
}


terraform init
terraform apply

-- 4. モジュール作成(db)

cd ~/test/modules/db/mysql


vim main.tf

resource "aws_db_instance" "db01" {
  identifier = "${var.cluster_name}db01"
  engine = "mysql"
  allocated_storage = 10
  instance_class = var.db_instance_type
  skip_final_snapshot = true
  db_name = "${var.cluster_name}db01"
  
  username = var.db_username
  password = var.db_password
}

 

vim variables.tf

variable "cluster_name" {
  description = "cluster_name"
  type = string
}

variable "db_instance_type" {
  description = "db_instance_type"
  type = string
}


variable "db_username" {
  description = "db_username"
  type = string
  sensitive = true
}

variable "db_password" {
  description = "db_password"
  type = string
  sensitive = true
}

 

vim outputs.tf

output "address" {
  value = aws_db_instance.db01.address
  description = "Connect to the database at this endpoint"
}

output "port" {
  value = aws_db_instance.db01.port
  description = "The port the database is listening on"
}

 

-- 5. モジュール作成(web)

cd ~/test/modules/services/web

vim main.tf


data "aws_vpc" "vpc01" {
  default = true
}

data "aws_subnets" "subnet01" {
  filter {
    name = "vpc-id"
    values = [data.aws_vpc.vpc01.id]
  }
}

data "terraform_remote_state" "rs01" {
  backend = "s3"
  config = {
    bucket = var.db_remote_state_bucket
    key = var.db_remote_state_key
    region = "ap-northeast-1"
  }
}

resource "aws_lb_target_group" "tg01" {
  name = "${var.cluster_name}tg01"
  port = local.server_port
  protocol = "HTTP"
  vpc_id = data.aws_vpc.vpc01.id
  
  health_check {
    path = "/"
    protocol = "HTTP"
    matcher = "200"
    interval = 15
    timeout = 3
    healthy_threshold = 2
    unhealthy_threshold = 2
  }
}

resource "aws_security_group" "sg01" {
  name ="${var.cluster_name}sg01"
  
}

resource  "aws_security_group_rule" "sg01_01" {
  type = "ingress"
  security_group_id = aws_security_group.sg01.id
  
  from_port = local.server_port
  to_port   = local.server_port
  protocol = local.tcp_protocol
  cidr_blocks = local.all_ips

}

resource "aws_launch_configuration" "lc01" {
  image_id        = "ami-0ed99df77a82560e6"
  instance_type   = var.instance_type
  security_groups = [ aws_security_group.sg01.id ]
  
  user_data     = templatefile("${path.module}/user_data.sh", {
    server_port = local.server_port
    db_address  = data.terraform_remote_state.rs01.outputs.address
    db_port     = data.terraform_remote_state.rs01.outputs.port
  })
  
  lifecycle {
    create_before_destroy = true
  }
}


resource "aws_autoscaling_group" "asg01" {
  launch_configuration = aws_launch_configuration.lc01.name
  vpc_zone_identifier = data.aws_subnets.subnet01.ids
  
  target_group_arns = [aws_lb_target_group.tg01.arn]
  health_check_type = "ELB"
  
  min_size = var.min_size
  max_size = var.max_size
  
  tag {
    key  = "Name"
    value = "${var.cluster_name}asg01"
    propagate_at_launch = true
  }
}

resource  "aws_security_group" "sg02" {
  name ="${var.cluster_name}sg02"
  
}

resource  "aws_security_group_rule" "sg02_01" {
  type = "ingress"
  security_group_id = aws_security_group.sg02.id
  
  from_port = local.http_port
  to_port = local.http_port
  protocol = local.tcp_protocol
  cidr_blocks = local.all_ips

}

resource  "aws_security_group_rule" "sg02_02" {
  type = "egress"
  security_group_id = aws_security_group.sg02.id
  
  from_port = local.any_port
  to_port = local.any_port
  protocol = local.any_protocol
  cidr_blocks = local.all_ips
}

 


resource "aws_lb" "alb01" {
  name = "${var.cluster_name}alb01"
  load_balancer_type = "application"
  subnets = data.aws_subnets.subnet01.ids
  security_groups = [aws_security_group.sg02.id]
  
}

resource "aws_lb_listener" "listener01" {
  load_balancer_arn = aws_lb.alb01.arn
  port = local.http_port
  protocol = "HTTP"
  
  default_action {
    type ="fixed-response"
    
    fixed_response {
      content_type ="text/plain"
      message_body = "404: page not found"
      status_code = 404
    }
  }
}


resource "aws_lb_listener_rule" "listener_rule01" {
  listener_arn = aws_lb_listener.listener01.arn
  priority = 100
  condition {
    path_pattern {
      values = ["*"]
    }
  }
  
  action {
    type = "forward"
    target_group_arn = aws_lb_target_group.tg01.arn
  }
}

 


vim variables.tf

variable "cluster_name" {
  description = "cluster_name"
  type = string
}

variable "db_remote_state_bucket" {
  description = "db_remote_state_bucket"
  type = string
}

variable "db_remote_state_key" {
  description = "db_remote_state_key"
  type = string
}

variable "instance_type" {
  description = "instance_type"
  type = string
}

variable "min_size" {
  description = "min_size"
  type = number
}

variable "max_size" {
  description = "max_size"
  type = number
}


locals {
  server_port = 8080
  http_port = 80
  any_port = 0
  any_protocol = "-1"
  tcp_protocol = "tcp"
  all_ips = ["0.0.0.0/0"]
}

 


vim outputs.tf


output "alb_dns_name" {
  value = aws_lb.alb01.dns_name
  description = "The domain name of the load balancer"
}

output "asg_name" {
  value = aws_autoscaling_group.asg01.name
  description = "The name of the Auot Scaling Group"
}

output "alb_security_group_id" {
  value = aws_security_group.sg02.id
  description = "The ID of the Security Group attached to the load balancer"
}

 

vim user_data.sh

#!/bin/bash
cat > index.html <<EOF
<h1>Hello, World</h1>
<p>DB address: ${db_address}</p>
<p>DB port: ${db_port}</p>
EOF

nohup busybox httpd -f -p ${server_port} &

 


-- 6. モジュールをgithubに登録

cd ~/test/modules

git init

vim .gitignore

.terraform
*.tfstate
*.tfstate.backup

git add .

git commit -m "initial commit"

git remote add origin git@github.com:gitusername/gitreponame.git

git push origin main

git tag -a "v0.0.1" -m "first release"

git push --set-upstream origin main

git push --follow-tags

 

-- 7. DB作成(dev)

cd ~/test/live/dev/db/mysql


vim main.tf

provider "aws" {
  region = "ap-northeast-1"
}


terraform {
  backend "s3" {
    bucket = "terraformbucket123"
    key = "dev/db/mysql/terraform.tfstate"
    region = "ap-northeast-1"
    
    dynamodb_table = "terraformtab123"
    encrypt = true
  }
}

module "db" {
  
  source = "git@github.com:gitusername/gitreponame.git//db/mysql?ref=v0.0.1"

  cluster_name = "cluster01dev"
  db_instance_type = "db.t2.micro"
  db_username="root"
  db_password="password"


}

 

vim outputs.tf

output "address" {
  value = module.db.address
  description = "Connect to the database at this endpoint"
}

output "port" {
  value = module.db.port
  description = "The port the database is listening on"
}


sshキーをgithubに登録

eval `ssh-agent`
ssh-add ~/.ssh/id_rsa

 

terraform init
terraform apply

 

-- 8. DB作成(prod)

cd ~/test/live/prod/db/mysql


vim main.tf

provider "aws" {
  region = "ap-northeast-1"
}


terraform {
  backend "s3" {
    bucket = "terraformbucket123"
    key = "prod/db/mysql/terraform.tfstate"
    region = "ap-northeast-1"
    
    dynamodb_table = "terraformtab123"
    encrypt = true
  }
}

module "db" {
  
  source = "git@github.com:gitusername/gitreponame.git//db/mysql?ref=v0.0.1"

  cluster_name = "cluster01prod"
  db_instance_type = "db.t2.micro"
  db_username="root"
  db_password="password"


}

 

vim outputs.tf

output "address" {
  value = module.db.address
  description = "Connect to the database at this endpoint"
}

output "port" {
  value = module.db.port
  description = "The port the database is listening on"
}


sshキーをgithubに登録

eval `ssh-agent`
ssh-add ~/.ssh/id_rsa

 

terraform init
terraform apply

 

 

-- 9. WEB作成(dev)


cd ~/test/live/dev/services/web


vim main.tf

provider "aws" {
  region = "ap-northeast-1"
}

terraform {
  backend "s3" {
    bucket = "terraformbucket123"
    key = "dev/services/web/terraform.tfstate"
    region = "ap-northeast-1"
    
    dynamodb_table = "terraformtab123"
    encrypt = true
  }
}


module "web" {
  
  source = "git@github.com:gitusername/gitreponame.git//services/web?ref=v0.0.1"

  cluster_name = "cluster01dev"
  db_remote_state_bucket = "terraformbucket123"
  db_remote_state_key = "dev/db/mysql/terraform.tfstate"
  
  instance_type = "t2.micro"
  min_size = 1
  max_size = 1

}


vim outputs.tf

output "alb_dns_name" {
  value = module.web.alb_dns_name
  description = "The domain name of the load balancer"
}


sshキーをgithubに登録

eval `ssh-agent`
ssh-add ~/.ssh/id_rsa


terraform init
terraform apply

 

 

-- 10. WEB作成(prod)


cd ~/test/live/prod/services/web

 

vim main.tf

provider "aws" {
  region = "ap-northeast-1"
}

terraform {
  backend "s3" {
    bucket = "terraformbucket123"
    key = "prod/services/web/terraform.tfstate"
    region = "ap-northeast-1"
    
    dynamodb_table = "terraformtab123"
    encrypt = true
  }
}


module "web" {
  source = "git@github.com:gitusername/gitreponame.git//services/web?ref=v0.0.1"
  
  cluster_name = "cluster01prod"
  db_remote_state_bucket = "terraformbucket123"
  db_remote_state_key = "prod/db/mysql/terraform.tfstate"
  
  instance_type = "t3.micro"
  min_size = 1
  max_size = 1
}

 


vim outputs.tf

output "alb_dns_name" {
  value = module.web.alb_dns_name
  description = "The domain name of the load balancer"
}

sshキーをgithubに登録

eval `ssh-agent`
ssh-add ~/.ssh/id_rsa


terraform init
terraform apply

 

-- 11. クリーンアップ

cd ~/test/live/dev/services/web
terraform destroy

cd ~/test/live/prod/services/web
terraform destroy


cd ~/test/live/dev/db/mysql
terraform destroy

cd ~/test/live/prod/db/mysql
terraform destroy

 

cd ~/test/live/global/s3

vim main.tf

backend "s3"を削除

terraform init -migrate-state

terraform destroy