คู่มือ Terraform บน DigitalOcean 2026 — Infrastructure as Code
A hands-on guide to managing DigitalOcean infrastructure as code with Terraform, covering provider setup, Droplet, VPC, Database, and Load Balancer resources, state file management, and team best practices.
สารบัญ
- Terraform คืออะไร ทำไมต้องใช้กับ Cloud
- ติดตั้ง Terraform + DigitalOcean Provider
- สร้าง Droplet ด้วย .tf ตัวอย่างจริง
- จัดการ VPC, Database, Load Balancer ผ่าน Terraform
- State file และ terraform plan/apply/destroy
- แนวทาง best practice สำหรับทีม
- ข้อผิดพลาดที่พบบ่อยและวิธีแก้ไข
- แนวทางปฏิบัติที่ดีที่สุด (Best Practices)
- FAQ
Terraform คืออะไร ทำไมต้องใช้กับ Cloud
Terraform เป็นเครื่องมือ Infrastructure as Code (IaC) จาก HashiCorp ที่ให้เขียนโครงสร้างพื้นฐาน (server, network, database ฯลฯ) เป็นไฟล์ config ภาษา HCL (HashiCorp Configuration Language) แทนการคลิกสร้างทีละอย่างผ่านหน้า DigitalOcean Control Panel หลักการทำงานคือแบบ “declarative” — เราประกาศว่าต้องการให้ infrastructure สุดท้ายมีหน้าตาอย่างไร แล้ว Terraform จะคำนวณเองว่าต้องสร้าง/แก้ไข/ลบอะไรบ้างเพื่อให้ตรงกับที่ประกาศไว้ ต่างจากสคริปต์แบบ “imperative” ที่ต้องเขียนขั้นตอนทีละคำสั่งเอง สำหรับทีมที่ใช้ DigitalOcean การใช้ Terraform มีข้อดีชัดเจนหลายอย่าง อย่างแรกคือ reproducibility — ไฟล์ .tf ที่ commit เข้า Git repo คือ “single source of truth” ของ infrastructure ทั้งหมด ใครก็ตามในทีมสามารถ clone repo แล้วรัน terraform apply เพื่อสร้าง environment เดียวกันขึ้นมาใหม่ได้ ไม่ว่าจะเป็น dev, staging หรือ production อย่างที่สองคือ version control — การเปลี่ยนแปลง infrastructure ทุกครั้งถูกบันทึกเป็น commit history สามารถ diff, review ผ่าน pull request, และ rollback ได้เหมือนโค้ดแอปพลิเคชันทั่วไป อย่างที่สามคือลดความผิดพลาดจากมนุษย์ (human error) การคลิกสร้าง Droplet ผ่าน dashboard ด้วยมือมีโอกาสลืมตั้งค่าบางอย่าง เช่น firewall หรือ SSH key ไม่ตรงกันระหว่าง environment แต่ Terraform รับประกันว่าทุกครั้งที่ apply จะได้ผลลัพธ์เหมือนเดิมตาม config ที่เขียนไว้ และสุดท้ายคือ disaster recovery — หาก Droplet หรือ Load Balancer ถูกลบโดยไม่ตั้งใจ หรือ region ทั้งหมดล่ม ทีมสามารถ apply ไฟล์ .tf ชุดเดิมกับ region อื่นเพื่อกู้คืนระบบได้เร็วกว่าการตั้งค่าใหม่ทั้งหมดด้วยมือมาก Terraform รองรับ DigitalOcean ผ่าน official provider ชื่อ digitalocean/digitalocean ที่ publish อยู่บน Terraform Registry ซึ่งครอบคลุมแทบทุกบริการหลัก ไม่ว่าจะเป็น Droplet, VPC, Load Balancer, Managed Database, Volume, Firewall, Domain/DNS และ Kubernetes ทำให้สามารถจัดการ infrastructure ทั้ง stack ด้วยเครื่องมือเดียวโดยไม่ต้องสลับไปมาระหว่าง CLI (doctl), API, และหน้าเว็บ
Terraform is an Infrastructure as Code (IaC) tool from HashiCorp that lets you write your infrastructure—servers, networks, databases, and more—as configuration files in HCL (HashiCorp Configuration Language) instead of clicking through the DigitalOcean Control Panel one resource at a time. It works declaratively: you declare what your final infrastructure should look like, and Terraform calculates what needs to be created, modified, or deleted to match your declaration. This differs from imperative scripts where you must write each step manually. For teams using DigitalOcean, the benefits are clear and compelling. First is reproducibility: your .tf files committed to Git become the single source of truth for your entire infrastructure. Anyone on the team can clone the repo and run terraform apply to recreate the same environment—dev, staging, or production—identically every time. Second is version control: every infrastructure change is tracked as a commit, reviewable via pull request, and rollbackable like any code change. Third is reducing human error: clicking to create a Droplet by hand leaves room for mistakes like forgetting SSH keys or firewall rules, but Terraform guarantees the same correct result every apply. Fourth is disaster recovery: if a Droplet or Load Balancer is accidentally deleted or an entire region fails, you can apply the same .tf file to another region and restore your system far faster than manual setup. Terraform supports DigitalOcean through the official provider called digitalocean/digitalocean, published on the Terraform Registry and covering nearly every major service: Droplets, VPC, Load Balancer, Managed Database, Volumes, Firewall, Domain/DNS, and Kubernetes. This means you can manage your entire infrastructure stack with one tool instead of switching between the CLI (doctl), API, and web dashboard.
- Declarative config: ประกาศผลลัพธ์ที่ต้องการ ไม่ต้องเขียนขั้นตอนทีละคำสั่งเหมือนสคริปต์ shell
- Version control ได้เหมือนโค้ด — commit, diff, code review ผ่าน pull request
- Reproducible: clone repo แล้ว
terraform applyได้ environment เดียวกันทุกครั้ง - ใช้ provider เดียว (
digitalocean/digitalocean) จัดการได้ทั้ง Droplet, VPC, Database, Load Balancer - ลดความเสี่ยง human error และช่วยเรื่อง disaster recovery เมื่อต้องสร้างระบบใหม่เร็วๆ
ติดตั้ง Terraform + DigitalOcean Provider
สิ่งที่น่าสนใจคือ การติดตั้ง Terraform บนเครื่อง local ทำได้หลายวิธีขึ้นกับระบบปฏิบัติการ บน macOS วิธีที่ง่ายที่สุดคือใช้ Homebrew ด้วยคำสั่ง brew tap hashicorp/tap && brew install hashicorp/tap/terraform บน Linux ที่รองรับ snap สามารถใช้ sudo snap install terraform --classic หรือจะเพิ่ม HashiCorp apt repository แล้วติดตั้งผ่าน apt-get install terraform ก็ได้ ส่วน Windows ใช้ Chocolatey ด้วย choco install terraform หลังติดตั้งเสร็จให้ตรวจสอบเวอร์ชันด้วย terraform version เพื่อยืนยันว่าคำสั่งพร้อมใช้งาน ขั้นตอนถัดไปคือประกาศ DigitalOcean provider ในไฟล์ config เพื่อบอก Terraform ว่าต้องดาวน์โหลด plugin ตัวไหนจาก Terraform Registry สร้างไฟล์ เช่น providers.tf แล้วใส่บล็อกนี้ terraform {\n required_providers {\n digitalocean = {\n source = \"digitalocean/digitalocean\"\n version = \"~> 2.0\"\n }\n }\n}\n\nprovider \"digitalocean\" {\n token = var.do_token\n} การ pin version ด้วย ~> 2.0 ช่วยป้องกันไม่ให้ minor version ใหม่ที่มี breaking change หลุดเข้ามาโดยไม่ตั้งใจตอนรัน terraform init ครั้งถัดไป เรื่องสำคัญที่สุดคือการจัดการ API token อย่างปลอดภัย — ต้องสร้าง Personal Access Token จากหน้า cloud.digitalocean.com/account/api/tokens (สิทธิ์ read/write) ก่อน แล้วห้าม hardcode token ลงในไฟล์ .tf โดยตรงเด็ดขาด เพราะไฟล์เหล่านี้มักถูก commit เข้า Git วิธีที่ถูกต้องคือประกาศเป็น variable variable \"do_token\" {\n type = string\n sensitive = true\n} แล้วส่งค่าเข้าไปตอนรันผ่าน environment variable export TF_VAR_do_token=\"dop_v1_xxxxx\" หรือเก็บไว้ในไฟล์ terraform.tfvars ที่เพิ่มเข้า .gitignore ไม่ให้หลุดขึ้น repo สุดท้ายรัน terraform init เพื่อให้ Terraform ดาวน์โหลด provider plugin ตาม config ที่ประกาศไว้ลงในโฟลเดอร์ .terraform/ เป็นอันเสร็จขั้นตอนเตรียมพร้อม
- macOS:
brew install hashicorp/tap/terraform/ Linux:snap install terraform --classic - ตรวจสอบด้วย
terraform versionหลังติดตั้งเสร็จทุกครั้ง - สร้าง Personal Access Token ที่ cloud.digitalocean.com/account/api/tokens ก่อนเริ่มเขียน provider block
- ห้าม hardcode token ในไฟล์ .tf — ใช้ variable แบบ
sensitive = trueแทน - รัน
terraform initเพื่อดาวน์โหลด provider plugin ก่อนใช้งานทุกครั้งที่เริ่ม project ใหม่
สร้าง Droplet ด้วย .tf ตัวอย่างจริง
เมื่อ provider พร้อมแล้ว ขั้นตอนถัดไปคือเขียน resource block สำหรับสร้าง Droplet จริง ตัวอย่างต่อไปนี้สร้าง Droplet ขนาด s-1vcpu-1gb (1 GiB RAM, 1 vCPU, 25 GB SSD ราคา $6/เดือน) ที่ region sgp1 (Singapore) ซึ่งเป็น region ที่ latency ต่ำที่สุดสำหรับผู้ใช้ในไทย resource \"digitalocean_droplet\" \"web\" {\n image = \"ubuntu-24-04-x64\"\n name = \"web-01\"\n region = \"sgp1\"\n size = \"s-1vcpu-1gb\"\n ssh_keys = [var.ssh_fingerprint]\n tags = [\"web\", \"production\"]\n} field image ใช้ slug ของ OS image (เช่น ubuntu-24-04-x64) ส่วน size ใช้ slug ของแผน Droplet ที่หาได้จาก doctl compute size list หรือเอกสาร provider — ถ้าต้องการ Droplet ที่แรงขึ้นสำหรับ workload จริงจัง เช่น 4 GiB RAM/2 vCPU/80 GB SSD ราคา $24/เดือน ก็เปลี่ยน size เป็น s-2vcpu-4gb ได้ทันทีโดยไม่ต้องแก้อย่างอื่น field ssh_keys ต้องอ้างอิง fingerprint ของ SSH key ที่อัปโหลดเข้าบัญชี DigitalOcean ไว้ก่อนแล้ว (ผ่าน dashboard หรือ digitalocean_ssh_key resource) เพื่อให้ล็อกอินแบบไม่ใช้รหัสผ่านได้ทันทีที่ Droplet บูตเสร็จ หลังเขียน config เสร็จ ให้รันตามลำดับ 3 คำสั่งหลัก เริ่มจาก terraform init เพื่อโหลด provider (ถ้ายังไม่ได้รัน) จากนั้น terraform plan เพื่อให้ Terraform เทียบ config กับสถานะปัจจุบันแล้วแสดงแผนการเปลี่ยนแปลงก่อนลงมือจริง เช่น + create แปลว่าจะมีการสร้างใหม่ ควรอ่าน plan ให้ละเอียดทุกครั้งก่อนไปขั้นตอนถัดไป สุดท้ายรัน terraform apply แล้วพิมพ์ yes ยืนยัน Terraform จะเรียก DigitalOcean API สร้าง Droplet จริงและรายงาน public IP กลับมาให้เมื่อเสร็จ หากต้องการเพิ่ม output ให้แสดง IP อัตโนมัติหลัง apply เสร็จ ให้เพิ่มบล็อกนี้ในไฟล์แยก เช่น outputs.tf output \"web_ip\" {\n value = digitalocean_droplet.web.ipv4_address\n} ทำให้ทุกครั้งที่ apply เสร็จจะเห็น IP แสดงในหน้าจอทันทีโดยไม่ต้องเปิด dashboard ไปเช็คเอง
image = \"ubuntu-24-04-x64\" และ size = \"s-1vcpu-1gb\" ($6/เดือน, 1 GiB RAM/1 vCPU/25 GB SSD)image = \"ubuntu-24-04-x64\"และsize = \"s-1vcpu-1gb\"($6/เดือน, 1 GiB RAM/1 vCPU/25 GB SSD)- เลือก
region = \"sgp1\"(Singapore) เพื่อ latency ต่ำสุดสำหรับผู้ใช้ไทย - อ้างอิง
ssh_keysด้วย fingerprint ที่อัปโหลดไว้ก่อน ไม่ใช้ password login
จัดการ VPC, Database, Load Balancer ผ่าน Terraform
Terraform ไม่ได้จำกัดแค่ Droplet เท่านั้น แต่ครอบคลุมบริการหลักเกือบทั้งหมดของ DigitalOcean ทำให้สามารถประกาศ network, database และ load balancer ไว้ในไฟล์เดียวกันแล้วเชื่อมโยงกันด้วย reference ภายใน config ได้เลย เริ่มจาก VPC ซึ่งเป็นเครือข่ายส่วนตัวที่ DigitalOcean ให้ใช้ฟรีไม่จำกัดจำนวนต่อบัญชี เหมาะกับการแยก environment เช่น dev/prod ไม่ให้เห็นกัน resource \"digitalocean_vpc\" \"prod\" {\n name = \"prod-vpc\"\n region = \"sgp1\"\n} จากนั้นเชื่อม Droplet เข้า VPC นี้ได้ทันทีด้วยการเพิ่ม vpc_uuid = digitalocean_vpc.prod.id ในบล็อก digitalocean_droplet ซึ่งเป็นข้อดีของ Terraform ที่ resource หนึ่งสามารถอ้างอิง attribute ของอีก resource ได้โดยตรงโดยไม่ต้อง copy ID มาใส่เอง สำหรับ Managed Database เช่น PostgreSQL ที่แผน Basic เริ่มต้น 1 vCPU/1 GiB RAM ราคา $15.15/เดือน เขียนได้ดังนี้ resource \"digitalocean_database_cluster\" \"pg\" {\n name = \"prod-postgres\"\n engine = \"pg\"\n version = \"16\"\n size = \"db-s-1vcpu-1gb\"\n region = \"sgp1\"\n node_count = 1\n private_network_uuid = digitalocean_vpc.prod.id\n} การใส่ private_network_uuid ชี้ไปยัง VPC เดียวกันทำให้ database ไม่มี public IP ที่เข้าถึงจากอินเทอร์เน็ตได้โดยตรง เข้าถึงได้เฉพาะ resource ที่อยู่ใน VPC เดียวกันเท่านั้น เป็นแนวทาง security ที่แนะนำ ส่วน Load Balancer ที่ราคาเริ่มต้น $12/เดือน ใช้กระจาย traffic ไปยัง Droplet หลายตัวที่ tag ไว้ เช่น resource \"digitalocean_loadbalancer\" \"web_lb\" {\n name = \"web-lb\"\n region = \"sgp1\"\n droplet_tag = \"web\"\n\n forwarding_rule {\n entry_port = 443\n entry_protocol = \"https\"\n target_port = 80\n target_protocol = \"http\"\n }\n\n healthcheck {\n port = 80\n protocol = \"http\"\n }\n} การใช้ droplet_tag แทนการระบุ Droplet ID ทีละตัวมีข้อดีคือเมื่อ scale เพิ่ม Droplet ใหม่ที่มี tag เดียวกัน (เช่นผ่าน digitalocean_droplet หลาย resource หรือ count) Load Balancer จะดึง Droplet เข้ากลุ่มปลายทางให้อัตโนมัติโดยไม่ต้องแก้ config ของ Load Balancer เอง
- VPC ฟรี ไม่จำกัดจำนวน — สร้างแยก dev/prod ด้วย
digitalocean_vpcก่อนสร้าง resource อื่น - เชื่อม resource ข้ามกันด้วย reference เช่น
vpc_uuid = digitalocean_vpc.prod.id - Managed PostgreSQL แผน Basic เริ่ม $15.15/เดือน (1 vCPU/1 GiB RAM) ผ่าน
digitalocean_database_cluster
State file และ terraform plan/apply/destroy
หัวใจของการทำงานของ Terraform คือ state file ซึ่งเป็นไฟล์ terraform.tfstate รูปแบบ JSON ที่ Terraform สร้างขึ้นอัตโนมัติหลัง apply ครั้งแรก ทำหน้าที่เก็บ mapping ระหว่าง resource ที่ประกาศในไฟล์ .tf กับ resource จริงที่มีอยู่บน DigitalOcean (ระบุด้วย ID จริง) ทุกครั้งที่รัน terraform plan หรือ terraform apply Terraform จะเทียบ 3 สถานะเข้าด้วยกัน คือ config ในไฟล์ .tf, state file, และสถานะจริงบน provider (refresh ผ่าน API) เพื่อคำนวณว่าต้อง create/update/destroy อะไรบ้าง คำสั่งหลัก 3 ตัวที่ใช้ทำงานประจำวันคือ terraform plan ซึ่งเป็น dry-run แสดงแผนการเปลี่ยนแปลงโดยไม่แก้อะไรจริง เหมาะสำหรับ review ก่อน merge pull request, terraform apply ที่นำแผนไปดำเนินการจริงและอัปเดต state file, และ terraform destroy ที่ลบ resource ทั้งหมดที่ Terraform ดูแลอยู่ตาม state — คำสั่งนี้อันตรายเพราะลบจริง ควรใช้เฉพาะ environment ทดสอบหรือเมื่อต้องการเลิกใช้ project จริงๆ เท่านั้น และควรรัน terraform plan -destroy ดูก่อนเสมอเพื่อยืนยันว่าจะลบเฉพาะที่ต้องการ ข้อควรระวังที่สำคัญที่สุดคือไฟล์ terraform.tfstate ห้าม commit เข้า Git repository โดยเด็ดขาด เพราะไฟล์นี้เก็บข้อมูลทุก attribute ของ resource รวมถึงค่าที่เป็นความลับ เช่น database password แบบ plain text ต้องเพิ่ม terraform.tfstate และ terraform.tfstate.backup เข้า .gitignore เสมอ สำหรับการทำงานเป็นทีม การเก็บ state file ไว้บนเครื่อง local คนเดียวเป็นปัญหาใหญ่ เพราะคนอื่นในทีมจะไม่เห็น state ล่าสุด ทำให้ apply ซ้ำหรือชนกันได้ วิธีแก้คือใช้ remote backend เก็บ state ไว้กลาง เช่น backend แบบ S3-compatible ที่ชี้ไปยัง DigitalOcean Spaces terraform {\n backend \"s3\" {\n endpoints = { s3 = \"https://sgp1.digitaloceanspaces.com\" }\n bucket = \"team-terraform-state\"\n key = \"prod/terraform.tfstate\"\n region = \"us-east-1\"\n skip_credentials_validation = true\n skip_region_validation = true\n }\n} การใช้ remote backend ทำให้ทุกคนในทีม apply จาก state เดียวกันเสมอ ลดปัญหา state ไม่ตรงกันระหว่างเครื่อง
terraform plan= dry-run ดูก่อน ไม่แก้อะไรจริง ควร review ทุกครั้งก่อน applyterraform apply= ดำเนินการจริงตามแผน แล้วอัปเดต state fileterraform destroy= ลบ resource ทั้งหมดตาม state — ใช้ระวังเป็นพิเศษ
แนวทาง best practice สำหรับทีม
เมื่อ project เริ่มโตขึ้นและมีหลาย environment หรือหลายคนทำงานร่วมกัน มีแนวทางที่ช่วยให้ Terraform codebase ดูแลง่ายและปลอดภัยขึ้นหลายข้อ อย่างแรกคือแยก state ต่อ environment ไม่ให้ dev/staging/production ใช้ state ไฟล์เดียวกัน วิธีที่นิยมคือใช้ Terraform Workspaces (terraform workspace new production) หรือจะแยกเป็นโฟลเดอร์ต่อ environment พร้อม backend key ต่างกัน เช่น key = \"dev/terraform.tfstate\" กับ key = \"prod/terraform.tfstate\" ก็ได้ผลลัพธ์เดียวกัน วิธีแยกโฟลเดอร์มักอ่านง่ายกว่าและลดโอกาส apply ผิด environment โดยไม่ตั้งใจ อย่างที่สองคือใช้ module เพื่อลดโค้ดซ้ำ ถ้ามี pattern เดิม เช่น “Droplet + Firewall + Reserved IP” ที่ต้องสร้างซ้ำหลาย service ควรห่อเป็น module แยกไฟล์ เรียกใช้ด้วย module \"api_server\" {\n source = \"./modules/droplet-stack\"\n name = \"api\"\n size = \"s-2vcpu-4gb\"\n region = \"sgp1\"\n} ทำให้แก้ pattern ครั้งเดียวมีผลกับทุก service ที่เรียก module นั้น อย่างที่สามคือจัดการ secret อย่างรัดกุม ไม่เก็บ token หรือ database password ไว้ใน .tf หรือ .tfvars ที่ commit เข้า repo โดยตรง ควรผ่าน environment variable ในขั้นตอน CI/CD หรือใช้ secret manager เฉพาะทาง และเปิด sensitive = true ให้ variable/output ที่เกี่ยวกับข้อมูลลับเสมอเพื่อไม่ให้ค่าเหล่านั้นแสดงในผลลัพธ์ของ terraform plan/apply บน log ของ CI อย่างที่สี่คือ integrate เข้า pipeline แทนการรัน apply จากเครื่อง local ของแต่ละคน ให้ pull request ที่แก้ไฟล์ .tf รัน terraform plan อัตโนมัติแล้วแปะผลลัพธ์เป็น comment ให้ทีม review ก่อน merge จากนั้น apply จริงเฉพาะตอน merge เข้า branch หลักเท่านั้น (ผ่าน CI job ที่มีสิทธิ์ apply) วิธีนี้ทำให้ทุกการเปลี่ยนแปลง infrastructure ผ่านการ review เหมือนโค้ดแอปพลิเคชัน ไม่มีใคร apply ตรงจากเครื่องตัวเองโดยไม่มีใครเห็น สุดท้ายคือตั้ง naming convention และ tag ให้ทุก resource สม่ำเสมอ เช่น tags = [\"env:production\", \"team:backend\"] ช่วยให้ค้นหา ติดตามค่าใช้จ่าย และเขียน droplet_tag ผูกกับ Load Balancer ได้ง่ายขึ้นเมื่อ project มี resource จำนวนมาก
- แยก state ต่อ environment ด้วย Terraform Workspaces หรือแยกโฟลเดอร์ + backend key ต่างกัน
- ห่อ pattern ที่ใช้ซ้ำเป็น module แทนการ copy-paste resource block
- ไม่ commit secret เข้า repo — ใช้ environment variable/secret manager +
sensitive = true
ข้อผิดพลาดที่พบบ่อยและวิธีแก้ไข
ข้อที่มักถูกมองข้ามคือ เมื่อใช้ Terraform ร่วมกับ DigitalOcean ในงานจริง มีปัญหาบางแบบที่พบซ้ำบ่อยจนควรรู้วิธีแก้ไว้ล่วงหน้า ปัญหาแรกคือ state drift — เกิดขึ้นเมื่อมีคนแก้ resource ผ่าน dashboard หรือ doctl โดยตรง (เช่น resize Droplet, เปลี่ยนกฎ Firewall) โดยไม่ผ่าน Terraform ทำให้ state file ที่ Terraform เก็บไว้ไม่ตรงกับสถานะจริงบน DigitalOcean อาการที่เจอคือรัน terraform plan แล้วเห็น diff ที่ไม่ได้แก้ไฟล์ .tf เลย วิธีตรวจสอบคือดูค่าปัจจุบันใน state ด้วย terraform state show digitalocean_droplet.web เทียบกับของจริง ถ้าต้องการให้ state sync กับของจริงโดยไม่แก้อะไร ให้รัน terraform apply -refresh-only (หรือ terraform refresh ในเวอร์ชันเก่า) แต่ทางที่ดีที่สุดคือกันไม่ให้เกิดตั้งแต่ต้น โดยตกลงกับทีมว่าการแก้ resource ที่ Terraform ดูแลต้องผ่านไฟล์ .tf เท่านั้น ห้ามคลิกแก้ผ่าน dashboard ปัญหาที่สองคือ provider authentication ล้มเหลว เจอ error ประมาณ “Error: Unable to authenticate you” หรือ “401 Unauthorized” สาเหตุที่พบบ่อยคือ token หมดอายุ/ถูก revoke, token มีสิทธิ์แค่ read แต่ config ต้องการ write, หรือชื่อ environment variable ไม่ตรงกับชื่อ variable ใน Terraform เช่นประกาศ variable \"do_token\" แต่ set export TF_VAR_token=... (ชื่อไม่ตรง ต้องเป็น TF_VAR_do_token) วิธีแก้คือสร้าง Personal Access Token ใหม่จาก cloud.digitalocean.com/account/api/tokens ให้สิทธิ์ read/write แล้วตรวจชื่อ environment variable ให้ตรงกับชื่อ variable เป๊ะๆ ปัญหาที่สามคือลำดับการสร้าง resource ผิด (dependency ordering) โดยปกติ Terraform คำนวณลำดับให้อัตโนมัติจากการอ้างอิง attribute ข้าม resource เช่น vpc_uuid = digitalocean_vpc.prod.id แต่บาง resource ไม่มีการอ้างอิง attribute กันตรงๆ ทั้งที่ต้องสร้างก่อน-หลัง เช่นต้องรอ Firewall ผูกกับ Droplet ให้เสร็จก่อนเปิด service กรณีแบบนี้ต้องระบุ depends_on เอง เช่น depends_on = [digitalocean_firewall.web_fw] ไม่งั้น Terraform อาจสร้าง resource สองตัวพร้อมกันแบบไม่รอกัน ปัญหาสุดท้ายคือ import mismatch หลังรัน terraform import ดึง resource เดิมเข้า state สำเร็จ แต่พอรัน terraform plan กลับเห็น diff เพราะไฟล์ .tf ที่เขียนเองยังไม่ตรงกับ attribute จริงทุกตัว (เช่น tags, region, size ไม่ตรง) วิธีแก้คือรัน terraform show เพื่อดูค่าจริงทั้งหมดใน state แล้วแก้ไฟล์ .tf ให้ตรงทีละ field จนกว่า terraform plan จะรายงาน “No changes”
- State drift: ตรวจด้วย
terraform state showและซิงก์ด้วยterraform apply -refresh-only - Auth error 401: เช็คชื่อ
TF_VAR_do_tokenให้ตรงกับชื่อ variable และสิทธิ์ token ต้องเป็น read/write - ใช้
depends_onเมื่อ resource ต้องรอกันแต่ไม่มีการอ้างอิง attribute โดยตรง - หลัง
terraform importให้เทียบด้วยterraform showจนกว่า plan จะขึ้น No changes
แนวทางปฏิบัติที่ดีที่สุด (Best Practices)
นอกจากการแยก state และใช้ module ตามที่กล่าวไปแล้ว ยังมีรายละเอียดเชิงปฏิบัติอีกหลายจุดที่ช่วยให้ Terraform บน DigitalOcean ทำงานได้มั่นคงขึ้นในระยะยาว โดยเฉพาะเมื่อทีมโตขึ้นและมีคน apply พร้อมกันได้ เรื่องแรกที่มักถูกมองข้ามคือ state locking บน remote backend แบบ S3-compatible อย่าง DigitalOcean Spaces backend นี้ไม่มีกลไก lock แบบเดียวกับที่ AWS S3 คู่กับ DynamoDB ให้มาโดยตรง หมายความว่าถ้าสองคนรัน terraform apply พร้อมกันมีโอกาส state เสียหายหรือ apply ทับกันได้ วิธีป้องกันที่ปฏิบัติได้จริงคือบังคับให้ apply ผ่าน CI pipeline เดียวที่ตั้ง concurrency แบบคิวเท่านั้น (เช่น GitHub Actions ตั้ง concurrency: group: terraform-prod) ไม่ให้ job สอง job รันพร้อมกัน แทนที่จะปล่อยให้ทุกคน apply จากเครื่อง local ได้อิสระ เรื่องที่สองคือ lock provider version ให้แน่นอนด้วยไฟล์ .terraform.lock.hcl ที่ Terraform สร้างให้อัตโนมัติหลัง terraform init ไฟล์นี้ควร commit เข้า Git เสมอ (ต่างจาก terraform.tfstate ที่ห้าม commit) เพราะมันล็อก checksum ของ provider เวอร์ชันที่ทดสอบแล้วให้ตรงกันทุกเครื่องในทีมและใน CI ป้องกันปัญหา provider เวอร์ชันใหม่ที่มี behavior เปลี่ยนหลุดเข้ามาโดยไม่ตั้งใจ เรื่องที่สามคือรัน quality check ก่อน apply เสมอ อย่างน้อยควรมี terraform fmt -check ตรวจ formatting, terraform validate ตรวจ syntax และ type ของ config ให้ถูกต้อง และถ้าต้องการเข้มขึ้นสามารถเพิ่มเครื่องมือ open-source อย่าง tflint หรือ tfsec เข้า pipeline เพื่อตรวจ best practice และช่องโหว่ security เบื้องต้น เช่น ลืมปิด public access บน resource ที่ควรเป็น private เรื่องสุดท้ายคือวางโครงสร้างโฟลเดอร์ repo ให้ชัดเจนตั้งแต่ต้น แยก module ที่ใช้ซ้ำออกจาก environment ที่เรียกใช้จริง เช่น infra/\n modules/\n droplet-stack/\n envs/\n dev/\n main.tf\n backend.tf\n prod/\n main.tf\n backend.tf แต่ละ environment มี backend.tf ชี้ state คนละไฟล์ตามที่กล่าวไปในหัวข้อก่อนหน้า และเรียก module เดียวกันจาก modules/ ด้วยค่า variable ต่างกัน พร้อมเขียน description กำกับทุก variable เพื่อให้คนอื่นในทีมเข้าใจได้ทันทีโดยไม่ต้องเปิดโค้ดจริง เช่น variable \"droplet_size\" {\n type = string\n description = \"Droplet size slug เช่น s-1vcpu-1gb\"\n default = \"s-1vcpu-1gb\"\n} โครงสร้างแบบนี้ทำให้ project ขยายไปหลาย environment หรือหลาย service ได้โดยไม่ต้อง copy-paste config ซ้ำ และลดโอกาส apply ผิด environment โดยไม่ตั้งใจ
- S3-compatible backend บน Spaces ไม่มี native state locking — บังคับ apply ผ่าน CI queue เดียวแทนปล่อย apply จากเครื่อง local อิสระ
- commit ไฟล์
.terraform.lock.hclเข้า Git เสมอเพื่อ lock provider version ให้ตรงกันทั้งทีม - รัน
terraform fmt -checkและterraform validateก่อน apply ทุกครั้ง เพิ่ม tflint/tfsec ได้ถ้าต้องการเข้มขึ้น
คำถามที่พบบ่อย (FAQ)
terraform import digitalocean_droplet.web DROPLET_ID เพื่อดึง resource ที่มีอยู่แล้วเข้ามาอยู่ใน state จากนั้นต้องเขียนไฟล์ .tf ให้ attribute ตรงกับของจริงเอง Terraform จะไม่ generate config ให้อัตโนมัติทั้งหมด (เว้นแต่ใช้ฟีเจอร์ import block ในเวอร์ชันใหม่ที่ช่วย generate ได้บางส่วน)terraform plan ใหม่เพื่อดูสถานะปัจจุบันเทียบกับ config แล้วแก้ปัญหาที่ทำให้ error จากนั้น apply ซ้ำได้ตามปกติ ไม่ต้องลบ state ทิ้งterraform import ไล่ทีละ resource กลับเข้า state ใหม่ ซึ่งเสียเวลาพอสมควรถ้ามี resource เยอะ นี่คือเหตุผลสำคัญที่ควรใช้ remote backend ที่มี versioning (เช่น DigitalOcean Spaces เปิด object versioning) ไว้ตั้งแต่ต้น เพื่อย้อนดึง state เวอร์ชันก่อนหน้ากลับมาได้ทันทีถ้าเกิดปัญหา