본문 바로가기
DevOps

[T1014-이론] 3장 기본 사용법 (1)

by 서어켜엉 2024. 6. 16.
해당 내용은 cloudNet@ 팀의 가시다 님이 진행하는 테라폼 스터디 T101 4기에서 다룬 내용과 "테라폼으로 시작하는 IaC" (한빛미디어) 저서 내용을 정리한 것입니다.

 

1. 주요 커맨드

  • 커맨드 사용법과 "help" 옵션
    기본적인 사용법은 terraform 커맨드만 입력하면 출력된다.

terraform
Usage: terraform [-version] [-help] <command> [args]

 

  • help
    terraform 커맨드로 시작하면 여러 가지 인수와 추가 커맨드가 구성된다. 서브 커맨드에 대해 사용 가능한 인수 값을 더 확인하고 싶다면 서브 커맨드와 함께 -help를 입력해 사용하면 된다.
terraform console -help
  • init
    terraform init 커맨드는 테라폼 구성 파일이 있는 작업 디렉토리를 초기화하는 데 사용된다. 
terraform init
  • validate
    terraform 구성 파일의 유효성을 확인한다. 작성된 코드파일의 문법, 종속성, 속성 이름이나 연결된 값의 정확성 확인을 수행
terraform validate
  • plan & apply
    terraform plan 명령은 테라폼으로 적용할 인프라의 변경 사항에 관해 실행 계획을 생성하는 동작
    terraform apply 명령은 plan에서 작성된 내용을 토대로 작업을 실행한다. plan명령으로 실행된 실행 계획이 없다면 새 실행 계획을 생성하고 해당 계획을 승인할 것인지 물어본다.
terraform plan
terraform apply

  • destroy
    terraform destroy 명령은 테라폼 구성에서 관리하는 모든 개체를 제거하는 명령이다.
terraform destroy

  • fmt
    format 또는 reformat의 줄임 표시로 terraform fmt 커맨드로 사용한다. 테라폼 구성 파일을 표준 형식과 표준 스타일로 적용하는데 사용한다. 구성파일의 가독성을 높이는 작업에 사용된다.
terraform fmt

2. HCL

HCL을 사용하는 이유

  • 대부분의 프로비저닝 대상 인프라와 서비스는 JSON 이나 YAML 같은 기계친화적은 언어로 소통을 한다. 하지만 사람에게 친화적인 언어가 아니므로, 가독성이 좋지 않다.
  • 이를 해결하기 위해 기계도 이해할 수 있으면서 동시에 사람에게 친화적인 언어를 개발해서 사용한다.
  • JSON 으로 표현하는 것보다 50~70% 더 간결하고 읽기 쉽게 작성할 수 있다.

HCL 표현식

// 한줄 주석 방법1
# 한줄 주석 방법2

/*
라인
주석
*/

locals {
  key1 = "value1"  # = 을 기준으로 키와 값이 구분
  myStr = "TF 와 UTF-8" # UTF-8 문자를 지원
  multiStr = <<EOF # Linux/macOS 에서는 EOF 와 같은 여러 줄 문자열 지원
  Multi
  Line
  String
EOF

  boolean1 = true  # boolean true
  boolean2 = false # boolean false
  
  decimal = 123 # 10진수
  octal = 0123  # 0으로 시작하는 숫자는 8진수
  hexadecimal = "0xD5" # 0x 값을 포함하는 string은 16진수
  scientific = 1e10 # 과학표기 법도 지원
  
  # function 호출 예시
  myprojectname = format("%s is myprojectname", var.project)
  
  # 3항 연산자 조건문 지원
  credentials = var.credentials == "" ? file(var.credentials_file): var.credentials
}

 

3. 테라폼 블록

  • 백엔드 블록
    백엔드 블록은 테라폼 실행 시 저장되는 State(상태 파일)의 저장 위치를 선언한다.
    주의할 점은 하나의 백엔드만 허용한다는 점이다.
    여러 작업자 간의 협업을 고려하면 상태 파일을 공유할 수 있는 외부 저장소가 필요하다.
  • State 잠금 동작
    기본적으로 활성화된 백엔드는 local 이다. 하지만 여러 사용자가 협업하는 외부 저장소를 사용할 때 State 잠금 동작을 사용하여 동시에 여러 사용자가 State 에 접근하는 것을 방지한다. 
  • 백엔드 설정 변경
    백엔드가 설정을 변경하려면 다시 init 명령을 수행해 State의 위치를 재설정 해야한다.
    • 이전 구성 유지
      -migrate-state
      이전 구성에서 최신의 state 스냅샷을 읽고 기록된 정보를 새 구성으로 전환한다.
    • 새로 초기화
      -reconfigure
      init를 실행하기 전에 terraform.tfstate 파일을 삭제해 테라폼을 처음 사용할 때처럼 작업공간을 초기화 한다.

4. 리소스 (Resource)

  • 리소스 구성

resource "<리소스 유형>" "<이름>" {
  <인수> = <값>
}

resource "local_file" "abc" {
  content = "123!"
  filename = "${path.module}/abc.txt"
}

 

  • 종속성
    테라폼의 종속성은 resource, module 선언으로 프로비저닝되는 각 요소의 생성 순서를 구분한다. 기본적으로 다른 리소스에서 값을 참조해 불러올 경우 생성 선후 관계에 따라 작업자가 의도하지는 않았지만 자동으로 연관 관계가 정의되는 암시적 종속성을 갖게 되고, 강제로 리소스 간 명시적 종속성을 부여할 경우에는 메타 인수인 depends_on을 활용한다.
resource "local_file" "abc" {
  content = "123!"
  filename = "${path.module}/abc.txt"
}

resource "local_file" "def" {
  depends_on = [
    local_file.abc
  ]

  content = "456!"
  filename = "${path.module}/def.txt"
}

 

  • 리소스 속성 참조
    • 인수 : 리소스 생성 시 사용자가 선언하는 값
    • 속성 : 사용자가 설정하는 것은 불가능하지만 리소스 생성 이후 획득 가능한 리소스 고유 값
# Terraform Code
resource "<리소스 유형>" "<이름>" {
  <인수> = <값>
}

# 리소스 참조
<리소스 유형>.<이름>.<인수>
<리소스 유형>.<이름>.<속성>
  • 수명주기
    • create_before_destroy
      리소스 수정 시 신규 리소스를 우선 생성하고 기존 리소스를 삭제
    • prevent_destroy
      해당 리소스를 삭제(Destroy)하려 할 때 명시적으로 거부
    • ignore_changes
      리소스 요소에 선언된 인수의 변경 사항을 테라폼 실행 시 무시
    • precondition
      리소스 요소에 선언된 인수의 조건을 검증
    • postcondition
      Plan과 Apply 이후의 결과를 속성 값으로 검증

create_before_destroy

resource "local_file" "abc" {
  content = "abc1!"
  filename = "${path.module}/abc.txt"
  
  lifecycle {
    create_before_destroy = true   # 생성 후 삭제
  }
}

prevent_destroy

resource "local_file" "abc" {
  content = "abc1!"
  filename = "${path.module}/abc.txt"
  
  lifecycle {
    prevent_destroy = true  # 삭제 방지
  }
}

ignore_changes

resource "local_file" "abc" {
  content = "abc1!"
  filename = "${path.module}/abc.txt"
  
  lifecycle {
    ignore_change = [
      content    # content 변경 무시
    ]
  }
}

precondition

variable "file_name" {
  default = "step0.txt"
}

resource "local_file" "abc" {
  content = "abc1!"
  filename = "${path.module}/abc.txt"
  
  lifecycle {
    precondition {
      condition = var.file_name == "step6.txt"
      error_message = "file name is not \"step6.txt\""
  }
}

postcondition

resource "local_file" "abc" {
  content = ""
  filename = "${path.module}/abc.txt"
  
  lifecycle {
    postcondition {
      condition = self.content != ""
      error_message = "content cannot be empty"
    }
  }
}

output "abc_content" {
  value = local_file.abc.id
}