การ deploy โครงสร้างพื้นฐานข้ามหลาย cloud provider พร้อมกันในโปรเจกต์เดียวเป็นสิ่งที่ Terraform ออกแบบมาให้ทำได้ดีที่สุด เพราะใช้ภาษา HCL เดียวกันควบคุมได้ทั้ง AWS, Azure, Google Cloud, DigitalOcean, Cloudflare และอื่น ๆ ผ่าน provider ที่แยกกัน ทำให้วางสถาปัตยกรรมแบบ multi-cloud หรือ hybrid-cloud ได้โดยไม่ต้องเรียนรู้ tooling ใหม่
บทความนี้อธิบายรูปแบบการเขียน config สำหรับ multi-cloud, การจัดการ credential แยกแต่ละ provider, การออกแบบ module ที่ใช้ซ้ำข้าม cloud, การจัดการ state file, และ use case ที่พบบ่อย พร้อมข้อควรระวังสำหรับทีมที่เพิ่งเริ่มออกแบบ multi-cloud ด้วย IaC
ทำไมต้อง Multi-Cloud
เหตุผลที่ทำให้องค์กรเลือกใช้หลาย cloud พร้อมกัน
- ลด vendor lock-in เพื่อไม่ต้องผูกขาดกับ provider รายเดียว
- ใช้ประโยชน์จาก service ที่โดดเด่นของแต่ละ provider (เช่น BigQuery ของ GCP, Azure AD, S3 ของ AWS)
- กระจายความเสี่ยงเมื่อ cloud ใด cloud หนึ่งล่ม ระบบยังทำงานต่อได้
- ปฏิบัติตามข้อกำหนด data sovereignty ที่บังคับให้ข้อมูลบางประเภทต้องอยู่ในผู้ให้บริการเฉพาะ
- ใช้ edge network ของ Cloudflare คู่กับ origin server บน cloud อื่น
ประกาศหลาย Provider ในโปรเจกต์เดียว
Terraform รองรับการประกาศ provider หลายตัวใน root module เดียว ตัวอย่างการใช้ AWS ร่วมกับ Cloudflare
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
cloudflare = {
source = "cloudflare/cloudflare"
version = "~> 4.0"
}
}
}
provider "aws" {
region = "ap-southeast-1"
}
provider "cloudflare" {
api_token = var.cloudflare_api_token
}
เมื่อรัน terraform init ระบบจะดาวน์โหลด provider binary ของทั้งสองตัว และ terraform plan/apply จะประสาน resource ข้าม provider ได้ใน run เดียวกัน
Provider Alias สำหรับหลาย Region หรือหลาย Account
หากต้องใช้ provider เดียวกันคนละ account หรือคนละ region ให้ใช้ alias
provider "aws" {
alias = "primary"
region = "ap-southeast-1"
}
provider "aws" {
alias = "dr"
region = "ap-southeast-2"
}
resource "aws_s3_bucket" "primary_log" {
provider = aws.primary
bucket = "myapp-log-sg"
}
resource "aws_s3_bucket" "dr_log" {
provider = aws.dr
bucket = "myapp-log-sydney"
}
แนวทางนี้เหมาะกับการทำ disaster recovery แบบ cross-region หรือการแยก billing account สำหรับแต่ละ business unit
การจัดการ Credential
ห้ามเก็บ API key ของ cloud provider ไว้ใน code หรือ state file โดยตรง แนวทางที่ปลอดภัย
- AWS: ใช้ IAM role + AWS SSO หรือ OIDC federation จาก CI/CD
- Azure: ใช้ Service Principal + client_secret เก็บใน Key Vault หรือใช้ Managed Identity
- GCP: ใช้ Service Account key หรือ Workload Identity Federation สำหรับ CI/CD
- Cloudflare: ใช้ API Token ที่จำกัดสิทธิ์ (ไม่ใช่ Global API Key)
- DigitalOcean: ใช้ Personal Access Token เก็บใน secret manager
ใน CI/CD pipeline แนะนำ OIDC federation ที่สุด เพราะไม่ต้องเก็บ long-lived credential ไว้เลย token จะหมดอายุภายในไม่กี่นาที
Pattern การออกแบบ Multi-Cloud Module
Pattern A — แยก root module ต่อ provider
เขียน root module แยกเป็น envs/aws/, envs/gcp/, envs/cloudflare/ แต่ละ directory มี state file แยก เหมาะเมื่อแต่ละ cloud มีวงจร lifecycle ต่างกัน เช่น AWS ปล่อยใหม่ทุกวัน แต่ Cloudflare แก้ไขนาน ๆ ครั้ง
ข้อดี: blast radius เล็ก, apply พร้อมกันไม่ชนกัน
ข้อเสีย: ส่งค่าระหว่าง cloud ต้องผ่าน remote state data source หรือ export/import ด้วยมือ
Pattern B — root module เดียวรวม provider ทั้งหมด
เหมาะเมื่อ resource ระหว่าง cloud มีการอ้างอิงกันตรง ๆ เช่น Cloudflare DNS record ต้องชี้ไปยัง AWS ALB DNS name ที่เพิ่งสร้าง สามารถใช้ output ของ aws resource ป้อนเข้า cloudflare_record ได้ทันที
resource "aws_lb" "web" {
name = "myapp-alb"
load_balancer_type = "application"
subnets = aws_subnet.public[*].id
}
resource "cloudflare_record" "web" {
zone_id = var.cloudflare_zone_id
name = "app"
type = "CNAME"
value = aws_lb.web.dns_name
proxied = true
}
ข้อดี: reference ข้าม cloud ตรง ๆ ไม่ต้องผ่าน remote state
ข้อเสีย: state file ใหญ่ขึ้น, plan/apply นานขึ้น, เสี่ยง lock กัน
การส่งค่าข้าม State File (Remote State Data)
ถ้าใช้ Pattern A ให้ root module หนึ่งอ่าน output จากอีก state file ผ่าน terraform_remote_state data source
data "terraform_remote_state" "aws_network" {
backend = "s3"
config = {
bucket = "myapp-tfstate"
key = "aws/network.tfstate"
region = "ap-southeast-1"
}
}
resource "cloudflare_record" "api" {
zone_id = var.cloudflare_zone_id
name = "api"
type = "A"
value = data.terraform_remote_state.aws_network.outputs.public_ip
}
การใช้ remote state data ทำให้แต่ละโปรเจกต์ทำงานแยกได้แต่ยังเชื่อมโยงค่ากันได้อัตโนมัติ
Use Case ที่พบบ่อย
Web application บน AWS + CDN Cloudflare
Origin server รันบน EC2 หรือ ECS, ใช้ Cloudflare เป็น CDN + WAF + DDoS protection — เขียน resource ALB + Cloudflare CNAME ใน project เดียว
Data Warehouse บน GCP + Application บน AWS
Application layer รันบน AWS EKS ส่งข้อมูลไปเก็บใน BigQuery ผ่าน Pub/Sub — เขียน Terraform สร้าง IAM role ทั้งสองฝั่งให้ cross-account access ถูกต้อง
Disaster Recovery ข้าม Cloud
Production อยู่บน AWS, DR site อยู่บน Azure — ใช้ Terraform กำหนด infra ทั้งสองให้ schema เหมือนกัน พร้อม failover script
Hybrid Cloud กับ on-premise
On-premise VMware หรือ Nutanix มี provider Terraform ของตัวเอง สามารถ deploy infra ทั้งใน data center และ public cloud ในโปรเจกต์เดียวได้
การจัดการ State File สำหรับ Multi-Cloud
แนวทางเก็บ state file ที่แนะนำ
- ใช้ backend เดียวทั้งโปรเจกต์ (เช่น S3 + DynamoDB lock) แม้จะ deploy ไปหลาย cloud
- หรือใช้ Terraform Cloud workspace เป็น backend กลาง บริหารง่ายและมี audit log ในตัว
- แยก key/path ใน backend ตาม environment และ scope เช่น
prod/aws-network,prod/gcp-data - เปิด encryption at rest และ versioning เพื่อ rollback ได้ในกรณี state corrupt
CI/CD สำหรับ Multi-Cloud
Pipeline ต้องรองรับ credential หลายชนิด ที่นิยมคือใช้ matrix job ให้แต่ละ cloud มี step apply ของตัวเอง ตัวอย่าง GitHub Actions
jobs:
terraform-apply:
strategy:
matrix:
stack: [aws, gcp, cloudflare]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: hashicorp/setup-terraform@v3
- name: Apply ${{ matrix.stack }}
working-directory: envs/${{ matrix.stack }}
run: |
terraform init
terraform apply -auto-approve
แต่ละ job ใช้ OIDC federation แยกตาม cloud ทำให้ไม่ต้องเก็บ key ใด ๆ ใน secret store ของ CI/CD เลย
ข้อควรระวัง
- อย่าพยายามสร้าง abstraction layer ที่ปิด native feature ของแต่ละ cloud เพราะจะสูญเสียข้อดีของ cloud นั้น
- Dependency ข้าม cloud ทำให้ blast radius กว้าง ถ้า AWS ล่มแล้ว Cloudflare record ชี้ไม่ถูกต้องก็แก้ลำบาก
- ราคาต่อ cloud ต่างกัน ต้องมี cost monitoring แยก เช่น AWS Cost Explorer, GCP Billing, Azure Cost Management
- การทำ network peering ข้าม cloud มีค่า egress สูงและ latency สูง ควรออกแบบให้ traffic หลักอยู่ใน cloud เดียว
- Provider version แต่ละตัว release ไม่พร้อมกัน ต้องจัดการ version pinning อย่างระวัง
- Multi-cloud ไม่ใช่คำตอบของทุกปัญหา ถ้าองค์กรไม่มี platform team พร้อม ให้เลือก cloud หลักก่อน
สรุป
Terraform ทำให้การเขียน infra แบบ multi-cloud เป็นเรื่องที่จัดการได้ผ่านภาษา HCL เดียวกัน โดยใช้ provider หลายตัว, alias สำหรับ account/region, และ pattern root-module ที่เลือกตามความแนบแน่นของ dependency ข้าม cloud หัวใจคือการจัดการ credential อย่างปลอดภัยผ่าน OIDC federation และการวางแผน state file ให้เหมาะกับขนาดทีม
ก่อนจะไป multi-cloud เต็มตัว แนะนำเริ่มจาก pattern ง่าย ๆ เช่น AWS + Cloudflare หรือ GCP + Cloudflare ก่อน เมื่อทีมคุ้นกับการจัดการ provider หลายตัวแล้วค่อยขยายไป cloud ที่สาม — การ over-engineer ตั้งแต่ต้นมักสร้าง overhead มากกว่าประโยชน์ที่ได้

