본문으로 건너뛰기

Terraform 인프라 관리

개요

DTA-WIDE 시스템은 코드형 인프라(Infrastructure as Code, IaC) 원칙에 따라 Terraform을 사용하여 모든 인프라를 관리합니다. 이 문서는 Terraform을 통한 인프라 관리 방법과 모범 사례를 설명합니다.

구성

디렉토리 구조

infrastructure/
├── terraform/ # Terraform 관련 코드
│ ├── modules/ # 재사용 가능한 모듈
│ │ ├── gcp-project/ # GCP 프로젝트 설정
│ │ ├── network/ # 네트워크 구성
│ │ ├── bigquery/ # BigQuery 모듈
│ │ ├── firestore/ # Firestore 모듈
│ │ ├── pubsub/ # Pub/Sub 토픽 및 구독
│ │ ├── memorystore/ # Memorystore 모듈
│ │ └── firebase/ # Firebase 모듈
│ ├── environments/ # 환경별 구성
│ │ ├── dev/ # 개발 환경
│ │ ├── staging/ # 스테이징 환경
│ │ └── prod/ # 프로덕션 환경
│ └── scripts/ # 유틸리티 스크립트
├── terragrunt/ # Terragrunt 구성
│ ├── terragrunt.hcl # 공통 Terragrunt 설정
│ ├── dev/ # 개발 환경
│ │ ├── bigquery/
│ │ ├── firestore/
│ │ ├── pubsub/
│ │ ├── memorystore/
│ │ └── firebase/
│ ├── staging/ # 스테이징 환경
│ │ └── ...
│ └── prod/ # 프로덕션 환경
│ └── ...
└── .github/ # GitHub Actions 워크플로우
└── workflows/
├── terraform-plan.yml
└── terraform-apply.yml

주요 모듈

GCP 프로젝트 모듈

  • 프로젝트 생성 및 구성
  • API 활성화
  • 서비스 계정 관리
  • 기본 IAM 정책 설정

네트워크 모듈

  • VPC 네트워크 구성
  • 서브넷 관리
  • 방화벽 규칙
  • VPC 피어링 및 서비스 연결

클라우드 서비스 모듈

  • BigQuery: 데이터 분석 및 웨어하우스
  • Firestore: NoSQL 데이터베이스
  • Pub/Sub: 메시징 서비스
  • Memorystore: 인메모리 데이터 저장소
  • Firebase: 모바일 및 웹 애플리케이션 플랫폼

환경 관리

환경 분리

DTA-WIDE는 다음 세 가지 환경으로 분리되어 있습니다:

  • 개발(dev): 개발자 테스트용
  • 스테이지(stage): QA 및 사전 배포 테스트용
  • 프로덕션(prod): 실제 서비스 운영

각 환경은 독립적인 Terraform 상태를 유지하며, 환경별로 다른 변수를 적용할 수 있습니다.

변수 관리

  • terraform.tfvars: 환경별 변수 설정
  • variables.tf: 변수 정의 및 기본값
  • locals.tf: 파생 변수 및 계산된 값

상태 관리

  • 원격 상태 저장소: GCS 버킷 사용
  • 상태 잠금: Cloud Storage 객체 버전 관리
  • 환경별 상태 분리

CI/CD 통합

파이프라인 구성

Terraform 작업은 Cloud Build 파이프라인에 통합되어 있습니다:

  1. 계획 단계: PR 생성 시 terraform plan 실행
  2. 적용 단계: 메인 브랜치 병합 시 terraform apply 자동 실행
  3. 검증 단계: 적용 후 인프라 검증 테스트 실행

관련 문서: TBD

보안 관행

  • 민감한 변수는 Secret Manager에 저장
  • 서비스 계정 권한 최소화
  • 변경 내역 감사 및 추적

관련 문서: TBD

모범 사례

버전 관리

  • Terraform 버전 고정(현재 1.5.x)
  • 제공자 버전 명시적 지정
  • 모듈 버전 고정

코드 품질

  • 일관된 코드 형식(terraform fmt)
  • 린팅 및 검증(terraform validate, tflint)
  • 문서화된 변수 및 출력값

변경 관리

  • 점진적 변경 적용
  • 변경 사항 사전 검토(terraform plan)
  • 롤백 계획 수립

재해 복구

백업 전략

  • 정기적인 상태 백업
  • 상태 파일 버전 관리
  • 수동 복구 절차 문서화

복구 절차

  • 상태 복원 방법
  • 수동 개입 프로세스
  • 복구 테스트 및 훈련

참고 자료

TBD

Terragrunt 활용

DTA-WIDE 시스템에서는 Terraform 관리를 위해 Terragrunt를 활용하고 있습니다. Terragrunt는 Terraform 래퍼로, DRY(Don't Repeat Yourself) 원칙을 지원하며 코드 재사용성을 높입니다.

Terragrunt 구성

디렉토리 구조

infrastructure/terragrunt/
├── dev/
│ ├── serverless/
│ │ └── dta-wide-api/
│ │ └── terragrunt.hcl
│ └── secret/
│ └── dta-wide-api/
│ ├── terragrunt.hcl
│ └── dta-wide-api.json
├── stage/
│ └── ...
└── prod/
└── ...

모듈 소스 관리

모든 Terraform 모듈은 중앙 저장소에서 관리됩니다:

git::git@github.com:weltcorp/gops-terraform-module.git//modules//[MODULE_NAME]

원격 상태 관리

Terragrunt 구성은 GCS 버킷을 사용하여 원격 상태를 저장합니다:

remote_state {
backend = "gcs"
config = {
project = "dta-cloud-de-dev"
location = "europe-west3"
bucket = "dta-cloud-de-dev-tf-state"
prefix = "serverless/dta-wide-api"
impersonate_service_account = "terraform@dta-cloud-de-dev.iam.gserviceaccount.com"
}
}

GitHub Actions 통합

Terraform/Terragrunt 작업은 GitHub Actions 워크플로우에 통합되어 자동화되어 있습니다.

워크플로우 구성

계획 워크플로우

  • PR 생성/업데이트 시 실행
  • Terraform 계획 생성 및 PR 코멘트로 표시
  • 구문 검사 및 코드 포맷팅 검증

적용 워크플로우

  • 메인 브랜치 병합 시 실행
  • 자동 승인 및 변경사항 적용
  • 결과 알림 전송

서비스 계정 권한

GitHub Actions는 Workload Identity Federation을 통해 GCP 서비스 계정을 사용합니다:

  • 서비스 계정: terraform@dta-cloud-de-dev.iam.gserviceaccount.com
  • 최소 권한 원칙 적용

사용 예시

Cloud Run 서비스 배포 예시

terraform {
source = "git::git@github.com:weltcorp/gops-terraform-module.git//modules//serverless"
}

remote_state {
backend = "gcs"
config = {
project = "dta-cloud-de-dev"
location = "europe-west3"
bucket = "dta-cloud-de-dev-tf-state"
prefix = "serverless/dta-wide-api"
impersonate_service_account = "terraform@dta-cloud-de-dev.iam.gserviceaccount.com"
}
}

locals {
project = "dta-cloud-de-dev"
location = "europe-west3"
connector = "dta-cloud-de-dev"

// set cloud sql instance
cloud_sql_instance = "db-dta-mono-dev"

// stable
app_version = "1.0.15-rc5"
secret_version = "12"
revision_name = "dta-wide-api-${replace(local.app_version, ".", "-")}-${local.secret_version}"

container_image = local.app_version
template_revision_name = local.revision_name
template_secret_version = local.secret_version
}

inputs = {
project = "dta-cloud-de-dev"
terraform_service_account = "terraform@dta-cloud-de-dev.iam.gserviceaccount.com"

cloud_run_service = [
{
name = "dta-wide-api"
template = {
revision = local.template_revision_name

scaling = {
min_instance_count = "2"
max_instance_count = "20"
}
vpc_access = {
connector = "projects/${local.project}/locations/${local.location}/connectors/${local.connector}"
egress = "PRIVATE_RANGES_ONLY"
}
timeout = "300s"
service_account = "dta-wide-api@dta-cloud-de-dev.iam.gserviceaccount.com"
containers = [
{
name = "dta-wide-api"
image = "asia.gcr.io/dta-cloud-de-dev/dta-wide-api:dta-wide-api-${local.container_image}"

env = [
{
name = "CONFIGURATION"
value_source = {
secret_key_ref = {
secret = "projects/dta-cloud-de-dev/secrets/dta-wide-api"
version = local.template_secret_version
}
}
}
]
resources = {
limits = {
cpu = "2"
memory = "1024Mi"
}
cpu_idle = true
startup_cpu_boost = "true"
}
ports = {
name = "http1"
container_port = "8080"
}
volume_mounts = [
{
name = "gcloud"
mount_path = "/app/credentials"
}
]
}
]
volumes = [
{
name = "cloudsql"
cloud_sql_instance = {
instances = [
"dta-cloud-de-dev:europe-west3:${local.cloud_sql_instance}",
"dta-cloud-de-prod:europe-west3:db-dta-mono-prod",
]
}
},
{
name = "gcloud"
secret = [
{
secret = "dta-wide-api-credentials"
default_mode = 292
items = [
{
version = "1"
path = "dta-wide-api-credentials.json"
}
]
}
]
}
]
max_instance_request_concurrency = "100"
execution_environment = "EXECUTION_ENVIRONMENT_GEN2"
}
location = "europe-west3"
description = "dta-wide-api"
ingress = "INGRESS_TRAFFIC_INTERNAL_LOAD_BALANCER"
launch_stage = "GA"
deletion_protection = false
traffic = [
{
type = "TRAFFIC_TARGET_ALLOCATION_TYPE_REVISION"
revision = local.revision_name
percent = "100"
tag = "stable"
}
]
}
]
}

Secret Manager 관리 예시

terraform {
source = "git::git@github.com:weltcorp/gops-terraform-module.git//modules//secret"
}

remote_state {
backend = "gcs"
config = {
project = "dta-cloud-de-dev"
location = "europe-west3"
bucket = "dta-cloud-de-dev-tf-state"
prefix = "secret/dta-wide-api"
impersonate_service_account = "terraform@dta-cloud-de-dev.iam.gserviceaccount.com"
}
}

locals {
extra_atlantis_dependencies = [
"*.json"
]
}

inputs = {
project = "dta-cloud-de-dev"
terraform_service_account = "terraform@dta-cloud-de-dev.iam.gserviceaccount.com"

secret = [
{
name = "dta-wide-api"
}
]

secret_version = [
{
secret_name = "dta-wide-api"
secret_data_file = "dta-wide-api.json"
}
]
}

배포 흐름

  1. 개발자가 코드 변경 후 PR 생성
  2. GitHub Actions가 terragrunt plan 실행 및 결과 표시
  3. 코드 리뷰 및 승인
  4. 메인 브랜치 병합
  5. GitHub Actions가 자동으로 terragrunt apply 실행
  6. 환경별 단계적 배포 (dev → stage → prod)

관련 문서: TBD

변경 이력

버전날짜작성자변경 내용
0.1.02025-03-26bok@weltcorp.com초기 문서 작성
0.2.02025-03-28bok@weltcorp.com인프라 스캐폴딩 구현 및 문서 업데이트

인프라 스캐폴딩 구현

DTA-WIDE 모노레포에 Terraform 및 Terragrunt 기반 인프라 코드의 스캐폴딩을 구현하였습니다. 이 구조는 문서화된 설계를 바탕으로 실제 코드 관리를 위한 기반을 제공합니다.

모듈 구조

각 클라우드 서비스 모듈은 다음과 같은 구조를 따릅니다:

modules/<서비스>/
├── main.tf # 리소스 정의
├── variables.tf # 입력 변수
├── outputs.tf # 출력값
└── README.md # 모듈 사용법 및 설명

모듈별 사용 사례

BigQuery 모듈

데이터 웨어하우스 및 분석용 BigQuery 데이터셋과 테이블을 관리합니다:

module "bigquery" {
source = "../../modules/bigquery"

project_id = var.project_id
dataset_id = "analytics_${var.environment}"
location = "europe-west3"

tables = {
events = {
schema = file("${path.module}/schemas/events.json"),
partitioning = {
type = "DAY",
field = "created_at"
},
clustering = ["user_id", "event_type"]
}
}
}

Firestore 모듈

NoSQL 데이터베이스 컬렉션 및 문서 구조를 관리합니다:

module "firestore" {
source = "../../modules/firestore"

project_id = var.project_id
location = "europe-west3"
database_id = "main"

collections = ["users", "products", "orders"]
}

환경별 배포

각 환경(dev, staging, prod)에 대한 Terragrunt 구성은 다음과 같은 구조를 따릅니다:

# terragrunt/dev/bigquery/terragrunt.hcl
include {
path = find_in_parent_folders()
}

terraform {
source = "git::git@github.com:weltcorp/gops-terraform-module.git//modules//bigquery"
# 로컬 개발 시 다음 소스 사용
# source = "../../../terraform/modules/bigquery"
}

inputs = {
project_id = "dta-cloud-de-dev"
dataset_id = "analytics_dev"
location = "europe-west3"
# ... 기타 환경별 설정
}

향후 업데이트 계획

  1. 모듈 구현 완료: 각 클라우드 서비스 모듈의 실제 구현
  2. 자동화 스크립트 개발: 배포 및 검증 스크립트 구현
  3. CI/CD 파이프라인 통합: GitHub Actions 워크플로우 구현
  4. 문서 자동화: terraform-docs 도구를 사용한 문서 자동 생성

Terragrunt 명령어 가이드

DTA-WIDE 프로젝트에서는 인프라 변경을 위해 Terragrunt를 사용합니다. 아래는 기본적인 명령어 가이드입니다.

기본 Terragrunt 명령어

# 모듈 초기화
terragrunt init

# 변경 사항 계획 확인
terragrunt plan

# 변경 사항 적용
terragrunt apply

# 리소스 삭제
terragrunt destroy

계획 파일 사용하기 (권장 워크플로우)

프로덕션과 같은 중요한 환경에서의 변경은 계획 파일을 사용하는 워크플로우를 권장합니다:

# 1. 변경 계획을 파일로 저장
terragrunt plan -out=tfplan

# 2. 계획 내용 검토 (선택사항)
terragrunt show tfplan

# 3. 저장된 계획 적용 (승인 메시지 없음)
terragrunt apply tfplan

계획 파일 워크플로우의 장점:

  • 일관성 보장: 계획 검토 시점과 적용 시점의 변경 사항이 동일하게 유지됩니다.
  • CI/CD 통합: 자동화된 파이프라인에서 승인 메시지 없이 적용 가능합니다.
  • 변경 이력 관리: 계획 파일을 저장해두면 변경 이력을 추적할 수 있습니다.
  • 협업 워크플로우: 다른 팀원이 계획 결과를 검토하고 승인할 수 있습니다.
  • 오류 방지: 계획 후 리소스 상태가 변경되었거나 동시 변경으로 인한 충돌을 방지합니다.

주의사항:

  • 계획 파일은 민감한 정보를 포함할 수 있으므로 안전하게 관리해야 합니다.
  • 계획 파일은 생성 후 짧은 시간 내에 적용하는 것이 좋습니다 (환경 변화 최소화).

환경별 Terragrunt 변경 적용 방법

각 환경(dev, stage, prod)에 변경 사항을 적용하려면 해당 환경의 디렉토리로 이동하여 명령어를 실행합니다:

# 개발 환경
cd infrastructure/terragrunt/dev/<모듈명>
terragrunt plan # 변경 사항 미리 보기
terragrunt apply # 변경 사항 적용

# 스테이징 환경
cd infrastructure/terragrunt/stage/<모듈명>
terragrunt plan
terragrunt apply

# 프로덕션 환경
cd infrastructure/terragrunt/prod/<모듈명>
terragrunt plan
terragrunt apply

유용한 Terragrunt 옵션

자동 승인 (자동 yes):

terragrunt apply -auto-approve

특정 리소스만 변경:

terragrunt apply -target=google_sql_database_instance.main

디버그 로그 출력:

TF_LOG=DEBUG terragrunt apply

여러 모듈 동시 적용:

cd infrastructure/terragrunt/dev
terragrunt run-all apply

계획 파일 저장 및 적용:

terragrunt plan -out=plan.tfplan
terragrunt apply plan.tfplan

Cloud SQL 변경 적용 예시

Cloud SQL에 VPC 네트워크 연결 정보 변경과 같은 구성을 적용하는 방법:

# 1. 개발 환경 Cloud SQL 변경
cd infrastructure/terragrunt/dev/cloudsql
terragrunt init
terragrunt plan # 변경 내용 확인
terragrunt apply # 변경 적용 (yes 입력 필요)

# 2. 스테이징 환경 Cloud SQL 변경
cd ../../stage/cloudsql
terragrunt init
terragrunt plan
terragrunt apply

# 3. 프로덕션 환경 Cloud SQL 변경 (주의: 다운타임 발생 가능)
cd ../../prod/cloudsql
terragrunt init
terragrunt plan
terragrunt apply

주의사항

  1. 프로덕션 변경 시 주의: 프로덕션 환경 변경은 서비스 영향을 최소화하기 위해 계획된 유지보수 시간에 수행해야 합니다.
  2. VPC 네트워크 변경: Cloud SQL 인스턴스의 VPC 네트워크 변경은 인스턴스 재시작이 필요하여 다운타임이 발생할 수 있습니다.
  3. 백업 확인: 중요한 변경 전에 최신 백업이 있는지 확인하세요.
  4. 단계적 적용: 항상 dev → stage → prod 순서로 변경을 적용하여 문제 발생 시 미리 대응할 수 있도록 합니다.
  5. 변경 내용 검토: terragrunt plan 단계에서 변경 내용을 철저히 검토하세요.

위 명령어들은 로컬 개발 환경에서 실행하는 것을 가정합니다. CI/CD 파이프라인을 통한 자동화된 배포도 가능하며, 이는 보안 및 일관성을 위해 권장됩니다.