เมื่อทีมขนาดใหญ่ใช้ remote backend ร่วมกัน ความเสี่ยงที่สำคัญที่สุดคือการที่สองคน apply พร้อมกันบน state เดียวกัน state file อาจเสียหาย หรือสร้าง resource ซ้ำในระบบ cloud จริง state locking เป็นกลไกที่ป้องกันปัญหานี้ โดยให้เฉพาะ process เดียวสามารถแก้ไข state ในช่วงเวลาหนึ่ง
บทความนี้จะอธิบายหลักการ locking ของ backend แต่ละแบบ วิธี troubleshoot เมื่อ lock ค้าง และ best practice ในการใช้ force-unlock อย่างปลอดภัย
State Locking ทำงานอย่างไร
เมื่อรัน plan, apply, destroy หรือ state * tool จะพยายาม acquire lock บน backend ก่อนเข้าถึง state ถ้ามีคนอื่นถือ lock อยู่ จะรอจนกว่าจะปล่อย (หรือ error หลัง timeout)
- Acquire — สร้าง record ระบุ user, pid, timestamp, operation
- Hold — ถือ lock ระหว่างที่กำลังแก้ไข state
- Release — ลบ record ออกเมื่อเสร็จงาน (หรือครบ lease time ของ backend)
Locking บน Backend แต่ละตัว
- S3 + DynamoDB — S3 ไม่มี native locking ต้องใช้ DynamoDB table (partition key
LockID) เป็น lock store - Azure Blob — ใช้ blob lease จาก Azure Storage (native, ไม่ต้อง config เพิ่ม)
- GCS — ใช้ object-level locking ของ Google Cloud Storage
- Cloud edition — locking ในตัว พร้อม UI แสดงว่าใครถือ lock
- Consul — ใช้ session-based lock ของ Consul KV
ตัวอย่าง DynamoDB Lock Table
# สร้าง DynamoDB table ครั้งเดียว
aws dynamodb create-table \
--table-name tf-state-lock \
--attribute-definitions AttributeName=LockID,AttributeType=S \
--key-schema AttributeName=LockID,KeyType=HASH \
--billing-mode PAY_PER_REQUEST \
--region ap-southeast-1
# backend.tf
terraform {
backend "s3" {
bucket = "mycompany-tfstate"
key = "network/vpc/terraform.tfstate"
region = "ap-southeast-1"
dynamodb_table = "tf-state-lock" # ← สำคัญ: เปิด locking
encrypt = true
}
}
Partition key LockID เก็บ path ของ state ที่ถูก lock เช่น mycompany-tfstate/network/vpc/terraform.tfstate-md5 ขนาด record เล็กมาก ใช้ PAY_PER_REQUEST จ่ายแค่ครั้งละเศษสตางค์
เมื่อ Lock ค้าง: force-unlock
บางครั้ง lock ไม่ถูก release เช่น process ถูก kill กลางคัน, เน็ตขาด, หรือ CI runner ล่ม ทำให้สมาชิกคนอื่นรัน apply ไม่ได้
# error message ที่เห็นบ่อย
Error acquiring the state lock
Lock Info:
ID: f4e9c8d7-1234-5678-9abc-def012345678
Path: mycompany-tfstate/network/vpc/terraform.tfstate
Operation: OperationTypeApply
Who: ci-runner@ip-10-0-1-23
Created: 2026-04-15 09:23:45.123 UTC
# ถ้าแน่ใจว่าไม่มีใคร apply จริง
$ terraform force-unlock f4e9c8d7-1234-5678-9abc-def012345678
Do you really want to force-unlock?
Enter a value: yes
ข้อควรระวังก่อนใช้ force-unlock
- ตรวจก่อนว่าไม่มีใคร apply จริง — ดู CI log, Slack ทีม, ssh ไปเครื่องของผู้ถือ lock ถามตรง ๆ
- อย่าใช้เป็น workflow ปกติ — ถ้า force-unlock บ่อย แสดงว่ามีปัญหา config/infra อื่น (CI timeout, network flakey)
- Backup state ก่อน —
aws s3 cp s3://.../terraform.tfstate ./backup-$(date +%s).tfstateก่อน force-unlock - Log การใช้งาน — บันทึกว่าใคร unlock เมื่อไหร่ เหตุผลอะไร ในช่อง ops ของทีม
ทางเลือก: -lock-timeout
แทนที่จะ error ทันทีเมื่อ lock ถูกถือ สามารถให้ tool รอสักพักด้วย flag -lock-timeout
# รอ lock สูงสุด 5 นาที
terraform apply -lock-timeout=5m
# รอ 30 วินาที (เหมาะสำหรับ CI ที่ไม่อยาก block นาน)
terraform apply -lock-timeout=30s
# ปิด locking ชั่วคราว (อันตราย ใช้เฉพาะ emergency)
terraform apply -lock=false
Best Practice สำหรับทีม
- ตั้ง
-lock-timeout=5mเป็น default ใน CI pipeline เพื่อรองรับ apply ที่ทับซ้อนกัน - แบ่ง state ตาม domain (network, compute, data) เพื่อลดโอกาส apply พร้อมกัน
- ใช้ workspace/environment แยกกันระหว่าง dev/staging/prod — แต่ละตัวมี state แยก lock คนละตัว
- เปิด CloudTrail บน DynamoDB table เพื่อดู history ของการ lock/unlock
- อย่าใช้
-lock=falseในการ run ปกติ เสมือนการเปิดประตูให้ corrupt state
Troubleshooting Lock Timeout
- IAM permission — role ที่รัน apply ต้องมี
dynamodb:GetItem,PutItem,DeleteItemบน lock table - Network — CI runner ต้องคุยกับ DynamoDB endpoint ได้ (ถ้าอยู่ใน private subnet ต้องมี VPC endpoint)
- Clock skew — nodes ที่เวลาไม่ตรงอาจทำให้ lease calculation ผิด (หาไม่บ่อย แต่เคยเจอใน container image เก่า)
- Table throttling — ถ้าใช้ provisioned capacity ต่ำเกินอาจโดน throttle ช่วง peak
สรุป
State locking เป็นกลไกที่ต้องมีในทีมขนาดใหญ่เพื่อป้องกัน state corruption และ resource drift DynamoDB เป็นตัวเลือกมาตรฐานบน AWS, Cloud edition มี locking ในตัว ส่วน Azure/GCS มี native lease ให้แล้ว เมื่อ lock ค้างให้ตรวจสอบว่าไม่มี apply จริงก่อนใช้ force-unlock และทำ backup state ไว้เสมอ ในบทความถัดไปจะพูดถึงการ backup และ recovery state file เมื่อเกิดเหตุการณ์ไม่คาดฝัน

