데이터 소스 — 프로바이더에서 인프라 정보 읽어오기
데이터 소스 — 프로바이더에서 인프라 정보 읽어오기
Terraform으로 새 인프라를 만들다 보면, 이미 존재하는 외부 정보를 가져와야 할 때가 있어요. AMI ID나 기존 VPC ID처럼요. 이때 쓰는 것이 데이터 소스(data source)예요. 데이터 소스는 프로바이더에서 데이터를 읽어오기만 하고, 리소스를 만들거나 수정하지 않아요.
출처: https://developer.hashicorp.com/terraform/language/data-sources
본문
데이터 소스는 뭘 하는 블록일까
많은 프로바이더는 설치하면 함께 쓸 수 있는 데이터 소스를 제공해요. 데이터 소스는 프로바이더에서 데이터를 가져오지만, 리소스를 만들거나 바꾸지는 않아요. 데이터에 접근하려면 data 블록을 선언하고 필요한 인자를 채워 두면 돼요. 프로바이더 문서에 인자가 무엇이 있는지 친절하게 나와 있으니 그걸 참고하면 됩니다.
data 블록은 표현식이나 Terraform 언어의 동적 기능을 그대로 지원하고, 데이터 객체를 어떻게 만들지 제어하는 여러 내장 인자도 함께 쓸 수 있어요. 자세한 내용은 data 블록 레퍼런스나 표현식 문서를 보세요.
각 data 블록은 하나의 데이터 소스와 연결돼요. Terraform은 데이터 소스에 대해 읽기(read) 연산만 수행할 수 있어요.
꼭 apply 때까지 기다려야 할 수도 있어요
Terraform은 보통 플랜 단계에서 데이터 소스를 조회하려고 해요. 그런데 구성에 따라 읽기를 apply 단계로 미룰 수도 있고, 그럴 땐 플랜 출력에 그 사실을 표시해 줘요. 다음 같은 상황이면 데이터 소스 읽기가 apply 시점까지 밀려요.
data블록 구성이 이번 플랜에서 변경되는 Terraform 관리 리소스에 직접 의존할 때data블록에 커스텀 조건이 있고, 그 조건이 변경 예정인 Terraform 관리 리소스에 직간접적으로 의존할 때data블록의 인자가 apply 단계에서 계산되는 값(computed value)을 참조할 때
데이터 소스가 다른 객체에 의존하면 플랜 중에는 조회 결과를 알 수 없어요. 그래서 그 값을 참조하는 리소스는 apply 단계 전까지 프로비저닝할 수 없고, 보간된 데이터 소스 속성은 모두 computed로 표시돼요.
반대로 data 블록 인자가 계산이 필요 없는 값을 참조하면, Terraform은 refresh 단계에서 데이터 소스를 읽고 상태를 갱신해요. 기본적으로 Terraform은 플랜을 만들기 전에 refresh를 수행하므로, 가져온 데이터를 계획 중에 바로 쓸 수 있고 diff에도 실제 반환값이 보여요.
데이터 소스를 선언하는 법
구성에 data 블록을 추가하고, 프로바이더 문서에 맞게 인자를 채워 넣으면 돼요. 데이터 소스 타입과 반환 데이터를 담을 이름(label)을 반드시 적어야 해요. 다음은 example이라는 이름의 aws_ami 데이터 소스를 선언한 예시예요.
data "aws_ami" "example" {
# . . .
}
이 블록은 Terraform이 aws_ami 데이터 소스에서 읽기를 수행하고, 결과를 example 객체로 내보내라는 뜻이에요. 반환 데이터를 쓰려면 data.<TYPE>.<LABEL>.<ATTRIBUTE> 문법을 사용해요. 예를 들어 data.aws_ami.example.id처럼요.
쿼리 조건은 data 블록 본문의 인자로 정해요. 다음 예시는 현재 사용자가 소유하고 Name 태그가 app-server, Tested 태그가 true인 가장 최근 AMI를 찾아요.
data "aws_ami" "example" {
most_recent = true
owners = ["self"]
tags = {
Name = "app-server"
Tested = "true"
}
}
데이터 소스 생성 방식을 제어하기
데이터 소스도 리소스처럼 여러 메타 인자(meta-arguments)로 생성 방식을 제어할 수 있어요. 인프라를 특정 순서대로 만들어야 한다면 depends_on 인자로 의존성 그래프를 조정해요. 그러면 Terraform이 지정한 의존 객체의 작업이 끝날 때까지 데이터 소스 조회를 미뤄요.
data 블록에는 precondition과 postcondition 블록을 넣어 데이터 소스가 어떻게 동작해야 하는지 가정이나 보증을 적을 수 있어요. 다음 예시는 AMI 데이터 소스에 nomad-server 태그가 달려 있는지 postcondition으로 확인해요.
data "aws_ami" "example" {
id = var.aws_ami_id
lifecycle {
postcondition {
condition = self.tags["Component"] == "nomad-server"
error_message = "tags[\"Component\"] must be \"nomad-server\"."
}
}
}
커스텀 조건은 오류를 더 이르고 맥락에 맞게 알려줘서, 구성 문제를 진단하기 쉬워져요. 유지보수하는 사람이 설계 의도를 이해하는 데도 도움이 돼요.
count와 for_each 메타 인자로 데이터 소스의 인스턴스를 여러 개 만들 수도 있어요. 각 인스턴스는 data.<NAME>[<KEY>] 문법으로 따로 참조할 수 있어요. count면 <KEY>가 1부터 시작하는 숫자이고, for_each면 컬렉션에 있는 각 항목의 값이 돼요. 예를 들어 data.azurerm_resource_group.rg["a_group"]처럼요.
별칭(alias)을 붙인 프로바이더 구성을 data 블록의 provider 메타 인자로 지정할 수도 있어요. 다음은 aws.uswest1이라는 프로바이더 구성을 사용하는 예시예요.
provider "aws" {
region = "us-east-1"
}
provider "aws" {
alias = "uswest1"
region = "us-west-1"
}
data "aws_ami" "web" {
provider = aws.uswest1
# ...
}
조회한 데이터를 참조하기
데이터 소스 속성은 data.<TYPE>.<LABEL>.<ATTRIBUTE> 문법으로 구성 곳곳에서 참조해요. 다음은 data.aws_ami.web.id에서 가져온 AMI ID로 AWS 인스턴스를 만드는 예시예요.
resource "aws_instance" "web" {
ami = data.aws_ami.web.id
instance_type = "t1.micro"
}
완성 예시
다음 예시는 Component = web 태그가 달린 가장 최근 AMI를 조회해요.
data "aws_ami" "web" {
filter {
name = "state"
values = ["available"]
}
filter {
name = "tag:Component"
values = ["web"]
}
most_recent = true
}
data 블록은 aws_ami 데이터 소스의 web 인스턴스를 만들어요. 타입과 이름의 조합은 유일해야 해요. data.aws_ami.web 같은 주소 접두사로 가져온 데이터를 참조할 수 있고, 주소는 data.<TYPE>.<NAME>.<ATTRIBUTE> 참조 표현식 문법을 따라요. 아래 resource 블록은 조회한 AMI ID를 ami 인자에 넣어요.
resource "aws_instance" "web" {
ami = data.aws_ami.web.id
instance_type = "t1.micro"
}
filter 블록과 그 안의 인자, most_recent 인자는 aws_ami 프로바이더만의 것이에요. 상세한 인자는 Terraform Registry의 AWS 프로바이더 문서에서 확인할 수 있어요.