새소식

IaC/Terraform

4주차(2)_Terraform Module Output, Add-On

  • -
CloudNet@ 팀의 가시다님께서 Leading 하시는 Terraform T101 Study 내용 요약

 

해당 Terraform Study 는 Terraform Up and Running 책을 기반으로 진행 중입니다.

 

 

 

테라폼 모듈에서 원하는 값을 Output 으로 출력하고, 별개의 리소스 모듈을 붙이는 방법을 알아보자.
코드는 이전 포스팅에서 이어진다.



1. Preview


  • Child Module 에서 VPC, Web 리소스를 별개의 모듈로 생성 후, Root Module 에서 서로 연결 !!

 

1. Module Output


1.1. Child Module Outputs.tf 생성

  • Web 모듈에서 VPC 모듈의 RDS 주소와 포트를 사용하고 싶기 때문에,
    VPC 모듈에서 Output 으로 RDS 주소와 포트를 변수처리해주자
  • 해당 작업은 Child Module 에서 진행한다 !!
output "address" {
  value       = aws_db_instance.scott_rds.address
  description = "Connect to the database at this endpoint"
}

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

output "vpcid" {
  value       = aws_vpc.scott_vpc.id
  description = "My VPC Id"
}

 

위 Output 은 Child Module 에서 생성해주었다.
Root Module 에서 해당 Output 을 사용하기 위해서는 재정의해줘야할 필요가 있다.

 

 

 

1.2. Root Module Outputs.tf 생성

  • Root Module 에서는 Child Module 에서 처리한 변수값을 실제 값으로 가져온다.
output "address" {
    value       = module.mysql.address
}

output "port" {
    value       = module.mysql.port
}

output "vpcid" {
    value       = module.mysql.vpcid
}
  • Root Module 에서 가져오는 모듈이름을 mysql 이라고 지정해주었고, Child 모듈에서 지정한 변수 이름을 가져오면된다.

 

모듈에서 Output 사용 구문은 다음과 같다.
module.<MODULE_NAME>.<OUTPUT_NAME>

 

그럼 출력 변수를 Web 리소스 모듈에 적용해보자



 

2. Module Add-on


우리는 앞서 'terraform_remote_state' 라는 데이터 소스를 배웠다.
이를 활용하면 모듈 또한 리소스 별로 분리시켜 관리할 수 있는 장점이 생긴다.

 

2.1. Child Module Main.tf

  • 우선 Child Module 의 main.tf 파일은 다음과 같다.

data "terraform_remote_state" "db" {
  backend = "s3"
  config = {
    bucket = var.s3_vpc_location
    key    = var.s3_vpc_key
    region = var.s3_vpc_region
  }
}

resource "aws_subnet" "scott_subnet1" {
  vpc_id     = data.terraform_remote_state.db.outputs.vpcid
  cidr_block = var.subnet1_cidr

  availability_zone = "ap-northeast-2a"

  tags = {
    Name = "${var.env}-terraform-subnet1"
  }
}

resource "aws_subnet" "scott_subnet2" {
  vpc_id     = data.terraform_remote_state.db.outputs.vpcid
  cidr_block = var.subnet2_cidr

  availability_zone = "ap-northeast-2c"

  tags = {
    Name = "${var.env}-terraform-subnet2"
  }
}

resource "aws_internet_gateway" "scott_igw" {
  vpc_id = data.terraform_remote_state.db.outputs.vpcid

  tags = {
    Name = "${var.env}-terraform-igw"
  }
}

resource "aws_route_table" "scott_rt" {
  vpc_id = data.terraform_remote_state.db.outputs.vpcid

  tags = {
    Name = "${var.env}-terraform-rt"
  }
}

resource "aws_route_table_association" "scott_rt_association1" {
  subnet_id      = aws_subnet.scott_subnet1.id
  route_table_id = aws_route_table.scott_rt.id
}

resource "aws_route_table_association" "scott_rt_association2" {
  subnet_id      = aws_subnet.scott_subnet2.id
  route_table_id = aws_route_table.scott_rt.id
}

resource "aws_route" "scott_default_route" {
  route_table_id         = aws_route_table.scott_rt.id
  destination_cidr_block = "0.0.0.0/0"
  gateway_id             = aws_internet_gateway.scott_igw.id
}

resource "aws_security_group" "scott_sg" {
  vpc_id      = data.terraform_remote_state.db.outputs.vpcid
  name        = "${var.env}-sg-web"
  description = "Terraform Study SG - Web"
}

resource "aws_security_group_rule" "scott_sg_inbound" {
  type              = "ingress"
  from_port         = 80
  to_port           = 80
  protocol          = "tcp"
  cidr_blocks       = ["0.0.0.0/0"]
  security_group_id = aws_security_group.scott_sg.id
}

resource "aws_security_group_rule" "scott_sg_outbound" {
  type              = "egress"
  from_port         = 0
  to_port           = 0
  protocol          = "-1"
  cidr_blocks       = ["0.0.0.0/0"]
  security_group_id = aws_security_group.scott_sg.id
}

data "aws_ami" "scott_amazonlinux2" {
  most_recent = true
  filter {
    name   = "owner-alias"
    values = ["amazon"]
  }

  filter {
    name   = "name"
    values = ["amzn2-ami-hvm-*-x86_64-ebs"]
  }

  owners = ["amazon"]
}

resource "aws_launch_configuration" "scott_launch_config" {
  name     = "${var.env}-terraform-launch-config"
  image_id        = data.aws_ami.scott_amazonlinux2.id
  instance_type   = var.ec2_type
  security_groups = [aws_security_group.scott_sg.id]
  associate_public_ip_address = true

  # Render the User Data script as a template
  user_data = templatefile("${path.root}/11_user-data.sh", {
    server_port = 80
    db_address  = data.terraform_remote_state.db.outputs.address
    db_port     = data.terraform_remote_state.db.outputs.port
  })

  # Required when using a launch configuration with an auto scaling group.
  lifecycle {
    create_before_destroy = true
  }
}

resource "aws_autoscaling_group" "scott_asg" {
  name                 = "${var.env}-terraform-asg"
  health_check_type    = "ELB"
  target_group_arns    = [aws_lb_target_group.scott_albtg.arn]
  launch_configuration = aws_launch_configuration.scott_launch_config.name
  vpc_zone_identifier  = [aws_subnet.scott_subnet1.id, aws_subnet.scott_subnet2.id]
  min_size             = var.asg_min_ec2
  max_size             = var.asg_max_ec2

  tag {
    key                 = "Name"
    value               = "${var.env}-terraform-asg"
    propagate_at_launch = true
  }
}

resource "aws_lb" "scott_alb" {
  name               = "${var.env}-terraform-alb"
  load_balancer_type = "application"
  subnets            = [aws_subnet.scott_subnet1.id, aws_subnet.scott_subnet2.id]
  security_groups    = [aws_security_group.scott_sg.id]

  tags = {
    Name = "${var.env}-terraform-alb"
  }
}

resource "aws_lb_listener" "scott_http" {
  load_balancer_arn = aws_lb.scott_alb.arn
  port              = 80
  protocol          = "HTTP"

  # By default, return a simple 404 page
  default_action {
    type = "fixed-response"

    fixed_response {
      content_type = "text/plain"
      message_body = "404: page not found - Terraform Study"
      status_code  = 404
    }
  }
}

resource "aws_lb_target_group" "scott_albtg" {
  name = "t101-alb-tg"
  port     = 80
  protocol = "HTTP"
  vpc_id   = data.terraform_remote_state.db.outputs.vpcid

  health_check {
    path                = "/"
    protocol            = "HTTP"
    matcher             = "200-299"
    interval            = 5
    timeout             = 3
    healthy_threshold   = 2
    unhealthy_threshold = 2
  }
}

resource "aws_lb_listener_rule" "scott_albrule" {
  listener_arn = aws_lb_listener.scott_http.arn
  priority     = 100

  condition {
    path_pattern {
      values = ["*"]
    }
  }

  action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.scott_albtg.arn
  }
}

 

이전에 File Layout 을 이용해서 tfstate 파일을 격리했을 때와 비슷한 구조이다.
주의해야할 부분을 다시 살펴보자

 

 

terrafrom_remote_state 부분에 버킷에 대한 Config 를 모두 변수처리해주었다.
  • 해당 부분은 Root Module 에서 지정해주기 위함이다.
data "terraform_remote_state" "db" {
  backend = "s3"
  config = {
    bucket = var.s3_vpc_location
    key    = var.s3_vpc_key
    region = var.s3_vpc_region
  }
}

 

 

EC2 User-data 파일의 위치를 path.root 로 지정해주었다.
  • path.root 를 통해, Root Module 위치에 있는 User-data 파일을 사용하기 위함이다.

resource "aws_launch_configuration" "scott_launch_config" {
  name     = "${var.env}-terraform-launch-config"
  image_id        = data.aws_ami.scott_amazonlinux2.id
  instance_type   = var.ec2_type
  security_groups = [aws_security_group.scott_sg.id]
  associate_public_ip_address = true

  # Render the User Data script as a template
  user_data = templatefile("${path.root}/11_user-data.sh", {
    server_port = 80
    db_address  = data.terraform_remote_state.db.outputs.address
    db_port     = data.terraform_remote_state.db.outputs.port
  })

  # Required when using a launch configuration with an auto scaling group.
  lifecycle {
    create_before_destroy = true
  }
}

 

2.2. Child Module Variables.tf

  • 다음과 같이 Variables 파일을 생성하자
variable "s3_vpc_location" {
    description = "S3 Backend Location"
    type        = string 
}

variable "s3_vpc_key" {
    description = "S3 Backend Key"
    type        = string  
}

variable "s3_vpc_region" {
    description = "S3 Backend Region"
    type        = string
}

variable "env" {
  description = "Environment"
  type        = string
}

variable "subnet1_cidr" {
  description = "Subnet3 CIDR Block"
  type        = string
}

variable "subnet2_cidr" {
  description = "Subnet4 CIDR Block"
  type        = string
}

variable "ec2_type" {
  description = "EC2 Instance Type"
  type        = string
}

variable "asg_max_ec2" {
  type = number
}

variable "asg_min_ec2" {
  type = number
}

 

2.3. Child Moudle Outputs.tf 생성

  • ALB DNS 를 Output 변수로 출력하기 위해 다음과 같은 파일을 생성하자.
output "alb_dns" {
  value       = aws_lb.scott_alb.dns_name
  description = "The DNS Address of the ALB"
}

 

2.4. Root Module main.tf 생성

  • 이제 해당 변수를 입력하여 실제 생성할 Root Module 의 main.tf 를 생성해보자
provider "aws" {
  region  = "ap-northeast-2"
}

terraform {
  backend "s3" {
    bucket = "scott-t101study-tfstate-files"
    key    = "stage/web/terraform.tfstate"
    region = "ap-northeast-2"
    dynamodb_table = "terraform-locks-week3-files"
  }
}

module "web" {
    source = "../../02_module/02_web/"

    s3_vpc_location     = "scott-t101study-tfstate-files"
    s3_vpc_key          = "stage/mysql/terraform.tfstate"
    s3_vpc_region       = "ap-northeast-2"

    env                 = "stg"
    subnet1_cidr        = "10.10.1.0/24"
    subnet2_cidr        = "10.10.2.0/24"

    ec2_type            = "t2.micro"
    asg_max_ec2         = 5
    asg_min_ec2         = 2
}
  • 앞서 terraform_remote_state 변수로 지정한 s3 Config 가 입력이 되어있다.

 

2.5. User-data 생성

  • user-data 위치를 root module로 지정했기 때문에 (${path.root}), user-data 파일을 생성해주자
#!/bin/bash
wget https://busybox.net/downloads/binaries/1.31.0-defconfig-multiarch-musl/busybox-x86_64
mv busybox-x86_64 busybox
chmod +x busybox

cat > index.html <<EOF
<h1>Terraform Study</h1>
<p>My RDS DB address: ${db_address}</p>
<p>My RDS DB port: ${db_port}</p>
EOF
nohup ./busybox httpd -f -p ${server_port} &

 

2.6. Root Module Output 생성

  • Child Module 에서 ALB DNS 를 출력 변수 처리했기 때문에, Root Module 에서도 동일하게 변수 처리해주자
output "alb_dns" {
    description = "DNS Address of the ALB"
    value       = module.web.alb_dns
}



3. 생성 확인


모든 것이 준비되었다면 리소스를 실제 배포해보고 ALB DNS 주소로 들어가보자

 

 

실습 진행 후에는 반드시 리소스를 삭제하자!

 


 

이번 포스팅에서는 Module 을 활용할 때, Output 으로 변수를 출력하는 법별개의 리소스 모듈을 연결하는 작업을 해보았다.

다음 포스팅에서는 반복되는 리소스 생성에 용이한 테라폼 반복문에 대해서 알아보겠다 !! (ex. 서브넷 등)

Contents

포스팅 주소를 복사했습니다