Production Checklist
ไอเดียในหนึ่งประโยค
หัวข้อที่มีชื่อว่า “ไอเดียในหนึ่งประโยค”การ ship Terraform กับ Terragrunt ขึ้น production บน GCP ไม่ใช่การตัดสินใจก้อนใหญ่ก้อนเดียว แต่เป็น checklist ของการตัดสินใจเล็ก ๆ หลายข้อ — แต่ละข้อครอบคลุมไปแล้วก่อนหน้านี้ในคอร์ส — ที่ต้องเป็นจริงพร้อมกันทั้งหมด
เช็กลิสต์
หัวข้อที่มีชื่อว่า “เช็กลิสต์”ไม่มีข้อไหนในนี้เป็นไอเดียใหม่เลยตอนนี้ สิ่งที่ใหม่คือการเห็นทั้งหมดเรียงกันเป็นลิสต์เดียว เพราะความพร้อมสำหรับ production คือทุกข้อเป็นจริงพร้อม ๆ กัน ไม่ใช่ข้อใดข้อหนึ่งทำได้ดีเป็นพิเศษ
- remote GCS backend ที่มี locking ในตัวตั้งไว้ตั้งแต่วันแรก บล็อก
backend "gcs"ตั้งไว้ก่อนapplyครั้งแรก ไม่ใช่มาเพิ่มทีหลังหลังจาก state file เสียหายจนต้องแก้ปัญหาแบบเร่งด่วน GCS ไม่ต้องมี lock-table resource แยกต่างหาก locking เป็นส่วนหนึ่งของ backend อยู่แล้ว ครอบคลุมไว้ใน State Management - provider กับ module version ต้อง pin ไว้ ไม่ปล่อยลอย
.terraform.lock.hclที่ commit ไว้ pin provider version กับ checksum ไว้แน่นอน modulesourcereference ใช้ tag หรือ version constraint ที่ระบุไว้ชัด ไม่ใช่ชื่อ branch ที่เลื่อนไปเรื่อย ๆ ใต้เท้าคุณ ครอบคลุมไว้ใน Foundations กับ Modules - ทุก
planถูก review ใน CI ก่อนทุกapplyโดย reviewer เฝ้าดู-/+resource replacement ที่ไม่คาดคิดโดยเฉพาะ ไม่ใช่แค่สแกนผ่าน plan แบบ code diff ครอบคลุมไว้ในบทเรียน CI/CD ของ module นี้ - ค่า sensitive ต้องไม่อยู่ใน variable และเท่าที่ทำได้ต้องไม่อยู่ใน state ดึงจาก Secret Manager ผ่าน
datasource แทนที่จะพิมพ์ใส่ไฟล์tfvarsโดยมี IAM จำกัดสิทธิ์อย่างแน่นหนาว่าใครอ่าน state bucket ได้บ้าง ครอบคลุมไว้ใน State Management กับบทเรียน secret ของ module นี้ tflint, security scanner, และถ้าเป็นไปได้ policy-as-code ต่อเข้ากับ CI เพื่อให้ pattern ที่รู้อยู่แล้วว่าแย่และกฎขององค์กรถูกบังคับใช้แบบเชิงกลไก ไม่ต้องพึ่งคนคอยจับทุกเคสด้วยตา ครอบคลุมไว้ในบทเรียน policy-as-code ของ module นี้dependencyblock ของ Terragrunt เป็นตัวขับลำดับการ rollout ไม่ใช่คนคอยจำเองว่า VPC ต้อง apply ก่อน database ครอบคลุมไว้ใน Terragrunt Fundamentalsterraform test/.tftest.hclครอบคลุม module ไหนที่ทำอะไรที่ไม่ trivial เพื่อให้การ refactor ภายใน module ถูกจับได้ด้วย assertion แทนที่จะให้ downstream consumer มาเจอเอาเองแบบไม่ทันตั้งตัว ครอบคลุมไว้ใน Modules
# gke/terragrunt.hcl and cloudsql/terragrunt.hcl both use dependency blocks# so rollout order is driven by Terragrunt, not a person's memory.dependency "vpc" { config_path = "../vpc"
mock_outputs = { network_self_link = "mock-network-self-link" private_subnet_self_links = ["mock-subnet-self-link"] } mock_outputs_allowed_terraform_commands = ["plan"]}
inputs = { network_self_link = dependency.vpc.outputs.network_self_link subnetwork = dependency.vpc.outputs.private_subnet_self_links[0]}run "sets_expected_database_version" { command = plan
variables { instance_name = "app-db-test" }
assert { condition = google_sql_database_instance.this.database_version == "POSTGRES_15" error_message = "Cloud SQL module did not default to the POSTGRES_15 engine" }}ระบบเดียวที่เชื่อมกันหมด: repo layout จริง
หัวข้อที่มีชื่อว่า “ระบบเดียวที่เชื่อมกันหมด: repo layout จริง”เอาทุกอย่างมารวมกันแล้วคอร์สนี้ก็รวมเป็นรูปทรงเดียวที่ชัดเจน ไม่ใช่ลิสต์ข้อเท็จจริงที่แยกกันเดี่ยว ๆ แต่เป็น repo หนึ่งอันที่หน้าตาแบบนี้จริง ๆ
infra/├── root.hcl├── vpc/│ └── terragrunt.hcl├── gke/│ └── terragrunt.hcl└── cloudsql/ └── terragrunt.hclroot.hcl อยู่บนสุดถือ configuration ของ backend และการ generate provider ที่ใช้ร่วมกัน ถูก include โดยทุก unit ข้างล่าง
remote_state { backend = "gcs"
generate = { path = "backend.tf" if_exists = "overwrite_terragrunt" }
config = { bucket = "acme-terraform-state" prefix = "${path_relative_to_include()}/terraform.tfstate" project = "acme-prod-123456" location = "US" }}include "root" { path = find_in_parent_folders("root.hcl")}
terraform { source = "../../modules/cloudsql"}
dependency "vpc" { config_path = "../vpc" mock_outputs = { network_self_link = "mock-network-self-link" private_subnet_self_links = ["mock-subnet-self-link"] } mock_outputs_allowed_terraform_commands = ["plan"]}
inputs = { network_self_link = dependency.vpc.outputs.network_self_link subnetwork = dependency.vpc.outputs.private_subnet_self_links[0]}ทุกชิ้นส่วนตรงนี้ย้อนกลับไปหาบทเรียนก่อนหน้าได้หมด GCS locking ในตัวคือเรื่อง locking ของ module State Management, root.hcl คู่กับ include คือ DRY backend pattern ของ Terragrunt Fundamentals, dependency block คือสิ่งที่ให้ cloudsql reference output จริงของ vpc ได้โดยไม่ต้องมีคนจำว่า unit ไหนต้อง apply ก่อน และ source ../../modules/cloudsql ที่ pin ไว้กับ tag ใน repo จริง ไม่ปล่อยลอย คือวินัยเรื่อง versioning ของ Modules
ทั้ง tree นี้ถูก ship ผ่าน CI/CD shape เดียวกับที่ผ่านมาใน module นี้เป๊ะ terragrunt run --all plan รันทุก pull request ที่แตะ infra/ โพสต์ output ให้ review และ terragrunt run --all apply รันแค่หลัง merge เข้า main authenticate ผ่าน Workload Identity Federation ตัวเดียวกัน ไม่ใช่ key ที่ดาวน์โหลดมา ไม่มีอะไรตรงนี้เป็นกลไกใหม่เลย ทั้งหมดคือชิ้นส่วนเดียวกันจากทุก module ก่อนหน้า เอามาต่อกันเป็นระบบเดียวที่ deploy vpc แล้วตามด้วย gke กับ cloudsql ตามลำดับที่ dependency block บอกไว้ gate ด้วย plan ที่มีคนอ่านจริง ๆ
flowchart TB root["root.hcl: shared GCS backend, built-in locking"] root --> vpc["vpc unit"] root --> gke["gke unit"] root --> cloudsql["cloudsql unit"] vpc -->|dependency block: network_self_link, subnet links| gke vpc -->|dependency block: network_self_link, subnet links| cloudsql pr["Pull request"] -->|terragrunt run --all plan| review["Reviewed for -/+ replacements"] review -->|merge to main| apply["terragrunt run --all apply"] apply --> vpc apply --> gke apply --> cloudsql