[ IaC ] Terraform resource lifecycle & meta arguments
Terraform resource lifecycle
Terraform은 인프라스트럭처 및 클라우드 리소스를 관리하기 위한 인프라스트럭처 코드 (Infrastructure as Code, IaC) 도구로서, 코드로 정의된 인프라스트럭처 리소스를 생성, 업데이트 및 삭제하는 데 사용됩니다. Terraform은 각 리소스에 대한 라이프사이클을 관리하며, 이것은 Terraform이 어떻게 리소스를 관리하고 언제 작업을 수행해야 하는지를 결정합니다. Terraform의 리소스 라이프사이클은 다음과 같이 요약될 수 있습니다:
- 생성(Create): 리소스가 아직 존재하지 않을 때, Terraform은 리소스를 생성합니다. 이것은 보통 terraform apply 명령을 실행할 때 발생하며, Terraform 구성 파일 (주로 .tf 확장자를 가진 파일)에 정의된 리소스를 생성합니다.
- 읽기(Read): 리소스의 현재 상태를 파악합니다. Terraform은 클라우드 제공 업체(API 또는 CLI 등을 통해)로부터 현재 상태를 읽어옵니다.
- 비교(Diff): Terraform은 현재 상태와 원하는 상태 사이의 차이를 계산합니다. 이 차이는 생성, 업데이트 또는 삭제할 리소스를 결정하는 데 사용됩니다.
- 업데이트(Update): 차이를 계산한 후, Terraform은 변경 사항을 적용하여 리소스를 원하는 상태로 업데이트합니다. 이 업데이트는 변경된 리소스에만 적용됩니다.
- 삭제(Delete): Terraform 구성 파일에서 리소스를 삭제하거나 상태 파일에서 관리 중인 리소스를 삭제할 경우, Terraform은 리소스를 삭제합니다.
이러한 라이프사이클 단계는 Terraform이 어떤 리소스를 만들어야 하며, 어떤 리소스를 업데이트하고 어떤 리소스를 삭제해야 하는지를 결정하는 데 중요합니다. 각 리소스는 resource 블록 내에서 정의되며, 리소스에 대한 설정은 Terraform 구성 파일에 정의됩니다.
Terraform에서 리소스의 라이프사이클을 관리하는 데 사용되는 주요 인수(arguments) 중 일부는 리소스 블록 내에 정의되며, 이러한 인수들은 리소스가 어떻게 생성, 업데이트 및 삭제되는지를 제어합니다. 리소스 블록의 인수는 해당 리소스 타입에 따라 다를 수 있지만, 일반적으로 다음과 같은 일부 일반적인 라이프사이클 관련 인수가 있습니다:
1. Count
count와 for_each는 여러 개의 리소스를 생성할 때 사용하며, 주로 여러 인스턴스나 네트워크 리소스를 생성할 때 사용
count메타 인수는 정수를 허용하고 count.index를 활용하여 count에서 생성한 인덱스에 접근이 가능합니다.
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
tags = {
Name = "vpc"
}
}
variable "subnets" {
type = list(object({
name = string
cidr_block = string
}))
default = [
{
name = "subnet-1"
cidr_block = "10.0.1.0/24"
},
{
name = "subnet-2"
cidr_block = "10.0.2.0/24"
},
{
name = "subnet-3"
cidr_block = "10.0.3.0/24"
},
{
name = "subnet-4"
cidr_block = "10.0.4.0/24"
}
]
}
resource "aws_subnet" "main" {
count = length(var.subnets)
vpc_id = aws_vpc.main.id
cidr_block = var.subnets[count.index].cidr_block
tags = {
Name = var.subnets[count.index].name
}
}
output "subnet_id" {
value = aws_subnet.main[*].id
}
count는 주로 length와 함께 사용되어 특정 배열의 크기만큼 생성할 때 사용됩니다.
length에서 count의 길이를 넣고 count.index로 길이만큼 반복문을 돌며 서브넷을 생성합니다.
count사용할 때의 문제점은 중간 값의 index를 가진 객체를 삭제하면 index를 정렬하기 위해 그 뒷 index를 가진 객체들도 모두 재 생성이 된다는 문제가 발생합니다.
이것은 의도한 자원의 사용이 아니기 때문에 이를 위해 for_each를 사용할 수 있습니다.
2. For each
모듈 및 리소스 유형과 함께 사용할 수 있으며, 이를 사용하기 위해서는 map, set에 대해 학습해야 합니다.
set은 유일한 값의 요소들로 이루어진 list, map은 key-value형식의 데이터입니다. (key는 string)
for_each 메타 인수는 Map 또는 String Set 를 허용하고 해당 또는 세트의 각 항목에 대한 인스턴스를 생성합니다. 각 인스턴스에는 연결된 고유한 인프라 개체가 있으며 구성이 적용될 때 각각 별도로 생성, 업데이트 또는 소멸됩니다.
# 예시 - Map을 사용한 for_each
resource "azurerm_resource_group" "rg" {
for_each = {
a_group = "eastus"
another_group = "westus2"
}
name = each.key
location = each.value
}
# 예시 - Set을 사용한 for_each
resource "aws_iam_user" "the-accounts" {
for_each = toset( ["Todd", "James", "Alice", "Dottie"] )
name = each.key
}
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
tags = {
Name = "vpc"
}
}
variable "subnets" {
type = list(object({
name = string
cidr_block = string
}))
default = [
{
name = "subnet-1"
cidr_block = "10.0.1.0/24"
},
{
name = "subnet-2"
cidr_block = "10.0.2.0/24"
},
{
name = "subnet-3"
cidr_block = "10.0.3.0/24"
},
{
name = "subnet-4"
cidr_block = "10.0.4.0/24"
}
]
}
resource "aws_subnet" "main" {
for_each = { for subnet in var.subnets : subnet.name => subnet } # 변환
vpc_id = aws_vpc.main.id
cidr_block = each.value.cidr_block
tags = {
Name = each.key
}
}
output "subnet_id" {
value = { for k, v in aws_subnet.main : v.tags.Name => v.id }
}
for_each를 사용하면 map, set의 형태에서 특정 값을 삭제한다 하더라도 그에 대한 value나 객체만을 삭제하면 되므로 indexing에 의한 불필요한 재 생성이 발생하지 않습니다.
3. depends_on
리소스 간의 족속성을 정의할 수 있으며 explicit dependency를 나타냅니다. list형태로 인수를 지정하며, 특정 리소스들 간의 의존성으로 어떤 객체가 먼저 생성되어야 할 지에 대한 순서를 지정해줄 수 있습니다.
resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
}
resource "aws_instance" "app" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
depends_on = [aws_instance.web]
}
4. lifecycle
lifecycle 블록을 사용하여 리소스의 라이프사이클을 세밀하게 제어할 수 있습니다. 이것은 특정 상황에서 리소스를 변경하지 않거나, 삭제하지 않도록 설정하는 데 유용합니다.
- ignore_changes: arguments로 오는 값은 속성 이름의 리스트이다. 기본적으로 테라폼은 현재 설정과 서버를 비교해서 업데이트 할 목록을 발견 후 설정에 맞게 원격 객체를 업데이트한다. lgnore_changes는 어떤 속성이 변경되었을 때 해당 변경을 Terraform이 무시하도록 지정하는 데 사용됩니다. 이것은 특정 속성의 변경을 무시하여 리소스를 다시 생성하지 않도록 할 때 유용합니다. ex) tags, instance_type, all
- replace_triggered_by: ignore_changes와 반대로 특정 리소스의 속성 변경으로 인해 전체 리소스가 재 생성될 지를 설정하는 방법입니다. 중요한 속성이 변경되었을 때 리소스를 대체하도록 구성할 수 있습니다.
- prevent_destroy: prevent_destroy는 리소스가 삭제되지 않도록 막는 데 사용됩니다. 이것은 실수로 중요한 리소스를 삭제하지 않도록 보호할 때 유용합니다.
- create_before_destroy: 기본적으로 원격 API 한계로 인해서 업데이트 되지 못하는 resource 변수를 바꿀때, 테라폼은 현재 객체를 삭제하고 새로운 설정을 가진 새로운 객체를 만든다. create_before_destroy는 리소스를 업데이트할 때 해당 리소스를 먼저 새로 생성한 다음 이전 리소스를 삭제하는 방식으로 작동하도록 지정하는 데 사용됩니다. 이것은 중단 시간을 최소화하거나 롤링 업데이트를 수행할 때 유용합니다.