-
04. 테라폼 모듈24년 11월 이전/Terraform Up and Running 2021. 6. 11. 22:49반응형
이 문서는 책 "테라폼 설치에서 운영까지"을 읽고 작성되었습니다. 최대한 요약해서 책 내용을 최소로 하는데 목표를 두고 있어서, 더 친절하고 정확한 내용을 원하신다면 책을 구매하시는 것을 추천드립니다. => 책 링크
3장을 모두 진행하고 오면 디렉토리 구조는 다음과 같다.
src |- ch03 |- global |- s3 |- stage |- data-stores |- mysql |- services |- webserver-cluster
여기서 4장 코드는
stage
를 복사해서prod
를 만들어서 다음과 같은 구조로 변경한다.src |- ch04 |- global |- s3 |- stage |- data-stores |- mysql |- services |- webserver-cluster |- prod (stage 복사) |- data-stores |- mysql |- services |- webserver-cluster
보통
stage
는 개발 환경,prod
는 상용 환경을 뜻한다. 그리고 이들의 코드는 각 환경에 의존해야 하는 코드 외 거의 유사하다. 때문에 필연적으로 중복되는 코드가 발생한다.module
은Terraform
에서 코드 중복을 제거하는 주요한 기능 중 하나이다. 이번 장에서는 이를 다룬다.모듈 만드는 법
이제
data-stores/mysql
코드를 모듈로 변경해보자. 먼저 다음과 같이 디렉토리 구조로 변경한다.src |- ch04 |- global |- s3 |- modules |- data-stores |- mysql |- services |- webserver-cluster |- stage |- data-stores |- mysql |- services |- webserver-cluster |- prod (stage 복사) |- data-stores |- mysql |- services |- webserver-cluster
먼저 3장에서 코드는 다음과 같았다.
src/ch03/stage/data-stores/mysql/main.tf
terraform { backend "s3" { bucket = "gurumee-terraform-state" key = "prod/data-stores/mysql/terraform.tfstate" region = "us-east-1" encrypt = true dynamodb_table = "gurumee-terraform-lock" } } provider "aws" { region = "us-east-1" } resource "aws_db_instance" "example" { engine = "mysql" allocated_storage = 10 instance_class = "db.t2.micro" name = "example_database" username = "admin" password = var.db_password skip_final_snapshot = true }
여기서
resource
부분만 따로 복사해서modules/data-stores/mysql/main.tf
에 붙여 넣는다. 그 후 다음과 같이 수정한다.src/ch04/modules/data-stores/mysql/main.tf
resource "aws_db_instance" "mysql" { engine = "mysql" allocated_storage = var.db_allocated_storage instance_class = var.db_instance_class name = var.db_name username = var.db_username password = var.db_password skip_final_snapshot = true }
위 코드는 프로그래밍 언어로 치면 함수 본문과 같다. 그리고 같은 경로에 다음과 같이
variables.tf
를 만든다.src/ch04/modules/data-stores/mysql/variables.tf
variable "db_allocated_storage" { type = number description = "The allocated storage for the database" } variable "db_instance_class" { type = string description = "The instance class for the database" } variable "db_name" { type = string description = "The name for the database" } variable "db_username" { type = string description = "The username for the database" } variable "db_password" { type = string description = "The password for the database" }
module
에서variables.tf
는 프로그래밍 언어로 치면 입력과 같다. 이제 출력을 담당하는outputs.tf
를 다음과 같이 만들어둔다.src/ch04/modules/data-stores/mysql/outputs.tf
output "address" { value = aws_db_instance.mysql.address } output "port" { value = aws_db_instance.mysql.port }
이렇게 하면
mysql
데이터베이스를 생성하는module
의 만들어진 것이다. 이런 방식으로services/webserver-cluster
역시module
로 만들 수 있다. 이는 스스로 해본다. 코드는 다음을 확인하면 된다.모듈 사용하는 법
이제
module
을 사용해보자.prod/data-stores/mysql
의main.tf
를 다음과 같이 수정한다.src/ch04/prod/data-stores/mysql/main.tf
# ... # resource 부분을 아래와 같이 변경한다. module "mysql" { source = "../../../modules/data-stores/mysql" db_allocated_storage = 10 db_instance_class = "db.t2.micro" db_name = "prod_db" db_username = "admin" db_password = var.db_password }
위와 같이 함수의 입력을 파라미터 전달하듯이,
module
의variables.tf
에서 선언했던 변수들의 값을 할당해주어야 한다. 이제outputs.tf
를 다음과 같이 변경한다.src/ch04/prod/data-stores/mysql/outputs.tf
output "address" { value = module.mysql.address } output "port" { value = module.mysql.port }
그렇다.
module.<모듈 이름>.<모듈에서 선언한 output 이름>
으로module
의 출력 값을 다른 코드에서 쓸 수가 있다. 그리고module
로 코드로 변경했을 때terraform plan
이나terraform apply
등의 명령어를 쓸 때terraform init
명령어를 먼저 해주어야 한다.module
을 사용할 경우terraform init
명령어 사용 시,.terraform/modules
라는 디렉토리가 생성되는데 이 때 필요한 코드가 저장되어 있다. 따라서module
을 추가/변경이 있을 경우 반드시terraform init
명령어가 필요하다.github에서 모듈 가져오기
이제
github
에서 소스코드를 가져와보자. 이번에는stage
환경에서 진행한다.main.tf
를 다음과 같이 변경한다.src/ch04/stage/data-stores/mysql/main.tf
# ... # resource 부분을 아래와 같이 변경한다. module "mysql" { source = "github.com/gurumee92/today-i-learned//terraform_up_and_running/src/ch04/modules/data-stores/mysql" db_allocated_storage = 10 db_instance_class = "db.t2.micro" db_name = "stage_db" db_username = "admin" db_password = var.db_password }
역시
outputs.tf
를prod
때와 같이module
의output
을 이용하도록 코드를 변경한다.src/ch04/stage/data-stores/mysql/outputs.tf
output "address" { value = module.mysql.address } output "port" { value = module.mysql.port }
이제
terraform init
후,terraform apply
하면 역시mysql
1대를 구성할 수 있음을 확인할 수 있다. 이 때 주의할 점은 해당 레포지토리 다음 경로를 "//" 이렇게 잡아주어야 한다.현재 내 레포지토리는 "github.com/gurumee92/today-i-learned"이다. 그리고 현재 작업한 코드 경로는 "terraform_up_and_running/src/ch04/modules/data-stores/mysql"에 있다.
따라서 "../today-i-learned//terraform_up_and_running/.." 이런식으로 경로를 잡아주어야 한다. 외부에서 코드를 가져올 때는 항상 최신인지 확인이 필요하다. 만약 최신 버전이 아니라면 다음 명령어로 업데이트가 가능한다.
$ terraform get -update
모듈 주의점
모듈을 만들 때, 다음과 같은 사항을 주의해야 한다.
- 파일 경로
- 인라인 블록
먼저 인라인 블록의 경우는 최대한 분리하는 것이 좋다. 3장에서
elb
의security group
의 코드는 다음과 같았다.src/ch03/stage/services/webserver-cluster/main.tf
# ... resource "aws_security_group" "elb" { name = "terraform-example-elb" ingress { from_port = 80 to_port = 80 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } } # ...
이 때
ingress
,egress
모두 다음과 같이 분리할 수 있다.src/ch04/modules/services/webserver-cluster/main.tf
# ... 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 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } # ...
이렇게 분리하면 뭐가 좋으냐.. 환경에 따라 같은
security group
일지라도 다른 inbound/outbound 설정이 가능하다. 가령security group elb
의 개발 환경에서는 port 12345가 inbound 규칙에 추가되어야 한다면 어떻게 할 것인가? 위와 같이 분리가 되었다면 그 작업은 매우 쉽다.security group elb
의 id를module
의output
으로 지정한 뒤 다음처럼 코드를 작성하면 된다.src/ch04/stage/services/webserver-cluster/main.tf
module "webserver_cluster" { source = "github.com/gurumee92/today-i-learned//terraform_up_and_running/src/ch04/modules/services/webserver-cluster" cluster_name = "websever-stage" db_remote_state_bucket = "gurumee-terraform-state" db_remote_state_key = "stage/data-stores/mysql/terraform.tfstate" instance_type = "t2.micro" min_size = 2 max_size = 2 server_port = 8080 } # 이렇게 추가하면 된다. resource "aws_security_group_rule" "allow_testing_inbound" { type = "ingress" security_group_id = module.webserver_cluster.elb_security_group_id from_port = 12345 to_port = 12345 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] }
이번엔 파일 경로이다. 3장의 경우
user-data
의 값을 주기 위해서user-data.tpl
을 작성하고 이에 대한 값을 넣어주었었다.src/ch03/stage/services/webserver-cluster/main.tf
# ... resource "aws_launch_configuration" "example" { image_id = "ami-0d5eff06f840b45e9" instance_type = "t2.micro" security_groups = [aws_security_group.instance.id] user_data = templatefile("user-data.tpl", { server_port = var.server_port, db_address = data.terraform_remote_state.db.outputs.address, db_port = data.terraform_remote_state.db.outputs.port, }) lifecycle { create_before_destroy = true } } # ...
하지만
module/service/webserver-cluster
에도user-data.tpl
을 넣더라도 정상적으로 작동하지 않을 것이다. 왜냐하면 현재terraform
명령어가 실행된 파일 경로에서 파일을 탐색하기 때문이다. 이를 위해서 코드를 다음과 같이 변경해야 한다.src/ch04/modules/services/webserver-cluster/main.tf
# ... resource "aws_launch_configuration" "example" { image_id = "ami-0d5eff06f840b45e9" instance_type = var.instance_type security_groups = [aws_security_group.instance.id] # 여기가 중요하다. ${path.module}로 값을 지정해주어야 한다. user_data = templatefile("${path.module}/user-data.tpl", { server_port = var.server_port, db_address = data.terraform_remote_state.db.outputs.address, db_port = data.terraform_remote_state.db.outputs.port, }) lifecycle { create_before_destroy = true } } # ...
${path.module}
로terraform
에서module
경로를 지정해주어야 코드가 정상적으로 동작한다. 이는 모듈 작업할 때 실수 할 수 있으니 잘 기억해두길 바란다.4장 전체 코드
4장 전체 코드는 다음 링크에서 확인할 수 있다.
728x90'레거시 > Terraform Up and Running' 카테고리의 다른 글
06. 테라폼을 팀에서 사용하기 (0) 2021.06.16 05. 테라폼 팁과 요령: 반복문, 조건문, 배포 및 주의사항 (1) 2021.06.14 03. 테라폼 상태 관리 (0) 2021.06.02 02. 테라폼 시작하기 (0) 2021.05.26 01. 왜 테라폼인가 (0) 2021.05.17