Running Many Units at Once
ไอเดียในหนึ่งประโยค
หัวข้อที่มีชื่อว่า “ไอเดียในหนึ่งประโยค”terragrunt run --all plan กับ terragrunt run --all apply operate กับทุก unit ใต้ directory tree ปัจจุบันพร้อมกัน ตามลำดับ dependency ที่ Terragrunt คำนวณเองจาก dependency block ของแต่ละ unit และ scope ให้แคบลงได้ด้วย --filter หรือห่อด้วย before_hook/after_hook สำหรับอะไรก็ตามที่ต้องเกิดขึ้นรอบ ๆ run ของ unit ใดตัวหนึ่ง
run —all: operate กับทุก unit พร้อมกัน
หัวข้อที่มีชื่อว่า “run —all: operate กับทุก unit พร้อมกัน”apply unit vpc แล้วค่อย compute แล้วค่อย database ทีละตัวด้วยมือทำได้ แต่ไม่ scale เกินไปกว่า unit ไม่กี่ตัว แถมยังผลักภาระให้คุณต้องจำลำดับที่ถูกต้องเองอยู่ดี run --all ตัดปัญหานี้ทิ้งด้วยการชี้ Terragrunt ไปที่ directory แล้วสั่งให้รัน command ที่กำหนดกับทุก unit ข้างใต้นั้น
terragrunt run --all planterragrunt run --all applyนี่คือ syntax ของ command ปัจจุบัน คุ้มค่าที่จะพูดให้ชัดว่ามาแทนอะไร เพราะ content จำนวนมากบนอินเทอร์เน็ตยังสอน syntax เก่าอยู่ terragrunt run-all plan แบบไม่มีช่องว่างก่อน --all รวมถึง command เก่ากว่าคือ terragrunt plan-all กับ terragrunt apply-all ถูกเอาออกไปหมดทั้งคู่แล้วบน Terragrunt version ปัจจุบัน การรันคำสั่งไหนในสามคำสั่งนี้ไม่ได้แค่ขึ้น deprecation warning แล้วรันต่อ แต่ fail ทันทีด้วย unknown command เพราะ Terragrunt ไม่ forward command ที่ไม่รู้จักให้อีกต่อไปโดย default ถ้า tutorial, blog post, หรือ internal wiki ไหนยังโชว์ run-all, plan-all หรือ apply-all อยู่ ให้ถือว่านั่นคือสัญญาณว่า content นั้นเก่ากว่า CLI ปัจจุบัน และต้อง verify ส่วนที่เหลือก่อนเชื่อ
scope ด้วย —filter และรันแบบ non-interactive ใน CI
หัวข้อที่มีชื่อว่า “scope ด้วย —filter และรันแบบ non-interactive ใน CI”รัน run --all จากบนสุดของ repository เลยแตะทุก unit ในนั้นหมด ซึ่งไม่ค่อยใช่สิ่งที่คุณต้องการตอน iterate เร็ว ๆ กับส่วนใดส่วนหนึ่งของ tree flag --filter scope run --all ให้แคบลงเหลือแค่บาง unit ตาม path
terragrunt run --all --filter './project-a/**' -- plan--filter คือวิธี scope multi-unit run แบบรวมและเป็นปัจจุบัน โดยมาแทน flag เก่าอย่าง --queue-include-dir กับ --queue-exclude-dir ควรพูดให้ชัดว่า “แทน” แปลว่าอะไรตรงนี้ เพราะอยู่ติดกับกลุ่มคำสั่งที่ถูกลบทิ้งจริง ๆ ในบทเดียวกันนี้: flag เก่าเป็น soft-deprecated ไม่ใช่ถูกลบ พิสูจน์จริง รัน flag เก่าตรง ๆ กับ Terragrunt 1.1.0 ที่ติดตั้งในคอร์สนี้:
$ terragrunt run --all --queue-include-dir './database' -- planINFO The following units will be run, starting with dependencies and then their dependents:╰── databaseไม่มี error unknown flag เลย — flag ถูกรับและรันต่อไปเหมือน --filter './database' เป๊ะ เทียบกับ run-all/plan-all/apply-all ข้างบนที่ fail ทันทีด้วย error จริงแบบ unknown command — พวกนั้น hard-removed ตาม migration guide ของ Terragrunt CLI-redesign --queue-include-dir/--queue-exclude-dir/--queue-strict-include ถูกอธิบายว่าเป็น flag ที่ควรย้ายออกจาก ไม่ใช่ flag ที่ถูกลบทิ้ง double dash ก่อน plan แยก flag ของ Terragrunt เองออกจาก command ที่จะรันกับแต่ละ unit ที่ match
CI pipeline ต้องการ flag เพิ่มอีกตัวหนึ่ง ปกติ Terragrunt ถาม confirmation ก่อน operation บางอย่าง ซึ่งไม่มีความหมายใน pipeline run แบบ non-interactive --non-interactive ข้าม prompt เหล่านั้นไป มาแทน flag เก่าอย่าง --terragrunt-non-interactive
terragrunt run --all --non-interactive -- applybefore_hook กับ after_hook: รัน command รอบ operation ของ unit
หัวข้อที่มีชื่อว่า “before_hook กับ after_hook: รัน command รอบ operation ของ unit”dependency block กับ run --all จัดการเรื่องลำดับ resource ข้าม unit แต่ workflow จริงจำนวนไม่น้อยต้องการรัน shell command ตามใจที่ผูกกับ operation ของ unit ใดตัวหนึ่งโดยเฉพาะ เช่น validate อะไรบางอย่างก่อน plan หรือแจ้งทีมหลัง apply สำเร็จ before_hook กับ after_hook block ที่ประกาศข้างใน terraform block ของ unit ทำสิ่งนั้นตรง ๆ
terraform { before_hook "validate_naming" { commands = ["plan", "apply"] execute = ["./scripts/validate-naming.sh"] }
after_hook "notify_slack" { commands = ["apply"] execute = ["./scripts/notify-slack.sh", "compute unit applied"] run_on_error = false }}commands list ว่า Terragrunt command ไหนบ้าง trigger hook นี้ validate_naming ข้างบนรันก่อนทั้ง plan และ apply บน unit นี้ ส่วน notify_slack รันเฉพาะหลัง apply ที่สำเร็จ execute คือ command จริงที่ Terragrunt รัน เขียนเป็น list ไม่ใช่ shell string เดี่ยว ๆ run_on_error = false บน notify_slack แปลว่าการแจ้งเตือน Slack fire เฉพาะตอน apply สำเร็จจริงเท่านั้น ถ้าตั้งเป็น true hook จะ fire แม้ run นั้น fail ก็ตาม ซึ่งเหมาะกับ hook ที่ต้อง cleanup หรือแจ้งเตือนตอน fail ไม่ว่าผลจะออกมายังไง
flowchart TD cmd["terragrunt run --all apply"] cmd --> vpc["vpc unit"] vpc --> compute["compute unit (depends on vpc)"] vpc --> database["database unit (depends on vpc)"] compute --> hook["after_hook: notify_slack"]
พิสูจน์: ลำดับจริง, find/list, และ --filter บน tree สาม unit
หัวข้อที่มีชื่อว่า “พิสูจน์: ลำดับจริง, find/list, และ --filter บน tree สาม unit”tree สาม unit จริง (vpc และ compute/database ที่ต่างพึ่งพา vpc ทั้งคู่ local backend) terragrunt find --dag และ terragrunt list --tree --dag — discovery command ไม่มี plan/apply เลย อ่าน dependency block เดียวกันกับที่ run --all จะใช้:
$ terragrunt find --dagvpccomputedatabase
$ terragrunt list --tree --dag.╰── vpc ├── compute ╰── databaseและ ordering log จาก run --all plan จริง รูปร่างเดียวกันเป๊ะ:
$ terragrunt run --all --non-interactive -- planINFO The following units will be run, starting with dependencies and then their dependents:╰── vpc ├── compute ╰── database--filter scope เหลือแค่ database — มีแค่ unit เดียวที่รัน vpc ไม่ถูกดึงเข้ามาเลย เพราะ mock_outputs_allowed_terraform_commands = ["plan"] ทำให้ database plan สำเร็จได้โดยไม่ต้องมี state จริงของ vpc:
$ terragrunt run --all --filter './database' --non-interactive -- planINFO The following units will be run, starting with dependencies and then their dependents:╰── database
❯❯ Run Summary 1 units Succeeded 1errors { retry / ignore }: ชื่อ block จริงในปัจจุบัน
หัวข้อที่มีชื่อว่า “errors { retry / ignore }: ชื่อ block จริงในปัจจุบัน”Terragrunt ปัจจุบันเรียก block นี้ว่า errors มี sub-block retry กับ ignore:
errors { ignore "missing_file" { ignorable_errors = [".*no file exists at.*"] message = "Ignoring missing source file for this demo unit" }}พิสูจน์ว่า ignore block เปลี่ยน exit code จริง ๆ — unit ที่ plan fail จริง (file() reference ไฟล์ที่ไม่มีอยู่) พร้อม ignore block ที่ match:
$ terragrunt run --non-interactive -- planSTDERR terraform: Error: Invalid function argumentWARN Ignoring error, reason: Ignoring missing source file for this demo unit$ echo $?0ถ้าไม่มี ignore block plan ที่ fail เหมือนกันเป๊ะนี้จะ exit non-zero พอมี ignore block error ที่ match จะถูกลดระดับเป็น warning และ run รายงานว่าสำเร็จ — regex ที่กว้างเกินตั้งใจจะกลืน failure ที่ CI pipeline ควรจะจับได้ไปเงียบ ๆ
อ่านโค้ดที่ AI สร้าง
หัวข้อที่มีชื่อว่า “อ่านโค้ดที่ AI สร้าง”plan-all/apply-all/run-allอยู่ในสคริปต์หรือ CI pipeline ที่ไหนก็ตาม — พิสูจน์ข้างบนว่าทั้งสามตัว fail ทันทีด้วยunknown commandterragrunt run --all --queue-include-dir ... -- planที่ถูกมองว่าพังหรือถูกเขียนใหม่พร้อม comment แบบ “removed in new Terragrunt” — พิสูจน์ข้างบนว่า flag นี้ยังรันได้ถูกต้อง- regex
ignorable_errorsที่ดูสมเหตุสมผลแต่ไม่ match error text จริงของ provider — ทดสอบ regex กับ error ที่ capture จริงเสมอ อย่าเดา
🐛 หา bug ในโค้ดนี้
errors {ignore "flaky_dns" { ignorable_errors = [".*DNS resolution failed.*"] message = "Known flaky DNS in this CI runner, ignoring"}}เฉลย
ไม่ใช่บั๊ก syntax — เป็นบั๊กเรื่อง scope ignorable_errors regex match กับ error text อะไรก็ได้ ที่เกิดระหว่างการรันของ unit นั้น ไม่ใช่เฉพาะ DNS-resolution failure เท่านั้น bug จริงของ provider ที่บังเอิญมีคำว่า “DNS resolution failed” เป็นบรรทัดหนึ่งข้างใน error ที่ใหญ่กว่าและไม่เกี่ยวข้องเลย ก็จะถูก ignore ไปเงียบ ๆ เหมือนกัน เพราะ regex matching ไม่มีแนวคิดเรื่อง “นี่คือ root cause จริง เทียบกับ text ที่บังเอิญปนมา”
Mental Model
หัวข้อที่มีชื่อว่า “Mental Model”run --all สร้าง DAG (พิสูจน์ข้างบนผ่าน find --dag/list --dag ที่คืนลำดับเดียวกันเป๊ะกับที่ run --all จริงใช้) แล้วเดินตามมัน --filter จำกัดว่า node ไหนของกราฟนั้นถูกเยี่ยมเลย errors block อยู่ชั้นล่างกว่าการเดินนั้นหนึ่งชั้น ตัดสินใจต่อ unit ว่า failure หนึ่งจะ fatal กับทั้ง run หรือถูก log แล้วถือเป็นความสำเร็จ (อันตราย ถ้า regex กว้างเกินไป)
Trade-offs
หัวข้อที่มีชื่อว่า “Trade-offs”| ทางเลือก | ได้อะไร | เสียอะไร |
|---|---|---|
--filter แทน --queue-include-dir | syntax ปัจจุบัน มีเอกสารรองรับ เป็น path-glob dialect จริง | สคริปต์เดิมที่ใช้ --queue-include-dir ไม่ได้พัง (พิสูจน์ข้างบน) |
errors { ignore } | failure mode ที่รู้จักและเข้าใจแล้ว ไม่ทำให้ run --all ทั้งก้อนหยุด | พิสูจน์ข้างบนด้วย SpotTheBug: regex ที่กว้างเกินตั้งใจกลืน failure จริงที่ไม่เกี่ยวข้องไปเงียบ ๆ |
ความเข้าใจผิดที่พบบ่อย
หัวข้อที่มีชื่อว่า “ความเข้าใจผิดที่พบบ่อย”- “flag ไหนที่ไม่อยู่ในเอกสารปัจจุบันแปลว่าพังแล้ว” พิสูจน์ข้างบน:
--queue-include-dirยังรันinit+planจริงได้โดยไม่มี error เลยบน Terragrunt 1.1.0 ที่ติดตั้งในคอร์สนี้ — soft-deprecated ไม่เหมือนกับถูกลบ - “
errors { ignore }block match แค่สถานการณ์ error เป๊ะ ๆ ที่ฉันเขียนมันไว้” พิสูจน์ข้างบนด้วย SpotTheBug: มัน match error text อะไรก็ได้ที่ regex บังเอิญ hit