State
State의 목적과 의미
이론 내용
- 상태 파일은 테라폼이 관리하는 인프라 리소스의 현재 상태를 기술합니다.
- 이 파일은 JSON 형식 또는 Terraform Cloud 및 Terraform Enterprise에서는 HCL 형식으로 저장될 수 있습니다.
- 테라폼 상태 파일을 직접 편집하거나 직접 읽는 코드로 작성해서는 안 됩니다. 이는 테라폼의 내부 동작 원리와 상태의 일관성을 보장하기 위한 것입니다.
팀 단위에서 테라폼을 운영하기 위한 고려사항
- 상태 파일을 저장하는 공유 스토리지 (Shared storage for state files)
- 팀원들이 동일한 테라폼 상태 파일을 사용하기 위해서는 상태 파일을 공유할 수 있는 위치가 필요합니다.
- 상태 파일 잠금 (Locking state files)
- 테라폼에서는 상태 파일을 잠그는 기능이 필요합니다.
- 동시에 여러 팀원이 같은 상태 파일을 업데이트하여 충돌이 발생할 경우 상태 파일의 일관성이 깨질 수 있으며, 경쟁 상태 (race condition)가 발생할 수 있습니다.
- 상태 파일 격리 (Isolating state files)
- 각 환경(예: DEV, STG, PROD)에 대한 상태 파일 격리가 필요합니다.
상태 파일을 버전 관리 시스템에 저장하는 것이 권장되지 않는 이유
- 수동 오류 (Manual error)
- 버전 관리 시스템을 사용할 경우, 팀원들이 최신 변경 사항을 가져오거나 push 하는 것을 잊어버리거나 잘못된 버전의 상태 파일을 사용할 수 있습니다.
- 이로 인해 테라폼이 이전 상태로 롤백되거나 예기치 않은 인프라 변경이 발생할 수 있습니다.
- 잠금 (Locking)
- 대부분의 버전 관리 시스템은 여러 팀 구성원이 동시에 하나의 파일을 수정할 수 있지만, 잠금 기능을 통한 동시 수정 방지는 제공하지 않습니다.
- 시크릿 (Secrets)
- 테라폼 상태 파일에는 인프라의 구성과 관련된 중요한 정보가 포함될 수 있습니다. 이 정보들은 평문으로 저장되기 때문에, 버전 관리 시스템에서 이 정보들이 노출될 위험이 있습니다.
- 특히 민감한 정보(예: 인증 토큰, 비밀번호 등)가 포함된 경우, 보안 문제가 발생할 수 있습니다.
원격 백엔드를 사용하자 - AWS S3, Azure Blob Storage, Google Cloud Storage, Consul 등
- 수동 오류 해결
- 원격 백엔드를 사용하면 테라폼이 자동으로 상태 파일을 백엔드에서 로드하고, apply 후에도 자동으로 상태 파일을 백엔드에 저장합니다.
- 잠금 (Locking)
- 대부분의 원격 백엔드는 자체적으로 잠금 메커니즘을 제공하여 여러 팀원이 동시에 상태 파일을 수정할 때 충돌을 방지할 수 있습니다.
- 예를 들어, 테라폼이 AWS S3를 원격 백엔드로 사용할 경우 S3의 객체 잠금 기능을 활용하여 apply 시 잠금을 설정할 수 있습니다.
- 또한 -lock-timeout=<TIME> 옵션을 사용하여 대기 시간을 지정할 수 있습니다.
- 시크릿 (Secrets)
- 대부분의 원격 백엔드는 데이터 전송 및 저장 시 기본적으로 암호화를 지원합니다.
- 예를 들어, AWS S3, Azure Blob Storage, Google Cloud Storage 등의 원격 백엔드는 데이터를 암호화하여 저장합니다.
원격 백엔드로 AWS S3 와 DynamoDB를 함께 사용하는 경우
상태 파일 확인 실습
- vpc.tf 파일 생성
provider "aws" {
region = "ap-northeast-2"
}
resource "aws_vpc" "myvpc" {
cidr_block = "10.10.0.0/16"
tags = {
Name = "t101-study"
}
}
- 실행 및 확인
# 리소스 배포
terraform init && terraform plan && terraform apply -auto-approve
# 현재 디렉토리 내에 상태 파일 확인
ls
# 상태 파일 출력(JSON 형식)
cat terraform.tfstate | jq
# terraform.tfstate 파일에서 아래 정보들을 확인
# 상태 파일에서 관리되는 리소스의 목록 출력
terraform state list
# 특정 리소스의 상세 정보 출력
terraform state show aws_vpc.myvpc
- 태그 수정 후 상태 파일 확인
provider "aws" {
region = "ap-northeast-2"
}
resource "aws_vpc" "myvpc" {
cidr_block = "10.10.0.0/16"
tags = {
Name = "tf-state"
}
}
- 실행 및 확인
# 리소스 배포 : plan 시 tfstate 상태와 코드 내용을 비교해서 검토
terraform plan && terraform apply -auto-approve
# 현재 디렉토리 내에 상태 파일들 확인 : 백업 파일 생성됨
ls terraform.tfstate*
# 상태 파일 비교 : 백업 파일과 원본 파일 비교
diff terraform.tfstate terraform.tfstate.backup
- 한번 더 태그 수정 후 상태 파일 확인
provider "aws" {
region = "ap-northeast-2"
}
resource "aws_vpc" "myvpc" {
cidr_block = "10.10.0.0/16"
tags = {
Name = "tf-state-tag-change"
}
}
- 실행 및 확인
# 리소스 배포 : plan 시 tfstate 상태와 코드 내용을 비교해서 검토
terraform plan && terraform apply -auto-approve
# 현재 디렉토리 내에 상태 파일들 확인 : 백업 파일 생성됨
ls terraform.tfstate*
# 상태 파일 비교 : 백업 파일과 원본 파일 비교
diff terraform.tfstate terraform.tfstate.backup
State 동기화
소개
- 테라폼에서는 Plan 과정에서 현재 State와 구성 파일의 차이를 분석하여 각 리소스에 대해 Create, Update, Delete, 그리고 Replace의 동작을 결정합니다.
- 각 동작은 Plan 및 Apply 출력에서 특정 기호를 통해 나타납니다.
- + (Create) : 새로운 리소스를 생성합니다.
- ~ (Update) : 기존 리소스의 수정이 필요한 경우입니다.
- - (Delete) : 기존 리소스를 삭제합니다.
- -/+ (Replace) : 기존 리소스를 새로운 리소스로 교체합니다. 기본적으로 삭제 후 생성하는 방식입니다.
- create_before_destroy 옵션을 사용하면 기본 동작인 삭제 후 생성 대신, 먼저 새로운 리소스를 생성한 다음에 기존 리소스를 삭제할 수 있습니다.
유형 별 실습
- 구성 파일에 추가된 리소스와 State에 따라 발생할 수 있는 동작들을 표로 정리해 보겠습니다.
유형 | 리소스 - 구성 파일(*.tf) | State 파일 | 실제 리소스 | 예상 동작 |
1 | O | 리소스 생성 | ||
2 | O | O | 리소스 생성 | |
3 | O | O | O | 동작 없음 |
4 | O | O | 리소스 삭제 | |
5 | O | 동작 없음 |
유형1 : 신규 리소스 정의 → 리소스 생성
- main.tf 파일 생성
locals {
name = "mytest"
}
resource "aws_iam_user" "myiamuser1" {
name = "${local.name}1"
}
resource "aws_iam_user" "myiamuser2" {
name = "${local.name}2"
}
- 실행 및 확인
# 리소스 배포
terraform init && terraform apply -auto-approve
# 상태 파일에서 관리되는 리소스의 목록 출력
terraform state list
# 특정 리소스의 상세 정보 출력
terraform state show aws_iam_user.myiamuser1
# 현재 디렉토리 내에 상태 파일들 확인
ls *.tfstate
# 상태 파일 출력(JSON 형식)
cat terraform.tfstate | jq
# 재실행 시 테라폼은 멱등성을 보장하는가?
terraform apply -auto-approve
# 현재 디렉토리 내에 상태 파일들 확인
ls *.tfstate
# iam 사용자 리스트 확인
aws iam list-users | jq
유형2 : 실제 리소스 수동 제거 → 리소스 생성
- 실행 및 확인
# 실제 리소스 수동 제거
aws iam delete-user --user-name mytest1
aws iam delete-user --user-name mytest2
# iam 사용자 리스트 확인
aws iam list-users | jq
# 상태 파일에서 관리하는 리소스 목록 출력
terraform state list
# 아래 명령어 실행 결과 차이는?
terraform plan
# 이 옵션은 테라폼이 원격 인프라의 현재 상태를 확인하기 위해 상태 파일을 업데이트(refresh)하지 않도록 합니다.
terraform plan -refresh=false
# 상태 파일 출력(JSON 형식)
cat terraform.tfstate | jq .serial
# 리소스 배포
terraform apply -auto-approve
# 상태 파일에서 관리하는 리소스 목록 출력
terraform state list
# 상태 파일 출력(JSON 형식)
cat terraform.tfstate | jq .serial
# iam 사용자 리스트 확인
aws iam list-users | jq
유형3 : 구성 파일, State, 형상 모두 일치하는 경우 → 동작 없음
- 실행 및 확인
# 리소스 배포
terraform apply -auto-approve
# 상태 파일 출력(JSON 형식)
cat terraform.tfstate | jq .serial
# 리소스 배포
terraform apply -auto-approve
# 상태 파일 출력(JSON 형식)
cat terraform.tfstate | jq .serial
# 리소스 배포
terraform apply -auto-approve
# 상태 파일 출력(JSON 형식)
cat terraform.tfstate | jq .serial
유형4 : 구성 파일에서 일부 리소스 삭제 → 리소스 삭제
- main.tf 파일 수정
locals {
name = "mytest"
}
resource "aws_iam_user" "myiamuser1" {
name = "${local.name}1"
}
- 실행 및 확인
# 리소스 배포
terraform apply -auto-approve
# 상태 파일에서 관리하는 리소스 목록 출력
terraform state list
# 특정 리소스의 상세 정보 출력
terraform state show aws_iam_user.myiamuser1
# 현재 디렉토리 내에 상태 파일들 확인
ls *.tfstate
# 상태 파일 출력(JSON 형식)
cat terraform.tfstate | jq
# iam 사용자 리스트 확인
aws iam list-users | jq
유형6 : 실수로 tfstate 파일 삭제 → 리소스 생성 실패
- 실행 및 확인
# 상태 파일에서 관리하는 리소스 목록 출력
terraform state list
# 실수로 tfstate 파일 삭제
rm -rf terraform.tfstate*
# 실행 계획
terraform plan
# iam 사용자 리스트 확인
aws iam list-users | jq
# 실행 계획
terraform plan -refresh=false
# 리소스 배포
terraform apply -auto-approve
# 상태 파일에서 관리하는 리소스 목록 출력
terraform state list
# 상태 파일 출력(JSON 형식)
cat terraform.tfstate | jq
# iam 사용자 리스트 확인
aws iam list-users | jq
- 테라폼 상태 파일과 실제 인프라가 불일치하는 경우 복구하는 방법 중 하나는 import를 사용하는 것입니다.
유형7 : 실수로 tfstate 파일 삭제 → import를 사용하여 tfstate 파일 복구
- 실행 및 확인
# iam 사용자 리스트 확인
aws iam list-users | jq
# import 도움말 : 빨간색 설명 출력...
terraform import
...
The import command expects two arguments.
Usage: terraform [global options] import [options] ADDR ID
...
# ADDR은 테라폼 구성 파일 내 리소스의 주소를 의미
# ID는 원격 인프라에서 해당 리소스를 식별하는 고유 ID
terraform import aws_iam_user.myiamuser1 mytest1
# 상태 파일에서 관리하는 리소스 목록 출력
terraform state list
# 상태 파일 출력(JSON 형식)
cat terraform.tfstate | jq
# 리소스 배포
terraform apply -auto-approve
Terraform Backend : AWS S3 + DynamoDB
- [사전 준비 1] 원격 공용 저장소 AWS S3 생성
# provider.tf
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "5.4.0"
}
}
required_version = ">= 1.4"
}
provider "aws" {}
# main.tf
resource "aws_s3_bucket" "main" {
bucket = var.bucket_name
tags = {
Name = "terraform test"
}
}
resource "aws_s3_bucket_versioning" "main" {
bucket = aws_s3_bucket.main.id
versioning_configuration {
status = "Enabled"
}
}
# variables.tf
variable "bucket_name" {
type = string
}
# terraform.tfvars
bucket_name = "<닉네임>-hello-t1014-remote-backend"
# 리소스 배포
terraform init && terraform apply -auto-approve
# 상태 파일에서 관리하는 리소스 목록 출력
terraform state list
# s3 리스트 확인
aws s3 ls
- [사전 준비 2] Locking을 위한 DynamoDB 테이블 생성
- DynamoDB를 사용하여 잠금을 구현하려면 LockID라는 기본 키가 있는 DynamoDB 테이블을 생성해야 합니다.
# main.tf
resource "aws_s3_bucket" "main" {
bucket = var.bucket_name
tags = {
Name = "terraform test"
}
}
resource "aws_s3_bucket_versioning" "main" {
bucket = aws_s3_bucket.main.id
versioning_configuration {
status = "Enabled"
}
}
resource "aws_dynamodb_table" "terraform_state_lock" {
name = "terraform-lock"
hash_key = "LockID"
billing_mode = "PAY_PER_REQUEST"
attribute {
name = "LockID"
type = "S"
}
}
# 리소스 배포
terraform init && terraform apply -auto-approve
# 상태 파일에서 관리하는 리소스 목록 출력
terraform state list
# 특정 리소스의 상세 정보 출력
terraform state show aws_dynamodb_table.terraform_state_lock
# DynamoDB 테이블 생성 확인
aws dynamodb list-tables --output text
aws dynamodb describe-table --table-name terraform-lock | jq
aws dynamodb describe-table --table-name terraform-lock --output table
- 테라폼 백엔드 설정 및 적용
# provider.tf
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "5.4.0"
}
}
backend "s3" {
bucket = "<닉네임>-hello-t1014-remote-backend"
key = "terraform/state-test/terraform.tfstate"
region = "ap-northeast-2"
dynamodb_table = "terraform-lock"
}
required_version = ">= 1.4"
}
provider "aws" {}
# main.tf
...
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr
tags = {
Name = "terraform VPC"
}
}
# variables.tf
...
variable "vpc_cidr" {
type = string
}
# terraform.tfvars
...
vpc_cidr = "192.168.0.0/16"
# 리소스 배포
terraform init && terraform apply -auto-approve
# 상태 파일에서 관리하는 리소스 목록 출력
terraform state list
# 현재 디렉토리 내에 상태 파일들 확인
ls *.tfstate
# AWS S3 버킷 내에 tfstate 파일 확인
MYBUCKET=<닉네임>-hello-t1014-remote-backend
aws s3 ls s3://$MYBUCKET --recursive --human-readable --summarize
- VPC 태그 수정 후 Locking 확인
# main.tf 수정
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr
tags = {
Name = "terraform VPC 2"
}
}
# apply 후 DynamoDB 테이블 확인
terraform apply
...
Enter a value: <입력하지 않고 대기>
- LockID 정보 확인 후 apply
- LockID : woo-hello-t1014-remote-backend/terraform/state-test/terraform.tfstate
- Info : {"ID":"e247b53f-3783-1268-93cd-494dc31a59f6","Operation":"OperationTypeApply","Info":"","Who":"DESKTOP-xxxx\\우인혁@DESKTOP-xxxx","Version":"1.8.5","Created":"2024-07-06T18:49:27.7582799Z","Path":"woo-hello-t1014-remote-backend/terraform/state-test/terraform.tfstate"}
- apply 완료 후 다시 DynamoDB 테이블 확인
- S3 버저닝 정보 확인
# main.tf 수정
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr
tags = {
Name = "terraform VPC 3"
}
}
# 리소스 배포
terraform apply -auto-approve
# S3 버킷에 파일 확인
aws s3 ls s3://$MYBUCKET --recursive --human-readable --summarize
# 버저닝된 파일 확인
aws s3api list-object-versions --bucket $MYBUCKET | egrep "Key|VersionId|LastModified"
- S3 버킷에 버전 표시 확인
테라폼 백엔드 단점
- backend 블록에서 변수나 참조를 사용할 수 없는 제약 때문에, S3 버킷 이름, 리전, DynamoDB 테이블 이름 등을 직접 입력해야 하는 불편함이 있습니다.
# 사용 불가
terraform {
backend "s3" {
bucket = var.bucket
region = var.region
dynamodb_table = var.dynamodb_table
key = "example/terraform.tfstate"
encrypt = true
}
}
- partial configuration 기능을 통해 일부 매개 변수를 전달하여 사용할 수 있습니다.
# backend.hcl
bucket = "terraform-up-and-running-state"
region = "us-east-2"
dynamodb_table = "terraform-up-and-running-locks"
encrypt = true
- 이 방법을 이용해도 모듈마다 고유한 key 값을 설정해야 하기 때문에 key 매개 변수는 테라폼 코드에 있어야 합니다.
terraform {
backend "s3" {
key = "example/terraform.tfstate"
}
}
- 각 모듈에 대해 backend 설정을 초기화하려면 terraform init 명령을 실행합니다. 이 때 -backend-config 인수를 사용하여 필요한 backend 매개 변수를 전달하면 됩니다.
terraform init -backend-config=backend.hcl
'Terraform > Terraform 101 Study' 카테고리의 다른 글
5주차 2편 테라폼 Runner (1) | 2024.07.14 |
---|---|
5주차 1편 테라폼 Module (1) | 2024.07.14 |
4주차 1편 테라폼 프로바이더 (0) | 2024.07.07 |
3주차 2편 테라폼 기초 - 기본 사용 3/3 (0) | 2024.06.30 |