การจัดการ Docker infrastructure ด้วย Terraform ช่วยให้ประกาศ container, network, volume และ image แบบ declarative ในโค้ด แทนที่จะเขียน docker-compose หรือเรียก CLI ทีละคำสั่ง เหมาะสำหรับทีมที่ต้องการรวม workflow ของ infrastructure ทั้งหมด (VM, network, DNS, container) ให้อยู่ในเครื่องมือเดียว พร้อมมี state file และ plan/apply workflow เหมือนกับการจัดการ cloud ทั่วไป
บทความนี้อธิบายการใช้ kreuzwerker/docker provider ของ Terraform ตั้งแต่การเชื่อมต่อ Docker daemon, การสร้าง container, network, volume, การ pull image, การใช้ร่วมกับ Docker Swarm และข้อจำกัดที่ควรรู้ก่อนใช้ในระดับ production
การตั้งค่า Docker Provider
Terraform ใช้ provider kreuzwerker/docker ซึ่งเป็น provider community ที่ครอบคลุม Docker API ได้ครบถ้วน
terraform {
required_providers {
docker = {
source = "kreuzwerker/docker"
version = "~> 3.0"
}
}
}
provider "docker" {
host = "unix:///var/run/docker.sock"
}
ถ้า Docker daemon อยู่คนละเครื่อง สามารถเชื่อมต่อผ่าน TCP พร้อม TLS
provider "docker" {
host = "tcp://docker.example.com:2376"
ca_material = file("~/.docker/ca.pem")
cert_material = file("~/.docker/cert.pem")
key_material = file("~/.docker/key.pem")
}
การสร้าง Container
resource docker_container สร้างและ manage lifecycle ของ container ให้อัตโนมัติ เมื่อ apply ใหม่ container จะถูกรีสตาร์ทเมื่อมีการเปลี่ยนแปลง
resource "docker_image" "nginx" {
name = "nginx:1.25-alpine"
keep_locally = false
}
resource "docker_container" "web" {
name = "web"
image = docker_image.nginx.image_id
ports {
internal = 80
external = 8080
}
restart = "unless-stopped"
env = [
"APP_ENV=production"
]
}
attribute image_id จะทำให้ Terraform รู้ hash ของ image จริง ๆ ไม่ใช่แค่ tag ที่อาจเปลี่ยนเนื้อหาได้ เมื่อ registry push image ใหม่ใน tag เดิม
Network
การสร้าง Docker network แยกต่างหาก ช่วยให้ container คุยกันผ่านชื่อ service ได้ โดยไม่ต้องผูกกับ IP
resource "docker_network" "app_net" {
name = "app-network"
driver = "bridge"
ipam_config {
subnet = "172.28.0.0/16"
}
}
resource "docker_container" "web" {
name = "web"
image = docker_image.nginx.image_id
networks_advanced {
name = docker_network.app_net.name
}
}
ใช้ driver bridge สำหรับ host เดียว, overlay สำหรับ Docker Swarm หลาย node, หรือ macvlan สำหรับให้ container มี IP บน LAN เดียวกับ host
Volume และ Data Persistence
แยก data ออกจาก container lifecycle ด้วย named volume เพื่อให้ข้อมูลไม่หายเมื่อ container ถูกลบ
resource "docker_volume" "db_data" {
name = "db-data"
}
resource "docker_container" "db" {
name = "postgres"
image = "postgres:15"
env = [
"POSTGRES_PASSWORD=${var.db_password}"
]
volumes {
container_path = "/var/lib/postgresql/data"
volume_name = docker_volume.db_data.name
}
}
สำหรับ bind mount ชี้ path บน host ตรง ๆ ใช้ host_path แทน volume_name แต่ควรใช้ named volume เป็นหลักเพื่อให้ย้าย host ได้ง่าย
Secret และ Environment
ค่าที่เป็นความลับ เช่น password, API token ไม่ควรเขียนตรง ๆ ใน tf file — ให้ส่งผ่านตัวแปรที่ Terraform Cloud, HashiCorp Vault หรือ environment variable
variable "db_password" {
type = string
sensitive = true
}
resource "docker_container" "db" {
env = [
"POSTGRES_PASSWORD=${var.db_password}"
]
}
flag sensitive = true ทำให้ Terraform log และ state file masking ค่านี้เวลาแสดงผล ลดความเสี่ยงที่ password จะโผล่มาใน CI log
Multi-Container Stack
การ deploy stack web + database + cache ในครั้งเดียว เขียนเป็น resource หลายตัวแล้วใช้ dependency อัตโนมัติ
resource "docker_network" "stack" {
name = "stack-net"
}
resource "docker_container" "redis" {
name = "cache"
image = "redis:7-alpine"
networks_advanced { name = docker_network.stack.name }
}
resource "docker_container" "api" {
name = "api"
image = "myapp:latest"
env = [
"REDIS_HOST=cache",
"DB_HOST=db"
]
networks_advanced { name = docker_network.stack.name }
depends_on = [docker_container.redis, docker_container.db]
}
Terraform จะสร้าง network ก่อน, จากนั้น redis/db พร้อมกัน, แล้วค่อย api เพราะ depends_on สามารถชี้ชัด dependency ได้
Docker Swarm Mode
สำหรับ cluster Swarm ใช้ resource docker_service แทน docker_container รองรับ replica, rolling update, และ placement constraint
resource "docker_service" "web" {
name = "web"
task_spec {
container_spec {
image = "nginx:1.25-alpine"
}
placement {
constraints = ["node.role == worker"]
}
}
mode {
replicated {
replicas = 3
}
}
endpoint_spec {
ports {
target_port = 80
published_port = 80
}
}
}
Swarm จัดการ scheduling, health check, rolling update, rollback ให้อัตโนมัติ เหมาะกับ environment เล็กถึงกลางที่ไม่ต้องถึงขั้นใช้ Kubernetes
การใช้งานร่วมกับ VM Provisioning
Use case ที่พบบ่อยคือ Terraform สร้าง VM ก่อน, ติดตั้ง Docker Engine ผ่าน cloud-init, แล้วใช้ docker provider ชี้ไปยัง VM นั้นเพื่อ deploy container
resource "digitalocean_droplet" "app" {
name = "app-host"
region = "sgp1"
size = "s-2vcpu-4gb"
image = "ubuntu-22-04-x64"
user_data = <<-EOT
#!/bin/bash
curl -fsSL https://get.docker.com | sh
usermod -aG docker ubuntu
EOT
}
provider "docker" {
alias = "app_host"
host = "ssh://root@${digitalocean_droplet.app.ipv4_address}"
}
resource "docker_container" "web" {
provider = docker.app_host
name = "web"
image = "nginx:alpine"
}
pattern นี้ใช้ SSH tunneling ของ docker-cli ติดต่อ Docker daemon บน remote host ทำให้ไม่ต้องเปิด Docker TCP port ออกสู่อินเทอร์เน็ต
ข้อจำกัดที่ควรรู้
- provider ไม่รองรับ docker compose file ตรง ๆ ต้องแปลง compose → HCL เอง
- ไม่มี health check orchestration ระดับ stack เท่า Kubernetes — ถ้าต้องการ self-healing ซับซ้อนให้ใช้ K8s
- การ scale container ต้องใช้ count หรือ for_each ซึ่งไม่สะดวกเท่า Swarm replica
- เมื่อเปลี่ยน config ของ container ส่วนใหญ่ Terraform จะ destroy + recreate ไม่ใช่ update — ระวัง downtime
- Log driver, resource limit, security options ต้องกำหนดผ่าน attribute ที่เฉพาะเจาะจง ไม่มี default sensible
ข้อควรระวัง
- อย่าเชื่อมต่อ Docker daemon ผ่าน TCP โดยไม่มี TLS — เท่ากับเปิด root shell ให้ใครก็ได้
- State file จะเก็บ env variable ที่ไม่ได้ mark sensitive เป็น plaintext ตรวจสิทธิ์ backend ให้เข้มงวด
- ใช้
keep_locally = falseใน docker_image เพื่อไม่ให้ image ค้างบน host หลัง destroy - สำหรับ production หลาย node ให้พิจารณา Kubernetes หรือ Nomad แทน Docker provider ล้วน
- Backup volume ด้วย tool เช่น restic หรือ Velero — Terraform ไม่ได้ backup data ให้
สรุป
Docker provider ของ Terraform เหมาะสำหรับการจัดการ container บน host เดียวหรือ Swarm cluster เล็ก ๆ ให้รวม workflow กับ infrastructure อื่น ๆ ไว้ในที่เดียวได้ เขียน resource container, network, volume, image, service เป็น HCL แล้วปล่อยให้ plan/apply จัดการ lifecycle
สำหรับ workload ที่ต้องการ orchestration ซับซ้อน เช่น auto-scaling, self-healing, multi-tenancy แนะนำใช้ Kubernetes ร่วมกับ Terraform (provision cluster) + Helm/Kustomize (deploy app) จะยืดหยุ่นกว่า — ส่วน Docker provider เหมาะกับ use case ที่ต้องการ container เพียงเพื่อ run service แบบ straightforward

