[Terraform] 테라폼 기초 (1) 본문
Terraform init
terraform init
명령어는 테라폼 구성 파일이 있는 작업 디렉토리를 초기화하는데 사용한다. 이 작업을 실행하는 디렉토리를
루트 모듈
이라고 부른다.
테라폼에서의 모듈이란?
테라폼이 실행되는 디렉토리를 모듈이라고 부른다. 모듈에는 테라폼 코드 정의를 위한 tf 또는 tf.json 확장자의 파일과 tfvars 같은 변수를 정의하는 파일이 포함된다. 일반적으로 기본 작업 디렉토리의 정의된 파일 집합을 루트 모듈이라고 부른다. 루트 모듈은 다른 모듈을 호출해 해당 루트 모듈이 구성하는 리소스 구성을 포함시킬 수 있고, 이렇게 호출되는 모듈을 자식 모듈이라고 한다.
테라폼을 시작하는데 사용되는 첫 번째 명령어로 테라폼에서 사용되는 프로바이더, 모듈 등의 지정된 버전에 맞춰 루트 모듈을 구성한다.
자바의 pom.xml 이나 노드의 package.json 등과 같이 의존성 정의를 읽고, 최초 실행 시 실행에 필요한 아티팩트나 라이브러리를 다운로드하고 준비시키는 역할과 비슷하다.
main.tf
파일이 존재하는 위치에서 terraform init
명령어를 수행하면 아래와 같이 성공적으로 초기화 되었다는 메시지를 확인할 수 있다.
-upgrade
0.14 버전 이후부터 프로바이더 종속성을 고정시키는 .terraform.lock.hcl
이 추가됐다. 작업자가 로컬에서 init
으로 받은 프로바이더, 모듈 버전으로 수행한 이후 다른 작업자나 리모트 환경에서 init
을 수행하는 경우, 지속적으로 업그레이드되는 각 프로바이더와 모듈로 인해 변경된 버전으로 설치될 가능성이 있다.
따라서 작업 당시의 버전 정보를 기입하고 .terraform.lock.hcl
파일이 있으면 해당 파일에 명시된 버전으로 init
을 수행한다. 이후 작업자가 의도적으로 버전을 변경하거나 코드에 명시한 다른 버전으로 변경하려면 terraform init -upgrade
를 수행해야 한다.
Terraform validate
단어의 의미 그대로 디렉토리에 있는 테라폼 구성 파일의 유효성을 확인한다. 서비스나 인프라의 상태를 확인하기 위한 원격 작업 혹은 API는 발생하지 않으며, 코드적인 유효성만 검토한다.
Terraform plan & apply
terraform plan
명령어는 테라폼으로 적용할 인프라의 변경 사항에 관한 실행 계획을 생성한다.
이를 통해 출력되는 결과를 확인하여 어떤 변경이 적용될지 사용자가 미리 검토하고 이해하는데 도움을 준다.
terraform apply
는 plan
명령어를 통해 작성된 적용 내용을 토대로 작업을 실행한다. 만약 plan
명령어로 생성된 실행 계획이 없다면, 자동으로 계획을 생성하고 해당 계획을 승인할 것인지 묻는 메시지가 출력된다.
-detailed-exitcode
실행 계획 생성 명령과 함께 사용하기 좋은 추가 옵션으로, 파이프라인 설계에서 활용할 수 있다. exitcode 또는 errorlevel은 동작의 결과를 숫자 코드로 제공한다.
각 숫자 코드는 자동화된 동작을 설계할 때 그 뒤 작업을 수행할지, 사용자에게 알릴지, 정지할지 등을 나타낸다.
- 0 : 변경사항이 없는 성공
- 1 : 오류가 있음
- 2 : 변경사항이 있는 성공-out
terraform plan -out=tfplan
명령어는 실행 계획을 바이너리 파일 형태로 추출하는 옵션이다.
-out=<파일명>
형태다. 해당 명령어로 생성된 tfplan
바이너리 파일을 apply
명령 수행 시 붙여 실행할 경우 실행 계획을 생성할지 묻는 과정이 생략된 것을 확인할 수 있다.
실행 계획이 이미 존재하기 때문에 해당 과정이 생략된 것이다. 만약 바이너리 파일이 생성된 이후 원본 tf 파일이 수정되었다면 terrafrom apply tfplan
명령어는 실행할 수 없다.
-replace
프로비저닝이 완료된 이후 terraform plan
과 terraform apply
실행 시 코드 변경이 없다면 실행 계획에 프로비저닝할 대상이 없지만, 사용자가 필요에 의해 특정 리소스를 다시 생성해야 하는 경우 -replace
옵션으로 대상 리소스 주소를 지정하면 대상을 삭제 후 생성하는 실행 계획이 발생한다.
terraform plan
과 terraform apply
모두 적용이 가능하다.
Terraform destroy
terraform destory
명령어는 테라폼 구성에서 관리하는 모든 객체를 제거하는 명령어다. 테라폼 코드로 구성된 리소스의 일부만 제거하기 위해서는 테라폼의 선언적 특성에 따라 삭제하려는 항목을 코드에서 제거하고, 다시 terraform apply
를 실행하는 방법이 있다.
하지만 모든 객체를 삭제하려면 terraform destory
명령어를 사용하면 된다. terraform destory
명령어 또한 apply
처럼 실행 계획을 필요로 하며 실행 계획 파일을 생성한 후 terraform destory
명령어를 실행하는 단계적으로 나누어 실행하는 방식 또한 가능하다.
파이프라인에서는 주로 terraform plan -destroy -out=<파일명>
로 실행 계획을 만들고 terraform apply
로 해당 실행 계획을 실행하는 방식으로 단계를 나누어 사용한다.
-auto-approve
apply
, destroy
명령어처럼 사용자의 승인이 필요할 경우 해당 승인 요청을 자동으로 승낙하는 옵션이다. 당연한 이야기지만 사용에 주의해야 한다.
Terraform fmt
terraform fmt
명령어는 테라폼 구성 파일을 표준 형식과 표준 스타일로 적용하는데 사용한다. 주로 구성 파일의 가독성을 향상시키는 목적으로 사용된다.
-recursive
옵션으로 하위 디렉토리의 테라폼 구성 파일을 모두 포함해 적용시킬 수 있다.
HCL
테라폼으로 인프라를 구성하기 위한 여러 선언 블록들이 존재한다.
- Terraform Block
- Resource Block
- Data Block
- Variable Block
- Local Block
- Output Block
Terraform Block
테라폼의 구성을 명시하는데 사용된다. 테라폼 버전이나 프로바이더 버전과 같은 값들을 명시적으로 선언하고 필요한 조건들을 입력하여 실행 오류를 최소화하기 위해 사용한다.
terraform {
required_version = "~> 1.9.3" # 테라폼 버전
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"
}
}
테라폼 버전 제약 구문에서 사용되는 크기 부호는 아래의 표에서 확인할 수 있듯 각기 다른 의미를 가지고 있다.
백엔드 블록의 구성은 테라폼 실행 시 저장되는 state
파일의 저장 위치를 선언한다. 백엔드 블록은 하나의 백엔드만 허용한다.
테라폼은 state
파일의 데이터를 사용해 코드로 관리된 리소스를 탐색하고 추적한다. 또한 작업자 간의 협업을 고려한다면 테라폼으로 생성한 리소스의 상태 저장 파일을 공유할 수 있는 외부 백엔드 저장소가 필요하다.
state
에는 외부로 노출되면 안되는 비밀번호와 같은 민감한 정보들이 포함되어 있을 수 있기 때문에 접근 제어 또한 필요하다.
기본적으로 활성화되는 백엔드는 local
이다. 상태를 작업자의 로컬 환경에 저장하고 관리하는 방식이다. 이외의 다른 백엔드 구성은 동시에 여러 작업자가 접근해 사용할 수 있도록 공유 스토리지 같은 개념을 갖는다.
공유되는 백엔드에 state
가 관리되면 테라폼이 실행되는 동안 .terraform.tfstate.lock.info
파일이 생성되면서 해당 state
를 동시에 사용하지 못하도록 잠금 처리를 한다.
Resource Block
리소스는 테라폼이 프로비저닝 도구라는 측면에서 가장 중요한 요소다. 리소스 블록은 선언된 항목을 생성하는 동작을 수행한다.
resource "<리소스 유형>" "<이름>" {
<인수> = <값>
}
- 리소스 유형은 첫 번째 언더바를 기준으로 앞은 프로바이더 이름, 뒤는 프로바이더에서 제공하는 리소스 유형을 의미한다.
local_file
의 경우local
프로바이더에 속한 리소스 유형이다. 따라서 해당 리소스의 유형이 어떤 프로바이더가 제공하는 것인지는 언더바 앞쪽의 이름을 보고 확인할 수 있다. - 이름의 경우 동일한 유형에 대해서 식별자 역할을 하기 때문에 유형이 같은 경우 동일한 이름을 사용할 수 없다.
lifecycle
메타 인수를 통해 리소스의 기본 생명주기를 사용자가 임의로 변경하는 것이 가능하다.
create_before_destroy (bool)
- 리소스 수정 시 신규 리소스를 우선 생성하고 기존 리소스를 삭제한다.
- 테라폼의 기본 생명주기는 삭제 후 생성이기 때문에 사용자는 의도적으로 수정된 리소스를 먼저 생성하기를 원할 수 있다.
- 생성되는 리소스가 기존 리소스로 인해 생성이 실패되거나 삭제 시 함께 삭제될 위험 또한 존재한다.
prevent_destroy (bool)
- 해당 리소스를
Destroy
하려고 할 때 명시적으로 거부한다.
- 해당 리소스를
ignore_changes (list)
- 리소스 요소에 선언된 인수의 변경 사항을 테라폼 실행 시 무시한다.
precondition
- 리소스 요소에 선언된 인수의 조건을 검증한다.
postcondition
plan
과apply
이후의 결과를 속성 값으로 검증한다.Data Block데이터 소스는 테라폼으로 정의되지 않은 외부 리소스 또는 저장된 정보를 테라폼 내에서 참조할 때 사용한다.
data "<리소스 유형>" "<이름>" {
<인수> = <값>
}
# 데이터 소스 참조
data.<리소스 유형>.<이름>.<속성>
- 리소스 유형은 첫 번째 언더바를 기준으로 앞은 프로바이더 이름, 뒤는 프로바이더에서 제공하는 리소스 유형을 의미한다.
local_file
의 경우local
프로바이더에 속한 리소스 유형이다.
따라서 해당 리소스의 유형이 어떤 프로바이더가 제공하는 것인지는 언더바 앞쪽의 이름을 보고 확인할 수 있다. - 이름의 경우 동일한 유형에 대해서 식별자 역할을 하기 때문에 유형이 같은 경우 동일한 이름을 사용할 수 없다.
resource "local_file" "abc" {
content = "123!"
filename = "${path.module}/abc.txt"
}
data "local_file" "abc" {
filename = local_file.abc.filename
}
resource "local_file" "def" {
content = data.local_file.abc.content
filename = "${path.module}/def.txt"
}
데이터 소스인 data.local_file.abc
는 리소스 local_file.abc
의 파일 이름을 참조하여 데이터 소스를 생성한다.
데이터 소스 local_file
은 읽어온 파일의 내용을 content
속성으로 참조할 수 있으므로 리소스 local_file.def
에서는 data.local_file.abc.content
로 읽혀진 데이터를 참조한다.
Variable Block
입력 변수는 인프라를 구성하는 데 필요한 속성 값을 정의하여 코드의 변경 없이 여러 인프라를 생성하는데 목적을 둔다.
테라폼에서는 이를 Input Variable
즉 입력 변수로 정의한다. 입력이라는 수식어가 붙는 이유는, 테라폼이 plan
실행 시 값을 입력한다는 점 때문이다.
variable "<이름>" {
<인수> = <값>
}
변수 정의 시 사용 가능한 메타인수는 다음과 같다.
default
: 변수에 할당되는 기본값 정의type
: 변수에 허용되는 값 유형 정의description
: 입력 변수의 설명validation
: 변수 선언의 제약조건을 추가해 유효성 검사 규칙 정의sensitive
: 민감한 변수 값임을 알리고 테라폼의 출력문에서 값 노출을 제한nullable
: 변수에 값이 없어도 됨을 지정
지원되는 변수의 유형 또한 여러가지가 존재한다. 사용 예시는 아래를 참고
variable "string" {
type = string
description = "var String"
default = "myString"
}
variable "number" {
type = number
default = 123
}
variable "boolean" {
default = true
}
variable "list" {
default = [
"meta",
"nvidia",
"apple",
"amazon",
"google",
"tesla",
"microsoft"
]
}
output "list_index_0" {
value = var.list.0
}
output "list_all" {
value = [
for name in var.list :
upper(name)
]
}
variable "set" {
type = set(string)
default = [
"google",
"vmware",
"amazon",
"microsoft"
]
}
variable "object" {
type = object({name=string, age=number})
default = {
name = "abc"
age = 12
}
}
variable "tuple" {
type = tuple([string, number, bool])
default = ["abc", 123, true]
}
variable "ingress_rules" {
type = list(object({
port = number,
description = optional(string),
protocol = optional(string, "tcp),
}))
default = [
{ port = 80, description = "web" }
{ port = 53, protocol = "udp" }
]
}
변수에 대한 유효성 검사는 validation
메타인수를 사용해 진행할 수 있다. 예시는 다음과 같다.
variable "image_id" {
type = string
description = "The id of the machine image (AMI) to use for the server"
validation {
condition = length(var.image_id) > 4
error_message = "The image_id value must exceed 4."
}
}
변수에 대한 참조는 코드 내에서 var.<이름>
형태로 할 수 있다. 선언된 variable
에 정의된 값이 없으면 terraform plan
혹은 apply
실행 후 변수 값을 입력하라는 항목을 확인할 수 있다.
변수 값을 입력하면 입력한 값으로 실행 계획을 생성하고 수행한다.
민감한 입력 변수를 사용해야 할 경우 다음과 같이 sensitive
메타인수를 사용하여 민감 여부를 선언할 수 있다.
variable "my_password" {
default = "password"
sensitive = true
}
resource "local_file" "abc" {
content = var.my_password
filename = "${path.module}/abc.txt"
}
기본 값이 추가되어 값을 입력받는 항목은 출력되지 않지만 테라폼의 실행 계획에서의 참조 변수 값이 감춰지는 것을 확인할 수 있다.
프로비저닝을 완료할 경우 출력에서는 값이 표현되지 않았지만 실제 생성되는 리소스 결과물에서는 지정한 값이 입력된 것을 확인할 수 있다.
sensitive
를 사용하여 민감한 변수로 지정하더라도 terraform.tfstate
파일에는 결과문이 평문으로 기록되기 때문에 state
파일의 보안에는 유의해야 한다.
Local Block
코드 내에서 사용자가 지정한 값 또는 속성 값을 가공해 참조 가능한 local
은 외부에서 입력되지 않고, 코드 내에서만 가공되어 동작하는 값을 선언한다.
선언된 모듈 내에서만 접근이 가능하고, 변수처럼 실행 시에 입력받을 수 없다.
locals
로 선언할 수 있으며, locals
내에 선언한 로컬 변수의 이름은 전체 루트 모듈 내에서 유일해야 한다.
variable "prefix" {
default = "hello"
}
locals {
name = "terraform"
content = "${var.prefix} ${local.name}"
my_info = {
age = 20
region = "KR"
}
my_nums = [1, 2, 3, 4, 5]
}
locals {
content = "duplicated content" # 중복 선언되었으므로 에러가 발생
}
선언된 로컬 변수는 local.<이름>
으로 참조할 수 있다. 테라폼 구성 파일을 여러 개 생성해 작업하는 경우 서로 다른 파일에 선언되어 있더라도 다른 파일에서 참조할 수 있다.
Output Block
출력 값은 주로 테라폼 코드의 프로비저닝 수행 후의 결과 속성 값을 확인하는 용도로 사용된다. 또한 테라폼 모듈 간, 워크스페이스 간 데이터 접근 요소로도 활용할 수 있다.
예를 들어 자바의 getter
와 비슷한 역할이라고 생각하면 좋다. 출력 값의 용도는 다음과 같이 정의할 수 있다.
- 루트 모듈에서 사용자가 확인하고자 하는 특정 속성 출력한다.
- 자식 모듈의 특정 값을 정의하고 루트 모듈에서 결괄르 참조한다.
- 서로 다른 루트 모듈의 결과를 원격으로 읽기 위한 접근 요소로 활용할 수 있도록 한다.
출력 값을 작성하면 단순 디버깅을 넘어 속성 값을 노출하고 접근할 수 있다.
모듈 내에서 생성되는 속성 값들은 output block
에 정의된다.
output "instance_ip_addr" {
value = "http://${aws_instance.server.private.ip}"
}
출력되는 값은 value
의 값이며 테라폼이 제공하는 조합과 프로그래밍적인 기능들에 의해 원하는 값을 출력할 수 있다.
주의할 점은 output
결과에서 리소스 생성 후 결정되는 속성 값은 프로비저닝이 완료되어야 최종적으로 결과를 확인할 수 있고 terraform plan
단계에서는 적용될 값을 출력하지 않는다는 것이다.
사용할 수 있는 메타인수는 다음과 같다.
description
: 출력 값 설명sensitive
: 민감한 변수 값임을 알리고 테라폼의 출력문에서 값 노출을 제한한다.- 값이 출력되지 않으므로 디버깅보다는 값을 노출시키지 않고 상위 모듈 또는 다른 모듈에서 참조하기 위한 목적으로 사용
depends_on
:value
에 담길 값이 특정 구성에 종속성이 있는 경우 생성되는 순서를 임의로 조정한다.precondition
: 출력 전에 지정된 조건을 검증한다.
사용 방법은 다음과 같다.
resource "local_file" "abc" {
content = "abc123"
filename = "${path.module}/abc.txt"
}
output "file_id" {
value = local_file.abc.id
}
output "file_abspath" {
value = local_file.abc.filename
}
terraform plan
을 사용할 경우 file_id
의 경우 실행 계획 단계에서는 알 수 없기 때문에 apply
실행 이후 확인할 수 있다고 출력된다.
apply
명령어를 실행하면 프로비저닝 이후 해당 값이 잘 출력되는 모습을 확인할 수 있다.
'DevOps' 카테고리의 다른 글
[Terraform] 테라폼 기초 (3) (1) | 2024.08.28 |
---|---|
[Terraform] 테라폼 기초 (2) (0) | 2024.08.19 |
[Helm] Helm Quick Start (0) | 2024.08.10 |
[Terraform] 테라폼 반복문 (0) | 2024.08.09 |
[Jenkins] CI/CD 파이프라인 구축 중 .gitIgnore 처리된 Config 파일 관리 (0) | 2024.07.19 |