안녕하세요.
개발자 OrElse 박찬영입니다.
지난해 9월 팀프로젝트가 끝나고,
당시 팀원분이 담당하셨던 DevOps 직무에 관심이 생겨
DevOps 관련 도구들을 경험해보는 시간을 가져왔습니다.
사실 Terraform보다는 Kubernetes 환경에서
사용되는 다양한 도구(ArgoCD, Kafka 등)에
더 초점을 맞추고 공부를 했었습니다.
(왜냐면 공고에는 항상 저런 기술들이 나열되어있길래...)
하지만,
DevOps 10년차가 넘는 분과 커피챗을 진행하고
잘못되었다는 것을 알게되었습니다.
"신입에게는 저런 기술들을 단순히 사용해본 경험이 중요한게 아니며
기술 나열을 위한 공부가 아닌 1년을 보냈으면 좋겠다.
Terraform또한 기존 방식처럼 사용해서는 안된다. 모듈을 사용하라"
라고 말씀하셨었습니다.
그 당시에는 조금 충격을 먹고 힘들었지만,
곧바로 공부를 시작했습니다.
Terraform 모듈..!
사실 그 전까지는 Terraform Resource를 단순히 나열해서 사용했습니다 ^^;
그냥 띄우면 되겠거니 했지만 그게 아님을 깨닫고
지나온 과정을 토대로 글을 작성해보려 합니다.
Terraform 모듈이란?
: 반복적인 코드를 재사용하고, 프로젝트를 구조화하며, 관리하기 쉽게 만들어주는 중요한 기능
쉽게 이해하자면,
저는 메서드와 같다고 생각합니다.
어디서든 정의해놓고 여러번 불러다 쓰니까요.
1. 원하는 Variables를 정의 - 메서드 매개변수 정의
2. 내부 로직은 리소스에 제공되는 정보만 작성 후 CSP와 Terraform이 알아서 수행 - 메서드 내부 로직
3. 결과는 Outputs를 통해 전달. - 메서드 결괏값
1. 모듈 구조
/my-module
├── main.tf ## 사용할 리소스 정의
├── variables.tf ## 리소스를 만들기 위해 제공되어야 하는 변수 정의
├── outputs.tf ## 리소스가 생성된 후 확인하고자하는 해당 리소스에 대한 정보 정의
├── versions.tf ## 해당 모듈을 사용 시 사용해야하는 버전 정의
├── README.md
등등...
- 아래는 사용 예시입니다.
## main.tf
resource "aws_vpc" "main" {
cidr_block = var.cidr_block
tags = {
Name = var.vpc_name
}
}
## variables.tf
variable "vpc_name" {
description = "VPC의 이름"
type = string
}
variable "cidr_block" {
description = "VPC의 CIDR 블록"
type = string
}
## outputs.tf
output "vpc_id" {
value = aws_vpc.main.id
}
=> 이 모듈을 이제 불러서 사용해보겠습니다.
## main.tf
module "vpc" {
source = "./my-module" # 모듈 경로
vpc_name = "my-vpc" # 변수 값
cidr_block = "10.0.0.0/16" # 변수 값
}
## outputs.tf
output "vpc_id" {
value = module.vpc.vpc_id
}
자 여기서 의문점이 생기실거에요.
'위의 main.tf는 뭐고, 아래 main.tf는 뭔데?!'
바로 아래는 루트모듈(Root Module)이고, 위는 자식모듈(Child Module)입니다.
2. 루트모듈과 자식모듈
루트모듈 : Terraform이 plan과 apply를 실제로 수행하기 위한 목적으로 작성된 Terraform 코드 모음집
자식모듈 : 다른 모듈의 Terraform 코드 내에서 호출하기 위한 목적으로 작성된 Terraform 코드 모음집
즉, 최상단에서 모든 자식 모듈들을 불러서 사용 가능한 존재가 바로 루트모듈입니다.
이 개념을 잘 숙지하시길 바랍니다!
하나의 조금은 복잡한 예시를 보여드리겠습니다.
terraform/
├── main.tf # 루트 모듈
├── variables.tf # 루트 모듈 변수
├── outputs.tf # 루트 모듈 출력
├── modules/
│ ├── network/ # 차일드 모듈 1
│ │ ├── main.tf
│ │ ├── variables.tf
│ │ ├── outputs.tf
│ └── compute/ # 차일드 모듈 2
│ ├── main.tf
│ ├── variables.tf
│ ├── outputs.tf
│ └── submodules/ # 중첩 차일드 모듈
│ ├── instance/ # 중첩 모듈 1
│ │ ├── main.tf
│ │ ├── variables.tf
│ │ ├── outputs.tf
│ └── disk/ # 중첩 모듈 2
│ ├── main.tf
│ ├── variables.tf
│ ├── outputs.tf
--------------------------------------------- Root main.tf
module "network" {
source = "./modules/network"
vpc_name = "main-vpc"
cidr = "10.0.0.0/16"
}
module "compute" {
source = "./modules/compute"
network_id = module.network.vpc_id
instance_count = 3
}
--------------------------------------------- compute main.tf
module "instances" {
source = "./submodules/instance"
network_id = var.network_id
count = var.instance_count
}
module "disks" {
source = "./submodules/disk"
instance_ids = module.instances.ids
disk_size = var.disk_size
}
--------------------------------------------- instance main.tf
resource "aws_instance" "this" {
count = var.count
ami = "ami-12345678"
instance_type = "t2.micro"
subnet_id = var.network_id
}
output "ids" {
value = aws_instance.this[*].id
}
--------------------------------------------- disk main.tf
resource "aws_ebs_volume" "this" {
count = length(var.instance_ids)
size = var.disk_size
availability_zone = "us-west-2a"
}
resource "aws_volume_attachment" "this" {
count = length(var.instance_ids)
instance_id = element(var.instance_ids, count.index)
volume_id = aws_ebs_volume.this[count.index].id
device_name = "/dev/xvdf"
}
바로 이해하기는 어렵지만, 간단하게 생각하시면
"중첩 메서드를 폴더를 통해 구현하는구나!"
라고 생각하시면 될 것 같습니다.
중첩 모듈을 쓸일이 있겠어..?란 생각을 가졌었지만,
고심끝에 구축한 제 Terraform 폴더 구조를 보니 사용하네요.

이번 시간은 여기까지 입니다.
사실 Terraform 모듈이 뭔지는 길지 않습니다.
폴더 구조를 이루는 것뿐이니까요.
어떻게 Terraform 모듈을 사용하느냐가 문제입니다.
다음 시간에는 Terraform 모듈을 작성하는 방법을 알아보겠습니다.
'DevOps' 카테고리의 다른 글
| 어떻게 하면 Terraform을 효율적으로 관리할 수 있을까? - 2. Terraform 모듈을 작성하는 방법 (0) | 2025.03.11 |
|---|
안녕하세요. 성장하고 싶은 개발자 orElse입니다. 지켜봐주세요.
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!