본문 바로가기
  • lakescript
스터디 이야기/Terraform

[T101] 4-2. Terraform - State

by lakescript 2024. 7. 4.

 

더보기

이 스터디는 CloudNet@에서 진행하는 T101 스터디를 참여하면서 공부하는 내용을 기록하는 블로그 포스팅입니다.

CloudNet@에서 제공해주는 자료들과 테라폼으로 시작하는 IaC 를 바탕으로 작성되었습니다.

 

State

테라폼은 state(상태)가 있는 stateful 애플리케이션입니다. 프로비저닝 결과에 따른 State를 저장하고 프로비저닝한 모든 내용을 저장된 상태로 추적합니다. 로컬 실행 환경에서는 terraform.tfstate 파일에 JSON 형태로 저장되고, 팀이나 조직에서의 공동 관리를 위해서는 원격 저장소에 저장하여 공유하는 방식으로 활용합니다. state에는 작업자가 정의한 코드와 실제 반영된 프로비저닝 결과를 저장하고, 이 정보를 토대로 리소스 생성/리소스 수정/리소스 삭제에 대한 작업을 수행합니다.

 

State의 목적과 의미

코드 변경 후 terraform apply 명령을 실행하면 이전에 생성된 리소스와 비교하여 생성하거나 수정하거나 삭제하는 동작이 수행되었습니다. 테라폼은 State를 통해 대상 환경에서 어떤 리소스가 테라폼으로 관리되는 리소스인지 판별하고 결과를 저장합니다. 

State의 역할

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

State 동작 확인을 위한 테라폼 구성 예제(use random provider)

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

위의 코드와 같이 main.tf를 작성하고 terraform apply 명령어를 실행 후 생성되는 terraform.tfstate를 확인합니다.

 

{
  "version": 4,
  "terraform_version": "0.14.11",
  "serial": 1,
  "lineage": "194c089d-26a6-44bd-6638-26e89a449ce2",
  "outputs": {},
  "resources": [
    {
      "mode": "managed",
      "type": "random_password",
      "name": "mypw",
      "provider": "provider[\"registry.terraform.io/hashicorp/random\"]",
      "instances": [
        {
          "schema_version": 3,
          "attributes": {
            "bcrypt_hash": "$2a$10$FhYFH3FKqZNWx9.t1DMvaurjqhsxEYloEoJNqniZme3xHZ/QbxGIe",
            "id": "none",
            "keepers": null,
            "length": 16,
            "lower": true,
            "min_lower": 0,
            "min_numeric": 0,
            "min_special": 0,
            "min_upper": 0,
            "number": true,
            "numeric": true,
            "override_special": "!#$%",
            "result": "%%1dqqmXKdzmmGpr",
            "special": true,
            "upper": true
          },
          "sensitive_attributes": []
        }
      ]
    }
  ]
}

 

테라폼에서는 이 JSON 형태로 작성된 State 파일을 통해 속성과 인수를 읽고 확인할 수 있습니다. 테라폼에서는 type과 name으로 고유한 리소스를 분류하며, 해당 리소스의 속성과 인수를 구성과 비교하여 리소스를 생성, 수정, 삭제 합니다.

 

State는 테라폼만을 위한 API로 정의할 수도 있습니다.

# 실행 계획 생성 시 저장되어 있는 State와 실제 형상을 비교하는 기본 실행
time terraform plan

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

Plan을 실행하면 암묵적으로 refresh 동작을 수행하면서 리소스 생성의 대상과 State를 기준으로 비교하게 됩니다. 이 작업은 프로비저닝 대상의 응답속도와 기존 작성된 State의 리소스 양에 따라 속도 차이가 발생합니다. 대향의 리소스를 관리해야 하는 경우 Plan 명령에서 -refresh=false 플래그를 사용하여 State를 기준으로 실행 계획을 실행하고, 이를 실행에 활용하여 대상 환경과 동기화 과정을 생략할 수 있습니다. 

 

State 동기화

테라폼 구성과 State 흐름

테라폼 구성 파일은 기존 State와 구성을 비교하여 실행 계획에서 생성, 수정, 삭제 여부를 결정합니다.

 

실행 계획 출력 기호와 의미

기호 의미
+
Create
- Destroy
-/+
Replace
~
Update in-place

Replace 동작은 기본값을 삭제 후 생성하지만 lifecycle의 create_before_destory 옵션을 통해 생성 후 삭제를 수행하도록 설정할 수 있습니다.

 

코드상 리소스 정의, State에 리소스 정의,실제 리소스 존재 여부에 따른 동작

유형 구성 리소스 정의 State의 구성 데이터 실제 리소스 기본 예상 동작
1 있음     리소스 생성
2 있음 있음   리소스 생성
3 있음 있음 있음 동작 없음
4   있음 있음 리소스 삭제
5     있음 동작 없음

유형 1

테라폼 구성 파일에 신규 리소스를 정의하고 Apply를 수행하면 State에 없는 리소스이므로 생성 작업을 수행합니다. 

resource "local_file" "foo"{
    content = "foo"
    filename = "${path.module}/foo.txt"
}

 

 

유형2

테라폼 구성 파일에 리소스가 있고 State에도 관련 구성 내용이 있지만 실제 리소스가 없는 경우 생성 작업을 수행합니다. 이 같은 상황은 테라폼으로 프로비저닝을 완료했지만 사용자가 수동으로 인프라를 삭제하는 경우에도 해당됩니다. 위에서 생성된 실제 리소스인 foo.txt 파일을 강제로 삭제하고 다시 terraform apply를 진행해보겠습니다.

실행 계획 작성을 위해 실제 리소스와의 Refresh 동작을 수행하여 State와 비교합니다. 실제 리소스를 삭제한 후 terraform plan, apply를 실행하면 다시 생성될 것이므로 실행 계획을 보여줍니다.

주의할 점은 CI/CD 설계시에 앞서 확인한 -refresh=false 인수를 추가하여 plan을 실행한 경우 State만을 확인하므로 이미 생성되었다고 판단하여 실제 리소스를 다시 만드는 작업은 발생하지 않습니다.

 

유형 3

테라폼 구성에 정의된 리소스로 생성된 프로비저닝 결과가 State에 있고 실제 리소스도 있는 경우라면 테라폼은 관련 리소스에 대한 변경 계획을 발생시키지 않습니다. 유형 2에 다시 terraform apply를 실행하여 프로비저닝 결과가 있는 경우 terraform으로 실행 계획을 생성하면 변경 사항은 없습니다. 코드와 State, 형상 모두가 일치하는 상태입니다.

 

 

유형 4

구성, State, 실제 리소스가 있는 상태에서 테라폼에서 정의한 리소스 구문을 삭제하면 사용자는 의도적으로 해당 리소스를 삭제하는 것입니다. 테라폼은 구성 파일을 기준으로 State와 비교하여 삭제된 구성을 실제 리소스에서 제거합니다.

# resource "local_file" "foo"{
#     content = "foo"
#     filename = "${path.module}/foo.txt"
# }

위에서 작성했었던 main.tf 파일의 내용을 모두 주석처리하고 terraform plan을 실행하면 없어진 코드 선언을 삭제하려는 작업을 수행하는 계획을 수행합니다.

 

유형 5

이미 만들어진 리소스만 있다면 테라폼의 State에 없는 내용이므로 테라폼으로 관리되지 않습니다. 따라서 해당 리소스에 대해서는 아무 작업도 수행할 수 없습니다.

  • 처음부터 테라폼으로 관리되지 않는 리소스인 경우
  • 테라폼으로 생성하고 구성과 State가 삭제된 경우

워크스페이스

State를 관리하는 가장 논리적인 가상 공간을 워크스페이스라고 합니다. 테라폼 구성 파일은 동일하지만 작업자는 서로 다른 State를 갖는 실제 대상으로 프로비저닝할 수 있습니다. 워크스페이스는 기본 default로 정의됩니다. 

다중 환경 프로비저닝 워크플로를 단일 테라폼으로 구성

terraform [global options] workspace

 

사용중인 워크스페이스 확인을 위해 terraform workspace list 명령으로 확인해보면 기본 default외에 다른 워크스페이스는 없고 사용중인 워크스페이스임을 나타내기 위해 앞에 * 기호가 붙어있습니다.

 

워크스페이스 관리 실습

State가 워크스페이스상에서 관리되는 방식을 확인해보겠습니다.

#AWS 프로바이더 선언 생략
resource "aws_instance" "mysrv1" {
  ami           = "ami-0ea4d4b8dc1e46212"
  instance_type = "t2.micro"
  
  tags = {
    Name = "t101-study"
  }
}

위와 같이 코드를 작성한 후 terraform init 후 terraform apply를 진행해보겠습니다.

(사진에서 destroy는 위에서 실행했던 terraform 파일에 대한 삭제입니다 ㅎ.. 같은 파일에서 수정하여 진행했습니다.)

 

위와 같이 생성된 것을 확인했으면 terraform plan을 다시 수행해보겠습니다.

구성에 변경이 없고 State의 리소스 구성 정보가 같으므로 변경사항이 없다는 메시지를 출력할 것입니다. (AWS 자격증명이 필요합니다!)

 

terraform workspace new workspace1

 

workspace를 생성해보겠습니다.

 

새로운 워크스페이스가 생성되면 실행한 root module 디렉토리에 terraform.tfstate.d 디렉토리가 생성되고 하위에 생성한 워크스페이스 이름이 있는 것을 확인하실 수 있습니다.

 

그 후 Plan을 실행해보겠습니다.

새로 생성한 워크스페이스에서는 기존 default 워크스페이스에서 관리하는 State와는 독립된 정보를 갖기 때문에 앞서 정의한 테라폼 구성의 리소스를 다시 생성하겠다고 출력합니다.

 

 

이렇게 workspace를 구분하면 동일한 구성에서 기존 인프라에 영향을 주지 않으면서 간편하게 테라폼 프로비저닝을 테스트하고 확인할 수 있습니다. 

워크스페이스 이름을 조건으로 하여 코드 구성

resource "aws_instance" "mysrv1" {
  count			= "${terraform.workspace == "default" ? 5:1 }"
  ami           = "ami-0ea4d4b8dc1e46212"
  instance_type = "t2.micro"
  tags = {
    Name = "t101-study",
    Contents = "HelloWorld! in ${terraform.workspace}"
  }
}

 테라폼 구성에서 terraform.workspace를 사용하여 워크스페이스 이름을 읽으면 워크스페이스 기준으로 문자열을 지정하거나 조건을 부여할 수 있습니다.

 

 

워크스페이스 삭제

terraform workspace delete workspace1

 

워크스페이스 장점

  • 하나의 루트 모듈에서 다른 환경을 위한 리소스를 동일한 테라폼 구성으로 프로비저닝하고 관리
  • 기존 프로비저닝된 환경에 영향을 주지 않고 변경 사항 실험 가능
  • 깃의 브랜치 전략처럼 동일한 구성에서 서로 다른 리소스 결과 관리

워크스페이스 단점

  • State가 동일한 저장소(로컬 또는 백엔드)에 저장되어 State 접근 권한 관리가 불가능
  • 모든 환경이 동일한 리소스를 요구하지 않을 수 있으므로 테라폼 구성에 분기 처리가 다수 발생 가능
  • 프로비저닝 대상에 대한 인증 요소를 완벽히 분리하기 어려움

 

워크스페이스의 가장 큰 단점은 완벽한 격리가 불가능하다는 점이다. 운영 환경을 위한 구성 또는 다수의 구성원이 테라폼으로 프로비저닝을 하는 상황에서 문제가 생길 수 있다. 시스템적으로 보완하기 위해 Terraform Cloud 환경의 워크스페이스를 활용하는 것을 권장합니다.