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でバージョン管理
-- 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"
}
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"
}
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"
}
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"
}
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