테라폼으로 시작하는 IaC 책을 기준으로 정리하였습니다.
3장 기본 사용법
3.1 주요 커맨드
help 옵션
# 서브커맨드 help 지원
terraform console -help
terraform init -help
init 초기화
# 테라폼 실행을 위해 코드 파일이 있는 디렉터리로 이동
# (참고) 테라폼이 실행되는 디렉터리 = 모듈(테라폼 코드 파일과 변수 파일), 기본 작업디렉터리는 '루트 모듈', 호출 모듈은 '자식 모듈'
$ cd my_dir/
# init 미실행한 상태에서 plan, apply 실행하면 안됨
# 초기화 : 코드 사용 구문 기반으로 필요한 프로바이더 플러그인을 찾고 설치, 추가로 '프로바이더/모듈/백엔드' 구성 설정/변경 시 수행 필요
$ terraform init
$ ls -al
$ tree .terraform # VS Code에서 탐색기 확인
# -upgrade : 0.14 버전 이후부터 프로바이더 종속성을 고정시키는 .terraform.lock.hcl이 추가됨.
# 작업자가 의도적으로 버전을 변경하거나 코드에 명시한 다른 버전으로 변경하려면 upgrade 사용
$ terraform init -upgrade
validate 파일 유효성 확인
- 디렉터리에 있는 테라폼 구성 파일의 유효성을 확인. API작업은 발생하지 않고, 코드적인 유효성만 검토함.
- API 작업이 발생하는 테라폼 Plan 동작과 달리 작성된 구성의 문법, 종속성, 속성 이름이나 연결된 값의 정확성 확인을 수행함.
$ terraform validate
plan 계획 & apply 실행
- terraform plan 명령은 테라폼으로 적용할 인프라의 변경 사항에 관한 실행 계획을 생성하는 동작. 또한 출력되는 결과를 확인하여 어떤 변경이 적용될지 사용자가 미리 검토하고 이해하는데 도움을 줌.
- 변경 사항을 실제로 적용하지는 않으므로, 적용 전에 예상한 구성이 맞는지 검토
- terraform apply 는 plan 계획을 기반으로 작업을 실행.
# plan 실행 : 구성 내용을 바탕으로 어떤 리소스가 생성되는지 상세 내역 출력, 기본값 자동 입력 적용
terraform plan
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# local_file.abc will be created
+ resource "local_file" "abc" {
+ content = "abc!"
+ content_base64sha256 = (known after apply)
+ content_base64sha512 = (known after apply)
+ content_md5 = (known after apply)
+ content_sha1 = (known after apply)
+ content_sha256 = (known after apply)
+ content_sha512 = (known after apply)
+ directory_permission = "0777"
+ file_permission = "0777"
+ filename = "./abc.txt"
+ id = (known after apply)
}
Plan: 1 to add, 0 to change, 0 to destroy. # 하나의 리소스가 추가되고, 변경되거나 삭제되는 것은 없을 예정
# apply 실행
$ terraform apply
# 1) plan 결과를 파일로 저장한 후 실행
# plan 결과를 지정된 파일(바이너리 형태) 이름으로 생성
$ terraform plan -out=tfplan
$ cat tfplan
$ file tfplan
# apply 실행 : 실행계획이 있으므로 즉시 적용됨
$ terraform apply tfplan
$ ls -al abc.txt
# 2) 코드파일 수정 후 실행
# 변경(신규 추가) 부분만 반영 확인
# 테라폼은 선언적 구성 관리를 제공하는 언어로 멱등성 idempotence을 갖고, 상태를 관리하기 때문에 동일한 구성에 대해서는 다시 실행하거나 변경하는 작업을 수행하지 않음
$ terraform apply
# 확인
$ terraform state list
$ tree
$ ls *.txt
# 3) replace 옵션 적용
# -replace : 프로비저닝이 완료 후 사용자에 필요에 의해 특정 리소스를 삭제 후 다시 생성. plan, apply 모두 적용 가능
# 현재 배포된 리소스 확인
$ terraform state list
$ local_file.abc
# 다시 생성 : 파일 생성 시간 확인
$ ls -l abc.txt
$ terraform apply -replace=local_file.abc -auto-approve
$ ls -l abc.txt
$ terraform apply -replace=local_file.abc -auto-approve
$ ls -l abc.txt
destroy 제거 & fmt
destroy: 테라폼 구성에서 관리하는 모든 개체를 제거하는 명령어
fmt : format 또는 reformat 줄임 표시로 terraform tmt 명령어로 수행, 테라폼 구성 파일을 표준 형식과 표준 스타일로 적용. 코드 가독성 높임
$ terraform destroy
$ terraform fmt
3.2 HCL
- IaC는 수동 프로세스가 아닌 코드를 통해 인프라를 관리하고 프로비저닝 하는 것을 말함
- 테라폼에서 HCL이 코드의 영역을 담당한다. HCL은 쉽게 읽을 수 있고 빠르게 배울 수 있는 언어의 특징을 가진다.
- 인프라가 코드로 표현되고, 이 코드는 곧 인프라이기 때문에 선언적 특성을 갖게 되고 튜링 완전한 Turing-complete 언어적 특성을 갖는다.
- 즉, 일반적인 프로그래밍 언어의 조건문 처리 같은 동작이 가능하다. 자동화와 더불어, 쉽게 버저닝해 히스토리를 관리하고 함께 작업 할 수 있는 기반을 제공.
- HCL을 사용하면 동일한 내용을 JSON으로 표현하는 것보다 더 간결하고 읽기 쉽게 작성할 수 있다.
// 한줄 주석 방법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 제공 - 링크
- 테라폼으로 인프라를 구성하기 위한 선언 블록도 다수 존재 : terraform, resource, data, variable, local, output
3.3 테라폼 블록
테라폼 블록은 테라폼 구성을 명시하는 데 사용된다.
테라폼 버전이나 프로바이더 버전과 같은 값들은 자동으로 설정되지만, 함께 작업할 때는 버전을 명시적으로 선언하고 필요한 조건을 입력하여 실행 오류를 최소화 할 것을 권장한다. [Docs] [Docs 예제]
테라폼 버전
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에서 사용 가능하며 버전 체계는 시맨틱 버전 관리 Semantic Versioning 방식을 따른다.
# version = Major.Minor.Patch
version = 1.3.4
- 시맨틱 버전 관리 방식
- Major 버전 : 내부 동작의 API가 변경 또는 삭제되거나 하위 호환이 되지 않는 버전
- Minor 버전 : 신규 기능이 추가되거나 개선되고 하위 호환이 가능한 버전
- Patch 버전 : 버그 및 일부 기능이 개선된 하위 호환이 가능한 버전
- 버전 제약 구문은 다른 프로그램 언어에서의 종속성 관리 시스템과 흡사하다.
- = 또는 연산자 없음 : 지정된 버전만을 허용하고 다른 조건과 병기할 수 없다.
- != : 지정된 버전을 제외한다.
- >, >=, <, <= : 지정된 버전과 비교해 조건(부등호)에 맞는 경우 허용한다.
- ~> : 지정된 버전에서 가장 자리수가 낮은 구성요소만 증가하는 것을 허용한다.
- ~> x.y 인 경우 y 버전에 대해서만, ~> x.y.z인 경우 z 버전에 대해서만 보다 큰 버전을 허용한다. (.은 소수점을 의미)
- ~> : 지정된 버전에서 가장 자리수가 낮은 구성요소만 증가하는 것을 허용한다.
HCL 코드 파일에 버전 명시
terraform {
required_version = "< 1.0.0"
}
resource "local_file" "abc" {
content = "abc!"
filename = "${path.module}/abc.txt"
}
프로바이더 버전
# v0.13 이전 버전
terraform {
required_version = "< 1.0.0"
}
resource "local_file" "abc" {
content = "abc!"
filename = "${path.module}/abc.txt"
}
# v0.13부터 적용
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.2.0"
}
azurerm = {
source = "hashicorp/azurerm"
version = ">= 2.99.0"
}
}
}
Cloud 블록
- Terraform Cloud, Terraform Enterprise는 CLI, VCS, API 기반의 실행 방식을 지원하고 cloud 블록으로 선언한다.
- cloud 블록은 1.1 버전에 추가된 선언으로 기존에는 state 저장소를 의미하는 backend의 remote 항목으로 설정했다. [참고: Docs]
- hostname의 기본값이 app.terraform.io 를 가리키며, 해당 주소는 테라폼 클라우드의 url 이다 [참고: Docs]
# v1.1 이전
terraform {
backend "remote" {
hostname = "app.terraform.io"
organization = "my-org"
workspades = {
name = "my-app-prod"
}
}
}
# v1.1 이후
terraform {
cloud {
hostname = "app.terraform.io"
organization = "my-org"
workspades = {
name = "my-app-prod"
}
}
}
백엔드 블록: State
- 백엔드 블록의 구성은 테라폼 실행 시 저장되는 State(상태 파일)의 저장 위치를 선언하며, 주의할 점은 하나의 백엔드만 허용한다는 점이다.
- 테라폼은 State의 데이터를 사용해 코드로 관리된 리소스를 탐색하고 추적한다.
- 작업자 간의 협업을 고려한다면 테라폼으로 생성한 리소스의 상태 저장 파일을 공유할 수 있는 외부 백엔드 저장소가 필요하다.
- 그리고 State에는 외부로 노출되면 안 되는 패스워드 또는 인증서 정보 같은 민감한 데이터들이 포함될 수 있으므로 State의 접근 제어가 필요하다.
state 잠금 동작
- 기본적으로 활성화되는 백엔드는 local이다.
- 상태를 작업자의 로컬 환경에 저장하고 관리하는 방식이다.
- 이 밖의 다른 백엔드 구성은 동시에 여러 작업자가 접근해 사용할 수 있도록 공유 스토리지 같은 개념을 갖는다.
- 공유되는 백엔드에 State가 관리되면 테라폼이 실행되는 동안 .terraform.tfstate.lock.info 파일이 생성되면서 해당 State를 동시에 사용하지 못하도록 잠금 처리를 한다.
파일 생성을 확인하고 싶다면 terraform apply를 실행하고 생성되는 잠금 파일을 확인해보자. 잠금 파일 내의 정보는 다음과 같다.
백엔드 블록에 local을 정의해서 terrform init을 수행한다.
terraform {
backend "local" {
path = "state/terraform.tfstate"
}
}
resource "local_file" "abc" {
content = "123456!"
filename = "${path.module}/abc.txt"
}
# 1) 현재 상태 확인
# init 시 백엔드 변경에 따른 마이그레이션 안내
$ terraform init
...
Enter a value: yes
...
# 상태 확인
$ ls terraform.tfstate*
$ tree state
$ cat state/terraform.tfstate | jq
$ cat state/terraform.tfstate | jq -r .serial
# apply
$ terraform apply -auto-approve
# 2) 수정 후 상태 확인
# content 내용 수정 이후 다시 apply
$ terraform plan
$ terraform apply -auto-approve
# 상태 확인
$ cat abc.txt
$ ls terraform.tfstate*
$ tree state
$ cat state/terraform.tfstate | jq
$ cat state/terraform.tfstate | jq -r .serial # 예시)현재: 10
$ cat state/terraform.tfstate.backup | jq -r .serial # 예시)백업: 7
# 3) 삭제 후 상태 확인
3.4 리소스
리소스 블록은 선언된 항목을 생성하는 동작을 수행한다.
리소스 구성: resource
- 리소스 블록은 resource로 시작한다. 이후 리소스 블록이 생성할 ‘리소스 유형’을 정의한다.
- 리소스 선언 : 리소스 유형(프로바이더이름_제공리소스유형), 동일한 유형에 대한 식별자 역할로 고유한 이름, 구성 인수들이 이름 뒤에 중괄호 내에 선언됨
- 리소스에서 사용되는 유형들은 프로바이더에 종속성을 갖는다. 특정 프로바이더의 유형만 추가해도 init 수행 시 해당 프로바이더를 설치한다.
resource "<리소스 유형>" "<이름>" {
<인수> = <값>
}
resource "local_file" "abc" {
content = "123"
filename = "${path.module}/abc.txt"
}
resource "aws_instance" "web" {
ami = "ami-a1b2c3d4"
instance_type = "t2.micro"
}
- 리소스 동작 보조 추가 메타인수를 정의 할 수 있다 → 뒤에서 자세히 설명
- depends_on : 종속성을 선언하며, 선언된 구성요소와의 생성 시점에 대해 정의
- count : 선언된 개수에 따라 여러 리소스를 생성
- for_each : map 또는 set 타입의 데이터 배열의 값을 기준으로 여러 리소스를 생성
- provider : 동일한 프로바이더가 다수 정의되어 있는 경우 지정
- lifecycle : 리소스의 수명주기 관리
- provisioner : 리소스 생성 후 추가 작업 정의
- timeouts : 프로바이더에서 정의한 일부 리소스 유형에서는 create, update, delete에 대한 허용 시간 정의 가능
종속성
- 테라폼 종속성은 resource, module 선언으로 프로비저닝되는 각 요소의 생성 순서를 구분짓는다.
- 기본적으로 다른 리소스에서 값을 참조해 불러올 경우 생성 선후 관계에 따라 작업자가 의도하지는 않았지만 자동으로 연관 관계가 정의되는 암시적 종속성을 갖게 되고, 강제로 리소스 간 명시적 종속성을 부여할 경우에는 메타인수인 depends_on을 활용한다. [참고: Docs]
resource "local_file" "abc" {
content = "123!"
filename = "${path.module}/abc.txt"
}
# 아래 두 가지 방법으로 종속성 주입 가능
# 1) 리소스 속성 주입
resource "local_file" "def" {
content = local_file.abc.content # 123!
filename = "${path.module}/def.txt"
}
# 2) deponds_on 선언으로 종속
resource "local_file" "def" {
depends_on = [
local_file.abc
]
content = "456!"
filename = "${path.module}/def.txt"
}
# 종속성에 따라 생성
$ terraform apply -auto-approve
...
Plan: 2 to add, 0 to change, 0 to destroy.
local_file.abc: Creating... <- 먼저 만들고
local_file.abc: Creation complete after 0s [id=5f30576af23a25b7f44fa7f5fdf70325ee389155]
local_file.def: Creating... <- 그 다음 만듬
local_file.def: Creation complete after 0s [id=5f30576af23a25b7f44fa7f5fdf70325ee389155]
$ ls *.txt
$ terraform state list
$ cat abc.txt
$ cat def.txt
$ diff abc.txt def.txt
# graph 확인 > graph-2.dot 파일 선택 후 오른쪽 상단 DOT 클릭
$ terraform graph
$ terraform graph > graph-2.dot
리소스 속성 참조
- 리소스 구성에서 참조 가능한 값은 인수와 속성이다
- 인수 : 리소스 생성 시 사용자가 선언하는 값
- 속성 : 사용자가 설정하는 것은 불가능하지만 리소스 생성 이후 획득 가능한 리소스 고유 값
# Terraform Code
resource "<리소스 유형>" "<이름>" {
<인수> = <값>
}
# 리소스 참조
<리소스 유형>.<이름>.<인수>
<리소스 유형>.<이름>.<속성>
아래 코드는 쿠버네티스 프로바이더의 Namespace 리소스를 생성하고 이후 Secret을 해당 Namespace에 생성하는 종속성을 리소스 인수 값 값으로 생성하는 예이다. Namespace의 이름만 변경해도, 해당 Namespace를 참조하는 모든 리소스가 업데이트되어 영향을 받는다.
resource "kubernetes_namespace" "example" {
metadata {
annotations = {
name = "example-annotation"
}
name = "terraform-example-namespace"
}
}
resource "kubernetes_secret" "example" {
metadata {
namespace = kubernetes_namespace.example.metadata.0.name # namespace 리소스 인수 참조
name = "terraform-example"
}
data = {
password = "P4ssw0rd"
}
}
- 리소스가 생성될 때, 사용자가 입력한 ‘인수’를 받아 실제 리소스가 생성되면 일부 리소스는 자동으로 기본값이나 추가되는 ‘속성’이 부여된다.
- 각 리소스마다 문서를 확인해보면 인수는 Arguments로 표현되어 있으며, 리소스 생성 후 추가되는 속성 값으로 Attributes에 안내되어 있다
- 리소스 속성을 참조하는 다른 리소스 또는 구성요소에서는 생성 후의 속성 값들도 인수로 가져올 수 있다.
수명주기
lifecycle은 리소스의 기본 수명주기를 작업자가 의도적으로 변경하는 메타인수다. 메타인수 내에는 아래 선언이 가능하다.
- create_before_destroy (bool): 리소스 수정 시 신규 리소스를 우선 생성하고 기존 리소스를 삭제
- prevent_destroy (bool): 해당 리소스를 삭제 Destroy 하려 할 때 명시적으로 거부
- ignore_changes (list): 리소스 요소에 선언된 인수의 변경 사항을 테라폼 실행 시 무시
- precondition: 리소스 요소에 선언해 인수의 조건을 검증
- postcondition: Plan과 Apply 이후의 결과를 속성 값으로 검증
resource "local_file" "abc" {
content = "lifecycle"
filename = "${path.module}/abc.txt"
lifecycle {
create_before_destroy = true
}
lifecycle {
prevent_destroy = true
}
lifecycle {
ignore_changes = [
content
]
#ignore_changes = all #모두 무시
}
lifecycle {
precondition {
condition = var.file_name == "abc.txt"
error_message = "file name is not \"abc.txt\""
}
}
lifecycle {
postcondition {
condition = self.content != ""
error_message = "content cannot empty"
}
}
}
create_before_destroy (bool): 리소스 수정 시 신규 리소스를 우선 생성하고 기존 리소스를 삭제
- 예를 들어 image 변경 되는 경우 해당 VM 리소스를 삭제하고 다시 생성
- 테라폼의 기본 수명주기는 삭제 후 생성이기 때문에 작업자가 의도적으로 수정된 리소스를 먼저 생성하기를 원할 수 있다.
- 이 경우 create_before_destroy 가 true로 선언되면 의도한 생성을 실행한 후 삭제로 동작한다.
- 하지만 생성되는 리소스가 기존 리소스로 인해 생성이 실패되거나 삭제 시 함께 삭제될 수 있으니 주의해야 한다.
- 잘못된 사례 1 : 리소스의 명시적 구분이 사용자가 지정한 특정 이름이나 ID인 경우 기존 리소스에 할당되어 있기 때문에 생성 실패
- 잘못된 사례 2 : 생성 후 삭제 시 동일한 리소스에 대한 삭제 명령이 수행되어 리소스가 모두 삭제
prevent_destroy (bool): 해당 리소스를 삭제 Destroy 하려 할 때 명시적으로 거부
ignore_changes (list): 리소스 요소에 선언된 인수의 변경 사항을 테라폼 실행 시 무시
- ignore_changes 리소스 요소의 인수를 지정해 수정 계획에 변경 사항이 반영되지 않도록 하는 것이다.
precondition: 리소스 요소에 선언해 인수의 조건을 검증
- 리소스 생성 이전에 입력된 인수 값을 검증하는 데 사용해 프로비저닝 이전에 미리 약속된 값 이외의 값 또는 필수로 명시해야 하는 인수 값을 검증할 수 있다.
- precondition은 프로비저닝해야 하는 클라우드 인프라의 VM을 생성할 때 내부적으로 검증된 이미지 아이디를 사용하는지 등과 같은 구성을 미리 확인하고 사전에 잘못된 프로비저닝을 실행할 수 없도록 구성할 수 있다.
postcondition: Plan과 Apply 이후의 결과를 속성 값으로 검증
- 프로비저닝 변경 이후 결과를 검증함과 동시에 의존성을 갖는 다른 구성의 변경을 맞는 효과가 있다.
- 종속성을 갖는 여러 리소스를 구성하는 경우, 리소스의 데이터가 다른 리소스 생성 시 활용될 때 원하는 속성이 정의되어야 하는 경우를 확인할 수 있다.
- 특히, 프로비저닝 이후에 생성되는 속성 값이 있으므로 영향을 받는 다른 리소스가 생성되기 전에 예상되지 않은 프로비저닝 작업을 방지할 수 있다.
'⛅ Cloud Study > 🏹 IaC' 카테고리의 다른 글
[Terraform] 프로비저너, terraform_data, moved, CLI 환경변수 (0) | 2024.06.30 |
---|---|
[Terraform] 테라폼 반복문, 조건문, 함수 (0) | 2024.06.29 |
[Terraform] 테라폼 데이터 관리 (0) | 2024.06.22 |
[Terraform] 테라폼 EC2 배포 실습 (0) | 2024.06.15 |
[Terraform] 테라폼의 개념과 실행 환경 구성 (0) | 2024.06.14 |