테라폼 기초 강좌 #3 변수·출력·로컬 값: 하드코딩을 걷어내는 세 가지 도구

4 분 소요

지난 글까지의 코드에는 버킷 이름과 리전이 문자열로 적혀 있었습니다. 실습에서는 문제가 없지만, 같은 구성을 개발용과 운영용으로 두 벌 만들려는 순간 파일을 복사해 문자열을 고치는 처지가 됩니다. 그 복사본 두 개는 곧 서로 어긋나기 시작하고, 콘솔 클릭 시절의 문제가 코드 위에서 재현됩니다. 답은 값을 코드 밖으로 빼내는 것이고, 테라폼은 이를 위해 변수(variable), 출력(output), 로컬 값(locals) 세 가지를 제공합니다. 이번 글에서 셋을 모두 다루겠습니다.

variable: 값의 입구 #

변수는 variable 블록으로 선언합니다. 관례상 variables.tf 파일에 모아 둡니다.

variables.tf
variable "env" {
  type        = string
  description = "배포 환경 이름 (dev, prod)"
}

variable "bucket_prefix" {
  type        = string
  description = "버킷 이름 앞부분"
  default     = "my-terraform"
}

선언한 변수는 var.이름으로 씁니다.

main.tf
resource "aws_s3_bucket" "hello" {
  bucket = "${var.bucket_prefix}-${var.env}"
}

문자열 안에 값을 끼워 넣는 ${...} 문법을 보간(interpolation)이라고 합니다. default가 있는 변수는 값을 주지 않으면 그 값을 쓰고, default가 없는 변수는 값이 들어올 때까지 실행되지 않습니다. type은 string 외에 number, bool, 그리고 list(string), map(string) 같은 컬렉션 타입을 지정할 수 있으며, 타입이 맞지 않는 값은 plan 단계에서 거부됩니다.

값을 넣는 네 가지 경로 #

default가 없는 env에 값을 주는 방법은 네 가지입니다.

  1. terraform.tfvars 파일: 디렉터리에 이 이름의 파일이 있으면 자동으로 읽습니다. env = "dev" 한 줄이면 됩니다.
  2. -var-file 플래그: terraform plan -var-file="prod.tfvars"처럼 파일을 골라서 지정합니다. 환경별 파일을 나눠 두는 실전 패턴의 기초입니다.
  3. -var 플래그: terraform plan -var="env=dev"로 한 개씩 지정합니다.
  4. 환경 변수: TF_VAR_env=dev처럼 TF_VAR_ 접두사를 붙이면 셸 환경 변수로 전달됩니다. CI에서 주로 쓰는 방식입니다.

여러 경로가 겹치면 나중 것이 이깁니다. 환경 변수가 가장 약하고, tfvars 파일이 그다음, 명령줄의 -var가 가장 강합니다. 아무 경로로도 값이 없으면 테라폼이 대화형으로 물어보는데, 자동화를 전제하는 도구에서 대화형 입력에 기대는 것은 좋은 습관이 아니므로 tfvars 파일을 기본으로 삼는 것을 권합니다.

validation: 잘못된 값은 입구에서 막습니다 #

envprod 대신 production이 들어와 버킷 이름이 어긋나는 종류의 사고는 변수 선언에서 막을 수 있습니다.

variables.tf
variable "env" {
  type        = string
  description = "배포 환경 이름"

  validation {
    condition     = contains(["dev", "prod"], var.env)
    error_message = "env는 dev 또는 prod만 허용합니다."
  }
}

허용 목록 밖의 값이 들어오면 plan이 시작하기도 전에 error_message와 함께 거부됩니다. 값에 제약이 있는 변수라면 validation을 붙여 두는 편이 몇 달 뒤의 실수를 막아 줍니다. 비밀번호 같은 값에는 sensitive = true를 붙일 수 있는데, plan 출력에서 값이 (sensitive value)로 가려집니다. 다만 #5에서 보겠지만 state 파일에는 값이 그대로 저장되므로, 가림은 화면 출력에 한정된 보호라는 점을 기억해야 합니다.

output: 값의 출구 #

만든 리소스의 정보를 밖으로 꺼낼 때는 output 블록을 씁니다. 관례상 outputs.tf에 모아 둡니다.

outputs.tf
output "bucket_name" {
  description = "생성된 버킷 이름"
  value       = aws_s3_bucket.hello.bucket
}

output "bucket_arn" {
  value = aws_s3_bucket.hello.arn
}

apply가 끝나면 터미널 마지막에 출력되고, 나중에 terraform output 명령으로 다시 볼 수 있습니다. terraform output -raw bucket_name처럼 값만 뽑으면 셸 스크립트에 물려 쓸 수 있습니다. 지금은 사람이 읽는 용도가 전부지만, #8에서 모듈을 다루면 output이 모듈끼리 값을 주고받는 통로가 됩니다.

locals: 내부 계산에 이름 붙이기 #

변수가 밖에서 들어오는 값이라면, 로컬 값은 안에서 계산해 이름을 붙여 두는 값입니다.

main.tf
locals {
  bucket_name = "${var.bucket_prefix}-${var.env}"

  common_tags = {
    Env       = var.env
    ManagedBy = "terraform"
  }
}

resource "aws_s3_bucket" "hello" {
  bucket = local.bucket_name
  tags   = local.common_tags
}

참조는 local.이름입니다(선언은 locals, 참조는 local로 단수인 것에 주의합니다). 같은 조합식이나 태그 맵을 여러 리소스에 반복해 적고 있다면 locals로 올릴 신호입니다. 변수와의 구분 기준은 단순합니다. 사용자가 바꿔야 하는 값이면 variable, 코드 안에서 조합되는 값이면 locals입니다. 모든 것을 변수로 만들면 쓰는 사람이 입력할 것이 늘어나기만 하므로, 입구는 좁게 유지하고 조합은 locals에 맡기는 편이 코드를 읽기 좋게 만듭니다.

정리 #

이번 글에서 다룬 내용입니다.

  • 변수는 variable 블록으로 선언하고 var.이름으로 씁니다. type을 지정하면 잘못된 형태의 값이 plan 전에 걸러집니다
  • 값의 입력 경로는 tfvars 파일, -var-file, -var, TF_VAR_ 환경 변수 네 가지이며, tfvars 파일을 기본으로 삼습니다
  • validation은 허용 범위 밖의 값을 입구에서 막고, sensitive는 화면 출력만 가립니다. state에는 값이 그대로 남습니다
  • output은 만든 리소스의 정보를 꺼내는 출구이며, 모듈 시대에는 모듈 간 통로가 됩니다
  • 밖에서 받을 값은 variable, 안에서 조합하는 값은 locals입니다

다음 글(#4 데이터 소스와 표현식)에서는 반대 방향을 다룹니다. 테라폼이 만들지 않은 기존 리소스의 정보를 읽어 오는 데이터 소스, 그리고 조건식과 for 표현식으로 값을 가공하는 방법입니다.

X