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

Module Versioning and Registries

version constraint บน module บอกว่า release ไหนของ Registry module หรือ Git-tagged module ที่ Terraform ได้รับอนุญาตให้ resolve ด้วยเหตุผลเดียวกับที่ version constraint ของ provider สำคัญ — ถ้าไม่มี terraform init -upgrade ธรรมดา ๆ ครั้งหนึ่งอาจดึง module release ใหม่ที่มี breaking change เข้ามาโดยไม่มีใคร review ก่อนเลย

นี่คือปัญหาเดียวกับที่ provider dependency lock file แก้ไข แค่ขยับขึ้นมาอีกชั้นหนึ่ง เมื่อ module block ดึง source มาจาก public Registry หรือ Git tag โดยไม่มี version constraint Terraform จะ resolve release ล่าสุดที่มีอยู่ทุกครั้งที่ init รันแบบ cold — clone ใหม่ใน CI, laptop เครื่องใหม่, cache ที่ถูกสร้างใหม่ ถ้าคนเขียน module ปล่อย breaking change มาใน release ใหม่นั้น — variable ที่เปลี่ยนชื่อ, default ที่ต่างไป, resource ที่ตอนนี้ต้อง replace แทนที่จะ update — การเปลี่ยนนั้นจะถูกดึงเข้ามาแล้ว apply โดยไม่มีใครในทีมได้อ่าน changelog ของ module นั้นก่อนเลย

module "vpc" {
source = "terraform-google-modules/network/google"
# no version constraint — always resolves to the latest release
project_id = var.project_id
network_name = "app-network"
}

การ pin version เปลี่ยนเรื่องนี้ให้เป็นการตัดสินใจที่จงใจและ review ได้ แทนที่จะเป็นสิ่งที่เกิดขึ้นเองโดยบังเอิญ

module "vpc" {
source = "terraform-google-modules/network/google"
version = "~> 9.0"
project_id = var.project_id
network_name = "app-network"
}

~> เรียกว่า pessimistic constraint operator ความหมายคืออนุญาตให้ upgrade ได้ แต่เฉพาะ upgrade ที่ไม่น่าจะทำให้อะไรพัง version = "~> 9.0" อนุญาตให้ Terraform resolve release ในสาย 9.x ตัวไหนก็ได้ — 9.1.0, 9.4.2, 9.12.0 — เพราะตาม semantic versioning ตัวเหล่านี้ควร backward compatible กัน แต่บล็อก 10.0.0 เอาไว้เด็ดขาด เพราะ major version bump คือจุดที่คนเขียน module ได้รับอนุญาตให้ใส่ breaking change เข้ามา

operator ตัวเดียวกันนี้ใช้ได้ละเอียดกว่านั้นอีกระดับ version = "~> 9.2.0" อนุญาต patch release อย่าง 9.2.1 กับ 9.2.7 แต่บล็อก 9.3.0 — มีประโยชน์ตอนที่คุณอยากกัน minor version bump ไว้ก่อนจนกว่าจะได้ review ก่อน ~> 9.0 ที่อนุญาตทั้งสาย 9.x คือ default ทั่วไปของทีมส่วนใหญ่ ส่วนรูปแบบที่แคบกว่าอย่าง ~> 9.2.0 เหมาะกับ module ที่เคยโดน minor version เปลี่ยน behavior มาก่อน

การ upgrade ข้ามขอบเขตที่ถูกบล็อกไว้ก็ยังทำได้ในคำสั่งเดียวเมื่อคุณพร้อม — แก้ version constraint ใน .tf file แล้วรัน terraform init -upgrade — constraint แค่ทำให้แน่ใจว่า upgrade นั้นเป็นสิ่งที่คนตัดสินใจทำเอง ไม่ใช่สิ่งที่เกิดขึ้นเงียบ ๆ ตอน init ครั้งถัดไป

ไม่ใช่ทุก module ที่ควรอยู่บน public Registry module ที่ encode security policy, naming convention หรือการเชื่อมต่อ service ภายในของบริษัทหนึ่งไม่มีเหตุผลที่ต้อง public และอาจไม่ควร public ด้วยซ้ำ Terraform รองรับ source mechanic แบบเดียวกันสำหรับกรณีนี้ — private Git repository (มักผ่าน SSH เพื่อให้ init authenticate ด้วย credential ที่มีอยู่แล้ว) หรือ private module registry เช่น private registry ของ Terraform Cloud หรือ registry ที่ host เองภายในองค์กร

module "internal_service" {
source = "git::ssh://[email protected]/acme-corp/terraform-modules.git//internal-service?ref=v1.4.0"
project_id = var.project_id
}

ถ้าคุณอยาก publish module ของตัวเองขึ้น public Terraform Registry Registry ต้องการ pattern ชื่อ repository แบบเฉพาะ คือ terraform-<PROVIDER>-<NAME> เช่น terraform-google-vpc สำหรับ module ที่ target provider google Registry จะ parse ชื่อนี้ตรง ๆ เป็น address ของ module (namespace/name/provider) ดังนั้น repository ที่ตั้งชื่อแบบอื่นจะไม่ถูกดึงเข้ามาเลย

flowchart LR
  subgraph pinned["Pinned: version = ~> 9.0"]
    p1["terraform init today"] --> p2["Resolves 9.4.2 (latest 9.x)"]
    p2 --> p3["terraform init -upgrade later"]
    p3 --> p4["Still resolves within 9.x, blocks 10.0.0"]
  end
  subgraph unpinned["Unpinned: no version constraint"]
    u1["terraform init today"] --> u2["Resolves 9.4.2 (latest overall)"]
    u2 --> u3["terraform init tomorrow"]
    u3 --> u4["Silently resolves 10.0.0 with breaking changes"]
  end
Module source pinned to a version tag compared with an unpinned source
ความเสี่ยงที่เกิดจาก module version constraint ที่ไม่ได้ pin ไว้คืออะไร
constraint version = ~> 9.0 อนุญาตอะไรและบล็อกอะไร
ทำไมองค์กรหนึ่งอาจเลือกใช้ private Git repository หรือ private registry สำหรับ module แทน public Terraform Registry
public Terraform Registry ต้องการ pattern ชื่อ repository แบบไหนสำหรับ module ที่จะ publish