목차
- 멀티 클라우드, 이제는 선택이 아니라 현실입니다
- Terraform 멀티 클라우드란? 한 번에 여러 클라우드를 관리하는 방법
- 디렉터리 구조 설계 — 이게 제일 중요합니다
- 실전 구현 1단계 — Provider 및 버전 설정
- 실전 구현 2단계 — 각 클라우드 리소스 모듈 만들기
- AWS VPC 모듈로 가상 네트워크 구성하기
- GCP VPC 모듈로 가상 네트워크 구성하기
- Azure VNet 모듈로 가상 네트워크 구성하기
- 환경별 실제 구성 파일로 모듈 조합하기
- 실전 구현 3단계 — 멀티 클라우드 배포 실행하기
- 멀티 클라우드 운영할 때 꼭 알아야 할 팁
- 자주 하는 실수와 해결 방법
- 마치며: Terraform 멀티 클라우드의 미래
멀티 클라우드, 이제는 선택이 아니라 현실입니다
혹시 이런 상황 겪어보신 적 있으신가요? 개발팀에서는 AWS를 쓰고, 데이터 분석팀에서는 GCP BigQuery를 쓰고, 어느 날 갑자기 Azure AD 연동도 필요하다고 하고… 처음엔 각 팀이 알아서 하면 되겠지 싶었는데, 인프라 엔지니어 입장에서 이걸 다 관리하려니 머리가 터질 것 같더라고요.
저도 딱 그 상황이었습니다. 몇 년 전에 회사에서 AWS만 쓰다가 GCP 프로젝트가 하나 붙고, 또 Azure가 붙고… 각 클라우드마다 콘솔 따로 열고, CLI 따로 설치하고, 권한 관리도 따로 하다 보니 진짜 정신없었거든요. 그때 Terraform으로 멀티 클라우드 환경을 구성하면서 삶이 좀 편해졌습니다 ㅎㅎ
이 글에서는 제가 직접 구성해서 운영 중인 Terraform으로 AWS, GCP, Azure를 동시에 관리하는 멀티 클라우드 IaC(Infrastructure as Code, 코드로 인프라 관리) 환경을 단계별로 풀어드릴게요. 처음 접하시는 분도 따라오실 수 있게 최대한 친절하게 써볼게요.

▲ Terraform 단일 코드베이스에서 AWS, GCP, Azure 세 클라우드를 동시에 관리하는 전체 아키텍처 개요도
Terraform 멀티 클라우드란? 한 번에 여러 클라우드를 관리하는 방법
Terraform은 HashiCorp에서 만든 오픈소스 IaC 도구입니다. 쉽게 말해, 클라우드 인프라를 코드로 정의하고 실행하면 실제 리소스가 만들어지는 방식이에요. 여러 클라우드를 한 번에 관리할 수 있다는 게 가장 큰 장점입니다.
여기서 핵심은 Provider(프로바이더)라는 개념입니다. Terraform은 각 클라우드사에서 제공하는 Provider 플러그인을 통해 해당 클라우드와 통신하거든요. AWS Provider, Google Provider, AzureRM Provider를 동시에 선언하면 하나의 Terraform 코드로 여러 클라우드를 한 번에 관리할 수 있어요.
| 구분 | AWS | GCP | Azure |
|---|---|---|---|
| Terraform Provider | hashicorp/aws | hashicorp/google | hashicorp/azurerm |
| 인증 방식 | Access Key / IAM Role | Service Account JSON / ADC | Service Principal / Managed Identity |
| 상태 저장소 | S3 + DynamoDB | GCS Bucket | Azure Blob Storage |
| 주요 IaC 용어 | Region, VPC, EC2 | Project, VPC, Compute | Subscription, VNet, VM |
저도 처음엔 각 클라우드마다 용어가 달라서 헷갈렸는데요, Terraform을 쓰면 이 차이를 Provider가 알아서 추상화해줘서 훨씬 편하더라고요.
디렉터리 구조 설계 — 이게 제일 중요합니다
멀티 클라우드 Terraform 프로젝트에서 제가 가장 많이 고민한 부분이 바로 디렉터리 구조였어요. 처음에 그냥 막 만들었다가 나중에 전부 갈아엎은 쓴 경험이 있거든요. 그래서 여러분은 처음부터 제대로 잡으셨으면 해서 제가 실제로 쓰는 구조를 공유합니다.
multi-cloud-infra/
├── providers.tf # 모든 Provider 선언
├── versions.tf # Terraform 버전 및 Provider 버전 고정
├── variables.tf # 공통 변수
├── outputs.tf # 공통 출력값
│
├── modules/ # 재사용 가능한 모듈
│ ├── aws/
│ │ ├── vpc/
│ │ ├── ec2/
│ │ └── s3/
│ ├── gcp/
│ │ ├── vpc/
│ │ ├── gce/
│ │ └── gcs/
│ └── azure/
│ ├── vnet/
│ ├── vm/
│ └── storage/
│
└── environments/ # 환경별 실제 구성
├── dev/
│ ├── main.tf
│ ├── variables.tf
│ └── terraform.tfvars
├── staging/
└── prod/
💡 핵심 포인트: 환경(dev/staging/prod)을 디렉터리로 분리하고, 각 클라우드별 모듈을 따로 관리하면 나중에 유지보수할 때 훨씬 편합니다. 처음엔 귀찮아 보여도, 실제로 운영하다 보면 정말 감사하게 되더라고요.
실전 구현 1단계 — Provider 및 버전 설정
자, 이제 실제 코드로 들어가 볼게요. 먼저 versions.tf에서 Terraform 버전과 각 Provider 버전을 고정해줍니다. 버전 고정 안 하면 나중에 Provider 업데이트되면서 갑자기 코드가 안 돌아가는 참사가 생기거든요. 저 이거로 한 번 크게 당했습니다 ㅎㅎ
# versions.tf
terraform {
required_version = ">= 1.5.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
google = {
source = "hashicorp/google"
version = "~> 5.0"
}
azurerm = {
source = "hashicorp/azurerm"
version = "~> 3.0"
}
}
# 원격 State 백엔드 — AWS S3 예시
backend "s3" {
bucket = "my-terraform-state-bucket"
key = "multi-cloud/terraform.tfstate"
region = "ap-northeast-2"
dynamodb_table = "terraform-state-lock"
encrypt = true
}
}
다음은 providers.tf입니다. 각 클라우드 인증 정보를 여기서 설정해요.
# providers.tf
# AWS Provider
provider "aws" {
region = var.aws_region
# 로컬에선 ~/.aws/credentials 사용
# CI/CD에선 환경변수 AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY 사용
}
# GCP Provider
provider "google" {
project = var.gcp_project_id
region = var.gcp_region
# GOOGLE_APPLICATION_CREDENTIALS 환경변수로 Service Account JSON 경로 지정
}
# Azure Provider
provider "azurerm" {
features {}
subscription_id = var.azure_subscription_id
# ARM_CLIENT_ID, ARM_CLIENT_SECRET, ARM_TENANT_ID 환경변수 사용
}
⚠️ 경고: 절대로 Access Key나 Secret Key를 코드에 하드코딩하면 안 됩니다! 환경변수나 Vault 같은 시크릿 관리 도구를 꼭 사용하세요. GitHub에 실수로 올렸다가 과금 폭탄 맞은 사례 정말 많거든요.
실전 구현 2단계 — 각 클라우드 리소스 모듈 만들기
이제 각 클라우드별로 기본 네트워크 리소스를 만들어볼게요. 실제로 제가 쓰는 패턴입니다.

▲ 하나의 Terraform 코드가 각 클라우드 플랫폼의 실제 리소스로 프로비저닝되는 과정을 보여주는 구성 다이어그램
AWS VPC 모듈로 가상 네트워크 구성하기
# modules/aws/vpc/main.tf
resource "aws_vpc" "main" {
cidr_block = var.cidr_block
enable_dns_hostnames = true
enable_dns_support = true
tags = merge(var.common_tags, {
Name = "${var.env}-vpc"
Cloud = "AWS"
})
}
resource "aws_subnet" "public" {
count = length(var.public_subnet_cidrs)
vpc_id = aws_vpc.main.id
cidr_block = var.public_subnet_cidrs[count.index]
availability_zone = var.availability_zones[count.index]
tags = merge(var.common_tags, {
Name = "${var.env}-public-subnet-${count.index + 1}"
})
}
resource "aws_internet_gateway" "main" {
vpc_id = aws_vpc.main.id
tags = var.common_tags
}
GCP VPC 모듈로 가상 네트워크 구성하기
# modules/gcp/vpc/main.tf
resource "google_compute_network" "main" {
name = "${var.env}-vpc"
auto_create_subnetworks = false
project = var.project_id
}
resource "google_compute_subnetwork" "main" {
name = "${var.env}-subnet"
ip_cidr_range = var.cidr_block
region = var.region
network = google_compute_network.main.id
project = var.project_id
private_ip_google_access = true
}
Azure VNet 모듈로 가상 네트워크 구성하기
# modules/azure/vnet/main.tf
resource "azurerm_resource_group" "main" {
name = "${var.env}-rg"
location = var.location
tags = var.common_tags
}
resource "azurerm_virtual_network" "main" {
name = "${var.env}-vnet"
address_space = [var.cidr_block]
location = azurerm_resource_group.main.location
resource_group_name = azurerm_resource_group.main.name
tags = var.common_tags
}
resource "azurerm_subnet" "main" {
name = "${var.env}-subnet"
resource_group_name = azurerm_resource_group.main.name
virtual_network_name = azurerm_virtual_network.main.name
address_prefixes = [var.subnet_cidr]
}
환경별 실제 구성 파일로 모듈 조합하기
# environments/dev/main.tf
# AWS 네트워크
module "aws_network" {
source = "../../modules/aws/vpc"
env = "dev"
cidr_block = "10.0.0.0/16"
public_subnet_cidrs = ["10.0.1.0/24", "10.0.2.0/24"]
availability_zones = ["ap-northeast-2a", "ap-northeast-2c"]
common_tags = {
Environment = "dev"
ManagedBy = "Terraform"
}
}
# GCP 네트워크
module "gcp_network" {
source = "../../modules/gcp/vpc"
env = "dev"
project_id = var.gcp_project_id
region = "asia-northeast1"
cidr_block = "10.1.0.0/20"
}
# Azure 네트워크
module "azure_network" {
source = "../../modules/azure/vnet"
env = "dev"
location = "koreacentral"
cidr_block = "10.2.0.0/16"
subnet_cidr = "10.2.1.0/24"
common_tags = {
Environment = "dev"
ManagedBy = "Terraform"
}
}
실전 구현 3단계 — 멀티 클라우드 배포 실행하기
이제 준비가 끝났습니다. 실제로 배포를 해볼게요. 명령어는 어느 클라우드나 동일합니다. 이게 Terraform의 진짜 강점이에요.
# 1. Terraform 초기화 (Provider 다운로드)
terraform init
# 2. 실행 계획 확인 (dry-run)
terraform plan -out=tfplan
# 3. 실제 배포 (AWS, GCP, Azure 동시에 리소스 생성)
terraform apply tfplan
# 4. 배포된 리소스 확인
terraform show
# 5. 특정 출력값 확인
terraform output
명령어 하나로 AWS VPC, GCP VPC, Azure VNet이 동시에 만들어집니다. 정말 신기하더라고요. 각 클라우드 콘솔에 들어가서 확인해보면 실제로 리소스가 생성되어 있을 거예요.
멀티 클라우드 운영할 때 꼭 알아야 할 팁
1. State 파일 관리는 신중하게
Terraform State 파일은 현재 인프라 상태를 기록한 중요한 파일입니다. 절대로 로컬에만 저장하면 안 됩니다. S3, GCS, Azure Blob Storage 같은 원격 백엔드에 저장하고, DynamoDB나 Blob Storage 잠금 기능으로 동시 접근을 방지하세요. 여러 팀이 동시에 배포하면서 State 파일이 꼬이는 참사를 막아야 거든요.
2. 환경 분리는 필수
dev, staging, prod를 완전히 분리된 디렉터리로 관리하세요. 실수로 prod 환경을 destroy하는 일을 막기 위해 terraform destroy는 스크립트로 자동화하지 말고 수동으로만 실행하는 게 좋습니다.
3. 변수 파일은 .gitignore에 추가
terraform.tfvars 파일에는 민감한 정보(API 키, 구독 ID 등)가 들어갑니다. 절대로 Git에 커밋하면 안 됩니다. .gitignore에 추가하고, CI/CD 파이프라인에서 환경변수로 주입하세요.
4. 비용 모니터링을 자동화하세요
멀티 클라우드는 각 클라우드의 비용이 따로 집계되기 때문에 전체 비용을 파악하기 어렵습니다. Terraform Cloud나 Infracost 같은 도구로 배포 전에 비용을 예측하는 게 좋습니다.
자주 하는 실수와 해결 방법
Q: 특정 클라우드 리소스만 삭제하고 싶은데 어떻게 하나요?
A: terraform destroy -target=module.aws_network 명령으로 특정 모듈만 삭제할 수 있습니다. 다만 이 방법은 State 파일 불일치를 초래할 수 있으니 주의하세요.
Q: Provider 버전을 업그레이드하면 기존 리소스가 삭제되나요?
A: 보통은 아닙니다. 다만 terraform plan으로 변경사항을 꼭 확인한 후 업그레이드하세요. 간혹 주요 버전 업그레이드 시 리소스 구조가 바뀔 수 있거든요.
Q: 기존 클라우드 리소스를 Terraform으로 관리하려면?
A: terraform import 명령을 사용합니다. 예: terraform import aws_vpc.main vpc-12345678. 기존 리소스를 State 파일에 등록한 후 코드를 작성하면 됩니다.
마치며: Terraform 멀티 클라우드의 미래
저는 지난 3년간 Terraform으로 AWS, GCP, Azure를 관리해오면서 정말 많은 이점을 느꼈습니다. 각 클라우드의 콘솔을 오갈 필요 없이 코드 한 곳에서 모든 걸 관리할 수 있다는 게 최고의 장점이에요. 팀 규모가 커져도 일관된 방식으로 인프라를 관리할 수 있고, 재해 복구나 환경 복제도 정말 쉬워집니다.
처음에는 디렉터리 구조를 잡는 데 시간이 걸릴 수 있지만, 한 번 제대로 설계해놓으면 정말 오랫동안 써먹을 수 있는 자산이 됩니다. 여러분도 이 글을 참고해서 Terraform 멀티 클라우드 환경을 구축해보세요. 분명 개발과 운영이 훨씬 편해질 거예요!