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. 서브넷 등)
'IaC > Terraform' 카테고리의 다른 글
5주차(2)_Terraform 반복문 for_each (0) | 2022.11.28 |
---|---|
5주차(1)_Terraform 반복문_count (0) | 2022.11.22 |
4주차(1)_Terraform Module (0) | 2022.11.17 |
3주차(2)_File Layout 을 이용한 tfstate 파일 격리 (0) | 2022.11.16 |
[Infracost] 테라폼 리소스 비용 한눈에 보기 - 2. Atlantis 연동 (2) | 2022.11.14 |
Contents