본문 바로가기
Tech/Terraform

[T101_3기] 4주차 - State & 모듈 & 협업 (1/3) - State

by 구름_쟁이 2023. 9. 10.

본 시리즈는 가시다님의 T101(테라폼으로 시작하는 IaC) 3기 진행 내용입니다. (가시다님 노션)

 

도서 정보

https://www.yes24.com/Product/Goods/119179333

 

테라폼으로 시작하는 IaC - 예스24

“현업에서 요구하는 진짜 IaC 사용법”테라폼으로 배우는 인프라 운영의 모든 것IaC는 효율적인 데브옵스와 클라우드 자동화 구축을 위해 꼭 필요한 기술로 각광받고 있다. 그중에서도 테라폼

www.yes24.com

실습 코드

https://github.com/terraform101

 

목차

1. State 목적과 의미, State 동기화
2. 모듈
3. 협업


 

 


1. State 목적과 의미

 

State의 목적과 의미

상태 파일 확인 실습 → Serial을 기준으로 State backup 관리

cat <<EOT > vpc.tf
provider "aws" {
  region  = "ap-northeast-2"
}

resource "aws_vpc" "myvpc" {
  cidr_block       = "10.10.0.0/16"

  tags = {
    Name = "t101-study"
  }
}
EOT
# 배포
terraform init && terraform plan && terraform apply -auto-approve

# 상태 파일 확인 : JSON 형식
ls
cat terraform.tfstate | jq | grep serial
...
"serial": 1,
...

  • 태그 수정 후 상태 파일 확인
cat <<EOT > vpc.tf
provider "aws" {
  region  = "ap-northeast-2"
}

resource "aws_vpc" "myvpc" {
  cidr_block       = "10.10.0.0/16"

  tags = {
    Name = "tf-state"
  }
}
EOT
# 배포 : plan 시 tfstate 상태와 코드 내용을 비교해서 검토
terraform plan && terraform apply -auto-approve

# 상태 파일 비교 : 백업 파일 생성됨
ls terraform.tfstate*
terraform.tfstate        terraform.tfstate.backup

diff terraform.tfstate terraform.tfstate.backup
<   "serial": 3,
---
>   "serial": 1,
...
    • 한번 더 태그 수정 후 상태 파일 확인

.backup 파일이 추가된 것 확인.

 

한번 더 변경하게 되면?

cat <<EOT > vpc.tf
provider "aws" {
  region  = "ap-northeast-2"
}

resource "aws_vpc" "myvpc" {
  cidr_block       = "10.10.0.0/16"

  tags = {
    Name = "tf-state-tag-change"
  }
}
EOT
# 배포
terraform plan && terraform apply -auto-approve

# 상태 파일 비교(바로 직전 상태 백업)
ls terraform.tfstate*
terraform.tfstate        terraform.tfstate.backup

diff terraform.tfstate terraform.tfstate.backup
<   "serial": 5,
---
>   "serial": 3,
...

바로 직전 상태를 항상 .backup으로 보관한다.

  • 다음 실습을 위해 삭제: terraform destroy -auto-approve

 

이론

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

 

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

  1. 상태 파일을 저장하는 공유 스토리지 Shared storage for state files
    • 각 팀원이 동일한 테라폼 상태 파일 사용을 위해서, 공유 위치에 저장이 필요
  2. 상태 파일 잠금 Locking state files
    • 잠금 기능 없이 두 팀원이 동시에 테라폼 실행 시 여러 테라폼 프로세스가 상태 파일을 동시에 업데이트하여 충돌 가능(경쟁 상태 race condition)
  3. 상태 파일 격리 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)하는 기능을 지원

 

다양한 State Backend - [참고: Docs]

출처 : https://medium.com/devops-mojo/terraform-remote-states-overview-what-is-terraform-remote-state-storage-introduction-936223a0e9d0

 

Available Backends (링크 - 들어가서 좌측 사이드 바)

  • local
  • remote : Terraform Cloud
  • azurerm
  • consul
  • cos
  • gcs
  • http
  • Kubernetes
  • oss
  • pg
  • s3

 

소개 및 확인 : 테라폼은 Stateful 애플리케이션. 프로비저닝 결과 State를 저장하고 추적에 활용

  • 개인 1인 : 로컬 환경으로 terraform.tfstate 파일에 JSON 형태로 저장
  • 팀이나 조직 : 공동 관리를 위해 원격 저장소에 저장해 공유 - 링크
  • State에는 작업자가 정의한 코드와 실제 반영된 프로비저닝 결과를 저장하고, 이 정보를 토대로 이후의 리소스 생성, 수정, 삭제에 대한 동작 판단 작업을 수행

State 역할

  • State에는 테라폼 구성과 실제를 동기화하고 각 리소스에 고유한 아이디(리소스 주소)로 맵핑
  • 리소스 종속성과 같은 메타데이터를 저장하고 추적
  • 테라폼 구성으로 프로비저닝 결과를 캐싱하는 역할을 수행

 

    • 실습을 위해서 5.1 디렉터리를 신규 생성 후 열기 → main.tf 파일 생성 - random_provider password
resource "random_password" "mypw" {
  length           = 16
  special          = true
  override_special = "!#$%"
}
    • 실행 : 랜덤 프로바이더는 테라폼 구성 내에서 무작위로 기입해야 되는 숫자, 패스워드, 문자열 등의 값을 생성하는 데 사용
# 
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 내용은 테라폼 콘솔에서 보일까요? (정답: x)
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

값을 변경한 경우

 

 

 

 


2. State 동기화

 

소개 : 테라폼 구성 파일은 기존 State와 구성을 비교해 실행 계획에서 생성, 수정, 삭제 여부를 결정한다

https://kschoi728.tistory.com/135

  • 테라폼 구성과 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 코드로 작업)

    • 실습을 위해서 5.2 디렉터리를 신규 생성 후 열기 → 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
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

refresh=false 옵션을 사용하는 경우 동기화 과정을 생략했기에 기존에 코드로 배포되었던 것(state)들은 있다치고~ 넘어가는 것이다.

그럼 궁금한게 여기서 새로운 3이라는 리소스를 추가한다면?

 

이러면 우리 refresh=false 옵션도 친히 새로운 리소스를 만들어야 한다고 일러준다.

 

 

유형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

같은 state인 경우 serial이 증가하지 않는다!

 

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

    • main.tf 파일 수정
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

코드상에서 리소스 삭제하고 apply 하게 되면 삭제된 내용대로 확인할 수 있다.

 

유형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
더보기

🤔 위 상황에서 복구 하는 방법은? import 등 방법이 있습니다!

이런 끔찍한 일이 현실(현업)에서 일어난다면 어떻게 해야할까...

여기에 적힌 import 방법 외에는 어떤 것들이 있는지 알아보자.

 

1. Terraform State Recovery

파일을 복구할 수 있는 백업이 있다면 복원한다.

첫 번째로 terraform.tfstate.backup 이 될 수 있겠다.

두 번째로는 git 기반으로 관리해두고 CI/CD 까지 구축해놨다면 가장 최근에 사용한 state 파일을 다시 복원할 수 있다.

(생각해보니 CD에서 apply를 하고나서의 state 파일 변경되는 내용을 git push 해야하겠다.)

 

2. Manual Resource Deletion and Terraform Apply

수동으로 리소스를 삭제하고, Terraform Apply를 다시 실행하여 리소스를 다시 생성할 수 있다. 그러나 이 방법은 downtime을 유발할 수 있다.

(차라리 import를 쓰는 게 더 나을 수도 있겠다...)

3. Using Remote Backends

리모트 백엔드를 사용하여 terraform.tfstate 파일을 클라우드에 저장하면, 이러한 문제를 방지할 수 있다. AWS S3, Azure Blob Storage 등 다양한 옵션이 있다.
(현업이라면 state 파일만이라도 꼭 Remote Backend를 사용해야겠다)

 

 

 

결론

 

이번 실습에서는 기존에 사용해보지 못했던 refresh=false 옵션에 대해서 좀 더 알아 보았다.

 

refresh=false 옵션을 사용하는 경우:

1. 빠른 실행을 원할 때:
리소스가 많은 경우 Terraform이 인프라의 현재 상태를 확인하는 것은 시간이 걸릴 수 있다. 이 때 refresh=false를 사용하면 이 시간을 단축할 수 있다.

2. API Rate Limits:
클라우드 프로바이더들은 대부분 API 요청에 대한 Rate Limit를 설정해두기 때문에, 자주 terraform plan을 실행하다 보면 Rate Limit에 걸릴 수 있다. 이럴 때 refresh=false 옵션을 사용하면 API 요청 수를 줄일 수 있다.

3. 인프라의 현재 상태에 대한 불확실성이 있을 때:
경우에 따라 실제 인프라의 상태가 Terraform의 state 파일과 다를 수 있다. 이 경우, refresh=false 옵션을 사용하여 Terraform이 실제 인프라의 상태를 확인하지 않도록 할 수 있다.

4. 네트워크 접속이 제한된 환경에서 실행할 때:
인터넷 연결이 제한적인 환경에서 Terraform을 실행해야 할 경우, refresh=false를 사용하여 Terraform이 외부 리소스의 상태를 체크하려고 시도하는 것을 방지할 수 있다.

댓글