5.State

5.1 State 목적과 의미

  • 상태 파일은 배포할 때마다 변경되는 프라이빗 API private API
  • 오직 테라폼 내부에서 사용하기 위한 것입니다.
  • 테라폼 상태 파일을 직접 편집하거나 직접 읽는 코드로 작성해서는 안된다

팀 단위에서 테라폼 운영 시 문제점

  • 상태 파일을 저장하는 공유 스토리지( Shared storage for state files)
    • 각 팀원이 동일한 테라폼 상태 파일 사용을 위해서, 공유 위치에 저장이 필요
  • 상태 파일 잠금 Locking state files
    • 잠금 기능 없이 두 팀원이 동시에 테라폼 실행 시 여러 테라폼 프로세스가 상태 파일을 동시에 업데이트하여 충돌 가능(경쟁 상태 race condition)
  • 상태 파일 격리 Isolating state files
    • 예를 들면 테스트 dev 와 검증 stage 과 상용 prodction 각 환경에 대한 격리가 필요

상태 파일 공유로 버전 관리 시스템 비추천

  1. 수동 오류 Manual error
    • 테라폼을 실행하기 전에 최신 변경 사항을 가져오거나 실행하고 나서 push 하는 것을 잊기 쉽습니다(?).
    • 팀의 누군가가 이전 버전의 상태 파일로 테라폼을 실행하고, 그 결과 실수로 이전 버전으로 롤백하거나 이전에 배포된 인프라를 복제하는 문제가 발생 할 수 있음.
  2. 잠금 Locking
    • 대부분의 버전 관리 시스템(VCS)은 여러 명의 팀 구성원이 동시에 하나의 상태 파일에 terraform apply 명령을 실행하지 못하게 하는 잠금 기능이 제공되지 않음.
  3. 시크릿 Secrets
    • 테라폼 상태 파일의 모든 데이터는 평문으로 저장됨. 민감 정보가 노출될 위험.

지원되는 원격 백엔드

  • AWS S3, Azure Blob Storage, Google Cloud Storage, Consul, Postgres database 등
  •  위 제약사항에 대한 해결방안!→ 링크 
  1. 수동 오류 해결 : plan/apply 실행 시 마다 해당 백엔드에서 파일을 자동을 로드, apply 후 상태 파일을 백엔드에 자동 저장
  2. 잠금(Lock) : apply 실행 시 테라폼은 자동으로 잠금을 활성화, `-lock-timout=<TIME>` 로 대기 시간 설정 지정 가능
  3. 시크릿 : 대부분 원격 백엔드는 기본적으로 데이터를 보내거나 상태 파일을 저장할 때 암호화(Encryption)하는 기능을 지원

책 내용

  • 소개 및 확인 : 테라폼은 Stateful 애플리케이션. 프로비저닝 결과 State를 저장하고 추적에 활용
    • 개인 1인 : 로컬 환경으로 terraform.tfstate 파일에 JSON 형태로 저장
    • 팀이나 조직 : 공동 관리를 위해 원격 저장소에 저장해 공유 - 링크
    • State에는 작업자가 정의한 코드와 실제 반영된 프로비저닝 결과를 저장하고, 이 정보를 토대로 이후의 리소스 생성, 수정, 삭제에 대한 동작 판단 작업을 수행
    State 역할
    • State에는 테라폼 구성과 실제를 동기화하고 각 리소스에 고유한 아이디(리소스 주소)로 맵핑
    • 리소스 종속성과 같은 메타데이터를 저장하고 추적
    • 테라폼 구성으로 프로비저닝 결과를 캐싱하는 역할을 수행

5.1 실습

resource "random_password" "mypw" {
  length           = 16
  special          = true
  override_special = "!#$%"
}

5.1 실행

terraform init && terraform plan
terraform apply -auto-approve

# State List 및 생성된 Password 확인
terraform state list
terraform state show random_password.mypw

# State에 저장된 result를 확인해보자! -> 어떻게 관리해야할까요?
ls *.tfstate
cat terraform.tfstate | jq
cat terraform.tfstate | jq | grep result
YLGmKyb3jOuI8sEf

# (참고) sensitive value 내용은 테라폼 콘솔에서 보일까요? 
echo "random_password.mypw" | terraform console
echo "random_password.mypw.result" | terraform console
  • 테라폼에서는 JSON 형태로 작성된 State를 통해 속성인수를 읽고 확인할 수 있다. 테라폼에서는 typename으로 고유한 리소스분류하며, 해당 리소스의 속성인수를 구성과 비교해 대상 리소스생성, 수정, 삭제한다.
  • State는 테라폼만을 위한 API로 정의할 수도 있다. Plan을 실행하면 암묵적으로 refresh 동작을 수행하면서 리소스 생성의 대상(클라우드 등)과 State를 기준으로 비교하는 과정을 거친다. 이 작업은 프로비저닝 대상의 응답 속도와 기존 작성된 State의 리소스 양에 따라 속도 차이가 발생한다. 대량의 리소스를 관리해야 하는 경우 Plan 명령에서 -refresh=false 플래그를 사용해 State를 기준으로 실행 계획을 생성하고, 이를 실행에 활용해 대상 환경과의 동기화 과정을 생략할 수 있다.
# 실행 계획 생성 시 저장되어 있는 State와 실제 형상을 비교하는 기본 실행
time terraform plan

# 실행 계획 생성 시 실제 형상과 비교하지 않고 실행 계획을 생성하는 -refresh=false 옵션
time terraform plan -refresh=false

5.2 State 동기화

  • 테라폼 구성과 State 흐름 : Plan 과 Apply 중 각 리소스에 발생할 수 있는 네 가지 사항, 아래 실행 계획 출력 기호와 의미기호 의미
    + Create
    - Destroy
    -/+ Replace
    ~ Updated in-place
    • Replace 동작은 기본값을 삭제 후 생성하지만 lifecycle의 create_before_destroy 옵션을 통해 생성 후 삭제 설정 가능
  • 유형 별 실습 + 문제상황 추가 : 테라폼 구성에 추가된 리소스와 State에 따라 어떤 동작이 발생하는지 다음 표로 살펴본다유형 구성 리소스 정의 State 구성 데이터 실제 리소스 기본 예상 동작
    1 있음     리소스 생성
    2 있음 있음   리소스 생성
    3 있음 있음 있음 동작 없음
    4   있음 있음 리소스 삭제
    5     있음 동작 없음
    6 있음   있음  

유형1 : 신규 리소스 정의 → Apply ⇒ 리소스 생성(처음부터 Terraform 코드로 작업)

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
cat terraform.tfstate | jq

terraform apply -auto-approve
ls *.tfstate

# iam 사용자 리스트 확인
aws iam list-users | jq

유형2 : 실제 리소스 수동 제거 → Apply ⇒ 리소스 생성

# 실제 리소스 수동 제거
aws iam delete-user --user-name mytest1
aws iam delete-user --user-name mytest2
aws iam list-users | jq

# 아래 명령어 실행 결과 차이는? (refresh 유무)
# 실제 리소스는 제거되었으나 -refrest=false로 인해 변경사항 없으므로 인식
terraform plan
terraform plan -refresh=false
cat terraform.tfstate | jq .serial

#
terraform apply -auto-approve
terraform state list
cat terraform.tfstate | jq .serial

# iam 사용자 리스트 확인
aws iam list-users | jq

유형3 : Apply → Apply - 코드, State, 형상 모두 일치한 경우 ⇒ 변경사항 없음

# Serial 값일 동일!
terraform apply -auto-approve
cat terraform.tfstate | jq .serial
terraform apply -auto-approve
cat terraform.tfstate | jq .serial
terraform apply -auto-approve
cat terraform.tfstate | jq .serial

유형4 : 코드에서 일부 리소스 삭제 → Apply ⇒ 리소스 삭제

locals {
  name = "mytest"
}

resource "aws_iam_user" "myiamuser1" {
  name = "${local.name}1"
}
#####################
# 코드 변경으로 인해 user2 삭제
terraform apply -auto-approve
terraform state list
terraform state show aws_iam_user.myiamuser1

ls *.tfstate
cat terraform.tfstate | jq

# iam 사용자 리스트 확인
aws iam list-users | jq

유형5 : 리소스만 있으며 코드, State는 없는 경우 → Import 또는 신규코드 작성

유형6 : 실수로 tfstate 파일 삭제 → plan/apply ← 책에는 없는 내용

# 실수로 tfstate 파일 삭제
rm -rf terraform.tfstate*

# 아래 두 명령 결과 차이는?
# Local에 State 파일이 없으므로 두 명령 모두 새로운 리소스 추가(add) 계획
terraform plan
terraform plan -refresh=false

# apply할 경우 어떤 결과 발생?
# 이미 리소스가 있으므로 Error 발생: EntityAlreadyExists: User with name mytest1 already exists.
terraform apply -auto-approve
terraform state list
cat terraform.tfstate | jq

# iam 사용자 리스트 확인
aws iam list-users | jq

# 다음 실습을 위해 iam user 삭제
aws iam delete-user --user-name mytest1

5.3 워크 스페이스

 

 

 

 

소개 : State관리하는 논리적인 가상 공간워크스페이스라고 한다 - 링크

  • CE와 Cloud/Enterprise에서 제공되는 Workspace의 기능을 비교해보자!Component Local Terraform Terraform Cloud/Enterprise
    Terraform configuration On disk In linked version control repository, or periodically uploaded via API/CLI
    Variable values As .tfvars files, as CLI arguments, or in shell environment In workspace
    State On disk or in remote backend In workspace
    Credentials and secrets In shell environment or entered at prompts In workspace, stored as sensitive variables(via Vault)
         
  • 테라폼 구성 파일은 동일하지만 작업자는 서로 다른 State를 갖는 실제 대상을 프로비저닝할 수 있다.
  • 워크스페이스는 기본 default로 정의된다. 로컬 작업 환경의 워크스페이스 관리를 위한 CLI 명령어로 workspace가 있다.
terraform workspace list
* default

실습

resource "aws_instance" "mysrv1" {
  ami           = "ami-0ea4d4b8dc1e46212"
  instance_type = "t2.micro"
  tags = {
    Name = "t101-week4"
  }
}

실행

# [분할/터미널1] 모니터링
export AWS_PAGER=""
while true; do aws ec2 describe-instances --query "Reservations[*].Instances[*].{PublicIPAdd:PublicIpAddress,InstanceName:Tags[?Key=='Name']|[0].Value,Status:State.Name}" --filters Name=instance-state-name,Values=running --output text ; echo "------------------------------" ; sleep 1; done

# [분할/터미널2] 배포
terraform init && terraform apply -auto-approve
terraform state list

#
cat terraform.tfstate | jq -r '.resources[0].instances[0].attributes.public_ip'
cat terraform.tfstate | jq -r '.resources[0].instances[0].attributes.private_ip'

# terraform.tfstate에 private 담긴 내용은?
cat terraform.tfstate | jq -r '.resources[0].instances[0].private' | base64 -d | jq

# 워크스페이스 확인
terraform workspace list

# graph 확인
terraform graph > graph.dot

신규 워크스페이스 생성 및 확인

# 새 작업 공간 workspace 생성 : mywork1
terraform workspace new mywork1
terraform workspace show

# 서브 디렉터리 확인 -> 향후 저장되는 State는 여기에!
tree terraform.tfstate.d
terraform.tfstate.d
└── mywork1

# plan 시 어떤 결과 내용이 출력되나요?
terraform plan

# apply 해보자!
terraform apply -auto-approve


# 워크스페이스 확인
terraform workspace list

# State 확인
cat terraform.tfstate | jq -r '.resources[0].instances[0].attributes.public_ip'
cat terraform.tfstate.d/mywork1/terraform.tfstate | jq -r '.resources[0].instances[0].attributes.public_ip'

# graph 확인
terraform graph > graph.dot


# (실습생략)새 작업 공간 workspace 생성 : mywork2
terraform workspace new mywork2

# 서브 디렉터리 확인
tree terraform.tfstate.d
...

# plan & apply
terraform plan && terraform apply -auto-approve
cat terraform.tfstate | jq -r '.resources[0].instances[0].attributes.public_ip'
cat terraform.tfstate.d/mywork1/terraform.tfstate | jq -r '.resources[0].instances[0].attributes.public_ip'
cat terraform.tfstate.d/mywork2/terraform.tfstate | jq -r '.resources[0].instances[0].attributes.public_ip'

# workspace 정보 확인
terraform workspace show
terraform workspace list

# 실습 리소스 삭제
terraform workspace select default
terraform destroy -auto-approve
terraform workspace select mywork1
terraform destroy -auto-approve
terraform workspace select mywork2
terraform destroy -auto-approve
  • 장점
    • 하나의 루트 모듈에서 다른 환경을 위한 리소스를 동일한 테라폼 구성으로 프로비저닝하고 관리
    • 기존 프로비저닝된 환경에 영향을 주지 않고 변경 사항 실험 가능
    • 깃의 브랜치 전략처럼 동일한 구성에서 서로 다른 리소스 결과 관리
      [참고 : 화해 - Git 브랜치 전략 수립을 위한 전문가의 조언들]
  • 단점
    • State가 동일한 저장소(로컬 또는 백엔드)에 저장되어 State 접근 권한 관리가 불가능(어려움)
    • 모든 환경이 동일한 리소스를 요구하지 않을 수 있으므로 테라폼 구성에 분기 처리가 다수 발생 가능
    • 프로비저닝 대상에 대한 인증 요소를 완벽히 분리하기 어려움⇒ 해결방안 1. 해결하기 위해 루트 모듈을 별도로 구성하는 디렉터리 기반의 레이아웃을 사용할 수 있다. ⇒ 해결방안 2. Terraform Cloud 환경의 워크스페이스를 활용
    • → 가장 큰 단점은 완벽한 격리가 불가능
블로그 이미지

감동맨

rkaehdaos의 블로그

,