테라폼 기초 강좌 #1 IaC와 테라폼: 콘솔 클릭 운영의 한계, 설치와 첫 apply

7 분 소요

AWS 콘솔에서 클릭 몇 번으로 EC2 인스턴스를 띄우고, 보안 그룹을 열고, 버킷을 만들어 서비스를 올렸습니다. 몇 달 뒤 같은 구성이 하나 더 필요해졌는데, 그때 어떤 화면에서 무엇을 선택했는지 기억이 나지 않습니다. 문서로 남긴 것도 없고, 지금 운영 환경이 정확히 어떤 설정인지는 콘솔을 화면마다 뒤져야만 알 수 있습니다. 인프라를 손으로 만들어 온 팀이라면 한 번은 겪는 상황입니다. 이 문제를 코드로 푸는 도구가 테라폼(Terraform)이고, 이 시리즈는 그 테라폼을 바닥부터 다루는 기초 9편입니다. IaC 개념에서 시작해 HCL 문법, 변수, state, 모듈을 거쳐 팀이 함께 쓰는 원격 state까지 올라가고, 이어지는 실전 10편에서 AWS 인프라 전체를 코드로 세운 뒤 운영 7편에서 팀 워크플로를 다루는 트랙의 첫 시리즈입니다. AWS 계정이 있고 기본 서비스를 만져 봤다는 전제(AWS 기초 강좌 수준)로 진행하겠습니다.

콘솔 클릭으로 만든 인프라가 무너지는 방식 #

콘솔 운영의 문제는 만들 때가 아니라 시간이 지난 뒤에 드러납니다.

  • 재현할 수 없습니다: 운영 환경과 같은 구성을 스테이징에 만들려면 수십 개 화면의 선택지를 그대로 반복해야 합니다. 하나라도 다르면 “스테이징에서는 되는데 운영에서는 안 되는” 종류의 문제가 생기고, 그 차이를 찾는 일은 두 콘솔을 나란히 놓고 눈으로 비교하는 수작업이 됩니다.
  • 이력이 없습니다: CloudTrail에 API 호출 기록은 남지만, 누가 왜 바꿨는지는 남지 않습니다. 보안 그룹에 열려 있는 22번 포트가 의도한 설정인지 임시 조치의 잔재인지 알 방법이 없습니다.
  • 리뷰가 없습니다: 코드는 병합 전에 동료가 검토하지만, 콘솔 클릭은 실행되는 순간이 곧 반영되는 순간입니다. 오타 하나, 착각 하나가 그대로 운영 장애가 됩니다.
  • 사람에게 종속됩니다: 전체 구성을 아는 사람이 팀을 떠나면 인프라는 아무도 전모를 모르는 블랙박스가 됩니다.

이 네 가지의 공통 원인은 하나입니다. 인프라의 현재 상태가 어디에도 글로 적혀 있지 않다는 것입니다.

IaC: 원하는 상태를 선언하면 도구가 맞춥니다 #

IaC(Infrastructure as Code, 코드형 인프라)는 인프라를 클릭이 아니라 코드 파일로 정의하는 방식입니다. 여기서 중요한 것은 코드의 성격입니다. AWS CLI 명령을 순서대로 나열한 셸 스크립트도 코드지만, 그것은 “버킷을 만들어라"라는 명령의 나열이라 두 번 실행하면 에러가 나거나 중복이 생깁니다. 테라폼의 코드는 “이 이름의 버킷이 존재해야 한다"라는 원하는 상태의 선언입니다. 실행하면 테라폼이 현재 상태와 선언을 비교해 부족한 것만 만들고, 남는 것만 지우고, 달라진 것만 고칩니다. 이미 선언과 일치하면 아무것도 하지 않습니다. 몇 번을 실행해도 결과가 같다는 뜻입니다.

인프라가 코드 파일이 되는 순간, 소프트웨어 개발이 쌓아 온 도구가 전부 인프라에도 적용됩니다. Git에 올리면 모든 변경에 커밋 이력이 남고, 변경은 PR로 리뷰를 거치며, 사고가 나면 이전 커밋으로 되돌릴 수 있고, 같은 코드를 다른 리전이나 다른 계정에 적용해 동일한 환경을 재현할 수 있습니다. 앞 절의 네 가지 문제에 대한 답이 모두 여기서 나옵니다.

테라폼의 위치: CloudFormation, CDK, Pulumi와의 비교 #

IaC 도구는 테라폼만 있는 것이 아닙니다. AWS를 쓴다면 후보는 보통 네 가지입니다.

도구지원 범위코드 형태특징
Terraform멀티 클라우드, SaaSHCL(선언형 전용 언어)사실상 표준, provider 생태계 최대
CloudFormationAWS 전용JSON, YAMLAWS 관리형, 상태를 AWS가 보관
AWS CDKAWS 전용TypeScript, Python 등범용 언어로 작성해 CloudFormation으로 변환
Pulumi멀티 클라우드TypeScript, Python 등범용 언어 선호 팀에 적합

CloudFormation과 CDK는 AWS에 깊게 통합되는 대신 AWS 밖을 정의할 수 없습니다. 테라폼은 provider라는 플러그인 구조로 AWS, GCP, Azure 같은 클라우드는 물론 GitHub 저장소, Cloudflare DNS, Datadog 알림까지 같은 문법으로 다룹니다. 실무에서 인프라는 클라우드 하나로 끝나는 경우가 드물어서, 이 범용성이 테라폼을 사실상 표준으로 만들었습니다. 채용 공고에서 IaC 항목에 가장 자주 등장하는 이름이기도 합니다.

하나 알아둘 배경이 있습니다. 테라폼은 2023년에 라이선스를 오픈 소스에서 BSL로 바꿨고, 이에 반발한 커뮤니티가 OpenTofu라는 포크를 만들어 지금까지 유지하고 있습니다. 두 도구는 현재도 문법이 거의 같아 이 시리즈의 내용 대부분이 그대로 통하며, 어느 쪽을 선택할지의 기준은 운영 시리즈 마지막 편에서 따로 다루겠습니다.

설치와 준비 #

이 시리즈는 테라폼 1.15, AWS provider 6.x 기준으로 작성합니다. macOS는 Homebrew로 설치합니다.

설치(macOS)
brew tap hashicorp/tap
brew install hashicorp/tap/terraform

우분투 계열은 HashiCorp 저장소를 등록하고 apt로 설치합니다.

설치(우분투)
wget -O - https://apt.releases.hashicorp.com/gpg | \
  sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] \
  https://apt.releases.hashicorp.com $(lsb_release -cs) main" | \
  sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update && sudo apt install terraform

설치를 확인합니다.

버전 확인
$ terraform version
Terraform v1.15.8

테라폼이 AWS에 리소스를 만들려면 자격 증명이 필요합니다. AWS CLI를 설정해 뒀다면(aws configure 또는 SSO 로그인) 테라폼이 같은 자격 증명을 자동으로 사용하므로 추가 설정은 없습니다.

첫 리소스: S3 버킷을 코드로 만들기 #

빈 디렉터리를 하나 만들고 main.tf 파일을 작성합니다. S3 버킷은 만들어 두는 것만으로는 요금이 나오지 않아 첫 실습으로 안전합니다.

main.tf
terraform {
  required_version = ">= 1.15.0"

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

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

resource "aws_s3_bucket" "hello" {
  bucket = "my-terraform-hello-20260822"

  tags = {
    ManagedBy = "terraform"
  }
}

문법은 다음 편에서 뜯어보고, 지금은 세 덩어리의 역할만 봅니다. terraform 블록은 테라폼 자체와 provider의 버전을 고정하고, provider 블록은 어느 리전에 만들지를 정하며, resource 블록이 “이 이름의 S3 버킷이 존재해야 한다"라는 선언입니다. S3 버킷 이름은 전 세계에서 유일해야 하므로 my-terraform-hello-20260822 부분은 자신만의 값으로 바꿔야 합니다.

이 디렉터리에서 처음 한 번은 초기화를 실행합니다.

초기화
$ terraform init
Initializing provider plugins...
- Installing hashicorp/aws v6.x.x...
Terraform has been initialized!

init은 코드에 선언된 provider 플러그인을 내려받는 준비 단계입니다. 다음은 이 시리즈 전체에서 가장 중요한 명령인 plan입니다.

계획 확인
$ terraform plan
Terraform will perform the following actions:

  # aws_s3_bucket.hello will be created
  + resource "aws_s3_bucket" "hello" {
      + bucket = "my-terraform-hello-20260822"
      ...
    }

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

plan은 아직 아무것도 만들지 않습니다. 선언과 현재 상태를 비교해 무엇을 할 것인지 미리 보여 주는 명령입니다. 콘솔 클릭에는 없던 관문이 여기 생겼습니다. 실행 전에 변경 내용을 눈으로 확인하고, 팀이라면 이 출력을 리뷰에 올릴 수 있습니다. 계획이 의도와 일치하면 적용합니다.

적용
$ terraform apply
...
Do you want to perform these actions?
  Enter a value: yes

aws_s3_bucket.hello: Creating...
aws_s3_bucket.hello: Creation complete after 2s

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

콘솔의 S3 화면을 열어 보면 버킷이 실제로 생겨 있습니다. 클릭 대신 코드 파일과 명령 두 개로 인프라를 만든 것입니다. 이 상태에서 terraform plan을 다시 실행하면 “No changes"가 나옵니다. 선언과 현실이 이미 일치하니 할 일이 없다는 뜻이고, 이것이 앞에서 말한 선언형의 동작입니다.

만든 것을 코드로 지우기 #

실습이 끝났으니 정리합니다. 콘솔에서 버킷을 찾아 지울 필요 없이 명령 하나면 됩니다.

삭제
$ terraform destroy
Plan: 0 to add, 0 to change, 1 to destroy.
  Enter a value: yes

Destroy complete! Resources: 1 destroyed.

테라폼은 자기가 만든 리소스를 전부 알고 있어서 그대로 되짚어 지웁니다. 실습용 인프라를 만들었다 지웠다 하기에 이보다 좋은 도구가 없습니다. 그런데 테라폼은 자기가 만든 것을 어떻게 기억하고 있었을까요? 디렉터리를 보면 terraform.tfstate라는 파일이 생겨 있습니다. 테라폼이 관리 중인 리소스의 목록과 상태를 기록한 파일로, 테라폼의 동작 원리 전체가 이 파일에 걸려 있습니다. #5에서 자세히 다루겠습니다.

정리 #

이번 글에서 다룬 내용입니다.

  • 콘솔 클릭 운영은 재현 불가, 이력 부재, 리뷰 부재, 사람 종속이라는 문제를 만듭니다. 공통 원인은 인프라의 현재 상태가 어디에도 적혀 있지 않다는 것입니다
  • IaC는 원하는 상태를 코드로 선언하는 방식입니다. 도구가 현재 상태와 비교해 차이만 반영하므로 몇 번을 실행해도 결과가 같고, Git의 이력과 리뷰와 롤백이 인프라에도 적용됩니다
  • CloudFormation과 CDK는 AWS 전용, 테라폼과 Pulumi는 멀티 클라우드입니다. provider 생태계와 채용 시장 모두에서 테라폼이 사실상 표준입니다
  • terraform init은 provider 준비, plan은 변경 내용 미리 보기, apply는 적용, destroy는 관리 중인 리소스 제거입니다
  • 테라폼은 관리 중인 리소스를 terraform.tfstate 파일에 기록합니다. 이 파일의 정체는 #5에서 다룹니다

다음 글(#2 HCL 핵심 문법)에서는 오늘 지나쳤던 main.tf의 문법을 제대로 뜯어봅니다. block, argument, 리소스 주소 같은 HCL의 뼈대와 plan, apply, destroy 사이클을 손에 익히겠습니다.

X