ข้ามไปยังเนื้อหา

Dependencies Between Units

dependency block ทำให้ unit หนึ่งอ่าน output ของอีก unit ได้ แม้ทั้งสอง unit จะมี Terraform state แยกกันคนละก้อนเลย และ mock_outputs ทำให้ unit ที่พึ่งพานั้น plan ได้ก่อนที่จะ apply unit ที่พึ่งพาจริง

ใน module Modules คุณเคยเห็น output ของ Terraform module หนึ่งไหลตรงเข้าไปเป็น input ของอีก module หนึ่ง network_id ที่มาจาก vpc module ถูกส่งเป็น argument เข้า compute module ทั้งสองต่อกันอยู่ใน main.tf เดียวกัน apply พร้อมกันเป็น terraform apply ครั้งเดียวกับ state ไฟล์เดียว composition แบบนี้ทำงานได้เพราะทั้งสอง module อยู่ใน Terraform run เดียวกัน Terraform เห็น dependency graph ทั้งหมดและ resolve ได้ในรอบเดียว

Terragrunt unit ตั้งใจทำลายสมมติฐานนั้น unit vpc กับ unit compute มี terragrunt.hcl ของตัวเอง มี state ไฟล์ของตัวเอง และมี terraform apply อิสระของตัวเอง ไม่มีอะไรใน Terraform module block ธรรมดาที่เอื้อมข้าม boundary นี้ได้ เพราะในมุมมองของ Terraform run ของแต่ละ unit เอง resource ของอีก unit ไม่มีอยู่จริงเลย การเชื่อม network ของ unit vpc เข้ากับ unit compute จึงต้องใช้กลไกที่สร้างมาเพื่อช่องว่างนี้โดยเฉพาะ นั่นคือ dependency block

dependency block เรียกชื่อ unit อื่นด้วย directory ของ unit นั้น แล้ว expose output ของ unit นั้นเป็นค่าที่ unit นี้ใช้ตรง ๆ ใน inputs ของตัวเองได้เลย

compute/terragrunt.hcl
include "root" {
path = find_in_parent_folders("root.hcl")
}
dependency "vpc" {
config_path = "../vpc"
mock_outputs = {
network_id = "mock-network-id"
}
mock_outputs_allowed_terraform_commands = ["plan"]
}
terraform {
source = "git::[email protected]:acme/infrastructure-modules.git//compute?ref=v1.2.0"
}
inputs = {
network_id = dependency.vpc.outputs.network_id
}

config_path = "../vpc" ชี้ไปที่ directory ของ unit vpc เบื้องหลัง Terragrunt รันสิ่งที่เทียบเท่ากับ terraform output กับ state ของ unit นั้น แล้ว expose ผลลัพธ์ออกมาเป็น dependency.vpc.outputs ทำให้ dependency.vpc.outputs.network_id เอื้อมเข้าไปหยิบค่า output network_id จาก state ที่ apply จริงของ unit vpc ชื่อ output เดียวกับที่ Terraform module ของ unit vpc เองประกาศไว้ด้วย output block ค่านั้นไหลตรงเข้า inputs ของ unit นี้ เหมือนค่า input อื่น ๆ ทั่วไป

การอ่าน output จริงทำงานได้ก็ต่อเมื่อ unit vpc ถูก apply ไปแล้วจริง ๆ และมี state จริงให้อ่าน ก่อนหน้านั้น plan บน compute จะ fail ทันที เพราะ dependency.vpc.outputs.network_id ไม่มีอะไรให้ resolve mock_outputs แก้ปัญหานี้ด้วยการให้ค่า placeholder ที่ Terragrunt ใช้แทนทุกครั้งที่ dependency ยังไม่ถูก apply และ mock_outputs_allowed_terraform_commands จำกัดว่า command ไหนบ้างที่ใช้ placeholder นี้ได้ ในตัวอย่างข้างบน mock-network-id แทนค่าจริงเฉพาะตอน plan เท่านั้น ส่วน apply จริงยังต้องการ output จริงของ unit vpc อยู่ดี ทำให้ค่า placeholder ไม่มีทางหลุดไปเขียนลง infrastructure จริงโดยไม่ตั้งใจ

เพราะ dependency block เรียกชื่อ unit อื่นตรง ๆ Terragrunt จึงเห็น graph ทั้งหมดว่า unit ไหนพึ่งพา unit ไหน ทั่วทั้ง directory tree เวลาคุณรัน operation กับหลาย unit พร้อมกัน ที่เป็นหัวข้อของบทถัดไป Terragrunt ใช้ graph นั้นคำนวณลำดับที่ถูกต้องเอง unit vpc ถูก apply ก่อน unit compute ที่อ่าน output โดยไม่มีใครต้อง hard-code ลำดับนั้นหรือคอยจำว่าต้องรัน unit ไหนก่อนหลังเอง

flowchart LR
  vpc["vpc unit (own state)"] -->|terraform output| dep["dependency vpc: outputs.network_id"]
  dep --> inputsBlock["compute unit inputs: network_id"]
  mock["mock_outputs: network_id = mock-network-id"] -.->|used only for plan, before vpc is applied| dep
output ของ unit vpc ไหลเข้า dependency block และ inputs ของ unit compute
ทำไม module composition แบบธรรมดาของ Terraform ถึงไม่พอสำหรับเชื่อม unit vpc เข้ากับ unit compute ใน Terragrunt
mock_outputs ใน dependency block มีไว้ทำอะไร
Terragrunt กำหนดลำดับ apply ที่ถูกต้องของหลาย unit ที่พึ่งพากันยังไง
ใน inputs block ของ unit compute dependency.vpc.outputs.network_id หมายถึงอะไร