관리 메뉴

근묵자흑

테라폼 팁과 요령: 반복문, if문, 배포 및 주의사항 본문

IaC/terraform

테라폼 팁과 요령: 반복문, if문, 배포 및 주의사항

Luuuuu 2025. 1. 12. 18:25

1. 반복문 활용하기

1.1 count 매개 변수를 이용한 반복

count는 테라폼에서 가장 기본적인 반복 방법입니다. 동일한 리소스를 여러 개 생성할 때 사용합니다.

resource "aws_instance" "web" {
  count = 3  # 3개의 동일한 인스턴스를 생성
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t2.micro"

  tags = {
    Name = "web-server-${count.index}"  # 각 인스턴스에 순차적인 이름 부여
  }
}

상세 설명:

  • count = 3: 리소스를 3번 반복 생성합니다.
  • count.index: 0부터 시작하는 인덱스 값을 제공합니다.
  • 생성되는 인스턴스 이름: "web-server-0", "web-server-1", "web-server-2"
  • 참조 방법: aws_instance.web[0], aws_instance.web[1], aws_instance.web[2]

활용 사례:

  • 여러 가용 영역에 걸친 서버 배포
  • 개발/테스트 환경의 다중 인스턴스 생성
  • 로드 밸런서의 타겟 그룹 구성

1.2 for_each를 사용한 반복문 처리

for_each는 map이나 set을 이용해 더 유연한 반복문을 구현할 수 있습니다. count와 달리 각 리소스에 고유한 식별자를 부여할 수 있습니다.

variable "instances" {
  type = map(object({
    instance_type = string
    environment   = string
  }))
  default = {
    "web-1" = {
      instance_type = "t2.micro"
      environment   = "dev"
    }
    "web-2" = {
      instance_type = "t2.small"
      environment   = "staging"
    }
  }
}

resource "aws_instance" "web" {
  for_each = var.instances  # map을 기반으로 인스턴스 생성

  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = each.value.instance_type  # map의 value에서 인스턴스 타입 참조

  tags = {
    Name        = each.key  # map의 key를 이름으로 사용
    Environment = each.value.environment  # map의 value에서 환경 정보 참조
  }
}

상세 설명:

  • for_each = var.instances: map 형태의 변수를 반복 처리
  • each.key: map의 키 값 참조 ("web-1", "web-2")
  • each.value: map의 값 참조 (instance_type, environment 등의 객체)
  • 참조 방법: aws_instance.web["web-1"], aws_instance.web["web-2"]

활용 사례:

  • 서로 다른 구성의 서버 배포
  • 다중 환경(개발/스테이징/운영) 리소스 관리
  • 태그 기반의 리소스 관리

1.3 for 표현식을 이용한 반복문 처리

for 표현식은 리스트나 맵의 데이터를 변환하여 새로운 데이터 구조를 만들 때 사용합니다.

locals {
  instance_types = ["t2.micro", "t2.small", "t2.medium"]

  instance_tags = {
    for type in local.instance_types :  # 리스트를 map으로 변환
    type => {
      size = split(".", type)[1]  # "micro", "small", "medium" 추출
      tier = type == "t2.micro" ? "free" : "paid"  # 조건에 따른 tier 설정
    }
  }
}

상세 설명:

  • 입력: ["t2.micro", "t2.small", "t2.medium"]
  • 출력 예시:
    {
    "t2.micro" = {
      size = "micro"
      tier = "free"
    }
    "t2.small" = {
      size = "small"
      tier = "paid"
    }
    "t2.medium" = {
      size = "medium"
      tier = "paid"
    }
    }

활용 사례:

  • 리소스 태그 정책 관리
  • 복잡한 데이터 구조 변환
  • 동적 리소스 구성

1.4 문자열 지시자를 사용하는 반복문

문자열 지시자를 사용하여 동적으로 문자열을 생성하고 처리할 수 있습니다.

locals {
  subnet_ids = ["subnet-1", "subnet-2", "subnet-3"]

  formatted_subnets = [
    for id in local.subnet_ids : 
    format("subnet-%s", id)  # 문자열 포맷팅
  ]

  # 여러 줄의 문자열 생성
  user_data = <<EOT
%{ for id in local.subnet_ids ~}
subnet_id=${id}
%{ endfor ~}
EOT
}

상세 설명:

  • format 함수: 문자열 템플릿 생성
  • ~: 줄바꿈 제어
  • 출력 예시:
    subnet_id=subnet-1
    subnet_id=subnet-2
    subnet_id=subnet-3

활용 사례:

  • 사용자 데이터 스크립트 생성
  • 구성 파일 템플릿 생성
  • 동적 정책 문서 생성

2. 조건문

2.1 count 매개 변수를 사용한 조건문

count를 사용하여 조건부로 리소스를 생성할 수 있습니다.

resource "aws_eip" "lb" {
  count = var.environment == "production" ? 1 : 0  # 프로덕션 환경에서만 생성
  vpc   = true
}

상세 설명:

  • 조건이 참이면 리소스 생성 (count = 1)
  • 조건이 거짓이면 리소스 미생성 (count = 0)
  • 참조 방법: aws_eip.lb[0].id (리소스가 생성된 경우에만 가능)

활용 사례:

  • 환경별 선택적 리소스 생성
  • 실험적 기능의 조건부 활성화
  • 비용 최적화를 위한 리소스 제어

2.2 for_each와 for 표현식을 사용한 조건문

map이나 set의 요소를 필터링하여 조건부로 리소스를 생성할 수 있습니다.

locals {
  prod_instances = {
    for name, instance in var.instances :
    name => instance
    if instance.environment == "production"  # 프로덕션 환경 인스턴스만 필터링
  }
}

resource "aws_instance" "prod" {
  for_each = local.prod_instances  # 필터링된 인스턴스만 생성

  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = each.value.instance_type
}

상세 설명:

  • map의 각 요소를 조건으로 필터링
  • 조건을 만족하는 요소만으로 새로운 map 생성
  • 필터링된 map을 기반으로 리소스 생성

활용 사례:

  • 특정 조건의 리소스만 선택적 생성
  • 태그 기반 리소스 필터링
  • 환경별 리소스 구성 관리

2.3 if 문자열 지시자가 있는 조건문

문자열 템플릿 내에서 조건부로 내용을 포함할 수 있습니다.

locals {
  environment = terraform.workspace
  common_tags = {
    Environment = local.environment
    Project     = "MyProject"
    Managed_By  = "Terraform"
    %{ if local.environment == "production" }
    Backup      = "true"
    Monitoring  = "enhanced"
    %{ endif }
  }
}

상세 설명:

  • 조건에 따라 추가 태그 포함
  • 들여쓰기와 줄바꿈 제어 가능
  • 중첩 조건문 사용 가능

활용 사례:

  • 환경별 태그 정책 적용
  • 조건부 구성 파일 생성
  • 동적 IAM 정책 생성

3. 무중단 배포

1. 주요 구성 요소

Launch Configuration

resource "aws_launch_configuration" "example" {
  lifecycle {
    create_before_destroy = true
  }
}
  • 인스턴스 시작 구성을 정의
  • create_before_destroy = true로 설정하여 무중단 배포 지원
  • 사용자 데이터를 통한 애플리케이션 구성 가능

Auto Scaling Group (ASG)

resource "aws_autoscaling_group" "example" {
  name = "${var.cluster_name}-${aws_launch_configuration.example.name}"
  min_elb_capacity = var.min_size

  lifecycle {
    create_before_destroy = true
  }
}
  • ASG 이름에 Launch Configuration 이름을 포함하여 변경 감지
  • min_elb_capacity를 통해 최소 정상 인스턴스 수 보장
  • create_before_destroy로 무중단 업데이트 지원

2. 무중단 배포 메커니즘

- 블루 그린 배포

  1. 새로운 Launch Configuration 생성
  2. ASG 업데이트 (이름 변경으로 인한 신규 ASG 생성)
  3. 새로운 인스턴스 생성 및 상태 확인
  4. 이전 인스턴스 제거

4. 테라폼의 주의사항

4.1 count와 for_each 제한 사항

Terraform의 count와 for_each는 강력한 기능이지만 몇 가지 중요한 제한 사항이 있습니다.

1. 리소스 출력을 count 또는 for_each에서 참조할 수 없음

# 잘못된 예시
resource "aws_instance" "server" {
  count = aws_instance.other.id != "" ? 1 : 0  # 오류: 리소스 출력을 count에서 참조
  ami   = "ami-0c55b159cbfafe1f0"
}

# 올바른 예시
variable "create_instance" {
  type    = bool
  default = true
}

resource "aws_instance" "server" {
  count = var.create_instance ? 1 : 0  # 변수를 사용하여 조건부 생성
  ami   = "ami-0c55b159cbfafe1f0"
}

2. module 내에서의 count와 for_each 제한

# 잘못된 예시
module "servers" {
  source = "./app_server"
  count  = length(var.subnet_ids)  # 모듈에서는 count 사용 불가
}

# 올바른 예시
resource "aws_instance" "servers" {
  count     = length(var.subnet_ids)
  subnet_id = var.subnet_ids[count.index]
}

4.2 무중단 배포의 제한 사항

1. create_before_destroy 제한

  • 모든 리소스가 create_before_destroy를 지원하지 않음
  • 데이터베이스와 같은 상태 유지가 필요한 리소스는 특별한 처리 필요
  • 의존성이 있는 리소스들 간의 순서 보장이 어려움
# 예시: create_before_destroy 사용
resource "aws_launch_configuration" "example" {
  # ... 설정 ...

  lifecycle {
    create_before_destroy = true
  }
}

2. 작업 시간 증가

  • 리소스 생성 후 삭제까지 시간이 두 배로 소요
  • 리소스가 많을 경우 전체 배포 시간 증가

4.3 유효한 plan의 실패

핵심은 terraform plan이 테라폼 상태 파일의 리소스만 확인한다는 것입니다. AWS 콘솔을 수동으로 클릭하는 등 테라폼 영역 밖에서 리소스를 생성하면 테라폼의 상태 파일에 포함되지 않으므로 plan 명령을 실행할 때 테라폼이 이를 고려하지 않습니다.
그리므로 유효하게 보이는 plan도 실패 할 수 있습니다.

1. 테라폼을 사용하기 시작했다면 테라폼만 사용해야 합니다.

2. 기존 인프라가 있을경우 import 명령을 사용해야 합니다.

4.4 리팩토링의 까다로움

코드형 인프라(Infrastructure as Code)에서는 코드의 외부 동작을 정의하는 요소에 특별히 주의를 기울여야 합니다. 이는 실제 인프라에 직접적인 영향을 미치기 때문입니다.

1. 항상 plan 명령을 사용

# 리팩토링 전 상태 확인
terraform plan

// 예상치 못한 변경사항 예시
resource "aws_instance" "example" {
  - ami           = "ami-0c55b159cbfafe1f0" -> null
  + ami           = "ami-new-version" # 완전한 교체 발생
  instance_type = "t2.micro"
}

주의사항

  • 모든 변경 사항을 적용하기 전에 반드시 plan 확인
  • 예상치 못한 리소스 재생성이 없는지 검토
  • 특히 프로덕션 환경에서는 더욱 신중하게 검토

2. 파기하기 전에 생성하기 (Create Before Destroy)

resource "aws_launch_configuration" "example" {
  image_id = "ami-0c55b159cbfafe1f0"

  lifecycle {
    create_before_destroy = true  # 새 리소스 생성 후 이전 리소스 제거
  }
}

resource "aws_autoscaling_group" "example" {
  name = "${var.cluster_name}-${aws_launch_configuration.example.name}"

  lifecycle {
    create_before_destroy = true
  }
}

적용 시나리오

  1. 새로운 리소스 생성
  2. 상태 확인 및 검증
  3. 트래픽 전환
  4. 이전 리소스 제거

3. 식별자 변경을 위한 상태 변경

# 리소스 이름 변경 시
terraform state mv 'aws_instance.old_name' 'aws_instance.new_name'

# 모듈 간 리소스 이동 시
terraform state mv 'module.old.aws_instance.example' 'module.new.aws_instance.example'

주의사항

1. 상태 파일 백업

   terraform state pull > terraform.tfstate.backup

2. 단계적 변경

   // 1단계: 새 리소스 추가
   resource "aws_instance" "new_name" {
     // 설정
   }

   // 2단계: state 이동
   // terraform state mv 'aws_instance.old_name' 'aws_instance.new_name'

   // 3단계: 이전 리소스 제거

4. 일부 매개 변수는 변경할 수 없음

resource "aws_instance" "example" {
  // 변경 불가능한 매개변수
  availability_zone = "us-west-2a"  # 변경 시 인스턴스 재생성 필요

  // 변경 가능한 매개변수
  instance_type = "t2.micro"     # 인스턴스 중지 후 변경 가능
  tags = {
    Name = "example"           # 즉시 변경 가능
  }
}

변경 불가능한 주요 매개변수

1. EBS 볼륨

   resource "aws_ebs_volume" "example" {
     availability_zone = "us-west-2a"  # 변경 불가
     size             = 40             # 증가만 가능
   }

2. RDS 인스턴스

   resource "aws_db_instance" "example" {
     engine            = "mysql"        # 변경 불가
     allocated_storage = 20             # 증가만 가능
   }

해결 전략

1.임시 리소스 사용

   // 1. 새 리소스 생성
   resource "aws_instance" "example_new" {
     availability_zone = "us-west-2b"
   }

   // 2. 데이터 마이그레이션

   // 3. 이전 리소스 제거
   resource "aws_instance" "example" {
     count = 0  // 또는 리소스 블록 제거
   }

2. 블루-그린 배포 활용

   resource "aws_autoscaling_group" "example" {
     name = "${var.cluster_name}-v${var.version}"

     lifecycle {
       create_before_destroy = true
     }
   }

'IaC > terraform' 카테고리의 다른 글

Terraform Mocks  (4) 2025.02.02
프로덕션 수준의 테라폼 코드  (3) 2025.01.19
Terraform Module  (8) 2025.01.05
Terraform State  (3) 2024.12.29
테라폼 로드밸런서 배포  (5) 2024.12.22