Remote state & locking
สิ่งที่จะสร้าง
หัวข้อที่มีชื่อว่า “สิ่งที่จะสร้าง”block remote_state ใน root.hcl ของแต่ละ cloud — ชิ้นที่ย้าย state ออกจาก laptop ของคุณเข้าไปที่ S3, GCS, และ Azure Blob Storage แต่ละตัวมี locking เพื่อไม่ให้ apply สองครั้งชนกัน เราจะใช้ path_relative_to_include() เพื่อให้ state ของทุก unit ลงจอดที่ path ที่สะท้อน filesystem แล้ว generate provider ที่ถูกต้องต่อ cloud
นี่ปิดฐานของ Terragrunt หลังจากนี้ ทุก module ในคอร์สสืบทอด remote, locked, encrypted state ฟรี ๆ — ชั้น network, cluster, data, และ iam ทำงานได้เลย
local state สอบตกทั้งสองข้อจาก Module 2: แชร์ไม่ได้และไม่ encrypt remote state แก้ทั้งคู่ state อยู่ใน cloud bucket, encrypt at rest; locking ทำให้แน่ใจว่าขณะที่ apply หนึ่งถือ lock อยู่ อีกตัวจะรอแทนที่จะทำไฟล์พัง นั่นคือสิ่งที่ให้เพื่อนร่วมทีม — หรือ CI job — apply ได้อย่างปลอดภัยโดยไม่ทับคุณ
แต่ละ cloud เก็บ backend ของตัวเอง: AWS state ใน S3, GCP state ใน GCS, Azure state ใน Azure Storage นี่ไม่ใช่แค่ความเป็นระเบียบ — แต่คือการแยก blast-radius และ data residency ปัญหากับ backend ของ cloud หนึ่งไม่ทำให้อีกสองตัวล่มได้ และ state ของแต่ละ cloud อยู่ใน cloud นั้น
path_relative_to_include() คือฟังก์ชันเล็ก ๆ ที่ทำให้สิ่งนี้ scale ได้ โดย return path ของ unit เทียบกับ root.hcl ที่ include อยู่ ดังนั้น live/aws/network เขียน state ไปที่ network/terraform.tfstate และ live/aws/cluster ไปที่ cluster/terraform.tfstate — อัตโนมัติ โดยไม่ต้องมี backend config ต่อ unit
ข้อดีข้อเสีย
หัวข้อที่มีชื่อว่า “ข้อดีข้อเสีย”remote state พร้อม locking เทียบกับ local state:
- Pros: แชร์ได้, encrypt, และปลอดภัยสำหรับ apply พร้อมกัน state รอดจาก laptop ที่หาย และ CI apply ต่อ source of truth เดียวกับคุณได้
- Cons: คุณต้อง bootstrap backend ก่อน (ปัญหาไก่กับไข่ด้านล่าง) และ lock ที่ค้างบางครั้งต้องใช้
terragrunt force-unlockต้นทุน operational เล็ก ๆ แลกกับความปลอดภัยก้อนใหญ่
แต่ละ cloud เก็บ backend ของตัวเอง เทียบกับ backend กลางตัวเดียวสำหรับทั้งสาม:
- Pros: การแยก — outage หรือ misconfiguration ของ backend หนึ่งกระทบตัวอื่นไม่ได้ — บวก data residency และ IAM ที่อยู่ภายในแต่ละ cloud
- Cons: สาม backend ต้องสร้างและ secure แทนที่จะเป็นตัวเดียว เรายอมรับ: การรวม state ของสาม cloud ไว้ที่ provider เดียวเท่ากับยื่นกุญแจของทั้ง platform ให้ provider นั้น ซึ่งขัดกับจุดประสงค์ของ multi-cloud
ติดตั้ง
หัวข้อที่มีชื่อว่า “ติดตั้ง”1. Bootstrap the backends
หัวข้อที่มีชื่อว่า “1. Bootstrap the backends”backend ต้องมีอยู่ก่อน Terraform จึงจะเก็บ state ลงไปได้ — คุณจัดการ state bucket ด้วย state ที่อยู่ในนั้นไม่ได้ สร้างแต่ละตัวครั้งเดียวด้วยมือ:
# AWS — S3 bucket + DynamoDB lock tableaws s3api create-bucket --bucket clouddeploy-tfstate-aws --region us-east-1aws s3api put-bucket-versioning --bucket clouddeploy-tfstate-aws \ --versioning-configuration Status=Enabledaws dynamodb create-table --table-name clouddeploy-locks \ --attribute-definitions AttributeName=LockID,AttributeType=S \ --key-schema AttributeName=LockID,KeyType=HASH \ --billing-mode PAY_PER_REQUEST --region us-east-1
# GCP — GCS bucket (locking is built in, no lock table needed)gcloud storage buckets create gs://clouddeploy-tfstate-gcp \ --location=us-central1 --uniform-bucket-level-access
# Azure — storage account + container (blob leasing provides locking)az group create --name clouddeploy-tfstate --location eastusaz storage account create --name clouddeploytfstate \ --resource-group clouddeploy-tfstate --location eastus --sku Standard_LRSaz storage container create --name tfstate --account-name clouddeploytfstate2. live/aws/root.hcl — S3 backend + DynamoDB lock
หัวข้อที่มีชื่อว่า “2. live/aws/root.hcl — S3 backend + DynamoDB lock”remote_state { backend = "s3" generate = { path = "backend.tf" if_exists = "overwrite_terragrunt" } config = { bucket = "clouddeploy-tfstate-aws" key = "${path_relative_to_include()}/terraform.tfstate" region = "us-east-1" encrypt = true dynamodb_table = "clouddeploy-locks" }}
generate "provider" { path = "provider.tf" if_exists = "overwrite_terragrunt" contents = <<EOFprovider "aws" { region = "us-east-1"}EOF}Recent Terraform can lock S3 state natively with
use_lockfile = true, retiring the separate DynamoDB table. This course keepsdynamodb_tablebecause it’s still the most widely deployed setup you’ll meet — but on a greenfield backend, the native lockfile is the simpler choice.
3. live/gcp/root.hcl — GCS backend + google provider
หัวข้อที่มีชื่อว่า “3. live/gcp/root.hcl — GCS backend + google provider”GCS lock state ในตัว จึงไม่มี lock table ให้จัดการ:
remote_state { backend = "gcs" generate = { path = "backend.tf" if_exists = "overwrite_terragrunt" } config = { bucket = "clouddeploy-tfstate-gcp" prefix = "${path_relative_to_include()}" }}
generate "provider" { path = "provider.tf" if_exists = "overwrite_terragrunt" contents = <<EOFprovider "google" { project = "YOUR_GCP_PROJECT_ID" region = "us-central1"}EOF}4. live/azure/root.hcl — azurerm backend + azurerm provider
หัวข้อที่มีชื่อว่า “4. live/azure/root.hcl — azurerm backend + azurerm provider”Azure lock state ผ่าน blob leasing ซึ่งมีในตัวเช่นกัน:
remote_state { backend = "azurerm" generate = { path = "backend.tf" if_exists = "overwrite_terragrunt" } config = { resource_group_name = "clouddeploy-tfstate" storage_account_name = "clouddeploytfstate" container_name = "tfstate" key = "${path_relative_to_include()}/terraform.tfstate" }}
generate "provider" { path = "provider.tf" if_exists = "overwrite_terragrunt" contents = <<EOFprovider "azurerm" { features {}}EOF}รูปทรงเดียวกันบนทุก cloud: block remote_state ที่ key มาจาก path_relative_to_include() และ block generate "provider" ต่างแค่ชนิด backend และ provider
ตรวจสอบผล
หัวข้อที่มีชื่อว่า “ตรวจสอบผล”init AWS network unit ใหม่; ตอนนี้ Terragrunt ตั้งค่า S3 backend และถ้าคุณเคยมี local state Terragrunt จะเสนอ migrate ให้:
cd live/aws/networkterragrunt initInitializing the backend...Successfully configured the backend "s3"! Terraform will automaticallyuse this backend unless the backend configuration changes.apply แล้วยืนยันว่า state object ลงจอดใน bucket จริง — สะท้อน path ของ unit:
terragrunt applyaws s3 ls s3://clouddeploy-tfstate-aws/network/# → terraform.tfstateพิสูจน์ว่า locking ทำงาน: เริ่ม apply แล้วขณะที่กำลังรันกลางคัน เปิดตัวที่สองใน terminal อื่น ตัวที่สองปฏิเสธที่จะไปต่อ:
Error acquiring the state lockLock Info: ID: 3f2a... Operation: OperationTypeApply Who: you@laptop ...สุดท้าย plan ทั้ง cloud ตอนนี้ที่ state เป็น remote และ locked แล้ว:
cd live/awsterragrunt run --all plan # No changes across the tree — state matches reality.คุณเสร็จเมื่อ init รายงานว่า backend ตั้งค่าแล้ว, state object โผล่ใน bucket ที่ path ของ unit, และ apply ตัวที่สองที่พร้อมกันถูก lock บล็อก
ตรวจสอบความเข้าใจ:
- ทำไมคุณต้องสร้าง state bucket และ lock table ก่อน Terraform จะใช้ได้ — และคุณจะจัดการอย่างไรโดยไม่ติดปัญหาไก่กับไข่?
- state locking ป้องกันอะไร และ backend S3, GCS, และ azurerm แต่ละตัวให้ locking มาอย่างไร?
path_relative_to_include()คำนวณอะไร และทำไมการใช้ใน state key ถึง scale ไปทั้ง tree ของ unit ได้?- ให้เหตุผลสองข้อว่าทำไมแต่ละ cloud ถึงเก็บ backend ของตัวเองแทนที่จะรวม state ของทั้งสาม cloud ไว้ที่เดียว
ตอนนี้ state อยู่ใน backend ของแต่ละ cloud, encrypt และ locked, พร้อม key ที่วางให้สะท้อน tree live/ — เขียนครั้งเดียวใน root.hcl แล้วทุก unit สืบทอด นั่นทำให้ฐานของ IaC ครบ: module ที่ reusable, Terragrunt unit แบบ DRY, และ remote locked state บนทั้งสาม cloud ทุก module จากนี้สร้างบนฐานนี้ ต่อไปเราจะเขียน infrastructure module จริงตัวแรก — network — แล้วยืนขึ้นมาสามแบบหลัง interface เดียว
Next: Networking →