<aside> 🙇‍♀️ Kakaoenterprise 인턴 수행 중 진행했던 발표 자료입니다.

</aside>

<aside> ⚠️ Provider는 모듈 안에서 정의하는 게 아니라 모듈이 사용되는 곳에서 직접 정의를 해야할 부분이기 때문에 module의 main.tf에는 정의해주지 않는다.

</aside>

1️⃣ 모듈 사용법

모듈 불러오기

module "webserver_cluster" {
	source = "../../../modules/services/webserver-cluster"
}

모듈의 변경사항 동기화

plan과 apply 수행 전에 get 명령어 실행

2️⃣ 모듈 입력

일반적인 프로그래밍 언어에서 함수를 구성할 수 있게 하려면 해당 함수에 입력 매개변수를 추가해야 함.

테라폼에서 모듈은 입력 변수를 추가할 수 있음

3가지 입력변수 정의

 variable "cluster_name" {
  description = "The name to use for all the cluster resources"
}

variable "db_remote_state_bucket" {
  description = "The name to use for S3 bucket for the database's remote state"
}

variable "db_remote_state_key" {
  description = "The path for the database's remote state in S3"
}

ELB 보안그룹 작성에서 “cluster_name” 동적으로 설정하기

resource "aws_security_group" "elb" {
  name = "${var.cluster_name}-elb"

  # 포트 80번 트래픽을 확인한 후, 연동
  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"] # 인터넷이 연결된 어느곳에서라도 접속
  }

  # 상태체크를 요청하기 위해 내보내는 트래픽 허용
  egress {
    from_port = 0
    protocol  = "-1"
    to_port   = 0
    cidr_blocks = ["0.0.0.0/0"]
  }
}

terraform_remote_state 데이터 소스 업데이트

db_remote_state_bucket 및 db_remote_state_key를 각각의 버킷 및 키 매개변수로 사용하도록

data "terraform_remote_state" "db" {
  backend = "s3"

  config = {
    bucket = "${var.db_remote_state_bucket}"
    key = "${var.db_remote_state_key}"
    region = "us-east-1"
  }
}

스테이징 환경 수정 (Stage)

module "webserver_cluster" {
  source = "../../../modules/services/webserver-cluster"

  cluster_name = "webserver-stage"
  db_remote_state_bucket = "lena-tf-state-bucket"
  db_remote_state_key = "stage/datastores/mysql/terraform/tfstate"
}

상용 환경 수정 (Prod)

module "webserver_cluster" {
  source = "../../../modules/services/webserver-cluster"

  cluster_name = "webserver-prod"
  db_remote_state_bucket = "lena-tf-state-bucket"
  db_remote_state_key = "prod/datastores/mysql/terraform.tfstate"
}

이처럼 입력 변수는 모듈의 API 이며, 다른 환경에서 어떻게 동작할지 제어한다.

대규모 트래픽을 수용하기 위한 클러스터의 개수 관리

세가지 입력 변수 추가

variable "instance_type" {
  description = "The type of EC2 Instances to run (e.g. t2.micro)"
}

variable "min_size" {
  description = "The minimum number of EC2 Instances in the ASG"
}

variable "max_size" {
  description = "The maximum number of EC2 Instancees in the ASG"
}

module의 변수 수정

## EC2 인스턴스를 ASG에 설정하는 시작 구성 ##
resource "aws_launch_configuration" "example"{
  image_id = "ami-40d28157"
  **instance_type = "${var.instance_type}"**
  security_groups = ["${aws_security_group.elb.id}"]

  # EC2 인스턴스에 포트 번호를 설정하는 비지박스 부분
  # - EC2 인스턴스의 사용자 데이터 설정을 통해 "Hello, World" 스크립트를 인스턴스 기동시 수행할 수 있게 된다.
  user_data = <<-EOF
#!/bin/bash
echo "Hello, World" >> index.html
echo "${data.terraform_remote_state.db.outputs.address}" >> index.html
echo "${data.terraform_remote_state.db.outputs.port}" >> index.html
nohup busybox httpd -f -p "${var.server_port}" &
EOF

  lifecycle {
    create_before_destroy = true
  }

}

## ASG 생성 ##
resource "aws_autoscaling_group" "example" {
  launch_configuration = "${aws_launch_configuration.example.id}"
  availability_zones = "${data.aws_availability_zones.all.names}"

  load_balancers = ["${aws_elb.example.name}"]  # 인스턴스가 시작할 때 ELB에 각 인스턴스를 등록하도록 ASG에 요청
  health_check_type = "ELB"

  **max_size = "${var.max_size}"
  min_size = "${var.min_size}"**

  tag {
    key = "Name"
    value = "${var.cluster_name}"
    propagate_at_launch =  true
  }
}

스테이징 환경 수정

## 모듈 활용 ##
module "webserver_cluster" {
  source = "../../../modules/services/webserver-cluster"

  cluster_name = "webserver-stage"
  db_remote_state_bucket = "lena-tf-state-bucket"
  db_remote_state_key = "stage/datastores/mysql/terraform/tfstate"

  **instance_type = "t2.micro"
  min_size = 2
  max_size = 2**
}

모듈 환경 수정

## 모듈 활용 ##
module "webserver_cluster" {
  source = "../../../modules/services/webserver-cluster"

  cluster_name = "webserver-stage"
  db_remote_state_bucket = "lena-tf-state-bucket"
  db_remote_state_key = "stage/datastores/mysql/terraform/tfstate"

  **instance_type = "t2.micro"
  min_size = 2
  max_size = 10**
}

3️⃣ 모듈 출력

만약 업무 시간 중에 클러스터 사용량이 더 높은 경우 자동 조정 일정을 사용하여 오전 9시에 서버 수를 늘리고 오후 5시에 서버수를 줄이고 싶다 하자

auto scaling schedule 사용

Auto scaling schedule

aws_autoscaling_schedule resource

resource "aws_autoscaling_schedule" "scale_out_during_business_hours" {
  scheduled_action_name = "scale-out-during-business-hours"
  min_size = 2
  max_size = 10
  desired_capacity = 10
  recurrence = "0 9 * * *"  # 매일 오전 9시
  autoscaling_group_name = ""
}

resource "aws_autoscaling_schedule" "scale-in-at-night" {
  scheduled_action_name = "scale-in-at-night"
  min_size = 2
  max_size = 10
  desired_capacity = 2
  recurrence = "0 17 * * *"  # 매일 오후 5시
  autoscaling_group_name = ""
}
output "asg_name" {
	value = "${aws_autoscaling_group.example.name}"
}
"${module.MODULE_NAME.OUTPUT_NAME}"

"${module.webserver_cluster.asg_name}"
resource "aws_autoscaling_schedule" "scale_out_during_business_hours" {
  scheduled_action_name = "scale-out-during-business-hours"
  min_size = 2
  max_size = 10
  desired_capacity = 10
  recurrence = "0 9 * * *"  # 매일 오전 9시
  **autoscaling_group_name = "${module.webserver_cluster.asg_name}"**
}

resource "aws_autoscaling_schedule" "scale-in-at-night" {
  scheduled_action_name = "scale-in-at-night"
  min_size = 2
  max_size = 10
  desired_capacity = 2
  recurrence = "0 17 * * *"  # 매일 오후 5시
  **autoscaling_group_name = "${module.webserver_cluster.asg_name}"**
}

클러스터 배포 시 테스트할 URL 출력

output "elb_dns_name" {
	value = "${aws_elb.example.dns_name}"
}

stage에 출력 전달

output "elb_dns_name" {
  value = "${module.webserver_cluster.elb_dns_name}"
}

4️⃣ 인라인 블록 → 별도의 리소스

일부 테라폼 리소스 구성은 인라인 블록 또는 별도의 리소스로 정의

모듈을 작성할 떄는 인라인 블록 대신 별도의 리소스로 작성

⚠️ 인라인 블록으로 작성한 트래픽 규칙은 작동되지 않는다.

수정 전, elb의 보안 그룹

resource "aws_security_group" "elb" {
  name = "${var.cluster_name}-elb"

  # 포트 80번 트래픽을 확인한 후, 연동
  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"] # 인터넷이 연결된 어느곳에서라도 접속
  }

  # 상태체크를 요청하기 위해 내보내는 트래픽 허용
  egress {
    from_port = 0
    protocol  = "-1"
    to_port   = 0
    cidr_blocks = ["0.0.0.0/0"]
  }
}

다음과 같이 모듈 변경 가능

resource "aws_security_group" "elb" {
  name = "${var.cluster_name}-elb"
}

resource "**aws_security_group_rule**" "allow_http_inbound" {
	type = "ingress"
	security_group_id = "${aws_security_group.elb.id}"
	
  from_port   = 80
  to_port     = 80
  protocol    = "tcp"
  cidr_blocks = ["0.0.0.0/0"] 
}

resource "**aws_security_group_rule**" "allow_all_outbound" {
	type = "egress"
	security_group_id = "${aws_security_group.elb.id}"

  from_port = 0
  protocol  = "-1"
  to_port   = 0
  cidr_blocks = ["0.0.0.0/0"]
}

별도의 리소스 (aws_security_group_rule)을 사용한 경우,

stage 환경에서 검증을 위해 추가 포트를 열어야 할 경우

필요한 출력 변수를 만들고

output "elb_security_group_id" {
  value = "${aws_security_group.elb.id}"
}

aws_security_group_rule 리소스를 추가만 하면 됨

resource "aws_security_group_rule" "allow_testing_inbound" {
  type = "ingress"
  security_group_id = "${module.webserver_cluster.elb_security_group_id}"

  from_port = 12345
  protocol  = "tcp"
  to_port   = 12345
  cidr_blocks = ["0.0.0.0/0"]
}

포트 추가 확인

포트 추가 확인

<aside> 📌 aws_security_group / aws_security_group_rule aws_route_table / aws_route aws_network_acl / aws_network_acl_rule aws_elb / aws_elb_attachment

</aside>

이전 폴더 구조

**tf-aws-provider**
	ㄴ 📁 global
		ㄴ 📁 s3  # Remote State Storage(s3 bucket) 
			ㄴ 📄 main.tf
			ㄴ 📄 output.tf
	ㄴ 📁 stage
		ㄴ 📁 datastores
			ㄴ 📁 mysql # Database
				ㄴ 📄 main.tf
				ㄴ 📄 var.tf
				ㄴ 📄 output.tf
		ㄴ 📁 services
			ㄴ 📁 webserver-cluster # ASG(EC2 instances) + ELB
				ㄴ 📄 main.tf
				ㄴ 📄 var.tf
				ㄴ 📄 data.tf
				ㄴ 📄 output.tf

모듈 생성

**tf-aws-provider
	ㄴ 📁 modules
		ㄴ 📁 services
			ㄴ 📁 webserver-cluster
				ㄴ 📄 var.tf
				ㄴ 📄 data.tf
				ㄴ 📄 output.tf
				ㄴ 📄 main.tf**
	ㄴ 📁 global
		ㄴ 📁 s3  # Remote State Storage(s3 bucket) 
			ㄴ 📄 main.tf
			ㄴ 📄 output.tf
	ㄴ 📁 stage
		ㄴ 📁 datastores
			ㄴ 📁 mysql # Database
				ㄴ 📄 main.tf
				ㄴ 📄 var.tf
				ㄴ 📄 output.tf
		ㄴ 📁 services
			ㄴ 📁 webserver-cluster # ASG(EC2 instances) + ELB
				ㄴ 📄 main.tf
				ㄴ 📄 var.tf
				ㄴ 📄 data.tf
				ㄴ 📄 output.tf
	**ㄴ 📁 prod
		ㄴ 📁 datastores
			ㄴ 📁 mysql # Database
				ㄴ 📄 main.tf
				ㄴ 📄 var.tf
				ㄴ 📄 output.tf
		ㄴ 📁 services
			ㄴ 📁 webserver-cluster # ASG(EC2 instances) + ELB
				ㄴ 📄 main.tf
				ㄴ 📄 var.tf
				ㄴ 📄 data.tf
				ㄴ 📄 output.tf**

⚠️ 오류

Untitled

→ global 에 있는 s3 버킷 생성 부분을 terraform apply 하지 않았었음

Untitled

→ database도 apply 하고 했는데도 실제 stage에서 module을 사용할 때 어떠한 저장된 state도 없다고 뜸

→ modules의 변경사항을 get 해오는 부분을 stage working dir 에서 했었는데 아닌가 싶어서 module을 작성해둔 modules / services / webserver-cluster dir 에서 get → plan → apply

Untitled

똑같음..

그냥 간단 예제로 한번 해보자 (보안그룹, 인스턴스)