ติดตั้ง Docker และ Docker Compose บน DigitalOcean Droplet (Ubuntu) พร้อมตัวอย่างรันเว็บจริง
คู่มือติดตั้ง Docker Engine และ Docker Compose plugin บน Droplet Ubuntu ตั้งแต่เพิ่ม repository ทางการ รันคอนเทนเนอร์แรก เขียน compose.yaml ไปจนถึงข้อควรระวังเรื่อง Docker ทะลุไฟร์วอลล์ UFW
ทำไมต้องใช้ Docker บน Droplet
Docker ช่วยให้แอปพลิเคชันและ dependency ทั้งหมดถูกแพ็กเป็น image เดียว รันได้เหมือนกันทั้งในเครื่องนักพัฒนาและบนเซิร์ฟเวอร์ ลดปัญหา "เครื่องผมรันได้แต่บนเซิร์ฟเวอร์พัง" และทำให้ย้ายเซิร์ฟเวอร์ง่าย บน สอนสร้าง Droplet แรก ที่มีสเปกเริ่มต้นเพียง 1 vCPU/1 GiB ก็รัน Docker ได้ แต่ถ้ารันหลายคอนเทนเนอร์พร้อมกัน แนะนำอย่างน้อย 2 GiB
เลือกวิธีติดตั้ง 2 แบบ
- Marketplace 1-Click "Docker": สร้าง Droplet จาก image ที่ติดตั้ง Docker มาให้แล้ว เร็วที่สุด เหมาะถ้าไม่อยากติดตั้งเอง ตรวจเวอร์ชันที่ติดมาในหน้า Marketplace
- ติดตั้งเองจาก repository ทางการของ Docker (บทความนี้): ได้เวอร์ชันล่าสุดและอัปเดตผ่าน
aptได้ตามปกติ เหมาะกับคนที่ต้องการควบคุมเวอร์ชัน
ก่อนเริ่ม
- Droplet Ubuntu LTS ที่ตั้งผู้ใช้ปกติ (ไม่ใช่ root) และเปิดไฟร์วอลล์แล้ว ตาม คู่มือความปลอดภัยเซิร์ฟเวอร์
- ล็อกอิน SSH ด้วยผู้ใช้ที่มีสิทธิ์
sudo
ขั้นที่ 1: เพิ่ม repository ของ Docker
sudo apt update
sudo apt install -y ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
คำสั่งนี้เป็นขั้นตอนตามเอกสารของ Docker ณ เวลาที่เขียน หากเอกสารทางการเปลี่ยน ให้ยึดตามหน้า docs.docker.com เป็นหลัก
ขั้นที่ 2: ติดตั้ง Docker Engine
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo systemctl enable --now docker
docker --version
docker compose version
สังเกตว่า Compose รุ่นใหม่เป็น plugin ใช้คำสั่ง docker compose (เว้นวรรค) ไม่ใช่ docker-compose (ขีด) แบบรุ่นเก่า
ขั้นที่ 3: ให้ผู้ใช้ปกติรัน docker ได้
sudo usermod -aG docker $USER
newgrp docker
docker run --rm hello-world
ถ้าเห็นข้อความต้อนรับ แปลว่า Docker ทำงานแล้ว
docker มีสิทธิ์เทียบเท่า root บนเครื่องโดยปริยาย เพราะสั่งเมานต์ดิสก์ของโฮสต์เข้าคอนเทนเนอร์ได้ ให้เพิ่มเฉพาะบัญชีที่ไว้ใจขั้นที่ 4: รันเว็บแรกด้วย Compose
สร้างโฟลเดอร์ ~/site แล้วสร้างไฟล์ compose.yaml
services:
web:
image: nginx:stable
restart: unless-stopped
ports:
- "80:80"
volumes:
- ./html:/usr/share/nginx/html:ro
สร้างไฟล์ html/index.html ใส่ข้อความอะไรก็ได้ จากนั้นรัน
docker compose up -d
docker compose ps
docker compose logs -f web
เปิด http://IP-ของ-Droplet ในเบราว์เซอร์ จะเห็นหน้าเว็บของคุณ
คำสั่งที่ใช้บ่อย
| คำสั่ง | ทำอะไร |
|---|---|
docker compose up -d | สร้างและรันเบื้องหลัง |
docker compose down | หยุดและลบคอนเทนเนอร์ (volume ที่ตั้งชื่อยังอยู่) |
docker compose pull && docker compose up -d | ดึง image ใหม่แล้วรีสตาร์ต |
docker ps -a | ดูคอนเทนเนอร์ทั้งหมด |
docker logs -f ชื่อ | ดู log แบบสด |
docker system df | ดูพื้นที่ที่ Docker ใช้ |
docker system prune | ล้างของที่ไม่ใช้ (อ่านคำเตือนก่อนยืนยัน) |
จุดที่คนพลาดบ่อย: Docker ทะลุ UFW
เมื่อคุณเปิดพอร์ตด้วย ports: "8080:80" Docker จะเพิ่มกฎ iptables ของตัวเอง ซึ่ง ข้ามกฎ UFW ทำให้พอร์ตนั้นเปิดสู่อินเทอร์เน็ตแม้ UFW จะไม่ได้อนุญาต วิธีรับมือ:
- ผูกกับ localhost ถ้าต้องการให้ reverse proxy บนโฮสต์เข้าถึงเท่านั้น:
"127.0.0.1:8080:80" - ใช้ Cloud Firewall ของ DigitalOcean ซึ่งกรองที่ระดับเครือข่ายก่อนถึงเครื่อง ดู Cloud Firewall และ VPC
- ไม่เปิดพอร์ตฐานข้อมูล (3306, 5432, 6379) ออกสาธารณะ ให้คอนเทนเนอร์คุยกันผ่าน network ภายในของ Compose
ตัวอย่าง: แอปเว็บ + ฐานข้อมูลในไฟล์เดียว
services:
app:
image: ghcr.io/example/myapp:latest
restart: unless-stopped
env_file: .env
depends_on:
- db
ports:
- "127.0.0.1:3000:3000"
db:
image: postgres:16
restart: unless-stopped
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
volumes:
- dbdata:/var/lib/postgresql/data
secrets:
- db_password
volumes:
dbdata:
secrets:
db_password:
file: ./db_password.txt
ตัวอย่างนี้ให้ฐานข้อมูลอยู่ภายใน network ของ Compose ไม่เปิดพอร์ตออกนอก และให้แอปเปิดเฉพาะ localhost เพื่อให้ Nginx ทำ reverse proxy และ SSL ดู Deploy Node.js บน Droplet สำหรับการตั้ง Nginx + Let's Encrypt แบบเดียวกัน
จัดการข้อมูลและพื้นที่ดิสก์
- ข้อมูลถาวรต้องอยู่ใน volume หรือ bind mount เสมอ ไม่เก็บในชั้นของคอนเทนเนอร์เพราะหายเมื่อสร้างใหม่
- Image และ log ของ Docker กินดิสก์ได้เร็ว ตั้ง log rotation ในไฟล์
/etc/docker/daemon.json:
{
"log-driver": "json-file",
"log-opts": { "max-size": "10m", "max-file": "3" }
}
แล้ว sudo systemctl restart docker (มีผลกับคอนเทนเนอร์ที่สร้างใหม่)
- สำรองข้อมูล volume เป็นประจำ ทั้ง snapshot และการ dump ฐานข้อมูล ดู Backups และ Snapshots
- ถ้าเก็บไฟล์ผู้ใช้ปริมาณมาก ย้ายไป คู่มือ Spaces Object Storage จะประหยัดดิสก์ของ Droplet
เมื่อไหร่ควรย้ายไป App Platform หรือ Kubernetes
| สถานการณ์ | ตัวเลือกที่เหมาะ |
|---|---|
| 1 เซิร์ฟเวอร์ ไม่กี่คอนเทนเนอร์ ต้องการประหยัดและควบคุมเอง | Docker Compose บน Droplet |
| อยากได้ deploy จาก Git อัตโนมัติ ไม่อยากดูแลเซิร์ฟเวอร์ | คู่มือ App Platform |
| หลายบริการ ต้อง scale ออกหลายเครื่อง | คู่มือ Kubernetes (DOKS) |
คำถามที่พบบ่อย
Docker กิน RAM มากไหม
ตัว Docker daemon เองใช้น้อย RAM ส่วนใหญ่ถูกใช้โดยแอปในคอนเทนเนอร์ ถ้า RAM ไม่พอให้เพิ่ม swap หรืออัปเกรดขนาด Droplet แทนการรันทุกอย่างบนเครื่องเล็กเกินไป
ใช้ docker-compose แบบเก่าได้ไหม
Compose V1 (คำสั่ง docker-compose) ถูกเลิกพัฒนาแล้ว แนะนำใช้ docker compose plugin รุ่นปัจจุบัน ไฟล์ compose ส่วนใหญ่ใช้ร่วมกันได้
อัปเดตเวอร์ชัน Docker อย่างไร
ใช้ sudo apt update && sudo apt upgrade ตามปกติ เมื่อติดตั้งผ่าน repository ทางการ ควรสำรองข้อมูลก่อนอัปเกรดครั้งใหญ่
ต้องใช้ Docker ไหมสำหรับเว็บ WordPress
ไม่จำเป็น แบบติดตั้งตรงบน LEMP เบากว่าและตรงไปตรงมา ดู ติดตั้ง WordPress บน DigitalOcean แต่ Docker สะดวกถ้าอยากรันหลายเว็บแยกกันบนเครื่องเดียว