S3, DynamoDB를 이용해서 Terraform tfstate 파일을 원격으로 관리해 보기

S3, DynamoDB를 이용해서 Terraform tfstate 파일을 원격으로 관리해 보는 방법 대해서 정리해 봤습니다.
2022.06.12

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

안녕하세요 클래스메소드 김재욱(Kim Jaewook) 입니다. 이번에는 S3, DynamoDB를 이용해서 Terraform tfstate 파일을 원격으로 관리해 보는 방법 대해서 정리해 봤습니다.

tfstate 파일이란?

terraform plan
terraform apply

apply 명령어를 사용하면 테라폼 폴더에 terraform.tfstate가 생성됩니다.


{
  "version": 4,
  "terraform_version": "1.2.0",
  "serial": 10,
  "lineage": "xxxxxxxxxxxxx",
  "outputs": {},
  "resources": [
    {
      "mode": "managed",
      "type": "aws_subnet",
      "name": "test-subnet",
      "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]",
      "instances": [
        {
          "schema_version": 1,
          "attributes": {
            "arn": "arn:aws:ec2:ap-northeast-1:xxxxxxxxxx:subnet/subnet-xxxxxxxxxx",
            "assign_ipv6_address_on_creation": false,
            "availability_zone": "ap-northeast-1a",
            "availability_zone_id": "apne1-az4",
            "cidr_block": "10.0.0.0/24",

tfstate 파일에는 테라폼의 버전과 각 리소스에 대한 정보가 들어있습니다.

여기서 tfstate 파일을 관리하기에 앞서, Terraform Backend를 이해할 필요가 있습니다.

Terraform Backend는 tfstate 파일을 어디에 저장하고 가져올지에 대한 설정을 의미합니다.

기본적으로 로컬에 저장이 되지만, 경우에 따라 S3 등 다양한 Backend Type을 설정할 수 있습니다.

여기서 단순히 S3에만 저장을 하는 것이 아니라 tfstate를 S3에 관리하면서 동시에 작업이 일어나지 않도록 DynamoDB에서 Lock 테이블을 생성해서 관리를 합니다.

S3, DynamoDB 생성


terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.0"
    }
  }
  backend s3 {
    bucket         = "jaewook-s3-bucket-tfstate" # S3 버킷 이름
    key            = "terraform/terraform.tfstate" # tfstate 저장 경로
    region         = "ap-northeast-1"
    dynamodb_table = "terraform-tfstate-lock" # dynamodb table 이름
  }
}

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

provider.tf 라는 파일에 다음과 같은 코드로 S3와 DynamoDB를 설정할 수 있습니다.

Failed to get existing workspaces: S3 bucket does not exist.

The referenced S3 bucket must have been previously created. If the S3 bucket was created within the last minute, please wait for a minute or two and try again.

하지만 이 상태로 init을 진행하게 되면, 다음과 같은 에러를 마주할 수 있는데, 현재 AWS 상에서 backend로 설정된 S3 버킷과 DynamoDB가 없는 경우 다음과 같은 에러를 마주하게 됩니다.

이번 블로그에서는 S3 버킷과 DynamoDB를 직접 생성하는 작업까지 진행하도록 하겠습니다.


resource "aws_s3_bucket" "test-tfstate" {
  bucket = "jaewook-s3-bucket-tfstate"
}

resource "aws_s3_bucket_versioning" "test-versioning" {
  bucket = aws_s3_bucket.test-tfstate.id
  versioning_configuration {
    status = "Enabled"
  }
}

resource "aws_dynamodb_table" "terraform_state_lock" {
  name           = "terraform-tfstate-lock"
  hash_key       = "LockID"
  billing_mode   = "PAY_PER_REQUEST"

  attribute {
    name = "LockID"
    type = "S"
  }
}

backend.tf 라는 파일에 다음과 같은 코드로 S3 버킷과 DynamoDB를 생성합니다. provider.tf와 backend.tf 파일을 생성했다면 terraform init으로 초기화를 진행합니다. (여기서 주의할 점은 provider.tf 부분에 backend 부분은 제거하고 진행해야 합니다.)

  • terraform plan
  • terraform apply

이어서 plan, apply를 통해서 S3 버킷과 DynamoDB를 생성합니다.

이제 콘솔 환경으로 들어가서 확인해 보면 DynamoDB 테이블이 생성된 것을 확인할 수 있습니다.

S3 버킷도 문제 없이 생성된 것을 확인할 수 있습니다.


terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.0"
    }
  }
  backend s3 {
    bucket         = "jaewook-s3-bucket-tfstate" # S3 버킷 이름
    key            = "terraform/terraform.tfstate" # tfstate 저장 경로
    region         = "ap-northeast-1"
    dynamodb_table = "terraform-tfstate-lock" # dynamodb table 이름
  }
}

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

이제 provider.tf에 backend 부분을 넣어줍니다.

  • terraform init
  • terraform plan
  • terraform apply

이제 S3 버킷과 DynamoDB 테이블이 만들어졌으므로, S3 버킷에 tfstate 파일을 저장하기 위해 초기화를 진행합니다.

  • Releasing state lock. This may take a few moments...

여기서 plan, apply 명령어를 입력하면 다음과 같은 문구가 뜨게 되는데, 이제 로컬이 아닌 S3 버킷에 저장된 tfstate를 통해 리소스를 관리하게 되며, 여러 사람이 동시에 작업할 때의 문제를 막아주게 됩니다.

S3 버킷에 들어가보면 tfstate파일이 업로드 된 것을 확인할 수 있습니다.


resource "aws_vpc" "test-vpc" {
  cidr_block = "10.0.0.0/16"
}

테스트를 위해 vpc를 만들고 apply를 합니다.

이제 새 폴더를 만들어서 provider.tf와 backend.tf만을 생성하고 아래 명령어를 입력해봅니다.

  • terraform init
  • terraform plan

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_vpc.test-vpc will be destroyed
  # (because aws_vpc.test-vpc is not in configuration)
  - resource "aws_vpc" "test-vpc" {

그러면 현재 소스코드와 tfstate를 비교해서 현재 소스 코드에 test-vpc를 생성하는 코드가 없으니 콘솔에 생성되어 있는 test-vpc를 삭제한다는 메시지를 확인할 수 있습니다.

  • No changes. Your infrastructure matches the configuration.

test-vpc를 생성하는 코드를 작성하면 위와 같이 인프라가 일치한다는 메시지를 확인할 수 있습니다.


resource "aws_vpc" "test-vpc" {
  cidr_block = "10.0.0.0/16"
}

resource "aws_subnet" "test-subnet" {
  vpc_id                  = aws_vpc.test-vpc.id
  availability_zone       = "ap-northeast-1a"
  cidr_block              = "10.0.0.0/24"
  map_public_ip_on_launch = true

  tags = {
    Name = "test-subnet"
  }
}

나아가서 Subnet을 추가하는 코드를 작성하고, terraform plan을 해 보면


Terraform will perform the following actions:

  # aws_subnet.test-subnet will be created
  + resource "aws_subnet" "test-subnet" {
      + arn                                            = (known after apply)
      + assign_ipv6_address_on_creation                = false
      + availability_zone                              = "ap-northeast-1a"
      + availability_zone_id                           = (known after apply)
      + cidr_block                                     = "10.0.0.0/24"

Subnet만 추가가 되는 것을 확인할 수 있습니다. 이 상태에서 apply를 하고 콘솔에서 확인합니다.

콘솔에서 문제없이 Subnet만 생성된 것을 확인할 수 있습니다.

본 블로그 게시글을 보시고 문의 사항이 있으신 분들은 클래스메소드코리아 (info@classmethod.kr)로 연락 주시면 빠른 시일 내 담당자가 회신 드릴 수 있도록 하겠습니다 !