Terraform (1) - Terraform이 하는 일과 첫 리소스 만들기
1. 개요
서버, 네트워크, 디스크 같은 인프라를 만들 때 웹 콘솔에서 버튼을 눌러 만들 수도 있다. 하지만 그렇게 만들면 “무엇을 어떻게 만들었는지”가 기록에 남지 않아서, 같은 환경을 다시 만들거나 다른 사람에게 설명하기 어렵다.
Terraform은 만들고 싶은 인프라를 코드로 적어 두면, 그 코드대로 인프라를 만들고 고치고 지워 주는 도구다. 이렇게 인프라를 코드로 관리하는 방식을 IaC(Infrastructure as Code)라고 한다.
이 글에서는 Terraform이 어떤 방식으로 동작하는지 알아보고, 클라우드 계정 없이 내 컴퓨터에서 파일 하나를 만드는 예제로 기본 흐름을 직접 따라 해 본다.
2. “무엇이 있어야 하는가”를 적는 도구
Terraform 코드에는 “어떤 순서로 무엇을 실행하라”가 아니라 “최종적으로 무엇이 있어야 한다” 를 적는다. 이런 방식을 선언형(declarative)이라고 한다.
| 방식 | 적는 내용 | 예 |
|---|---|---|
| 절차형 (셸 스크립트) | 실행할 명령과 순서 | “서버를 만들어라. 그다음 디스크를 붙여라.” |
| 선언형 (Terraform) | 최종 상태 | “서버 1대와 디스크 1개가 있어야 한다.” |
선언형의 장점은 같은 코드를 여러 번 실행해도 안전하다는 것이다. Terraform은 코드에 적힌 상태와 실제 상태를 비교해서 차이만 실행한다. 이미 서버가 있으면 새로 만들지 않고, 코드에서 서버를 지우면 실제 서버도 지운다.
3. 핵심 용어
처음에 알아야 할 용어는 네 가지다.
| 용어 | 뜻 |
|---|---|
| provider | Terraform이 실제로 무언가를 만들 때 쓰는 플러그인. AWS용, GCP용, 로컬 파일용 등이 따로 있다 |
| resource | Terraform이 만들고 관리하는 대상 하나. 서버 1대, 파일 1개가 각각 resource다 |
| state | Terraform이 “내가 지금까지 무엇을 만들었는지” 적어 두는 기록 파일 (terraform.tfstate) |
| plan / apply | plan은 “무엇을 바꿀지” 미리 보여 주고, apply는 실제로 바꾼다 |
전체 흐름은 다음과 같다.
코드(.tf) ──┐
├──> terraform plan : 코드, state, 실제 상태를 비교해 할 일 계산
state 파일 ──┘ │
▼
terraform apply : provider를 통해 실제로 만들고 바꾸고 지움
│
▼
state 파일 갱신 : 이번에 만든 것을 기록
AWS에 서버를 만드는 코드든, 이 글처럼 파일을 만드는 코드든 흐름은 같다. provider만 다르다.
4. 설치
macOS에서는 HashiCorp 공식 Homebrew 저장소로 설치한다.
brew tap hashicorp/tap
brew install hashicorp/tap/terraform
Ubuntu 같은 다른 운영체제는 공식 설치 문서의 안내를 따른다. 설치가 끝나면 버전을 확인한다.
terraform version
# Terraform v1.16.4
5. 첫 코드 작성
실습용 디렉터리를 만들고 그 안에 main.tf 파일을 만든다. Terraform 코드 파일은 확장자가 .tf다.
mkdir terraform-practice
cd terraform-practice
main.tf에 다음 내용을 적는다. 이 코드는 “hello.txt라는 파일이 이런 내용으로 있어야 한다”는 선언이다.
# main.tf
terraform {
required_providers {
local = { # 이 코드에서 쓸 provider 이름
source = "hashicorp/local" # 어디서 내려받을지: 로컬 파일을 다루는 공식 provider
version = "~> 2.5" # 2.5 이상 3.0 미만 버전 사용
}
}
}
resource "local_file" "hello" { # resource "종류" "이름"
filename = "${path.module}/hello.txt" # 만들 파일 경로 (path.module = 이 .tf 파일이 있는 디렉터리)
content = "Hello, Terraform!\n" # 파일 내용
}
코드는 블록 두 개로 되어 있다.
terraform블록: 이 코드에 필요한 provider를 적는다. 여기서는 로컬 파일을 만드는hashicorp/local을 쓴다.resource블록: 만들 대상을 적는다. 첫 번째 이름local_file은 리소스 종류로, provider가 정해 둔 이름 중에서 고른다. 두 번째 이름hello는 코드 안에서 이 리소스를 부르는 내가 붙인 이름이다. 이 리소스는 앞으로local_file.hello로 가리킨다.
블록 안의 이름 = 값 줄은 인자(argument)라고 부른다. 어떤 인자를 쓸 수 있는지는 리소스 종류마다 다르고, Terraform Registry의 provider 문서에서 확인한다.
6. init, plan, apply
6.1 init: provider 내려받기
terraform init은 코드에 적힌 provider를 내려받는다. 새 디렉터리에서 처음 한 번, provider를 바꿨을 때 다시 실행한다.
terraform init
Terraform has been successfully initialized!
실행하면 디렉터리에 두 가지가 생긴다.
.terraform/: 내려받은 provider 파일.terraform.lock.hcl: 실제로 설치된 provider 버전 기록. 다른 사람도 같은 버전을 쓰도록 git에 함께 올린다
6.2 plan: 무엇을 할지 미리 보기
terraform plan은 실제로 바꾸지 않고, 코드대로 만들려면 무엇을 해야 하는지만 보여 준다.
terraform plan
Terraform will perform the following actions:
# local_file.hello will be created
+ resource "local_file" "hello" {
+ content = <<-EOT
Hello, Terraform!
EOT
+ content_md5 = (known after apply)
+ filename = "./hello.txt"
+ id = (known after apply)
...
}
Plan: 1 to add, 0 to change, 0 to destroy.
+와will be created: 새로 만든다는 뜻이다.(known after apply): 실제로 만들어 봐야 정해지는 값이다. 파일의 해시값이나 id가 그렇다.Plan: 1 to add: 하나를 만들고, 바꾸거나 지우는 것은 없다.
6.3 apply: 실제로 만들기
terraform apply는 plan 결과를 한 번 더 보여 주고, yes를 입력해야 실행한다.
terraform apply
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
local_file.hello: Creating...
local_file.hello: Creation complete after 0s [id=ae9c7cc6...]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
파일이 만들어졌는지 확인한다.
cat hello.txt
# Hello, Terraform!
ls
# hello.txt main.tf terraform.tfstate
hello.txt와 함께 terraform.tfstate가 새로 생겼다. 이것이 3장에서 말한 state 파일이다.
7. state: Terraform의 기억
state 파일에는 Terraform이 만든 리소스와 그 속성이 기록된다. 목록은 다음 명령으로 본다.
terraform state list
# local_file.hello
state가 있어서 Terraform은 “이미 만든 것”을 알 수 있다. 같은 코드로 plan을 다시 실행해 보면 할 일이 없다고 나온다.
terraform plan
No changes. Your infrastructure matches the configuration.
이번에는 Terraform 밖에서 파일을 직접 지운 뒤 plan을 실행해 본다.
rm hello.txt
terraform plan
# local_file.hello will be created
Plan: 1 to add, 0 to change, 0 to destroy.
state에는 파일이 있다고 기록돼 있지만, 실제로 확인해 보니 없다. 그래서 Terraform은 코드와 맞추기 위해 다시 만들겠다고 한다. terraform apply를 실행하면 파일이 다시 생긴다. 이처럼 Terraform은 plan을 실행할 때마다 코드, state, 실제 상태를 함께 비교한다.
state 파일은 지우거나 손으로 고치지 않는다. 지우면 Terraform이 자기가 만든 리소스를 잊어버려서, 같은 것을 또 만들려고 한다. 또 state에는 비밀번호 같은 민감한 값이 그대로 들어갈 수 있으므로, 실제 프로젝트에서는 git에 올리지 않고 원격 저장소(S3 같은 backend)에 따로 보관한다.
8. 바꾸고 지우기
8.1 코드를 바꿨을 때
main.tf의 content를 바꾼다.
content = "Hello again!\n"
plan을 실행하면 이번에는 다른 기호가 나온다.
# local_file.hello must be replaced
-/+ resource "local_file" "hello" {
~ content = <<-EOT # forces replacement
- Hello, Terraform!
+ Hello again!
EOT
...
}
Plan: 1 to add, 0 to change, 1 to destroy.
must be replaced와 -/+는 기존 것을 지우고 새로 만든다는 뜻이다. # forces replacement가 붙은 인자(content) 때문에 교체가 일어난다. 파일이라면 문제가 없지만, 서버나 디스크라면 안에 있던 데이터가 사라진다. 그래서 apply 전에 plan을 꼭 확인해야 한다.
plan에 나오는 기호는 네 가지다.
| 기호 | 뜻 | 주의할 점 |
|---|---|---|
+ |
새로 만든다 (will be created) |
|
~ |
지우지 않고 속성만 바꾼다 (will be updated in-place) |
|
-/+ |
지우고 새로 만든다 (must be replaced) |
데이터가 사라질 수 있다 |
- |
지운다 (will be destroyed) |
데이터가 사라진다 |
어떤 인자가 제자리 변경(~)이고 어떤 인자가 교체(-/+)인지는 리소스 종류마다 다르다. plan이 알려 주므로 외울 필요는 없다.
8.2 전부 지우기
실습이 끝나면 terraform destroy로 이 코드가 만든 리소스를 모두 지운다. apply처럼 yes를 입력해야 실행된다.
terraform destroy
local_file.hello: Destroying... [id=cba977d4...]
local_file.hello: Destruction complete after 0s
Destroy complete! Resources: 1 destroyed.
hello.txt가 사라진다. main.tf는 그대로 있으므로 terraform apply를 다시 실행하면 똑같이 다시 만들 수 있다. 코드가 남아 있는 한 인프라는 언제든 다시 만들 수 있다는 것이 IaC의 핵심이다.
9. 정리
Terraform은 “무엇이 있어야 하는가”를 코드로 선언하는 도구다. 코드는 terraform, resource 같은 블록과 이름 = 값 인자로 이루어지고, 실제로 무언가를 만드는 일은 provider가 한다.
작업 흐름은 init(provider 설치) → plan(할 일 확인) → apply(실행) → destroy(정리)다. Terraform은 만든 것을 state 파일에 기록하고, plan을 실행할 때마다 코드, state, 실제 상태를 비교해 차이만 실행한다.
apply 전에는 plan에 -/+나 -가 있는지 꼭 확인한다. 다음 글에서는 값이 코드에 고정돼 있는 지금의 예제를 변수, 출력, 반복으로 바꿔서 여러 리소스를 한 번에 다루는 방법을 정리한다.