본문 바로가기
  • lakescript
스터디 이야기/Terraform

[T101] 1-3. Terraform - HCL, 테라폼 블록

by lakescript 2024. 6. 15.

 

더보기

이 스터디는 CloudNet@에서 진행하는 T101 스터디를 참여하면서 공부하는 내용을 기록하는 블로그 포스팅입니다.

CloudNet@에서 제공해주는 자료들과 테라폼으로 시작하는 IaC 를 바탕으로 작성되었습니다.

 

 

 

HCL

HCL은 하시코프사에서 IaC와 구성 정보를 명시하기 위해 개발된 오픈 소스 도구입니다.

 

Infrastructure as Code는 수동 프로세스가 아닌 코드를 통해 인프라를 관리하고 프로비저닝하는 것을 말합니다. IaC이전에는 UI 클릭이나 개별적인 스크립트를 사용하여 프로비저닝하는 방식을 사용했었기 때문에 자동화하기 어려웠고, 이런 작업들을 진행할 때에 작업자의 숙련도나 전문성에 따라 실수가 발생할 수 있었습니다. 그리고 스크립트 방식은 작업자가 정의한 순서대로 동작하지만 순차적인 방식이기 때문에 실행 중 오류가 발생하면 다시 Rollback하기 어려웠습니다. 테라폼에서는 HCL이 코드 영역을 담당합니다. 그리고 쉽게 읽을 수 있고 빠르게 배울 수 있는 언어적 특성도 갖고 있습니다. 더하여 인프라가 코드로 표현되고, 이 코드는 곧 인프라이기 때문에 선언적 특성을 갖게 되고 Turing-complete한 언어적인 특성까지 갖고 있습니다. 이렇게 코드화된 인프라는 주된 목적인 자동화와 더불어, 쉽게 프로비저닝하여 히스토리를 관리하고 함께 작업할 수 있는 기반을 만들 수 있게 됩니다.

 

왜 HCL 일까?

HCL을 이용한 테라폼 구성 JSON을 이용한 CloudFormation 구성
resource "local_file" "abc"{
    content = "abc!"
    filename = "${path.module}/abc.txt"
}
{
    "resource": [
        {
            "local_file": [
                { "abc": [
                    { "content":"abc!",
                       "filenale":"${path.module}/abc.txt"
                    }
                ]
            }
        ]
    }
 ]
}

 

대부분의 프로비저닝 대상 인프라와 서비스는 JSON과 YAML 방식의 기계 친화적인 언어로 작성됩니다. 하시코프사에서는 HCL 이전에 Ruby와 같은 프로그래밍 언어를 사용해 JSON 같은 구조를 만들기 위해 노력했고, 사람 친화적인 언어를 만들기 위해 노력했습니다. 하지만 JSON은 사람 친화적이지만 구문이 상당히 길어지고 주석이 지원되지 않는다는 단점이 존재했습니다. YAML의 경우 처음 접하는 사용자가 실제 구조를 만들어내고 익숙해지는 데에 어려움이 존재했고, 관리 대상이 많아지고 구성이 복잡해질 경우 리소스 구성을 파악하기가 어렵습니다. 

 

HCL을 이용한 테라폼 구성 JSON을 이용한 CloudFormation 구성
name = "${var.PilotServerName}-vm" "name" : {
    "Fn::Join" : ["-", PilotServerName, vm]]
}"

 

 

HCL 표현식

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

/*
라인
주석
*/

locals {
  key1     = "value1"     # = 를 기준으로 키와 값이 구분되며
  myStr    = "TF ♡ UTF-8" # UTF-8 문자를 지원한다.
  multiStr = <<EOF
  Multi
  Line
  String
  with anytext
EOF

  boolean1    = true   # boolean true
  boolean2    = false  # boolean false를 지원한다.
  deciaml     = 123    # 기본적으로 숫자는 10진수,
  octal       = 0123   # 0으로 시작하는 숫자는 8진수,
  hexadecimal = "0xD5" # 0x 값을 포함하는 스트링은 16진수,
  scientific  = 1e10   # 과학표기 법도 지원한다.

  # funtion 호출 예
  myprojectname = format("%s is myproject name", var.project)

  # 3항 연산자 조건문을 지원한다.
  credentials = var.credentials == "" ? file(var.credentials_file) : var.credentials
}

 

HCL 표현식에서는 일반적으로 코드에서 사용되는 주석 표기부터 변수 정의 등을 포함하고 프로그래밍적인 연산과 구성 편의를 높이기 위한 function도 존재합니다. 

 

테라폼 블록

테라폼 버전이나 프로바이더 버전과 같은 값들은 자동으로 설정되지만 다른 사람과 함께 작업할 때는 버전을 명시적으로 선언하고 필요한 조건을 입력하여 실행 오류를 최소화할 것을 권장합니다. 또한 협업을 위한 관점으로 확장할 경우 다른 사람이 실행하는 경우에 대비하여 테라폼의 구성 정보를 명확하게 정의해야 오류를 방지할 수 있습니다.

즉, 오늘 실행하던, 3년 후에 실행하던 동일한 결과를 얻을 수 있어야 합니다!

 

테라폼 설정 블록의 구조

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"
  }
}

 

테라폼 내에서 버전이 명시되는 terraform, module에서 사용 가능하며 버전에 대한 제약을 설정함으로써 테라폼, 프로바이더, module이 항상 정의한대로 실행되는 것을 목적으로 합니다. 이때 버전 체계는 Semantic Verioning 방식을 따릅니다.

 

 

버전 관리 방식의 예

# version = Major.Minor.Patch
version = 1.3.4

 

Semantic Versioning 방식을 이해하면 테라폼과 프로바이더의 버전 업그레이드 시 영향도를 추축해볼 수 있고, 모듈화의 결과물을 공유하는 방식에도 적용할 수 있습니다.

  • Major 버전 : 내부 동작의 API가 변경 또는 삭제되거나 하위 호환이 되지 않는 버전
  • Minor 버전 : 신규 기능이 추가되거나 개선되고 하위 호환이 가능한 버전
  • Patch 버전 : 버그 및 일부 기능이 개선된 하위 호환이 가능한 버전

버전 제약 구문은 다른 프로그램 언어에서의 종속성 관리 시스템과 비슷합니다. 

  • = 또는 연산자 없음 : 지정된 버전만을 허용
  • != : 지정된 버전을 제외
  • >, >=, <, <= : 지정된 버전과 비교해 조건(부등호)에 맞는 경우 허용
    • ~> : 지정된 버전에서 가장 자리수가 낮은 구성요소만 증가하는 것을 허용
      • ~> x.y 인 경우 y 버전에 대해서만, ~> x.y.z인 경우 z 버전에 대해서만 보다 큰 버전을 허용
선언된 버전 의미 고려 사항
1.0.0 테라폼 v1.0.0만을 허용한다. 테라폼을 업그레이드하기 위해서는 선언된 버전을 변경해야만 한다.
>= 1.0.0 테라폼 v1.0.0 이상의 모든 버전을 허용한다. v1.0.0 버전을 포함해 그 이상의 모든 버전을 허용해 실행된다.
~> 1.0.0 테라폼 v1.0.0을 포함한 v1.0.x 버전을 허용하고 v1.x는 허용하지 않는다. 부버전에 대한 업데이트는 무중단으로 이루어진다.
>= 1.0, <2.0.0 테라폼 v1.0.0 이상 v2.0.0 미만인 버전을 허용한다. 주버전에 대한 업데이트를 방지한다.

 

 

프로바이더 버전

테라폼 0.13 버전 이전에는 provider 블록과 함께 버전을 명시했지만 해당 버전 이후 프로바이더의 버전은 terraform 블록에서 required_providers에 정의합니다.

v0.13이전 v.0.13 이후
provider "aws"{
    version = ">= 4.2.0"
    region = "us-east-1"
}

provider "azurerm" {
    version = "> 2.99.0"
    features {}
}
terraform {
    required_providers {
        aws = {
            source = "hashicorp/aws"
            version = "~> 4.2.0"
        }
         azurerm = {
            source = "hashicorp/azurerm"
            version = ">= 2.99.0"
        }
    }
}

 

각 프로바이더의 이름에 소스 경로와 버전을 명시하며, terraform resitry 공식 페이지에서 원하는 프로바이더를 선택하여 확인하실 수 있습니다.

우측 상단의 [USE PROVIDER] 를 클릭하면 위의 사진처럼 해당 버전을 사용하는 샘플 코드가 보여집니다.

 

Cloud 블록

Terraform Cloud, Terraform Enterprise는 CLI, VCS, API 방식의 실행 방식을 지원하고 cloud 블록으로 선언합니다. 

v1.1 이전 v1.1 이후
terraform {
    backend "remote" {
        hostname = "app.terraform.io"
        organization = "my-org"
        workspaces {
            name = "my-app"
        }
    }
}
terraform {
    cloud {
        hostname = "app.terraform.io"
        organization = "my-org"
        workspaces {
            name = "my-app"
        }
    }
}

 

cloud 블록은 1.1 버전에 추가된 선언으로 기존에는 State 저장소를 의미하는 backend의 remote 항목으로 설정했습니다. hostname은 기본값으로 테라폼 클라우드의 url인 app.terraform.io를 가리킵니다.

 

백엔드 블록

백엔드 블록의 구성은 테라폼 실행 시 저장되는 State(상태 파일)의 저장 위치를 선언합니다. 주의할 점은 하나의 백엔드만 허용한다는 점입니다. 테라폼은 State의 데이터를 사용해 코드로 관리된 리소스를 탐색하고 추적합니다. 또한 작업자 간의 협업을 고려한다면 테라폼으로 생성된 리소스의 상태 저장 파일을 공유할 수 있는 외부 백엔드 저장소가 필요합니다. 그리고 State에는 외부로 노출되면 안되는 패스워드 또는 인증서 정보 같은 민감한 데이터들이 포함될 수 있으니 접근에 대한 제어도 필요합니다.

 

State 잠금 동작

기본적으로 활성화되는 백엔드는 local입니다. 상태를 작업자의 local 환경에 저장하고 관리하는 방식입니다. 이 밖의 다른 백엔드 구성은 동시에 여러 작업자가 접근하여 사용할 수 있도록 공유 스토리지 같은 개념을 갖습니다. 공유되는 백엔드에 State가 관리되면 테라폼이 실행되는 동안 .terraform.tfstate.lock.info 파일이 생성되면서 해당 State를 동시에 사용하지 못하도록 잠금 처리를 합니다.

 

기존에 이미 한번이라도 State가 생성된 테라폼 코드인 경우 terraform init을 수행할 때 백엔드 설정이 변경되면서 새로운 백엔드로의 전환을 진행합니다. 이때 2가지의 실행 옵션이 설정 가능합니다.

 

이전 구성 유지(migrate-state)

terraform init -migrate-state

 

-migrate-state는 terraform.tfstate의 이전 구성에서 최신의 state 스냅샷을 읽고 기록된 정보를 새 구성으로 전환합니다.

 

 

새로 초기화(reconfigure)

terraform init -reconfigure

 

-reconfigure는 terraform init을 실행하기 전에 terraform.tfstate 파일을 삭제해 테라폼을 처음 사용할 때처럼 이 작업 공간(디렉토리)를 초기화하는 동작입니다.

 

 

참고

- https://github.com/hashicorp/hcl

- https://registry.terraform.io/