본문 바로가기

Infrastructure/Terraform

[Terraform] 기본 개념

Terraform Basic

이번 장에서는 "Terraform"이 무엇인지 알아보고 기본적은 사용법에 대해서 알아본다.
모든 소스 코드는 리포지토리에 올려두었다.


Terraform의 코드형 인프라란?

  • 코드형 인프라(IaC) 도구를 사용하면 GUI(그래픽 사용자 인터페이스)가 아닌 구성 파일로 인프라를 관리할 수 있다.

  • IaC를 사용하면 버전화, 재사용 및 공유할 수 있는 리소스 구성을 정의하여 안전하고 일관되며 반복 가능한 방식으로 인프라를 구축, 변경 및 관리할 수 있다.

  • "Terraform"은 "HashiCorp"의 인프라 코드 도구로, 사람이 읽을 수 있는 선언적 구성 파일에서 리소스와 인프라를 정의하고 인프라의 수명 주기를 관리할 수 있다.

  • "Terraform"을 사용하면 수동으로 인프라를 관리하는 것보다 몇 가지 이점이 있으며 대표적인 이점은 아래와 같다.

    • 여러 클라우드 플랫폼에 적용하여 인프라를 관리할 수 있다.
    • 사람이 읽을 수 있는 구성 언어는 인프라 코드를 빠르게 작성하는 데 도움이 된다.
    • "Terraform"의 상태를 사용하면 배포 전체에서 리소스 변경 사항을 추적할 수 있다.
    • 버전 제어에 대한 구성을 커밋하여 인프라에서 안전하게 협업할 수 있다.

인프라 관리

  • 공급자라고 하는 "Terraform Plugin"을 사용하면 "Terraform API"를 통해 클라우드 플랫폼 및 기타 서비스와 상호작용할 수 있다.
  • "HashiCorp" 및 "Terraform 커뮤니티"는 AWS, Azure, GCP, K8S, Helm 등에서 리소스를 관리하기 위해 1,000개 이상의 공급자를 작성하였다.
  • Terraform Registry에서 작성되어 있는 공급자를 찾을 수 있으며, 공급자를 찾지 못한 경우 직접 작성할 수 있다.

배포 워크플로 표준화

  • 공급자는 컴퓨팅 인스턴스 또는 사설 네트워크와 같은 개별 인프라 단위를 리소스로 정의한다.
  • 다양한 공급자의 리소스를 모듈이라는 재사용 가능한 Terraform 구성으로 구성하고 일관된 언어 및 워크플로로 관리할 수 있다.
  • "Terraform"의 구성 언어는 선언적으로, 작업을 수행하기 위해 단계별 지침이 필요한 절차적 프로그래밍 언어와 달리 인프라에 대해 원하는 최종 상태를 설명한다.
  • "Terraform 공급자"는 리소스 간의 종속성을 자동으로 계산하여 올바른 순서로 리소스를 생성하거나 삭제한다.

  • 아래는 "Terraform"에서 사용되는 기본적인 개념이다.

    • Scope: 프로젝트의 인프라를 식별한다.
    • Author: 인프라에 대한 구성을 작성한다.
    • Initialize: "Terraform"이 인프라를 관리하는 데 필요한 플러그인을 설치한다.
    • Plan: 구성(configuration)과 일치하도록 "Terraform"이 수행할 변경 사항을 미리 확인한다.
    • Apply: 계획된 변경 사항을 적용한다.

인프라 추적

  • "Terraform"을 사용하면 원격 상태 백엔드로 인프라에서 협업할 수 있다.
  • "Terraform Cloud"를 사용하면 팀원과 상태를 안전하게 공유하고 "Terraform"이 실행할 수 있는 안정적인 환경을 제공하며 여러 사람이 한 번에 구성을 변경할 때 경합 상태를 방지할 수 있다.
  • "Terraform Cloud"를 GitHub, GitLab 등과 같은 버전 제어 시스템(VCS)에 연결하여 VCS에 대한 구성 변경 사항을 커밋할 때 인프라 변경 사항을 자동으로 제안할 수 있다.

Terraform 설치

Mac OS 환경에서 Terraform 설치 방법에 대해서 알아본다. 다른 OS에서의 설치 방법은 "참고 자료"의 "Terraform 설치"를 참고하도록 한다.

  • "Homebrew"패키지의 저장소인 "HashiCorp" 탭을 설치한다.
$ brew tap hashicorp/tap
  • hashicorp/tap/terraform을 설치한다.
$ brew install hashicorop/tap/terraform
  • 이렇게 설치가 완료된 경우 서명된 바이너리(signed binary)가 설치되고 새로운 공식 릴리즈가 출시될 때마다 자동으로 업데이트 된다.
  • 만약 수동으로 업데이트하고 싶은 경우 아래와 같이 업데이트가 가능하다.
# Homebrew 업데이트
$ brew update
# Terraform 업그레이드
$ brew upgrade hashicorp/tap/terraform

탭 완성 활성화

Mac OS, Zsh 환경에서 "Terraform" 명령에 대해 탭 완성을 활성화 하는 방법에 대해서 알아본다. 다른 환경이라면 "참고 자료"의 "Terraform 설치"를 참고하도록 한다.

$ touch ~/.zshrc
# 자동 완성 패키지 설치
$ terraform -install-autocomplete

인프라 구축

  • "AWS 클라우드"에 "EC2 인스턴스"를 생성하는 방법에 대해서 알아본다.

main.tf

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.16"
    }
  }

  required_version = ">= 1.2.0"
}

provider "aws" {
  region = "ap-northeast-2"
}

resource "aws_instance" "test_server" {
  ami           = "ami-0fae1a46f783f1bb3"
  instance_type = "t2.micro"

  tags = {
    Name = "Instance-For-Terraform-Test"
  }
}

테라폼 블록(Terraform Block)

  • terraform{} 블록에는 "Terraform"이 인프라를 프로비저닝하는 데 사용할 필수 공급자(Provider)를 포함하여 "Terraform" 설정이 포함되어 있다.
  • 각 공급자(Provider)에 대해 source 속성은 선택적 호스트 이름, 네임스페이스 및 제공자 유형을 정의하고, "Terraform"은 기본적으로 "Terraform 레지스트리"에서 공급자를 설치한다.
  • 예시에서 aws 공급자의 소스는 hashicorp/aws로 정의되고, registry.terraform.io/hashicorp/aws와 같은 의미가 된다.
  • required_providers 블록에 정의된 각 공급자에 대한 버전 제약 조건을 설정할 수 있다.
  • version 속성은 선택 사항이지만 "Terraform"이 사용자의 구성과 작동하지 않는 공급자 버전을 설치하지 않도록 공급자 버전을 제한하기 위해 사용하는 것이 권장된다.
  • 공급자 버전을 지정하지 않으면 "Terraform"은 초기화 중에 최신 버전을 자동으로 다운로드한다.

공급자(Provider)

  • 예시에서 aws로 지정된 것처럼, provider 블록은 지정된 공급자(Provider)를 구성하며, 공급자는 "Terraform"이 리소스를 만들고 관리하는 데 사용하는 플러그인이다.
  • "Terraform" 구성에서 여러 공급자 블록을 사용하여 다른 공급자의 리소스를 관리할 수 있으며, 다른 공급자를 함께 사용할 수도 있다.

자원(Resource)

  • resource 블록을 사용하여 인프라의 구성 요소를 정의한다.
  • "리소스"는 EC2 인스턴스와 같은 물리적 또는 가상의 구성 요소이거나 "Heroku" 애플리케이션과 같은 논리적 리소스일 수 있다.
  • resource 블록에는 "리소스 유형"과 "이름"이라는 두 개의 문자열이 있다.
  • 예시에서 "리소스 유형"은 aws_instance이고, "이름"은 test_server다.
  • "Terraform"은 공급자를 사용하여 aws_instance 리소스를 관리하고, aws "리소스 유형"과 "리소스 이름"은 조합되어 리소스의 고유한 ID를 구성한다.
  • 예시에서 생성되는 EC2 인스턴스의 ID는 aws_instance.test_server다.

디렉터리 초기화(Initialize the directory)

  • 새 구성을 생성하거나 버전 제어에서 기존 구성을 체크아웃할 때 terraform init 커맨드를 사용한다.
  • 구성 디렉터리를 초기화하면 구성에 정의된 공급자를 다운로드하여 설치한다.
$ terraform init

Initializing the backend...

Initializing provider plugins...
- Finding hashicorp/aws versions matching "~> 4.16"...
- Installing hashicorp/aws v4.17.0...
- Installed hashicorp/aws v4.17.0 (signed by HashiCorp)

Terraform has created a lock file .terraform.lock.hcl to record the provider
selections it made above. Include this file in your version control repository
so that Terraform can guarantee to make the same selections by default when
you run "terraform init" in the future.

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.
  • 예시에서 "Terraform"은 aws 공급자를 다운로드하여 현재 작업 디렉토리의 숨겨진 하위 디렉토리인 .terraform에 설치한다.
  • terraform init 명령은 설치된 공급자 버전을 출력하며, "Terraform"은 .terraform.lock.hcl이름의 잠긴 파일을 생성하는데 해당 파일에는 사용된 정확한 공급자의 버전이 포함된다.

구성 형식 지정 및 유효성 검사(Format and validate the configuration)

  • 모든 구성 파일에서 일관된 형식을 사용하는 것이 좋다.
  • terraform fmt 커맨드는 가독성과 일관성을 위해 현재 디렉터리의 구성을 자동으로 업데이트한다.
$ terraform fmt
  • terraform validate 커맨드를 사용하여 구성이 구문적으로 유효하고 내부적으로 일관성이 있는지 확인할 수 있다.
$ terraform validate
Success! The configuration is valid.

인프라 만들기

  • terraform apply 커맨드를 사용하여 구성 파일을 적용할 수 있다.
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # aws_instance.test_server will be created
  + resource "aws_instance" "test_server" {
      + ami                                  = "ami-0fae1a46f783f1bb3"
      + arn                                  = (known after apply)
      + associate_public_ip_address          = (known after apply)
      + availability_zone                    = (known after apply)
      + cpu_core_count                       = (known after apply)
# ...

Plan: 1 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: 
  • "변경 사항"을 적용하기 전에 "Terraform"은 구성과 일치하도록 인프라를 변경하기 위해 "Terraform"이 수행할 작업을 설명하는 실행 계획을 출력한다.
  • 출력 형식은 "Git"과 같은 도구에서 생성된 "diff" 형식과 유사하다.
  • 출력의 + 옆에 aws_instance.test_server가 있으며 이것은 "Terraform"이 해당 리소스를 생성하는 것을 의미한다.
  • (known after apply)인 경우 리소스가 생성될 때까지 값을 알 수 없음을 의미한다.
    예를 들어, AWS는 리소스를 생성할 때 ARN(Amazon Resource Name)을 할당하므로 "Terraform"은 "AWS API"가 해당 값을 반환할 때까지 속성값을 알 수 없다.

상태 검사(Inspect state)

  • 구성을 적용하면 "Terraform"은 terraform.tfstate라고 하는 파일에 데이터를 저장한다.
  • "Terraform"은 ID와 속성을 terraform.tfstate 파일에 저장하므로 해당 리소스를 업데이트하거나 삭제할 수 있다.
  • "Terraform 상태 파일"은 "Terraform"이 관리하는 리소스를 추적할 수 있는 유일한 방법이며, 민감한 정보를 포함할 수 있으므로 신뢰할 수 있는 팀 구성원만 액세스할 수 있도록 제한해야 한다.
  • 운영 환경에서는 "Terraform Cloud" 또는 "Terraform Enterprise"를 사용하여 상태를 원격으로 저장하는 것이 좋다.
  • "Terraform"은 상태를 저장하고 관리하는 데 사용할 수 있는 다른 원격 백엔드도 지원한다.
  • terraform show 커맨드를 사용하여 현재 상태를 검사할 수 있다.
$ terraform show
# aws_instance.app_server:
resource "aws_instance" "test_Server" {
    ami                          = "ami-0fae1a46f783f1bb3"
    arn                          = "arn:aws:ec2:ap-northeast-2:561656980159:instance/i-01e03375ba238b384"
    associate_public_ip_address  = true
    availability_zone            = "ap-northeast-2a"
# ...
    root_block_device {
        delete_on_termination = true
        device_name           = "/dev/sda1"
        encrypted             = false
        iops                  = 0
        tags                  = {}
        throughput            = 0
        volume_id             = "vol-031d56cc45ea4a245"
        volume_size           = 8
        volume_type           = "standard"
    }
}
  • "Terraform"이 EC2 인스턴스를 생성할 때 AWS 공급자로부터 리소스의 메타데이터도 수집하고 메타데이터를 상태 파일에 기록한다.

수동으로 상태 관리

  • "Terraform"의 기본 제공 커맨드인 terraform state를 사용하여 고급 상태 관리를 사용할 수 있다.
  • 아래와 같이 list 옵션을 사용하여 프로젝트 상태의 리소스를 나열할 수 있다.
$ terraform state list
aws_instance.test_Server

문제 해결 (Troubleshooting)

  • terraform validate 커맨드의 결과는 성공으로 출력되지만 실제로 적용할 때 오류가 발생한다면 아래와 같은 원인을 추측해볼 수 있다.

    • ap-northeast-2 이외의 리전을 사용하는 경우: AMD ID의 경우 리전별로 다르기 때문에 사용하는 리전에 맞는 AMD ID를 선택해야 한다.
    • AWS 계정에 기본 VPC가 없는 경우: 웹 UI에서 AWS VPC 대시보드로 이동하여 해당 리전에서 새 VPC를 생성하고 해당 VPC에 서브넷 및 보안 그룹을 연결한다.
      이후, aws_instance의 리소스에 보안 그룹 ID(vpc_security_group_ids) 및 서브넷 ID(subnet_id) 파라미터를 추가하고 새 보안 그룹 및 서브넷의 값으로 바꿔준다.

인프라 변경

  • 위에서 생성한 aws_instance.test_server 인스턴스의 ami를 수정하는 예시를 살펴보도록 한다.
  • main.tf 파일에서 ami를 다른 태그로 변경한다. 예시에서는 "Amazon Linux 2 AMI(HVM)" "64비트 ARM AMI"에서 "64비트 x86 AMI"으로 변경하였다.
# ...
resource "aws_instance" "test_server" {
  ami           = "ami-035233c9da2fabf52"
  instance_type = "t2.micro"

  tags = {
    Name = "Instance-For-Terraform-Test"
  }
}
  • AWS 공급자의 경우 인스턴스가 생성되어 있는 시점에서는 AMI 변경이 불가능하다는 것을 알고 있으므로 새로운 인스턴스를 생성하고 기존의 인스턴스를 삭제한다.
resource "aws_instance" "test_server" {
-  ami           = "ami-0fae1a46f783f1bb3"
+  ami           = "ami-035233c9da2fabf52"
   instance_type = "t2.micro"
}
  • 구성을 변경하고 terraform apply 커맨드를 입력하면 "Terraform"이 변경 사항을 기존 리소스에 어떻게 적용하는지 확인할 수 있다.
$ terraform apply
aws_instance.test_server: Refreshing state... [id=i-***]

Terraform used the selected providers to generate the following execution plan.
Resource actions are indicated with the following symbols:
-/+ destroy and then create replacement

Terraform will perform the following actions:

  # aws_instance.app_server must be replaced
-/+ resource "aws_instance" "test_server" {
      ~ ami                          = "ami-0fae1a46f783f1bb3" -> "ami-035233c9da2fabf52" # forces replacement
      ~ arn                          = "arn:aws:ec2:ap-northeast-2:561656980159:instance/i-***" -> (known after apply)

# ...

Plan: 1 to add, 0 to change, 1 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value:
  • 출력에서 사용된 -/+는 "Terraform"이 리소스를 내부에서 업데이트하는 대신 리소스를 삭제하고 다시 생성함을 의미한다.
  • "Terraform"이 일부 속성을 내부에서 업데이트하는 경우 ~ 접두사를 사용한다.
  • "실행 계획"을 살펴보면 AMI 변경으로 "Terraform"이 인스턴스를 대체하도록 강제하는 것을 확인할 수 있고, 엔지니어는 실행 계획을 확인하고 기존 시스템을 삭제하지 않도록 변경 사항을 조정할 수 있다.

인프라 파괴

  • 인프라가 더 이상 필요하지 않은 경우 보안 노출 및 비용을 줄이기 위해 인프라를 파괴할 수 있다.
  • 예를 들어, 서비스에서 운영 환경을 제거하거나 빌드 또는 테스트 시스템과 같은 수명이 짧은 환경을 관리할 수 있다.

파괴(Destroy)

  • terraform destroy 명령어는 "Terraform 프로젝트"에서 관리하는 리소스를 종료한다.
  • 해당 명령어는 모든 리소스를 종료한다는 점에서 terraform apply와 반대의 동작을 한다.
  • 단, 현재 "Terraform 프로젝트"에서 관리하지 않는 다른 곳에서 실행 중인 리소스는 삭제하지 않는다.
$ terraform destroy

Terraform used the selected providers to generate the following execution plan.
Resource actions are indicated with the following symbols:
  - destroy

Terraform will perform the following actions:

  # aws_instance.test_server will be destroyed
  - resource "aws_instance" "test_server" {
      - ami                          = "ami-035233c9da2fabf52" -> null
      - arn                          = "arn:aws:ec2:ap-northeast-2:561656980159:instance/i-***" -> null
#...

Plan: 0 to add, 0 to change, 1 to destroy.

Do you really want to destroy all resources?
  Terraform will destroy all your managed infrastructure, as shown above.
  There is no undo. Only 'yes' will be accepted to confirm.

  Enter a value:
  • - 접두사는 인스턴스가 소멸됨을 의미한다.
  • apply와 동일하게 "Terraform"은 실행 계획을 표시하고 변경하기 전에 사용자가 승인하기를 기다린다.
  • 사용자가 yes를 입력하면 아래와 같이 인프라가 파괴된다.
aws_instance.test_server: Destroying... [id=i-***]
aws_instance.test_server: Still destroying... [id=i-***, 10s elapsed]
aws_instance.test_server: Still destroying... [id=i-***, 20s elapsed]
aws_instance.test_server: Still destroying... [id=i-***, 30s elapsed]
aws_instance.test_server: Destruction complete after 31s

Destroy complete! Resources: 1 destroyed.
  • apply와 동일하게 "Terraform"은 리소스를 파괴할 순서를 결정한다.
  • 예시의 경우 다른 종속성이 없는 단일 인스턴스를 식별하여 인스턴스를 삭제했지만, 리소스가 여러 개인 경우 "Terraform"은 종속성을 존중하기 위해 적절한 순서로 리소스를 제거한다.

입력 변수 정의(Define Input Variables)

  • 지금까지의 예시를 살펴보면 모든 값들이 "하드 코딩"된 것을 확인할 수 있다.
  • 지금부터 "Terraform"의 구성파일을 동적이고 유연하게 만들 수 있는 변수를 사용하는 방법에 대해서 알아본다.

변수로 인스턴스 이름 설정

  • main.tf 파일이 위치하는 경로에 variables.tf 파일을 생성한다. 아래의 예시는 인스턴스 이름을 변수로 만든 파일이다.

variables.tf

variable "instance_name" {
  description = "Value of the Name tag for the EC2 instance"
  type        = string
  default     = "Instance-For-Terraform-Test"
}
  • main.tf 파일에서 인스턴스의 이름을 하드 코딩된 값이 아니라 variables.tf 파일의 변수를 사용할 수 있도록 변경한다.

main.tf

resource "aws_instance" "test_server" {
  ami           = "ami-035233c9da2fabf52"
  instance_type = "t2.micro"

  tags = {
    Name = var.instance_name
  }
}

구성 적용

  • terraform apply 명령을 사용하여 구성 파일을 적용한다.
$ terraform apply

Terraform used the selected providers to generate the following execution plan.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # aws_instance.test_server will be created
  + resource "aws_instance" "test_server" {
      + ami                          = "ami-035233c9da2fabf52"
      + arn                          = (known after apply)
#...

Plan: 1 to add, 0 to change, 0 to destroy.

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

aws_instance.test_server: Creating...
aws_instance.test_server: Still creating... [10s elapsed]
aws_instance.test_server: Still creating... [20s elapsed]
aws_instance.test_server: Still creating... [30s elapsed]
aws_instance.test_server: Still creating... [40s elapsed]
aws_instance.test_server: Creation complete after 50s [id=i-***]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
  • 이번에는 구성을 적용할 때 -var 플래그를 통해서 변수를 전달하여 인스턴스의 이름을 정의해본다.
$ terraform apply -var "instance_name=Instance-For-Terraform-Variable-Test"
aws_instance.test_server: Refreshing state... [id=i-***]

Terraform used the selected providers to generate the following execution plan.
Resource actions are indicated with the following symbols:
  ~ update in-place

Terraform will perform the following actions:

  # aws_instance.test_server will be updated in-place
  ~ resource "aws_instance" "test_server" {
        id                           = "i-***"
      ~ tags                         = {
          ~ "Name" = "Instance-For-Terraform-Test" -> "Instance-For-Terraform-Variable-Test"
        }
  # ...

Plan: 0 to add, 1 to change, 0 to destroy.

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

aws_instance.test_server: Modifying... [id=i-***]
aws_instance.test_server: Modifications complete after 7s [id=i-***]

Apply complete! Resources: 0 added, 1 changed, 0 destroyed.
  • 구성 파일을 다시 적용하면 "Terraform"은 인스턴스의 Name 태그를 새로운 이름으로 업데이트한다.
  • 명령줄을 통해 변수를 설정하면 해당 값이 저장되지 않고, "Terraform"은 변수를 사용하고 설정하는 다양한 방법을 지원하므로 명령을 실행할 때 변수를 반복적으로 입력하지 않아도 된다.

출력이 있는 쿼리 데이터

  • 이번에는 리소스를 생성할 때 출력되는 값을 변수로 사용하는 방법에 대해서 알아본다.

출력 EC2 인스턴스 구성

  • main.tf, variables.tf 파일이 위치하는 경로에 outputs.tf 파일을 생성한다.
  • 아래의 예시는 EC2 인스턴스의 ID와 IP 주소에 대한 출력을 정의하는 구성파일이다.
output "instance_id" {
  description = "ID of the EC2 instance"
  value       = aws_instance.test_server.id
}

output "instance_public_ip" {
  description = "Public IP address of the EC2 instance"
  value       = aws_instance.test_server.public_ip
}
  • Output의 경우 구성을 적용한 결과로 생성되는 값이기 때문에 먼저 구성을 적용해야 한다.
$ terraform apply
aws_instance.test_server: Refreshing state... [id=i-***]

Changes to Outputs:
  + instance_id        = "i-***"
  + instance_public_ip = "54.186.202.***"

You can apply this plan to save these new output values to the Terraform state,
without changing any real infrastructure.

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

Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

Outputs:

instance_id = "i-***"
instance_public_ip = "54.186.202.***"
  • "Terraform"은 구성을 적용할 때 출력 값을 화면에 출력하고, terraform output 명령으로 출력을 쿼리한다.
$ terraform output
instance_id = "i-***"
instance_public_ip = "54.186.202.***"

참고 자료

'Infrastructure > Terraform' 카테고리의 다른 글

[Terraform] String Functions  (0) 2023.03.15
[Terraform] Numeric Functions  (0) 2023.03.15
[Terraform] 표준 모듈 구조  (0) 2023.01.25
[Terraform] 공급자(Provider) 버전 관리  (0) 2023.01.25
[Terraform] 원격 상태 저장  (0) 2023.01.25