Running Many Units at Once
ไอเดียในหนึ่งประโยค
หัวข้อที่มีชื่อว่า “ไอเดียในหนึ่งประโยค”terragrunt run --all apply จะไล่ทุก unit ที่อยู่ใต้ directory ปัจจุบัน apply ตามลำดับที่ dependency graph จากบทก่อนหน้ากำหนดไว้ และ --filter ก็ทำให้จำกัดการไล่นั้นให้เหลือแค่บางส่วนของ tree ได้ โดยไม่เปลี่ยนวิธีคำนวณลำดับเลย
run —all: รันทุก unit ทั่วทั้ง tree
หัวข้อที่มีชื่อว่า “run —all: รันทุก unit ทั่วทั้ง tree”เมื่อ directory หนึ่งมี unit มากกว่าหนึ่งตัว การรัน terragrunt plan หรือ terragrunt apply ทีละ unit ด้วยมือย่อมไม่ scale run --all กระจาย command เดียวไปทั่วทุก unit ที่อยู่ใต้ directory ปัจจุบัน
terragrunt run --all planterragrunt run --all applyต้องระวังผลการค้นหาและ tutorial เก่า ๆ ในเรื่องนี้เป็นพิเศษ เพราะโมดูลนี้เป็นจุดที่ข้อมูลล้าสมัยได้เร็วมาก terragrunt run-all plan และ terragrunt run-all apply คือ syntax เก่าและถูกลบทิ้งไปแล้วเช่นกัน — บน Terragrunt 1.1 จะ fail ด้วย unknown command เหมือนกับ terragrunt plan-all กับ terragrunt apply-all ที่เก่ากว่านั้นอีก มีแค่ run --all เท่านั้นที่ใช้ได้บนเวอร์ชันปัจจุบัน ถ้า guide ที่กำลังอ่านอยู่บอกให้รัน apply-all ให้ถือว่านั่นเป็นสัญญาณว่าเนื้อหาที่เหลือของ guide นั้นอาจล้าสมัยไปด้วย
จำกัดขอบเขตด้วย —filter
หัวข้อที่มีชื่อว่า “จำกัดขอบเขตด้วย —filter”การรันทุก unit ทั้ง repository ไม่ใช่สิ่งที่ต้องการเสมอไป บางครั้งแก้แค่ unit ของ subscription เดียว แล้วอยากจะ plan เฉพาะส่วนนั้น flag --filter จำกัดขอบเขต run --all ให้เหลือแค่บางส่วนของ tree ด้วย glob pattern
terragrunt run --all --filter './subscription-a/**' -- plan--filter คือวิธีปัจจุบันที่มีเอกสารรองรับสำหรับ scope การรัน — แต่ควรพูดให้ชัดว่า --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:╰── databaseINFO [database] terraform: Initializing the backend...ไม่มี error unknown flag ไม่มีการปฏิเสธเลย — flag ถูกรับและรันต่อไปเหมือน --filter './database' เป๊ะ เทียบกับ terragrunt 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 ที่ถูกลบทิ้ง ควรใช้ --filter สำหรับงานใหม่ แต่สคริปต์เดิมที่ใช้ flag เก่าไม่ได้พัง — เช็คด้วย terragrunt run --help ก่อนสมมติเป็นอย่างอื่น ส่วน -- ก่อน plan คือตัวแยก flag ของ Terragrunt เองออกจาก command ที่จะรันข้าม unit ต่าง ๆ ซึ่งสำคัญขึ้นทันทีที่ run --all เริ่มรวม flag อย่าง --filter เข้ากับ Terraform command ที่อยู่เบื้องหลัง
สำหรับ CI pipeline ที่ไม่ควรมีอะไรหยุดรอ human confirm prompt เลย ให้เพิ่ม --non-interactive
terragrunt run --all --non-interactive -- apply--non-interactive มาแทน flag --terragrunt-non-interactive แบบเก่า อีกหนึ่งรูปแบบที่ต้องระวังใน material เก่า เพราะหลาย flag เคยมี prefix terragrunt- ซึ่ง Terragrunt เวอร์ชันปัจจุบันตัดทิ้งไปแล้ว
before_hook และ after_hook: แทรกเข้าไปในการรัน unit
หัวข้อที่มีชื่อว่า “before_hook และ after_hook: แทรกเข้าไปในการรัน unit”run --all บอก Terragrunt ว่าจะแตะ unit ไหนบ้างและตามลำดับอะไร แต่บางครั้ง unit หนึ่งก็ต้องการ side effect ห่อรอบ plan หรือ apply ของตัวเอง เช่น validation script ก่อนหน้า หรือการแจ้งเตือนหลังจากนั้น before_hook และ after_hook block ที่ประกาศไว้ใน terraform block ของ unit จะรัน shell command ใด ๆ ก็ได้ ณ จุดนั้นพอดี
terraform { before_hook "validate_inputs" { commands = ["plan", "apply"] execute = ["./scripts/validate-inputs.sh"] }
after_hook "notify_slack" { commands = ["apply"] execute = ["./scripts/notify-slack.sh", "vm apply finished"] run_on_error = true }}commands ระบุว่า Terragrunt command ไหนบ้างที่ trigger hook นี้ execute คือ command จริงที่ Terragrunt รัน และ run_on_error = true บน after_hook ด้านบนหมายความว่าการแจ้งเตือน Slack จะทำงานแม้ apply เองจะ fail ก็ตาม มีประโยชน์เมื่อต้องการรู้เรื่อง failure ด้วย ไม่ใช่แค่ตอนสำเร็จ เพราะ hook พวกนี้อยู่ใน terragrunt.hcl ของ unit เอง hook จะทำงานไม่ว่า unit นั้นจะถูก apply เดี่ยว ๆ หรือเป็นส่วนหนึ่งของ run --all apply ที่กวาดไปทั่วหลาย unit ก็ตาม
flowchart LR cmd["terragrunt run --all apply"] cmd --> vnet["vnet unit"] vnet --> vm["vm unit (depends on vnet)"] vnet --> database["database unit (depends on vnet)"] vm --> hook["after_hook notify_slack"]
พิสูจน์: ลำดับจริง, find/list, และ --filter บน tree สาม unit
หัวข้อที่มีชื่อว่า “พิสูจน์: ลำดับจริง, find/list, และ --filter บน tree สาม unit”tree สาม unit จริง (vnet และ vm/database ที่ต่างพึ่งพา vnet ทั้งคู่ local backend) terragrunt find --dag และ terragrunt list --tree --dag — discovery command ไม่มี plan/apply เลย อ่าน dependency block เดียวกันกับที่ run --all จะใช้:
$ terragrunt find --dagvnetdatabasevm
$ terragrunt list --tree --dag.╰── vnet ├── database ╰── vmและ ordering log จาก run --all plan จริง รูปร่างเดียวกันเป๊ะ:
$ terragrunt run --all --non-interactive -- planINFO The following units will be run, starting with dependencies and then their dependents:╰── vnet ├── database ╰── vm--filter scope เหลือแค่ database — มีแค่ unit เดียวที่รัน vnet ไม่ถูกดึงเข้ามาเลย เพราะ mock_outputs_allowed_terraform_commands = ["plan"] ทำให้ database plan สำเร็จได้โดยไม่ต้องมี state จริงของ vnet:
$ 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 สร้าง”ตอนรีวิว multi-unit run config ที่ AI สร้าง ให้เช็คสิ่งเหล่านี้:
plan-all/apply-all/run-allอยู่ในสคริปต์หรือ CI pipeline ที่ไหนก็ตาม — พิสูจน์ข้างบนว่าทั้งสามตัว fail ทันทีด้วยunknown commandบน Terragrunt ปัจจุบันterragrunt run --all --queue-include-dir ... -- planที่ถูกมองว่าพังหรือถูกเขียนใหม่พร้อม comment แบบ “removed in new Terragrunt” — พิสูจน์ข้างบนว่า flag นี้ยังรันได้ถูกต้อง มีแค่--filterเท่านั้นที่เป็นคำแนะนำ ปัจจุบัน ไม่ใช่ตัวเดียวที่ ใช้งานได้- 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