테라폼 실전 강좌 #1 프로젝트 설계와 부트스트랩: 전체 그림, 백엔드, 공통 태그
기초 강좌 아홉 편으로 문법과 원리를 갖췄으니, 이제 실제로 쓸 만한 인프라를 처음부터 끝까지 세워 볼 차례입니다. 이 실전 시리즈 열 편의 목표는 하나입니다. 웹 서비스 하나가 운영에 필요로 하는 네트워크, 컴퓨팅, 데이터베이스, 시크릿, 권한, 도메인 전부를 테라폼 코드로 만드는 것입니다. 문법 설명은 기초 편 링크로 대신하고, 여기서는 실무에서 실제로 내리게 되는 설계 결정을 중심으로 진행하겠습니다.
열 편에 걸쳐 만들 것 #
먼저 완성형을 그려 둡니다. 가상의 웹 서비스 myapp의 인프라입니다.
사용자
│
├── Route 53 (도메인) ← #10
│ │
│ ├── CloudFront ── S3 (정적 자산) ← #5, #10
│ │
│ └── ALB (HTTPS) ← #4, #10
│ │
│ ├── Auto Scaling EC2 ← #3, #4
│ └── ECS Fargate ← #8
│ │
│ └── RDS (PostgreSQL) ← #5
│
├── VPC: 퍼블릭·프라이빗 서브넷 × 2 AZ, NAT ← #2
├── 시크릿: SSM·write-only 인수 ← #6
├── IAM: 인스턴스 역할, GitHub OIDC ← #7
└── 환경 분리: dev·prod + 모듈 리팩터링 ← #9컴퓨팅 계층을 EC2 Auto Scaling으로 먼저 만들고 #8에서 ECS Fargate로 전환하는 흐름을 일부러 넣었습니다. 실무에서 인프라는 한 번에 완성되지 않고 갈아타며 진화하는데, 그 전환을 테라폼으로 안전하게 해내는 경험이 이 시리즈에서 가장 실전적인 부분이기 때문입니다.
비용 이야기를 먼저 합니다 #
기초 편의 S3 버킷과 달리 이번 시리즈의 리소스에는 요금이 나옵니다. 큰 것만 짚으면 NAT 게이트웨이(시간당 + 데이터 처리), ALB(시간당), RDS(인스턴스 시간), ECS Fargate(vCPU·메모리 시간)입니다. 소액이지만 켜 둔 채 잊으면 어느 날 요금이 튀는 경험을 하게 됩니다. 그래서 이 시리즈는 두 가지 원칙으로 진행합니다.
- 실습이 끝나면
terraform destroy로 지웁니다. 코드가 남아 있으니 다음 편 시작 때 apply 한 번으로 그대로 복원됩니다. 인프라를 부담 없이 지웠다 살리는 것 자체가 IaC의 힘을 체감하는 연습입니다. - 크기는 실습 최소로 잡습니다. t3.micro, db.t4g.micro, NAT 1개처럼 가장 작은 구성으로 만들고, 운영 크기와의 차이는 #9의 환경 분리에서 변수로 표현합니다.
부트스트랩: state 버킷과 백엔드 #
기초 #9에서 정리한 대로, state 버킷은 테라폼 밖에서 한 번 만듭니다.
aws s3api create-bucket --bucket myapp-tfstate \
--region ap-northeast-2 \
--create-bucket-configuration LocationConstraint=ap-northeast-2
aws s3api put-bucket-versioning --bucket myapp-tfstate \
--versioning-configuration Status=Enabled프로젝트 디렉터리를 만들고 뼈대 파일을 놓습니다. 지금은 단일 디렉터리로 시작하고, 코드가 충분히 자란 #9에서 모듈로 리팩터링합니다. 처음부터 완벽한 구조를 고민하느라 멈추는 것보다, 동작하는 코드를 리팩터링하는 쪽이 배우는 것도 많고 실무의 실제 순서이기도 합니다.
myapp-infra/
├── backend.tf # terraform 블록, 백엔드, provider 버전
├── providers.tf # provider 설정
├── variables.tf
├── outputs.tf
└── main.tf # 앞으로 리소스가 쌓일 자리# backend.tf
terraform {
required_version = ">= 1.15.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 6.0"
}
}
backend "s3" {
bucket = "myapp-tfstate"
key = "myapp/dev/terraform.tfstate"
region = "ap-northeast-2"
use_lockfile = true
}
}key를 처음부터 myapp/dev/로 잡아 둔 것에 주목합니다. 지금은 dev 하나뿐이지만 #9에서 prod가 생길 때 myapp/prod/가 나란히 놓일 자리를 미리 만들어 둔 것입니다.
default_tags: 모든 리소스에 자동으로 태그 걸기 #
provider 설정에는 실무 표준 장치를 하나 넣습니다.
# providers.tf
provider "aws" {
region = var.region
default_tags {
tags = {
Project = "myapp"
Env = var.env
ManagedBy = "terraform"
}
}
}default_tags는 이 provider로 만드는 모든 리소스에 태그를 자동으로 붙입니다. 리소스마다 tags를 적지 않아도 되고, 빠뜨리는 리소스도 없어집니다. 태그는 장식이 아니라 비용 분석의 기본 축이라서(비용 탐색기에서 태그별 집계), 프로젝트 첫날 걸어 두는 것과 나중에 수백 개 리소스에 소급하는 것은 수고가 크게 다릅니다. 리소스 개별 tags는 default_tags 위에 병합되므로, 개별 이름표(Name)는 리소스 쪽에서 붙이면 됩니다.
# variables.tf
variable "region" {
type = string
default = "ap-northeast-2"
}
variable "env" {
type = string
default = "dev"
validation {
condition = contains(["dev", "prod"], var.env)
error_message = "env는 dev 또는 prod만 허용합니다."
}
}첫 실행: 빈 인프라의 baseline #
리소스가 하나도 없는 상태로 초기화와 plan을 실행합니다.
$ terraform init
Initializing the backend...
Successfully configured the backend "s3"!
$ terraform plan
No changes. Your infrastructure matches the configuration.“No changes"가 이 프로젝트의 출발선입니다. 백엔드가 S3에 붙었고, 잠금이 걸리고, 코드와 현실이 일치하는 상태에서 시작합니다. 앞으로 아홉 편 동안 이 디렉터리에 리소스를 쌓아 가며, 매 편의 끝은 항상 “plan이 의도와 일치하는가"로 검증하겠습니다.
정리 #
이번 글에서 다룬 내용입니다.
- 열 편의 완성형은 VPC 위의 ALB, Auto Scaling EC2에서 ECS Fargate로 전환, RDS와 S3, 시크릿과 IAM, 도메인과 HTTPS까지 갖춘 웹 서비스 인프라입니다
- NAT, ALB, RDS, Fargate는 유료입니다. 실습 후 destroy로 지우고 다음 편에 apply로 복원하는 리듬으로 진행합니다
- state 버킷은 테라폼 밖에서 만들고, 백엔드 key는 환경 분리를 내다보고
myapp/dev/로 잡았습니다 - provider의 default_tags로 모든 리소스에 Project, Env, ManagedBy 태그가 자동으로 붙습니다. 비용 분석의 기본 축인 태그는 첫날 걸어 둡니다
- 단일 디렉터리로 시작해 #9에서 모듈로 리팩터링합니다. 동작하는 코드를 리팩터링하는 것이 실무의 순서입니다
다음 글(#2 VPC 네트워크 코드화)에서는 모든 것의 토대인 네트워크를 만듭니다. 퍼블릭·프라이빗 서브넷을 두 AZ에 걸쳐 배치하고, 라우팅과 NAT 게이트웨이까지 코드로 세우겠습니다.