3주차 - Terraform 상태 격리방안

이 내용은 CloudNet@ 에서 진행하는 테라폼 기초 입문 스터디에 대한 연재글입니다.

스터디에서 사용하는 교재는 Terraform Up & Running 2nd Edition 입니다.


Prerequisites

아래에서 3주차 스터디 내용을 공유합니다.

교재의 3장을 다룹니다.

들어가며...

테라폼의 상태 관리에 대해 정리하고자 합니다. 왜 상태관리를 해야하는지, 어떤방안이 있으며 어떤식으로 작성하면 좋은지 작성합니다.

테라폼 상태관리의 필요성

테라폼은 기본적으로 생성한 인프라에 대한 정보를 상태파일에 기록합니다.

본격적으로 테라폼을 도입하고, 코드를 작성/배포하다 보면 자연스럽게 여러 사람들이 작업하겠지요. 그렇다면 자연스레 아래와 같은 요구사항이 발생할 수 있습니다.

테라폼 상태 공유 방법

운영, 프로덕션 레벨에서 팀 단위로 인프라 구성 코드를 공유하는 방법은 여러가지가 있습니다.

Git 등의 VCS를 이용하면?

이런 문제를 해결하기 위해선, 원격 백엔드를 사용합니다. 1장에서 배운 "backend"의 저장공간을 원격 저장소로 설정하는 것을 의미합니다.

테라폼의 원격 백엔드 를 사용하면?

테라폼 상태 관리

그렇다면, 원격 백엔드를 활용한 상태관리를 살펴봅시다.

제 3장의 예시를 보면 dev, staging 환경을 S3, DynamoDB로 분리하긴 했지만, 상태파일 자체가 단일인 상황은 막을 수 없습니다. 따라서, 책에서는 상태 격리에 대해서는 두가지 접근법을 함께 사용하기를 제안합니다.

  1. 테라폼의 Workspace (이하 워크스페이스) 라는 개념

    • 복수개의/분리된/이름이 지정된 워크스페이스를 사용하여 상태파일을 격리합니다.
  2. 분리된 파일 레이아웃 지정

    • 개발환경, 스테이징 환경, 실제 프로덕션 환경(!)에 대한 분리를 통해 실수를 방지할 수 있습니다.
    • 디렉토리 구조를 통한 분리를 의미합니다.
      • [예습!] 이는 모듈화 및 테라폼 내의 function 기능과도 밀접한 영향을 가집니다. 필요한 사항에 대해서는 프로그램을 작성하거나 모듈화를 잘 하여 반복되는 코드를 없애자는 것이 주요 골자죠.

상태관리 (1): Workspace 설정

그렇다면, 상태관리를 가능하게 하는 방법 중 하나인 Workspace를 알아봅시다.

테라폼은 기본적으로 default 라는 워크스페이스를 사용합니다. 새 작업공간을 만들기 위해서는 terraform workspace 커맨드를 사용합니다.

그렇다면 워크스페이스를 변경하는 것은 어떤 의미를 가질까요?

상술하였듯, 워크스페이스 지정으로는 문제를 해결할 수 없습니다. 후술할 파일 레이아웃을 함께 지정하여 작업하는 것이 권장됩니다. 어떤 이유로 인해 워크스페이스 만을 사용할 수 없는지 아래에서 설명하겠습니다:

  1. 먼저, 모든 작업 공간의 상태 파일은 동일한 백엔드(예. 동일한 S3 버킷)에 저장합니다. 모든 작업 공간이 동일한 인증과 접근 통제를 사용합니다.
    • E.g., 테스트 환경과 프로덕션 환경이 다른 백엔드를 사용하는 경우, 백엔드에 다른 보안 수준의 통제 설정을 수행하는 것은 불가능합니다.
  2. 코드나 터미널에 현재 작업 공간에 대한 정보가 표시 되지 않습니다. 코드 탐색 시 한 작업 공간에 배치된 모듈은 다른 모든 작업 공간에 배치된 모듈과 동일합니다.
    • 이로 인해 인프라를 제대로 파악하기 어려워 유지 관리가 어렵게 됩니다.
  3. 위 두 항목의 결합된 문제가 발생 할 수 있음. 예를 들면 테스트 환경이 아닌 프로덕션 환경에서 terraform destroy 명령을 실행 할 수 있습니다..... :scream_cat:
    • 검증과 운영 환경이 동일한 인증 매커니즘을 사용하기 때문에 위 오류에서 보호할 방법이 없습니다.
  4. 따라서 파일 레이아웃을 이용한 격리를 함께 사용할 것을 권장합니다.

상태관리 (2): 파일 레이아웃을 이용한 구성파일 격리

상태관리 방법 중 또다른 하나는 파일 레이아웃을 잡는 것입니다. 핵심은 아래와 같습니다.

예시를 위해, 아래와 같은 구조를 가진다고 하죠.

.
├── global
│   └── s3
│       ├── main.tf
│       └── outputs.tf
├── mgmt
│   ├── services
│   └── vpc
├── prod
│   ├── services
│   └── vpc
└── stage
    ├── data-stores
    │   └── mysql
    │       ├── main-vpgsg.tf
    │       ├── main.tf
    │       ├── outputs.tf
    │       ├── terraform.tfstate
    │       └── variables.tf
    └── services
        └── webserver-cluster
            ├── main.tf
            └── user-data.sh

상태관리 예제 (1)

이 문단에서는 ELB, ASG, 그리고 RDS가 구축된 환경을 만들고, 이에 대해 디렉토리 구조를 아래와 같이 작성하여 실습하도록 하겠습니다.

├── global
│   └── s3
│       ├── main.tf
│       └── outputs.tf
└── stage
    ├── data-stores
    │   └── mysql
    │       ├── main-vpgsg.tf
    │       ├── main.tf
    │       ├── outputs.tf
    │       └── variables.tf
    └── services
        └── webserver-cluster
            ├── main.tf
            └── user-data.sh

코드의 위치는 아래와 같습니다:

RDS 생성 도중 배울 요소

웹 서버 클러스터 배포 중 배울 요소

웹 서버 클러스터를 배포하며 terraform_remote_state 라는 값과, 테라폼의 내장 함수(build-in function)에 대해 살펴봅시다.

data.terraform_remote_state.<NAME>.outputs.<ATTRIBUTE>
user_data = <<EOF
#!/bin/bash
echo "Hello, World" >> index.html
echo "${data.terraform_remote_state.db.outputs.address}" >> index.html
echo "${data.terraform_remote_state.db.outputs.port}" >> index.html
nohup busybox httpd -f -p ${var.server_port} &
EOF
function_name()
format(<FMT>, <ARGS>, ...)
# 참고: 테라폼 콘솔 사용에 관하여
terraform console
> format("%.3f", 3.14159265359)
"3.142"
templatefile(<PATH>, <VARS>)
#!/bin/bash

cat > index.html <<EOF
<h1>Hello, World</h1>
<p>DB address: ${db_address}</p>
<p>DB port: ${db_port}</p>
EOF

nohup busybox httpd -f -p ${server_port} &
resource "aws_launch_configuration" "example" {
  image_id        = "ami-0fb653ca2d3203ac1"
  instance_type   = "t2.micro"
  security_groups = [aws_security_group.instance.id]

  # Render the User Data script as a template
  user_data = templatefile("user-data.sh", {
    server_port = var.server_port
    db_address  = data.terraform_remote_state.db.outputs.address
    db_port     = data.terraform_remote_state.db.outputs.port
  })

  # Required when using a launch configuration with an auto scaling group.
  lifecycle {
    create_before_destroy = true
  }
}

Lessons Learned

제 3장에서는 아래의 내용을 반드시 기억하셨으면 좋겠습니다.

  1. 테라폼 파일을 다수의 사람과 함께 관리할 때 상태관리는 선택이 아닌 필수입니다.
    1. 워크스페이스 설정을 수행합니다.
    2. 파일 레이아웃을 함께 잡아, 실수를 최대한으로 줄입시다.
  2. 민감정보는 시크릿 저장소와 같은 서비스를 활용합시다.
  3. 속성 참조와 내장함수를 통해, 코드반복을 대폭축소합시다.

Tips and tricks

이것으로 제 3장을 마칩니다. 긴 글 읽어주셔서 감사합니다.