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

Module Composition and Sources

source argument บน module block บอก Terraform ว่าจะไปเอา code ของ module นั้นมาจากไหน และ infrastructure จริงที่ไม่เล็กเกิดจากการเอา module เล็ก ๆ หลายตัวมาประกอบกัน — output ของ module หนึ่งถูกส่งตรงเข้าไปเป็น input variable ของอีก module หนึ่ง

ทุก module block ต้องมี source และมีสามแบบหลักที่คุณจะเจอบ่อยมาก local relative path ชี้ไปที่ directory ภายใน repository ของคุณเอง public Terraform Registry resolve address สั้น ๆ แบบ <namespace>/<name>/<provider> ไปเป็น module ที่ publish ไว้และมี version Git URL ชี้ตรงไปที่ repository โดยจะ pin ไว้ที่ tag, branch หรือ commit ไหนก็ได้ผ่าน ?ref=

# local relative path
module "bucket" {
source = "./modules/s3-bucket"
}
# public Terraform Registry
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "~> 5.0"
}
# Git URL pinned to a specific tag
module "vpc_from_git" {
source = "git::https://github.com/acme/terraform-modules.git//vpc?ref=v1.4.0"
}

ทั้งสามแบบคือคำตอบที่ต่างกันของคำถามเดียวกัน คือ Terraform จะไปเอาไฟล์ .tf ของ child module นี้มาจากไหน ไม่มีอะไรในวิธีเขียน variables.tf, main.tf หรือ outputs.tf ข้างใน module ที่ต้องเปลี่ยนตาม source แบบไหนที่ caller เลือกใช้

ประกอบ module: output ของตัวหนึ่งกลายเป็น input ของอีกตัว

หัวข้อที่มีชื่อว่า “ประกอบ module: output ของตัวหนึ่งกลายเป็น input ของอีกตัว”

configuration แบบแบนราบตัวเดียวที่มี resource เป็นร้อยตัวนั้นอ่านยากและ reuse ไม่ได้เลย infrastructure จริงถูกสร้างจาก module เล็ก ๆ ที่ focus งานเดียว แล้วต่อกันผ่าน output กับ input vpc module หนึ่งเปิด identifier ที่ module อื่นต้องการ

modules/vpc/outputs.tf
output "vpc_id" {
description = "ID of the created VPC"
value = aws_vpc.this.id
}
output "private_subnet_ids" {
description = "IDs of the private subnets"
value = aws_subnet.private[*].id
}

ec2-instance module ประกาศ input ที่ต้องการเพื่อ launch เข้าไปใน network นั้น

modules/ec2-instance/variables.tf
variable "vpc_id" {
description = "ID of the VPC to launch the instance into"
type = string
}
variable "subnet_id" {
description = "ID of the subnet to launch the instance into"
type = string
}

root configuration คือจุดที่ต่อทั้งสอง module เข้าด้วยกันจริง โดยส่ง output ของ module หนึ่งตรงเข้าไปเป็น input ของอีก module หนึ่ง

# root main.tf
module "vpc" {
source = "./modules/vpc"
cidr_block = "10.0.0.0/16"
}
module "web_server" {
source = "./modules/ec2-instance"
vpc_id = module.vpc.vpc_id
subnet_id = module.vpc.private_subnet_ids[0]
}

ทั้งสอง module ไม่จำเป็นต้องรู้จักกันเลย vpc module ไม่รู้ด้วยซ้ำว่าจะมี ec2-instance module มา consume output ของตัวเองในอนาคต และ ec2-instance module ก็ไม่รู้ว่า vpc_id ที่ได้รับมาจาก module หรือแค่ string ที่ hardcode ไว้ root configuration เป็นจุดเดียวที่รู้จักทั้งสอง module และเป็นคนเชื่อมทั้งสองเข้าด้วยกัน

pattern ทั่วไปอย่าง “VPC ที่มี public/private subnet” ถูกแก้ปัญหามาแล้วหลายรอบ public Terraform Registry มี community module ที่ดูแลดีและถูกใช้กันแพร่หลาย — terraform-aws-modules/vpc/aws เป็นหนึ่งในจุดเริ่มต้นที่พบบ่อยที่สุดสำหรับ AWS networking — และการหยิบตัวนี้มาใช้มักคุ้มค่ากับเวลาของคุณมากกว่าการเขียนสิ่งเดียวกันขึ้นมาเองตั้งแต่ศูนย์

module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "~> 5.0"
name = "acme-app-vpc"
cidr = "10.0.0.0/16"
azs = ["us-east-1a", "us-east-1b"]
private_subnets = ["10.0.1.0/24", "10.0.2.0/24"]
public_subnets = ["10.0.101.0/24", "10.0.102.0/24"]
}

อย่างไรก็ตาม “ใช้กันแพร่หลาย” ไม่เหมือนกับ “เชื่อได้แบบไม่ต้องตรวจ” ก่อนสร้าง production infrastructure บน Registry module ตัวไหน ควรอ่านให้แน่ใจว่า module นั้น create อะไรจริง ๆ บ้าง — resource, ค่า default และ IAM permission ที่สร้างในนามคุณ Registry module ก็ยังเป็น code ของคนอื่นที่รันด้วย AWS credential ของคุณอยู่ดี

flowchart LR
  subgraph root["Root configuration"]
    vpcmod["module vpc"]
    ec2mod["module web_server"]
  end
  subgraph vpc["Child module: vpc"]
    vpcout1["output vpc_id"]
    vpcout2["output private_subnet_ids"]
  end
  subgraph ec2["Child module: ec2-instance"]
    ec2in1["var.vpc_id"]
    ec2in2["var.subnet_id"]
  end
  vpcmod --> vpcout1
  vpcmod --> vpcout2
  vpcout1 -->|module.vpc.vpc_id| ec2in1
  vpcout2 -->|module.vpc.private_subnet_ids[0]| ec2in2
  ec2in1 --> ec2mod
  ec2in2 --> ec2mod
A vpc module's outputs feeding an ec2-instance module's inputs from the root configuration
บทเรียนนี้พูดถึง module source สามแบบไหนบ้าง
ในตัวอย่างการประกอบ vpc กับ ec2-instance ec2-instance module ได้รับ subnet ที่ต้อง launch เข้าไปอย่างไร
vpc module ในตัวอย่างต้องรู้หรือไม่ว่ามี ec2-instance module มา consume output ของตัวเอง
ควรหยิบ public Terraform Registry module มาใช้แทนการเขียนเองตั้งแต่ศูนย์เมื่อไร