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

Terraform Workspaces

Terraform workspace ทำให้ configuration เดียวเก็บ state file แยกกันได้หลายอัน แต่ละอันมีชื่อของตัวเอง เป็นวิธีถูก ๆ ที่จำลอง dev, staging, prod แต่ workspace ไม่ได้เปลี่ยนอะไรเกี่ยวกับ backend, provider, หรือ Azure subscription ที่ configuration นั้นคุยด้วยจริง ๆ เลย

terraform workspace คือชุดคำสั่งในตัวสำหรับจัดการ state ที่มีชื่อ ภายใน working directory เดียว สร้าง workspace หนึ่งอันต่อ environment แล้ว Terraform จะเก็บ state file แยกกันโดยสมบูรณ์ให้แต่ละอัน

Terminal window
terraform workspace new dev
terraform workspace new staging
terraform workspace new prod
terraform workspace list
# dev
# staging
# * prod
terraform workspace select dev

workspace ที่ active อยู่คือตัวชี้เฉย ๆ ว่า workspace ไหนถูกเลือกอยู่จะเป็นตัวกำหนดว่า plan หรือ apply ครั้งถัดไปจะอ่านและเขียน state file ไหน ถ้าใช้ local backend แบบ default Terraform จะเก็บไว้ใต้ terraform.tfstate.d แยก subdirectory ต่อ workspace หนึ่งอัน

Terminal window
tree terraform.tfstate.d
terraform.tfstate.d
├── dev
└── terraform.tfstate
├── staging
└── terraform.tfstate
└── prod
└── terraform.tfstate

ถ้าใช้ remote backend อย่าง azurerm แนวคิดเดียวกันนี้ยังใช้ได้ Terraform จะแยก namespace ของ state blob ต่อ workspace แทนที่จะแยกต่อ local directory แต่ผลลัพธ์เหมือนกันทุกประการ คือ dev มองไม่เห็นและเขียนทับ resource ของ prod ไม่ได้ เพราะอยู่คนละ state กันโดยสิ้นเชิง

Terraform เปิดชื่อของ workspace ที่กำลัง select อยู่ให้ใช้ผ่านค่า built-in ชื่อ terraform.workspace ซึ่ง interpolate ได้ทุกที่ที่ต้องการ string การใช้งานที่พบบ่อยที่สุดคือเลือกขนาดหรือชื่อต่อ environment จาก lookup map

locals {
vm_size = {
dev = "Standard_B1s"
staging = "Standard_B2s"
prod = "Standard_D2s_v5"
}
}
resource "azurerm_linux_virtual_machine" "app" {
name = "app-${terraform.workspace}"
resource_group_name = azurerm_resource_group.app.name
location = azurerm_resource_group.app.location
size = local.vm_size[terraform.workspace]
admin_username = "azureuser"
# ...
}

select dev แล้ว VM จะได้ชื่อ app-dev และขนาด Standard_B1s select prod ไฟล์ .tf เดียวกันนี้จะได้ app-prod ขนาด Standard_D2s_v5 โดยไม่มีโค้ดซ้ำที่ไหนเลย นี่คือจุดขายของ workspace คือมีชุด .tf เดียว มีที่แก้ bug จุดเดียว และความต่างต่อ environment ขับเคลื่อนด้วย workspace ไหนกำลัง active อยู่ล้วน ๆ

นี่คือจุดที่พลาดง่าย terraform workspace new สร้างแค่ state file ใหม่เท่านั้น ไม่ได้สร้าง backend configuration ใหม่ ไม่ได้สร้าง provider configuration ใหม่ และไม่ได้สร้าง Azure subscription ใหม่ ทั้งหมดนั้นอยู่ใน .tf code เดียวกัน แชร์กันทุกตัวอักษรข้ามทุก workspace

terraform {
backend "azurerm" {
resource_group_name = "tfstate-rg"
storage_account_name = "tfstateacct001"
container_name = "tfstate"
key = "app.terraform.tfstate"
}
}
provider "azurerm" {
features {}
subscription_id = "00000000-0000-0000-0000-000000000000"
}

ทุก workspace ทั้ง dev, staging, และ prod อ่านเขียนผ่าน backend block เดียวกันนั้น และ authenticate ผ่าน provider block เดียวกันนั้น กับ subscription_id เดียวกันนั้น ไม่มี credential ที่ scope ต่อ workspace ไม่มี subscription ที่ scope ต่อ workspace และไม่มี copy ของ .tf code ที่ scope ต่อ workspace สิ่งเดียวที่ต่างกันระหว่าง workspace คือ state file ไหนถูกแตะ กับอะไรก็ตามที่เลือก key ไว้ด้วย terraform.workspace

นั่นทำให้ workspace เข้ากันไม่ดีกับ environment ที่ต้องการ Azure subscription, region, หรือ credential ที่แยกกันจริง ๆ และสร้างความเสี่ยงจริงที่เฉพาะเจาะจงขึ้นมาอันหนึ่ง คือไม่มีอะไรใน tooling หยุดใครสักคนไม่ให้อยู่บน prod workspace ทั้งที่คิดว่ายังอยู่บน dev แล้ว apply การเปลี่ยนแปลงที่ตั้งใจไว้สำหรับ environment เล็ก ๆ ตรงเข้าไปที่ production ได้ เพราะ backend, provider, และ subscription เบื้องหลังไม่เคยเปลี่ยนเลยสักนิด

flowchart LR
  config["One Terraform config, one backend block"] -->|terraform workspace new dev| devWs["dev workspace"]
  config -->|terraform workspace new staging| stagingWs["staging workspace"]
  config -->|terraform workspace new prod| prodWs["prod workspace"]
  devWs --> devState["dev state file"]
  stagingWs --> stagingState["staging state file"]
  prodWs --> prodState["prod state file"]
  devWs -.-> shared["Same subscription_id, same backend, same .tf code"]
  stagingWs -.-> shared
  prodWs -.-> shared
One Terraform config and one backend, three workspaces, three state files, but everything else shared including the Azure subscription
การสร้าง Terraform workspace ใหม่ isolate อะไรจาก workspace อื่นจริง ๆ
อะไรที่แชร์เหมือนกันทุกประการข้ามทุก workspace ใน configuration เดียวกัน
terraform.workspace มักถูกใช้ทำอะไร
ความเสี่ยงจริงที่เฉพาะเจาะจงซึ่ง Terraform workspace สร้างขึ้นมาคืออะไร