Production Checklist
ไอเดียในหนึ่งประโยค
หัวข้อที่มีชื่อว่า “ไอเดียในหนึ่งประโยค”การเอา Terraform กับ Terragrunt ขึ้น production ไม่ใช่การตัดสินใจก้อนใหญ่ก้อนเดียว แต่เป็น checklist ของการตัดสินใจเล็ก ๆ หลายอัน — แต่ละอันถูกพูดไปแล้วก่อนหน้านี้ในคอร์ส — ที่ต้องเป็นจริงพร้อมกันทั้งหมด
เช็กลิสต์
หัวข้อที่มีชื่อว่า “เช็กลิสต์”ไม่มีข้อไหนในนี้เป็นไอเดียใหม่เลย ณ จุดนี้ สิ่งที่ใหม่คือการเห็นทั้งหมดเรียงกันเป็น list เดียว เพราะความพร้อมสำหรับ production จริง ๆ แล้วก็แค่ทุกข้อในนี้เป็นจริงพร้อมกัน ไม่ใช่ข้อไหนข้อหนึ่งทำได้ดีเป็นพิเศษ
- remote S3 backend พร้อม locking ตั้งไว้ตั้งแต่วันแรก
use_lockfile = trueบน S3 backend ตั้งไว้ก่อนapplyครั้งแรก ไม่ใช่มาทำทีหลังตอนที่ state file พังแล้วบังคับให้ต้องแก้ พูดไว้ใน State Management - provider กับ module version pin ไว้ ไม่ปล่อยลอย
.terraform.lock.hclที่ commit ไว้ pin version กับ checksum ของ provider ไว้ชัดเจน ส่วนsourceของ module ใช้ tag หรือ version constraint เจาะจง ไม่ใช้ branch ที่เคลื่อนไหวได้ใต้เท้าเราเอง พูดไว้ใน Foundations กับ Modules - ทุก
planถูก review ใน CI ก่อนทุกapplyโดย reviewer สแกนหา resource replacement แบบ-/+ที่ไม่คาดคิดโดยเฉพาะ ไม่ใช่ skim plan แบบ code diff พูดไว้ในบท CI/CD ของ module นี้ - เก็บค่า sensitive ออกจาก variable และถ้าเป็นไปได้ออกจาก state ดึงจาก secrets manager ผ่าน
datasource แทนที่จะพิมพ์ลงในtfvarsพร้อมกับ encrypt state backend เองไว้ตอน rest พูดไว้ใน State Management กับบท secrets ของ module นี้ tflint, security scanner และ policy-as-code ต่อเข้ากับ CI ให้ pattern ที่รู้อยู่แล้วว่าไม่ดีกับ rule ขององค์กรถูกบังคับใช้แบบ mechanical แทนที่จะพึ่งคนคอยจับทุกเคสด้วยตา พูดไว้ในบท policy-as-code ของ module นี้- Terragrunt
dependencyblock เป็นตัวขับ rollout order ไม่ใช่คนคอยจำว่า VPC ต้อง apply ก่อน database พูดไว้ใน Terragrunt Fundamentals terraform test/.tftest.hclครอบคลุม module ไหนที่ทำอะไรที่ไม่ใช่เรื่องธรรมดา ให้การ refactor internal ของ module ถูกจับด้วย assertion แทนที่จะไปเจอตอน downstream consumer งงว่าเกิดอะไรขึ้น พูดไว้ใน Modules
# vpc/terragrunt.hcl and rds/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 = { vpc_id = "mock-vpc-id" private_subnet_ids = ["mock-subnet-id"] } mock_outputs_allowed_terraform_commands = ["plan"]}
inputs = { vpc_id = dependency.vpc.outputs.vpc_id subnet_id = dependency.vpc.outputs.private_subnet_ids[0]}run "sets_expected_engine" { command = plan
variables { identifier = "app-db-test" }
assert { condition = aws_db_instance.this.engine == "postgres" error_message = "RDS module did not default to the postgres engine" }}หนึ่งระบบที่เชื่อมกัน คือ repo layout จริง
หัวข้อที่มีชื่อว่า “หนึ่งระบบที่เชื่อมกัน คือ repo layout จริง”เอาทุกอย่างมาต่อกัน คอร์สนี้รวมเป็นรูปเดียวที่ชัดเจน — ไม่ใช่ list ของข้อเท็จจริงที่ไม่เกี่ยวกัน แต่เป็น repository เดียวที่หน้าตาแบบนี้จริง ๆ
infra/├── root.hcl├── vpc/│ └── terragrunt.hcl├── ec2/│ └── terragrunt.hcl└── rds/ └── terragrunt.hclroot.hcl อยู่บนสุด เก็บ backend configuration กับ provider generation ที่ใช้ร่วมกัน ถูก include ด้วยทุก unit ข้างใต้
remote_state { backend = "s3" config = { bucket = "acme-terraform-state" key = "${path_relative_to_include()}/terraform.tfstate" region = "us-east-1" use_lockfile = true }}include "root" { path = find_in_parent_folders("root.hcl")}
terraform { source = "../../modules/rds"}
dependency "vpc" { config_path = "../vpc" mock_outputs = { vpc_id = "mock-vpc-id" private_subnet_ids = ["mock-subnet-id"] } mock_outputs_allowed_terraform_commands = ["plan"]}
inputs = { vpc_id = dependency.vpc.outputs.vpc_id subnet_id = dependency.vpc.outputs.private_subnet_ids[0]}ทุกชิ้นในนี้ย้อนกลับไปหาบทเรียนก่อนหน้าได้หมด use_lockfile = true คือเรื่อง locking ของ module State Management root.hcl บวก include คือ pattern DRY backend ของ Terragrunt Fundamentals dependency block คือตัวที่ทำให้ rds reference output จริงของ vpc ได้โดยไม่ต้องมีคนจำว่า unit ไหนต้อง apply ก่อน และ source ../../modules/rds ที่ใน repo จริงควร pin ไว้กับ tag ไม่ใช่ปล่อยลอย คือวินัยเรื่อง versioning ของ Modules
ทั้ง tree นี้ deploy ผ่าน CI/CD shape เดียวกับที่พูดไปก่อนหน้านี้ใน module นี้เป๊ะ ๆ คือ terragrunt run --all plan รันทุก pull request ที่แตะ infra/ post output ให้ review และ terragrunt run --all apply รันหลัง merge เข้า main เท่านั้น ใช้ OIDC-federated role ตัวเดียวกัน ไม่ใช่ key ที่เก็บไว้ ไม่มีอะไรในนี้เป็น mechanism ใหม่เลย ทั้งหมดคือชิ้นเดียวกันจากทุก module ก่อนหน้า ต่อกันเป็นระบบเดียวที่ deploy vpc แล้วตามด้วย ec2 กับ rds ตามลำดับที่ dependency block อธิบายไว้ gate ด้วย plan ที่คนอ่านจริง ๆ
flowchart TB root["root.hcl: shared S3 backend, use_lockfile = true"] root --> vpc["vpc unit"] root --> ec2["ec2 unit"] root --> rds["rds unit"] vpc -->|dependency block: vpc_id, subnet_ids| ec2 vpc -->|dependency block: vpc_id, subnet_ids| rds pr["Pull request"] -->|terragrunt run --all plan| review["Reviewed for -/+ replacements"] review -->|merge to main| apply["terragrunt run --all apply"] apply --> vpc apply --> ec2 apply --> rds