본문 바로가기

DevOps/Terraform

[T102 1주차] (3) 테라폼 기본 사용법(1)

cloudNet@ 팀의 가시다 님이 진행하는 테라폼 102 스터디 1주차 정리입니다.

1주차 내용: 

테라폼 HCL 분석

 

 

1. 테라폼 HCL

 

HCL이란?

hashiCorp Configuration Language은 하시코프사에서 IaC와 구성 정보를 명시하기 위해 개발된 오픈 소스 도구이다

 

HCL 특징 ?

  • 선언적 특성을 가지며, 튜링-complete한 언어적 특성을 갖는다.
    • 튜링 complete란?  어떤 프로그래밍 언어나 추상 기계가 튜링 기계와 동일한 계산 능력을 가진다는 의미이다. 
    • 즉, 그안에서 프로그래밍 언어와 같이 조건문,for문,while 등 자동화에 필요한 동작들이 가능하다.
  • 자동화와 더불어, 쉽게 버저닝해 히스토리를 관리하고 함께 작업 할 수 있는 기반을 제공. ( SCM과 연계 가능 )
  • 예시 *** 
# HCL

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


# JSON 

{
  "resource": [
  {
    "local_file": [
      {
        "abc": [
          {
            "content":"abc!",
            "filenale":"${path.module}/abc.txt"
          }
        ]
     }
   ]
  }
 ]
}
  • HCL을 사용하면 동일한 내용을 JSON 으로 표현하는 것 보다 더 간결하고 읽기 쉽게 작성된다.

 


2. 테라폼 블록 

 

테라폼 블록이란?

테라폼 버전이나 , 프로바이더 버전과 같은 값들을 설정하고, 멱등성을 보장하기 위해서 이러한 부분을 명시적으로 선언하고

필요 조건들을 입력하여, 실행오류를 최소화 하기 위해 사용된다.

 

예시 ***

terraform {
  required_version = "~> 1.3.0" # 테라폼 버전

  required_providers { # 프로바이더 버전을 나열
    random = {
      version = ">= 3.0.0, < 3.1.0"
    }
    aws = {
      version = "4.2.0"
    }
  }

  cloud { # Cloud/Enterprise 같은 원격 실행을 위한 정보
    organization = "<MY_ORG_NAME>"
    workspaces {
      name = "my-first-workspace"
    }
  }

  backend "local" { # state를 보관하는 위치를 지정
    path = "relative/path/to/terraform.tfstate"
  }
}
  • 테라폼 내에서 버전이 명시되는 부분은 (테라폼, 프로바이더, 모듈 ) 3군데 인데, 버전 체계는 시멘틱 버전 관리 방식을 따른다.
  • 시멘틱 버전 관리 방식이란?
    • Major 버전 : 내부 동작의 API가 변경 또는 삭제되거나 하위 호환이 되지 않는 버전
    • Minor 버전 : 신규 기능이 추가되거나 개선되고 하위 호환이 가능한 버전
    • Patch 버전 : 버그 및 일부 기능이 개선된 하위 호환이 가능한 버전
# version = Major.Minor.Patch
version = 1.3.4
  • 테라폼 버전 : terraform version 명령어 입력시 확인 가능하다.
# 테라폼 버전에 대한 문제가 생길수 있는 경우를 한번 살펴보자
# 테라폼 eks 배포 플러그인을 보면 1.3 버전 이상의 terraform 버전을 요구하는것을 알 수 있다.

terraform {
  # 이부분은 왜 주석처리 되있을까? 
  /* 
  cloud {
    workspaces {
      name = "learn-terraform-eks"
    }
  }
  */

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.4.0"
    }
  }
  required_version = ">= 1.3"
  ##...
}
  • 프로바이더 버전
    • 테라폼 0.13 버전 이후 부터는 terraform 블록 안에서 프로바이더 버전을 정의

  • 모듈 버전
    • Module은 공통으로 활용할 수 있는 다수의 .tf 파일들로 모아 정의하는 것으로 라이브러리 같은 개념으로 사용하며 Module Block을 통해 선언합니다.
    • Root Module과 Child Module로 분류됩니다.
      • Root Module - 작업을 수행하는 디렉터리에 있는 테라폼 코드 모음
      • Child Module - 다른 Module에서 참조하여 사용하는 테라폼 코드 모음
    • Module의 하위 Argument 중 Source를 통해 참조할 Module을 명시합니다. (Local paths, Terraform Registry, Github, G3 Buckets, GCS buckets 등)
    • 테라폼은 Terraform Registry로 다양한 Module의 저장소를 제공합니다.
    • Module을 처음 사용 시 반드시 terraform init 을 실행해야 합니다.
module "webserver_cluster" {
  source = "../../../modules/services/webserver-cluster"
}

 


3. 백엔드 블록

  • 백엔드 블록의 구성은 테라폼 실행 시 저장되는 State(상태 파일)의 저장 위치를 선언한다.
  • 주의할 점은 하나의 백엔드만 허용한다는 점이다.
  • 테라폼은 State 데이터를 사용하여 리소스를 탐색하고 추적 한다.
  • 작업자 간의 협업을 고려한다면 테라폼으로 생성한 리소스의 상태 저장 파일을 공유할 수 있는 외부 백엔드 저장소가 필요하다.
  • 기본적으로 활성화되는 백엔드local이다.
  • 상태를 작업자의 로컬 환경저장하고 관리하는 방식이다.
  • 이 밖의 다른 백엔드 구성은 동시에 여러 작업자가 접근해 사용할 수 있도록 공유 스토리지 같은 개념을 갖는다.
  • 공유되는 백엔드에 State가 관리되면 테라폼이 실행되는 동안.terraform.tfstate.lock.info 파일이 생성되면서 해당 State를 동시에 사용하지 못하도록 잠금 처리를 한다.
  • state파일이 테라폼에 어떤 영향을 미치는지 알아보자
cat <<EOT > main.tf
provider "aws" {
  region = "ap-northeast-2"
}

resource "local_file" "abc" {
  content  = "123456!"
  filename = "${path.module}/abc.txt"
}
EOT
  • terraform apply 
  • 실행후 생성되는 terraform.tfstate 파일을 확인해볼것이다.
terraform apply

------------------------------------------------------------------------
Plan: 1 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

aws_instance.example: Creating...
aws_instance.example: Still creating... [10s elapsed]
aws_instance.example: Still creating... [20s elapsed]
aws_instance.example: Still creating... [30s elapsed]
aws_instance.example: Creation complete after 31s [id=i-0c9fd8aa1a2556fce]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
{
"ID":"2aa5287d-aa2c-c6d9-1554-daf2c60b120d", 
"Operation":"OperationTypeApply", # 어떤 동작으로 인해 잠금 파일이 생성되었는지? 
"Info":"","Who":"mzc01-kook@MZC01-KOOK.local", # 작업자 정보
"Version":"1.3.2", # 테라폼 버전
"Created":"2023-07-05T04:18:26.998256Z",
"Path":"terraform.tfstate" # 잠김 파일 위치
}
  • 수정이 있을시 어떻게 될까?
ls terraform.tfstate*

# 2개 파일이 생성되있다.
# terraform.tfstate terraform.tfstate.backup
  • 이번에는 백엔드 블록을 통해 path를 수정해보도록 하겠다.
terraform {
  backend "local" {
    path = "state/terraform.tfstate"
  }
}

resource "local_file" "abc" {
  content  = "123456!"
  filename = "${path.module}/abc.txt"
}
  • 백엔드가 수정되었으니 init을 다시 해주어야 한다.
# init 시 백엔드 변경에 따른 마이그레이션 안내
terraform init
...
Enter a value: yes
...

#
ls terraform.tfstate*
tree state
cat state/terraform.tfstate | jq
cat state/terraform.tfstate | jq -r .serial

# 
terraform apply -auto-approve
  • 백엔드에 state파일의 경로를 수정해 주었더니 의도한대로 로컬 위치에 저장이 되었다.


4. 리소스 블록

 

  • 리소스 선언은 리소스 유형, 동일한 유형에 대한 식별자 역할
  • 이름,구성 인수들이 이름 뒤에 중괄호 내에 선언됨
resource "local_file" "abc" {
  content  = "123"
  filename = "${path.module}/abc.txt"
}

resource "aws_instance" "web" {
  ami = "ami-a1b2c3d4"
  instance_type = "t2.micro"  
}
  • 종속성 : 
    • resource, module 선언으로 프로비저닝 되는 각 요소의 생성 순서를 구분 짓을수 있다.
    • depends_on 이라는 메타인수를 사용하여 종속성을 부여한다.
    • 종속성에 대한 확인은 VS Code의 확장 Extension pack 중 graphviz을 설치하여 그림으로 확인할수 있다.

  • 설치 후에 apply 하고 나서 terraform graph > graph-1.dot 
  • 해당 파일을 VSCode에서 확인 하면 알수없는 파일형태로 보이지만 오른쪽 구석에 dot 클릭하면 그림으로 볼수 있다.

  • 종속성을 부여하는 2가지 방법
    • depends_on을 활용할수 있다.
    • 테라폼 파일을 작성할때 두개의 리소스 간의 참조값을 활용하여 만들수 있다.
resource "local_file" "abc" {
  content  = "123!"
  filename = "${path.module}/abc.txt"
}

resource "local_file" "def" {
  depends_on = [ # depends_on을 사용하는 방법
    local_file.abc
  ]

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

resource "local_file" "def" {
  content  = local_file.abc.content # 참조값을 활용하는 방법
  filename = "${path.module}/def.txt"
}
  • 리소스 속성에서 참조 가능한 값은 인수, 속성이다.
    • 인수: 리소스 생성시 사용자가 선언하는 값
    • 속성: 사용자가 설정하는 것은 불가능 하지만, 리소스 생성 이후 획득 할수 있는 리소스 고유 값
# Terraform Code
resource "<리소스 유형>" "<이름>" {
  <인수> = <값>
}

# 리소스 참조
<리소스 유형>.<이름>.<인수>
<리소스 유형>.<이름>.<속성>
  • 수명주기
    • lifecycle은 말그대로 리소스의 수명 주기를 작업자가 의도적으로 변경 할수 있는 메타인수이다.
- create_before_destroy (bool): 리소스 수정 시 신규 리소스를 우선 생성하고 기존 리소스를 삭제
- prevent_destroy (bool): 해당 리소스를 삭제 Destroy 하려 할 때 명시적으로 거부
- ignore_changes (list): 리소스 요소에 선언된 인수의 변경 사항을 테라폼 실행 시 무시
- precondition: 리소스 요소에 선언해 인수의 조건을 검증
- postcondition: Plan과 Apply 이후의 결과를 속성 값으로 검증
  • create_before_destroy : 리소스 수정 시 신규 리소스를 우선 생성하고 기존 리소스를 삭제하는 메카니즘
  • 기본적으로 default는 삭제 후 생성 이므로, 라이프사이클 부분을 생략해도 똑같이 동작한다.
    • 하지만 생성되는 리소스가 기존 리소스로 인해 생성이 실패되거나 삭제 시 함께 삭제될 수 있으니 주의해야 한다.
      • 잘못된 사례 1 : 리소스의 명시적 구분이 사용자가 지정한 특정 이름이나 ID인 경우 기존 리소스에 할당되어 있기 때문에 생성 실패
      • 잘못된 사례 2 : 생성 후 삭제 시 동일한 리소스에 대한 삭제 명령이 수행되어 리소스가 모두 삭제
resource "local_file" "abc" {
  content  = "lifecycle - step 1"
  filename = "${path.module}/abc.txt"

  lifecycle {
    create_before_destroy = false
  }
}
  • 삭제 후 create 해서 replace 하는것을 확인 할 수 있다.
Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run "terraform apply" now.
local_file.abc: Refreshing state... [id=7e817d811209db576335597bcd326518fe13398b]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
-/+ destroy and then create replacement

Terraform will perform the following actions:

  # local_file.abc must be replaced
-/+ resource "local_file" "abc" {
      ~ content              = "lifecycle - step 1" -> "lifecycle - step 111111111" # forces replacement
      ~ content_base64sha256 = "mWPFMXSOz+Q/uQKVbykSD/aZqtO8THFqRCcH2NaqkXc=" -> (known after apply)
      ~ content_base64sha512 = "Wa0KB/Qm0YIFpVWUcDMFSiC42nNRLzGsB+MOox8EnJMWGwJ5gkfx4GdODN9BX9lPO+C1lWMAhUNTOLD6j1cBRg==" -> (known after apply)
      ~ content_md5          = "973913f00967dad5c7f311448854eadf" -> (known after apply)
      ~ content_sha1         = "7e817d811209db576335597bcd326518fe13398b" -> (known after apply)
      ~ content_sha256       = "9963c531748ecfe43fb902956f29120ff699aad3bc4c716a442707d8d6aa9177" -> (known after apply)
      ~ content_sha512       = "59ad0a07f426d18205a555947033054a20b8da73512f31ac07e30ea31f049c93161b02798247f1e0674e0cdf415fd94f3be0b595630085435338b0fa8f570146" -> (known after apply)
      ~ id                   = "7e817d811209db576335597bcd326518fe13398b" -> (known after apply)
        # (3 unchanged attributes hidden)
    }
테라폼은 immutable 하다.