기본적으로 하나의 리소스 블록은 하나의 실제 인프라 객체를 구성한다. 반복적으로 유사한 객체를 블록을 일일히 하나씩 블록을 작성하여 관리하기 힘든 경우, 프로그래밍 언어에서 사용되는 반복문과 유사한 기능을 가진 키워드를 사용할 수 있다. Terraform에선 count와 for_each가 있다.
Meta Argument - count
리소스 또는 모듈 블록에 count 값이 정수인 인수가 포함된 경우, Terraform은 해당 정수만큼의 인스턴스를 생성한다.
resource "aws_instance" "server" {
count = 4 # create four similar EC2 instances
ami = "ami-a1b2c3d4"
instance_type = "t2.micro"
tags = {
Name = "Server ${count.index}"
}
}
count.index : 인스턴스에 해당하는 고유한 인덱스 번호, 0부터 시작한다.
- count가 설정되면, Terraform은 블록과 이와 연관된 리소스 혹은 모듈 인스턴스를 0으로 시작하는 인덱스 번호로 식별한다.
- <TYPE>.NAME 혹은 module.<NAME> (ex: aws_instance.server)는 리소스 블록을 참조한다.
- <TYPE>.<NAME>[<INDEX>] 또는 module.<NAME>[<INDEX>] (ex: aws_instance.server[0], aws_instance.server[1]) 등은 개별 인스턴스를 참조한다.
Meta Argument - for_each
map 혹은 문자열의 set을 사용하여 map이나 set(집합)에 대한 각 요소들에 대한 인스턴스를 만들 수 있다.
map
resource "azurerm_resource_group" "rg" {
for_each = tomap({
a_group = "eastus"
another_group = "westus2"
})
name = each.key
location = each.value
}
set of strings
resource "aws_iam_user" "the-accounts" {
for_each = toset(["Todd", "James", "Alice", "Dottie"])
name = each.key
}
child module
# my_buckets.tf
module "bucket" {
for_each = toset(["assets", "media"])
source = "./publish_bucket"
name = "${each.key}_bucket"
}
# publish_bucket/bucket-and-cloudfront.tf
variable "name" {} # this is the input parameter of the module
resource "aws_s3_bucket" "example" {
# Because var.name includes each.key in the calling
# module block, its value will be different for
# each instance of this module.
bucket = var.name
# ...
}
resource "aws_iam_user" "deploy_user" {
# ...
}
- each.key : 이 인스턴스에 해당하는 map key 혹은 set의 멤버
- each.value : 이 인스턴스에 해당하는 map의 value (set의 경우 each.key와 동일)
count 대신 for_each를 사용하는 경우
인스턴스가 거의 동일하다면 count, 정수로 식별될 수 없는 고유한 값이 필요한 경우 for_each가 더 적절하다.
variable "subnet_ids" {
type = list(string)
}
resource "aws_instance" "server" {
# Create one instance for each subnet
count = length(var.subnet_ids)
ami = "ami-a1b2c3d4"
instance_type = "t2.micro"
subnet_id = var.subnet_ids[count.index]
tags = {
Name = "Server ${count.index}"
}
}
해당 코드는 subnet_ids 리스트의 문자열 값 대신 index로 식별되기 때문에 취약점이 있다.
리스트 인덱스의 특성상, 리스트의 중간 요소가 제거되면 제거된 요소 이후의 모든 인스턴스의 subnet_id값이 변경되어야 하기 때문에 비효율적이기도 하다.
count와 for_each의 제한 사항
- count와 for_each는 함께 사용할 수 없다.
- 상태 파일과의 의존성 문제
- count와 for_each는 리소스의 순서(index)나 식별자(key)에 의존적이다.
- 따라서, 리소스의 순서나 식별자의 변경사항이 생기는 경우, 해당 리소스를 삭제하고 다시 만들게 된다.
- 값의 타입 제한
- count는 정수형
- for_each는 set, map 타입만 사용 가능하다.
테라폼 조건문 - 삼항연산자
테라폼의 경우 삼항 연산자를 이용하여 단일 값이나 표현식을 조건적으로 설정이 가능하다.
variable "env" {
default = "dev"
}
resource "aws_instance" "example" {
ami = var.env == "prod" ? "ami-prod" : "ami-dev"
instance_type = "t2.micro"
}
var.env == "prod" 조건을 만족하면(참이면) ami의 값은 "ami-prod" 아니면, "ami-dev"
local values
locals {
instance_types = [for i in range(3) : i == 0 ? "t2.micro" : "t2.small"]
}
output "instance_types" {
value = local.instance_types
}
- 데이터를 미리 처리하거나 반복 및 조건을 포함한 계산된 값을 정의할 때 local과 함께 조합하여 사용할 수 있다.
- 선언할 땐 locals, 참조할 땐 local 로 참조한다.
테라폼의 주의사항 및 제한사항
무중단 배포의 제한 사항
기본적으로 Terraform 자체에서 무중단 배포를 보장하진 않는다. 아래의 이유로 인해 배포 중 중단(downtime)이 발생할 수 있다.
- 리소스의 교체 작업
- 특정 리소스를 삭제하고 재생생해야 하는 경우에 서비스가 일시적으로 중단될 수 있다.
- AWS RDS의 스토리지 타입 변경, 로드 밸런서 리스터 프로토콜 변경 등
- 순서와 종속성 문제
- 리소스 간의 종속성으로 인해 특정 리소스가 삭제된 후에야 다른 리소스를 생성할 수 있는 경우
- immutable 속성
- 일부 클라우드 리소스는 특정 속성을 수정할 수 없으며, 변경하기 위해선 기존 리소스를 삭제 후 새로 생성해야 한다.
하지만 테라폼에서 아예 무중단 배포를 구현할 수 없는 것은 아니다.
테라폼은 blue-green 배포와 canary 배포처럼 고급 배포 전략에 대한 가이드를 제공한다.
https://developer.hashicorp.com/terraform/tutorials/aws/blue-green-canary-tests-deployments
Use Application Load Balancers for blue-green and canary deployments | Terraform | HashiCorp Developer
Configure AWS application load balancers to release an application in a rolling upgrade with near-zero downtime. Incrementally promote a new canary application version to production by building a feature toggle with Terraform.
developer.hashicorp.com
유효한 Plan의 실패
테라폼은 plan단계에서 유효한 계획이 생성되었을지라도, 실제 배포(apply) 중 실패가 발생할 수 있다.
- 외부 상태의 변화
- plan 이후, 다른 사용자가 클라우드 리소스를 변경하거나 삭제하면 apply가 실패할 수 있다.
- 동시성 문제
- 여러 사용자가 동일한 테라폼 상태를 동시에 수정하면 경재애 상태가 발생할 수 있다.
- 이러한 문제를 해결하기 위해 테라폼 백엔드를 이용하고, 락킹 기능이 있는 것이다.
- 리소스 한도 초과
- 클라우드 provider의 자체적인 리소스 제한을 초과할 경우, apply에선 실패할 수 있다.
최종 일관성 (Eventual Consistency)
분산 시스템에서 데이터 업데이트가 즉시 모든 노드(서버)에 동기화되지 않고, 시간이 지나면 최종적으로 모든 노드가 동일한 상태에 도달하는 것을 보장하는 일관성 모델
클라우드 provider는 최종 일관성 모델을 사용하는 경우가 많기에, 테라폼 실행 이후 상태가 완전히 반영되기까지 시간이 걸릴 수 있다.
- 즉각적인 상태 확인 불가
- 테라폼이 리소스를 생성하거나 수정한 직후, 클라우드 provider의 API에서 상태를 조회한 경우, 아직 변경한 상태가 반영되지 않을 수 있다.
- 종속성 문제
- 테라폼은 선언적 구성을 기반으로 하지만, 종속 리소스가 준비되기 전에 종속성을 가진 리소스를 생성하려 할 수 있다.
- 이러한 문제를 해결하기 위해 depends_on Meta Argument를 사용한다.
resource "aws_instance" "example" { depends_on = [aws_security_group.example] ... }
- 배포 안전성 보장이 요구됨
- 최종 일관성 문제에 대응하기 위해 sleep, retry를 워크플로우에 포함시켜야 하는 경우도 있다.
'IaC' 카테고리의 다른 글
테라폼 스터디 8주차 - 테라폼 코드 테스트 (0) | 2025.02.01 |
---|---|
테라폼 스터디 - 7주차 (프로덕션 수준의 인프라 구성) (0) | 2025.01.18 |
스터디 5주차 - 모듈 추가 내용 (0) | 2025.01.05 |
테라폼 스터디 5주차 - 모듈 (3) | 2025.01.05 |
테라폼 스터디 - 4주차 (1) | 2024.12.29 |