리소스 다루기
리소스 다루기 (Resource Block)
구성 언어의 세계에서 한 단계 더 들어가면, Terraform이 실제로 "만드는 것"이 뭔지 궁금해져요. 그게 바로 **리소스(resource)**인데, 리소스 블록은 인프라 한 조각을 정의하고 그걸 어떤 설정으로 만들지 지정하는 블록이에요. 특정 리소스가 받는 인자는 프로바이더가 정하기 때문에, 구체적인 인자 목록은 해당 프로바이더 문서를 봐야 해요.
리소스 블록은 단순히 "이걸 만들어"라고만 하지 않아요. 몇 개를 만들지(count·for_each), 다른 리소스에 의존하는지(depends_on), 파괴될 때 어떤 행동을 할지(lifecycle), 원격 장비에서 어떤 명령을 실행할지(provisioner)까지 블록 안에서 선언해요. 그래서 리소스 블록 하나가 곧 하나의 배포 단위가 돼요.
리소스 블록의 기본 구조
리소스는 resource "<TYPE>" "<LABEL>" 형태로 선언해요. 타입은 어떤 인프라를 만들지(프로바이더가 정하는 리소스 타입 또는 내장 terraform_data), 라벨은 그 리소스를 상태 파일에서 추적하기 위한 이름이에요. 라벨은 실제 인프라 설정에는 영향을 주지 않아요.
resource "<TYPE>" "<LABEL>" {
# 인자와 중첩 블록
count = 1
# ...
}
설정에서 이 리소스를 참조할 때는 항상 <TYPE>.<LABEL> 문법을 써요. 예를 들어 resource "aws_vpc" "main"이라면 다른 곳에서 aws_vpc.main.id처럼 참조하죠.
블록이 지원하는 인자와 메타 인자
리소스 블록엔 여러 메타 인자가 있어요. 모두 Terraform 언어에 내장되어 리소스 생성 방식을 제어해요.
count— 같은 리소스를 동일하거나 유사한 설정으로 여러 개 만들 때 써요.for_each와 같은 블록에 함께 쓸 수 없어요.for_each— 리소스를 각 블록마다 따로 쓰지 않고, map이나 string set을 받아 비슷한 리소스들을 한 번에 만들 때 써요.count와 상호 배타적이에요.depends_on— 이 리소스가 의존하는 상위 리소스를 지정해요. Terraform은 상위 리소스의 작업을 모두 끝내기 전에 이 리소스를 건드리지 않아요.provider— 이 리소스를 만들 때 대체 프로바이더 설정(별칭)을 쓰도록 지정해요.lifecycle— 리소스에 대한 작업을 언제 어떻게 수행할지 정하는 수명주기 규칙이에요. 예를 들어create_before_destroy,prevent_destroy,ignore_changes같은 규칙을 넣을 수 있어요.
lifecycle 블록 전체 모양은 아래와 같아요. 조건(precondition·postcondition)을 넣어 생성 전후에 리소스 상태를 검증할 수도 있어요.
resource "aws_instance" "example" {
# ...
lifecycle {
create_before_destroy = true
prevent_destroy = false
ignore_changes = [tags]
precondition {
condition = var.image_id != ""
error_message = "image_id가 비어 있으면 안 됩니다."
}
}
}
데이터 소스와 프로비저너
리소스와 짝을 이루는 개념으로 **데이터 소스(data)**가 있어요. data 블록은 이미 존재하는 인프라나 외부 값을 읽어 올 때 쓰고, resource처럼 새로 만들지 않아요. 아래 예시는 EC2 인스턴스의 루트 볼륨을 데이터 소스로 조회하면서, 관리 리소스의 결과를 검증하기 위해 postcondition을 함께 쓰는 모습이에요.
data "aws_ebs_volume" "example" {
filter {
name = "volume-id"
values = [aws_instance.example.root_block_device[0].volume_id]
}
lifecycle {
postcondition {
condition = self.encrypted
error_message = "서버의 루트 볼륨이 암호화되어 있지 않습니다."
}
}
}
프로비저너는 리소스 생성 전후에 추가 작업을 할 때 써요. 예를 들어 file 프로비저너로 파일을 복사하고, remote-exec로 원격에서 명령을 실행할 수 있어요. 연결 설정은 connection 블록으로 지정해요.
resource "aws_instance" "web" {
# ...
connection {
type = "ssh"
user = "root"
password = var.root_password
host = self.public_ip
}
provisioner "file" {
source = "script.sh"
destination = "/tmp/script.sh"
}
provisioner "remote-exec" {
inline = [
"chmod +x /tmp/script.sh",
"/tmp/script.sh args",
]
}
}
한 가지 기억할 점은, 프로비저너는 보통 "마지막 수단"(last resort)으로 여겨진다는 거예요. 가급적 리소스의 능력으로 해결하고, 프로비저너는 인프라 자체로 표현할 수 없는 설정을 다룰 때만 쓰는 게 권장돼요. 명령이 실패했을 때 계속할지 실패로 둘지도 on_failure = continue 같은 옵션으로 정할 수 있어요.
더 알아보기
- Terraform 구성 언어 — HCL 기초 문법
- 모듈 재사용 — 여러 리소스를 묶는 방법
- 공식 문서: Resource block, Meta-arguments