테라폼 기초 강좌 #7 리소스 수명주기 제어: 교체 조건, create_before_destroy, prevent_destroy

5 분 소요

#2에서 plan의 -/+ 기호를 만났습니다. 지우고 다시 만든다는 뜻이었고, 그때는 “반드시 멈춰서 확인하라"고만 했습니다. 이번 글에서는 그 교체를 정면으로 다룹니다. 어떤 변경이 교체를 일으키는지, 교체가 불가피할 때 중단을 줄이는 방법, 그리고 애초에 교체와 삭제가 일어나지 못하게 막는 방법입니다. 도구는 모두 lifecycle이라는 중첩 블록 하나에 모여 있습니다. 운영 데이터베이스를 테라폼으로 관리하게 되는 순간부터는 이번 글의 내용이 안전장치의 전부이므로, 기초 시리즈에서 가장 실전적인 편이라고 할 수 있습니다.

교체는 provider가 정합니다 #

같은 리소스라도 속성에 따라 변경의 무게가 다릅니다. EC2 인스턴스에서 instance_type을 바꾸면 update(정확히는 중지 후 변경)로 처리되지만, AMI를 바꾸면 교체입니다. 실행 중인 서버의 운영 체제 이미지를 갈아 끼울 수는 없기 때문입니다. 어느 속성이 교체를 강제하는지는 provider가 리소스 스키마에 정의해 두었고, 우리가 외울 필요는 없습니다. plan이 알려 주기 때문입니다.

plan 출력
  # aws_instance.web must be replaced
-/+ resource "aws_instance" "web" {
      ~ ami = "ami-0abc..." -> "ami-0def..." # forces replacement
    }

# forces replacement 주석이 붙은 속성이 교체의 원인입니다. plan 출력에서 이 주석을 찾는 습관이 이번 글의 출발점입니다. 교체 대상이 stateless 웹 서버라면 받아들이면 되지만, 데이터를 품은 리소스라면 다음 도구들이 필요해집니다.

create_before_destroy: 순서를 뒤집어 공백을 없앱니다 #

기본 교체 순서는 지우고 나서 만들기입니다. 옛것이 사라진 뒤 새것이 뜰 때까지 그 리소스는 존재하지 않으므로, 서비스 중인 리소스라면 그 시간만큼 중단이 생깁니다. lifecycle 블록으로 순서를 뒤집을 수 있습니다.

main.tf
resource "aws_instance" "web" {
  ami           = data.aws_ami.al2023.id
  instance_type = "t3.micro"

  lifecycle {
    create_before_destroy = true
  }
}

이제 교체가 필요하면 새것을 먼저 만들어 확인한 뒤 옛것을 지웁니다. 다만 공짜는 아닙니다. 잠시나마 둘이 공존하므로, 이름이 유일해야 하는 리소스(S3 버킷, IAM 역할 등)는 이름 충돌로 생성이 실패합니다. 이름에 무작위 접미사를 붙이는 name_prefix 인수를 함께 쓰는 것이 짝 패턴입니다. 시작·중지가 아니라 교체로만 갱신되는 리소스(launch template 기반 구성 등)에서 특히 자주 쓰게 됩니다.

prevent_destroy: 지워지면 안 되는 것에 거는 자물쇠 #

운영 데이터베이스나 로그 버킷처럼 삭제 자체가 사고인 리소스에는 자물쇠를 걸어 둡니다.

main.tf
resource "aws_db_instance" "main" {
  # ...

  lifecycle {
    prevent_destroy = true
  }
}

이 리소스를 지우는 계획(destroy는 물론, 교체의 삭제 단계 포함)이 만들어지는 순간 테라폼이 에러를 내며 멈춥니다. 리팩터링 중 실수로 리소스 블록 이름을 바꿔 교체가 계획되는 경우(#5의 state mv를 잊은 경우) 같은 사고를 마지막 단계에서 막아 주는 안전망입니다. 한 가지 한계는 알아 둬야 합니다. 이 보호는 테라폼 안에서만 유효합니다. 콘솔에서 손으로 지우는 것은 막지 못하며, 블록 자체를 코드에서 삭제하면 보호도 함께 사라집니다. 그래도 데이터가 있는 리소스에 이 두 줄을 넣는 비용은 0에 가깝고 막아 주는 사고는 크므로, 습관으로 삼을 가치가 충분합니다.

ignore_changes: 코드 밖 변경과 공존하기 #

#5에서 드리프트는 되돌려야 할 어긋남이라고 했지만, 예외가 있습니다. 다른 시스템이 그 속성을 바꾸는 것이 정상 동작인 경우입니다. 오토 스케일링이 인스턴스 수를 조정하는 Auto Scaling 그룹의 desired_capacity가 전형입니다. 테라폼 코드에는 초기값 2가 적혀 있는데 스케일링이 4로 늘려 놓았다면, 다음 apply가 이것을 2로 되돌리며 멀쩡한 서버를 줄여 버립니다.

main.tf
resource "aws_autoscaling_group" "web" {
  desired_capacity = 2
  # ...

  lifecycle {
    ignore_changes = [desired_capacity]
  }
}

ignore_changes에 나열한 속성은 실제 값이 코드와 달라도 plan이 차이로 취급하지 않습니다. 최초 생성 때만 코드 값이 쓰이고, 이후의 변경은 외부 시스템에 맡기는 것입니다. 단, 이것은 해당 속성에 대한 테라폼의 눈을 감는 일이므로 범위는 최소한으로 잡아야 하며, ignore_changes = all 같은 전면 무시는 그 리소스를 사실상 관리 포기하는 것과 같습니다.

교체를 강제하기: -replace 플래그 #

반대로 코드는 그대로인데 교체가 필요할 때가 있습니다. 인스턴스가 알 수 없는 이유로 이상해져서 새로 말고 싶은 경우입니다. 예전에는 terraform taint라는 명령으로 리소스에 “오염” 표시를 남겼지만, 이 방식은 폐기 예정이 되었고 지금은 plan·apply의 플래그로 대체되었습니다.

실행
terraform apply -replace="aws_instance.web"

지정한 리소스만 교체하는 계획이 만들어지며, plan 출력에 교체 이유가 명시되므로 taint 방식보다 의도가 분명합니다. 서버를 고치는 대신 갈아 끼우는 운영 방식과 잘 맞는 도구입니다.

정리 #

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

  • 어떤 속성이 교체를 강제하는지는 provider 스키마가 정하며, plan의 # forces replacement 주석으로 확인합니다
  • create_before_destroy는 교체 순서를 뒤집어 공백을 없앱니다. 이름이 유일해야 하는 리소스는 name_prefix와 짝으로 씁니다
  • prevent_destroy는 삭제 계획 자체를 에러로 막는 자물쇠입니다. 데이터가 있는 리소스에는 습관처럼 걸어 둡니다
  • ignore_changes는 외부 시스템이 바꾸는 것이 정상인 속성을 plan에서 제외합니다. 범위는 최소한으로 잡습니다
  • 강제 교체는 -replace 플래그로 합니다. taint 명령은 폐기 수순입니다

다음 글(#8 모듈 기초)에서는 커져 가는 코드를 정리하는 도구인 모듈을 다룹니다. 반복되는 리소스 묶음에 이름을 붙여 재사용하는 방법과, 남이 만든 검증된 모듈을 가져다 쓰는 방법입니다.

X