[Terraform 4장] How to Create Reusable Infrastructure with Terraform Modules
본 글은 Terraform Up& Running 책에 대하여 4장에 대한 내용 정리 글이다.
Terraform를 사용해 효율적으로 재사용 가능한 인프라를 구성하려면, 여러 환경(예: 스테이징, 운영환경)에서 동일한 코드를 반복해서 복사·붙여넣지 않고도 인프라를 손쉽게 배포할 수 있어야 한다. 이를 위해 가장 중요한 개념이 모듈(Module)이다.
스테이징과 프로덕션 환경의 유사성
- 일반적으로 팀 내부 테스트를 위한 staging 환경과 실제 사용자를 위한 운영 환경으로 분리하여 운영한다.
- 두 환경은 거의 동일해야 하며, 비용 절감을 위해 스테이징에서 리소스 규모가 운영환경 대비 규모가 작을 수 있다.
여러 환경에 동일한 코드를 반복해 작성하면 유지보수와 변경 관리가 복잡해지게 된다. 프로그래밍 언어에서 함수형 구조로 코드를 재사용하듯이, Terraform에서는 모듈로 코드를 추상화하여 재사용한다. 모듈을 활용하면 스테이징과 프로덕션 환경에서 같은 웹 서버, 데이터베이스 설정 등을 손쉽게 공유할 수 있다.
아래 그림과 같이 Staging, Prod 환경의 공통 코드를 한 군데 모듈에 모아두고, 그 모듈을 여러 환경(스테이징, 프로덕션 등)에서 호출만 하는 방식이다.
4-1. 테라폼 모듈
Terraform에서의 모듈(Module)이란 Terraform 구성 파일(.tf)들이 모여 있는 폴더 자체를 의미한다. 루트 모듈(Root Module)과 재사용 가능한 모듈(Reusable Module)의 차이점은 아래와 같다.
- 루트 모듈: terraform apply를 직접 실행하는 모듈.
- 재사용 가능한 모듈: 다른 모듈(또는 루트 모듈)에서 호출되어 재사용되는 모듈.
<예시 1. 기본 변수>
variable "cluster_name" {
description = "The name to use for all the cluster resources"
type = string
}
variable "db_remote_state_bucket" {
description = "The name of the S3 bucket for the database's remote state"
type = string
}
variable "db_remote_state_key" {
description = "The path for the database's remote state in S3"
type = string
}
여기서 헷갈릴 수 있는 부분을 정리하면, Terraform에서 변수(variables.tf)는 “이 모듈이 어떤 값을 입력으로 받을 수 있다”는 선언 역할만 한다는 것이다. 실제로 그 변수에 할당될 구체적인 값은 모듈을 호출하는 쪽(예: 스테이징 환경의 stage/services/webserver-cluster/main.tf 등)에서 정의한다.
물론 모듈쪽 variable.tf 파일 내 default 설정을 통해 모듈을 호출하는 쪽에서 별다른 지정을 하지 않는 경우 기본값이 설정되어질 수 있도록 할 수 있다. 그러나 책의 예시에서는 환경마다 다른 값을 반드시 지정해주어야 하므로, default를 생략하고 모듈 호출 시 직접 입력하도록 의도하였다.
modules/…/main.tf 구성내용
이 부분(모듈 내부의 main.tf)이 곧 스테이징·프로덕션 등 여러 환경에서 공통으로 활용될 템플릿이며, 해당 모듈이 “어떤 리소스(EC2, ALB 등)를 어떻게 생성할 것인지”를 정의한 청사진(blueprint)역할을 한다.
modules/…/main.tf 의 역할을 정리해보자면,
(1) stage나 prod 같은 루트 모듈(환경별 디렉터리)에서 module "webserver_cluster" { ... } 블록으로 변수를 넘겨주면,
(2) 이 값들이 modules/…/main.tf 내 리소스 정의에서 사용하는 var.<변수명>에 매핑되어,
(3) 최종적으로 어떤 이름, 어떤 인스턴스 타입, 몇 대의 인스턴스를 띄울지 등 리소스의 구체적 모습이 결정되어 진다.
아래 alb 보안그룹 예시처럼, aws_security_group 리소스를 정의할 때 var.cluster_name이라는 입력 변수로부터 받은 값을 활용해 최종 리소스 이름을 결정한다. 만약 루트 모듈(stage/.../main.tf)에서 cluster_name = "webservers-stage"라고 전달했다면, 최종적으로 생성되는 Security Group 이름은 "webservers-stage-alb"가 된다.
<예시 1. alb 보안그룹 예시>
resource "aws_security_group" "alb" {
name = "${var.cluster_name}-alb"
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"]
}
}
4-3. 모듈 로컬 (Module Local)
- 로컬 값(Local Values)은 모듈 내부에서 고정적으로 사용되는 값을 한 곳에서 관리하는 방법이다.
- 같은 숫자나 문자열을 여러 리소스에서 반복할 때, 로컬 값으로 추상화하면 코드 중복과 실수를 크게 줄일 수 있다.
- 입력 변수(Input Variables)를 최소화하여 불필요한 옵션 노출을 방지할 수 있게 된다.
- 일반적으로 모듈 내부에서만 쓰는 고정·공통 값은 로컬 값으로 지정한다.
모듈 로컬은 단어 뜻 그래도 모듈 내부에서만 쓰이는 변수를 지칭한다. 모듈 외부에서 값을 주입하거나 변경할 수 없게 함으로써 잘못된 오버라이드 방지한다.
예를 들어, HTTP 80번 포트나 0.0.0.0/0 CIDR 같은 “고정값”을 여러 리소스에서 반복해 쓰는 경우,
하드코딩된 값을 직접 수정하다 보면 오탈자나 실수로 인한 에러가 발생할 수 있다. 이때, 로컬 값을 사용하면 이를 한 곳에서 정의를 함으로써 모듈 내부 전체에서 재사용이 가능토록 한다.
로컬 값은 locals { ... } 블록 내부에서 정의된다.
locals {
http_port = 80
any_port = 0
any_protocol = "-1"
tcp_protocol = "tcp"
all_ips = ["0.0.0.0/0"]
}
아래 예시는 ELB 보안 그룹에 local 항목을 활용한 결과이다. 이 결과물을 통해 여러 곳에 반복되던 80, 0.0.0.0/0, -1 등의 값을 모두 로컬 값으로 치환하여 관리가 되어질 수 있게 되어진다.
resource "aws_security_group" "alb" {
name = "${var.cluster_name}-alb"
ingress {
from_port = local.http_port
to_port = local.http_port
protocol = local.tcp_protocol
cidr_blocks = local.all_ips
}
egress {
from_port = local.any_port
to_port = local.any_port
protocol = local.any_protocol
cidr_blocks = local.all_ips
}
}
로컬 값 vs 입력변수 차이점
- 입력 변수(Input Variables)
- 모듈을 사용하는 쪽(stg, prod)에서 값을 주입받음
- 모듈 외부에서 환경(stg, prod)마다 서로 다른 값을 손쉽게 설정 가능
- 로컬 값(Local Values)
- 모듈 내부에서만 사용되며, 외부에서 변경 불가
- “반복되는 값”이나 “중간 계산 결과” 등을 모듈 내에서만 재사용할 때 활용
- 하드코딩 없이 모듈 코드 품질을 개선하고, DRY(Don’t Repeat Yourself) 원칙을 지킴
Module Output
- 목적: 모듈 내부에서 생성되거나 계산된 값을 바깥(루트 모듈)으로 전달
- 사용 형태: outputs.tf 파일에 output "<이름>" { value = ... } 형태로 정의
- 접근 방법: 모듈을 호출하는 쪽에서는 module.<모듈_이름>.<output_이름> 구문으로 값을 가져옴
<예시: ALB dns 주소 확인>
출력 변수를 사용하여 모듈에서 생성한 ALB 주소를 외부에서 확인할 수 있다. 아래와 같이 모듈 내부, 외부를 구성함으로써 “모듈 → 루트 모듈” 순으로 정보가 전달되며 DNS 정보가 전달된다.
모듈 내부 (modules/…/outputs.tf)
output "alb_dns_name" {
value = aws_lb.example.dns_name
description = "The domain name of the load balancer"
}
모듈 외부 (stage/…/outputs.tf 또는 prod/…/outputs.tf)
output "alb_dns_name" {
value = module.webserver_cluster.alb_dns_name
description = "The domain name of the load balancer"
}
'Automation > Terraform' 카테고리의 다른 글
[Terraform] 3장 How to manage Terraform State (terraform up&running 책 정리) (1) | 2024.12.28 |
---|---|
[Terraform] 2장 Getting Started with Terraform (terraform up&running 책 정리) (0) | 2024.12.21 |
[Terraform] 1장 Why Terraform? (terraform up&running 책 정리) (0) | 2024.12.14 |
[Terraform] terraform state에 대해 알아보자 (0) | 2024.11.17 |
댓글