หนึ่งในปัญหาที่พบบ่อยที่สุดเมื่อเริ่มใช้ Docker คือข้อมูลหายทุกครั้งที่ Container ถูกลบหรือสร้างใหม่ ปัญหานี้เกิดจากการเข้าใจผิดเกี่ยวกับวิธีที่ Docker จัดการ filesystem ภายใน Container ซึ่งโดยธรรมชาติแล้วเป็น ephemeral — สร้างขึ้นมาพร้อม Container และหายไปพร้อมกันเมื่อ Container ถูกลบ Docker Volumes คือกลไกที่ Docker ออกแบบมาเพื่อแก้ปัญหานี้โดยเฉพาะ ช่วยให้ข้อมูลที่สำคัญอย่างฐานข้อมูล ไฟล์ที่ผู้ใช้อัปโหลด หรือ configuration files ยังคงอยู่ได้แม้ Container จะถูกหยุดหรือลบไปแล้ว บทความนี้จะอธิบายแนวคิด Docker Volumes อย่างละเอียด พร้อมคำสั่งและตัวอย่างการใช้งานจริงที่ทดสอบบน VPS Ubuntu แล้ว

Docker Storage — เข้าใจ Writable Layer ก่อน

เพื่อเข้าใจว่าทำไม Volumes ถึงจำเป็น ต้องทราบก่อนว่า Docker จัดการ filesystem ของ Container อย่างไร Docker Image สร้างขึ้นจากหลาย Layer ที่ซ้อนกัน (Union Filesystem) และทุก Layer เป็น read-only เมื่อ Container ถูกสร้างจาก Image Docker จะเพิ่ม Writable Layer ด้านบนสุดเข้าไป การเขียน ลบ หรือแก้ไขไฟล์ใดๆ ภายใน Container จะเกิดขึ้นบน Writable Layer นี้เท่านั้น ไม่ได้แตะ Image Layer เดิม

ปัญหาคือ Writable Layer ผูกติดกับ Container instance นั้นๆ โดยตรง เมื่อ Container ถูกลบด้วย docker rm หรือ docker-compose down Writable Layer ก็ถูกลบไปด้วยพร้อมกันทั้งหมด นั่นหมายความว่าถ้าคุณรัน MySQL ใน Container และเก็บข้อมูลไว้ใน Writable Layer ข้อมูลทั้งหมดจะหายทันทีเมื่อ Container ถูกลบ Docker มีกลไก 3 แบบสำหรับจัดการข้อมูลที่ต้องการความคงทน ได้แก่ Volumes, Bind Mounts และ tmpfs Mounts

Docker Volume คืออะไร และทำงานอย่างไร

Docker Volume คือ directory พิเศษที่ Docker Engine จัดการแยกออกจาก Container lifecycle อย่างสมบูรณ์ ข้อมูลใน Volume ถูกเก็บอยู่ใน host filesystem ที่ path /var/lib/docker/volumes/<volume-name>/_data โดย Docker Engine เป็นผู้ดูแล ไม่ใช่ผู้ใช้งานโดยตรง ทำให้มีข้อดีหลายอย่างเหนือการเก็บข้อมูลแบบอื่น

Volume มีวงจรชีวิต (lifecycle) เป็นของตัวเองแยกจาก Container โดยสิ้นเชิง คุณสามารถลบ Container ได้โดยไม่กระทบข้อมูลใน Volume และสามารถ attach Volume เดิมกลับเข้า Container ใหม่ได้ทันที นอกจากนี้ Volume ยังรองรับ Volume Driver ที่ช่วยให้เก็บข้อมูลบน remote storage เช่น NFS, AWS EBS หรือ Azure Disk ได้อีกด้วย

ประเภทของ Docker Storage: Volume vs Bind Mount vs tmpfs

ก่อนจะเจาะลึก Volume ควรทำความเข้าใจทั้ง 3 ตัวเลือกเพื่อเลือกให้ถูกกับงาน

ประเภท เก็บข้อมูลที่ไหน Persistence เหมาะกับ
Named Volume /var/lib/docker/volumes/ ถาวร (ลบเองได้) Database, user uploads, production data
Bind Mount path บน host (กำหนดเอง) ถาวร (ขึ้นกับ host) Development, config files, source code
tmpfs Mount RAM (ไม่เขียน disk) ชั่วคราว (หายเมื่อ stop) Session cache, temp data, secrets
Anonymous Volume /var/lib/docker/volumes/ (random ID) ถาวรแต่หาชื่อยาก ไม่แนะนำสำหรับ production

ในงาน production แนะนำให้ใช้ Named Volume เป็นหลัก เพราะ Docker จัดการ lifecycle ให้และตั้งชื่อที่มีความหมายได้ ส่วน Bind Mount เหมาะกับ development workflow ที่ต้องการแก้ไขโค้ดแล้วเห็นผลใน Container ทันที

การสร้างและจัดการ Docker Volume ด้วยคำสั่ง

เริ่มต้นด้วยคำสั่งพื้นฐานที่ต้องรู้:

# สร้าง Named Volume ใหม่
docker volume create myapp_data

# ดูรายการ Volume ทั้งหมด
docker volume ls

# ดูรายละเอียดของ Volume
docker volume inspect myapp_data

# ลบ Volume (ต้องไม่มี Container ใช้งานอยู่)
docker volume rm myapp_data

# ลบ Volume ที่ไม่มี Container ใช้แล้ว (Prune)
docker volume prune

# ลบ Volume พร้อม Container ตอน docker-compose down
docker-compose down -v

ตัวอย่าง output ของ docker volume inspect:

[
    {
        "CreatedAt": "2026-06-09T10:00:00+07:00",
        "Driver": "local",
        "Labels": {},
        "Mountpoint": "/var/lib/docker/volumes/myapp_data/_data",
        "Name": "myapp_data",
        "Options": {},
        "Scope": "local"
    }
]

การใช้ Volume กับ Docker Run และ Docker Compose

มีสองวิธีหลักในการ mount Volume เข้า Container: ผ่าน flag -v หรือ --mount ใน docker run และผ่าน volumes: ใน docker-compose.yml

วิธีที่ 1 — docker run กับ -v flag

# รูปแบบ: -v <volume-name>:<path-ใน-container>
docker run -d \
  --name mysql_db \
  -e MYSQL_ROOT_PASSWORD=secret \
  -e MYSQL_DATABASE=myapp \
  -v mysql_data:/var/lib/mysql \
  mysql:8.0

# Bind Mount — ผูกกับ path จริงบน host
docker run -d \
  --name nginx_web \
  -v /home/user/website:/usr/share/nginx/html:ro \
  -p 80:80 \
  nginx:alpine

วิธีที่ 2 — docker-compose.yml (แนะนำสำหรับ production)

version: '3.8'

services:
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: secret
      POSTGRES_DB: myapp
    volumes:
      - postgres_data:/var/lib/postgresql/data
    restart: unless-stopped

  app:
    image: myapp:latest
    depends_on:
      - db
    volumes:
      - app_uploads:/app/uploads
      - app_logs:/app/logs
    ports:
      - "8000:8000"

# ประกาศ Named Volume ในระดับ top-level
volumes:
  postgres_data:
  app_uploads:
  app_logs:

การประกาศ volumes: ในระดับ top-level ของ docker-compose.yml ทำให้ Docker สร้าง Named Volume อัตโนมัติถ้ายังไม่มี และข้อมูลจะคงอยู่แม้รัน docker-compose down ต้องใช้ docker-compose down -v จึงจะลบ Volume ด้วย

การ Backup และ Restore Docker Volume

การ backup ข้อมูลจาก Volume เป็นส่วนสำคัญของการดูแล production server ต่อไปนี้คือวิธีมาตรฐานที่ใช้บ่อยที่สุด

Backup Volume เป็นไฟล์ tar.gz

# Backup Volume ชื่อ postgres_data ไปยังโฟลเดอร์ปัจจุบัน
docker run --rm \
  -v postgres_data:/source:ro \
  -v $(pwd):/backup \
  alpine \
  tar czf /backup/postgres_data_$(date +%Y%m%d).tar.gz -C /source .

# ตรวจสอบไฟล์ backup
ls -lh postgres_data_*.tar.gz

Restore Volume จากไฟล์ backup

# สร้าง Volume ใหม่สำหรับ restore
docker volume create postgres_data_restore

# Restore จาก tar.gz
docker run --rm \
  -v postgres_data_restore:/target \
  -v $(pwd):/backup \
  alpine \
  sh -c "cd /target && tar xzf /backup/postgres_data_20260609.tar.gz"

Backup Database โดยตรง (แนะนำสำหรับ PostgreSQL/MySQL)

# PostgreSQL — ใช้ pg_dump แทน tar (ได้ consistent dump)
docker exec postgres_container pg_dump -U myuser mydb \
  > backup_$(date +%Y%m%d).sql

# MySQL — ใช้ mysqldump
docker exec mysql_container mysqldump \
  -u root -psecret myapp \
  > mysql_backup_$(date +%Y%m%d).sql

# Restore PostgreSQL
cat backup_20260609.sql | docker exec -i postgres_container \
  psql -U myuser mydb

เคล็ดลับสำหรับ Production: อย่า backup ด้วย tar ตรงๆ ขณะ Database กำลังทำงาน เพราะอาจได้ไฟล์ที่ไม่สอดคล้องกัน (inconsistent state) ให้ใช้ pg_dump หรือ mysqldump ซึ่งออกแบบมาให้ dump ได้ขณะ Database ยังรันอยู่อย่างปลอดภัย สำหรับ MongoDB ใช้ mongodump และตั้ง cron job ให้ backup อัตโนมัติทุกคืนแล้วส่งไป remote storage อีกที

การแชร์ Volume ระหว่างหลาย Container

หนึ่งในประโยชน์ที่ทรงพลังของ Docker Volume คือความสามารถในการแชร์ข้อมูลระหว่างหลาย Container พร้อมกัน ซึ่งมีประโยชน์มากสำหรับสถาปัตยกรรม microservices ที่ต้องการแชร์ไฟล์กัน

version: '3.8'

services:
  # App server เขียนไฟล์ upload
  app:
    image: myapp:latest
    volumes:
      - shared_uploads:/app/uploads

  # Nginx Proxy อ่านไฟล์ upload เพื่อ serve static files
  nginx:
    image: nginx:alpine
    volumes:
      - shared_uploads:/usr/share/nginx/html/uploads:ro
    ports:
      - "80:80"

  # Worker process อ่านไฟล์ upload เพื่อ process
  worker:
    image: myworker:latest
    volumes:
      - shared_uploads:/worker/input:ro

volumes:
  shared_uploads:

ในตัวอย่างนี้ Container ทั้ง 3 ตัวแชร์ Volume shared_uploads เดียวกัน แต่ nginx และ worker mount แบบ read-only (:ro) เพื่อป้องกันการเขียนทับโดยไม่ตั้งใจ ควรกำหนด :ro ให้กับ Container ที่ต้องการอ่านอย่างเดียวเสมอ เป็น best practice ด้านความปลอดภัย

Volume Permissions และการแก้ปัญหาเรื่อง User

ปัญหา permission เป็นเรื่องที่พบบ่อยมากเมื่อใช้ Volume บน Linux เนื่องจาก UID/GID ของ process ใน Container อาจไม่ตรงกับ ownership ของ directory บน host ทำให้เกิด Permission Denied error

# ตรวจสอบ ownership ของ Volume บน host
ls -la /var/lib/docker/volumes/myapp_data/_data

# วิธีแก้ปัญหา: กำหนด user ใน Dockerfile
# เพิ่มใน Dockerfile ของ app
RUN groupadd -r appgroup && useradd -r -g appgroup appuser
RUN chown -R appuser:appgroup /app
USER appuser

# หรือ กำหนด user ตอน run
docker run -d \
  --user 1000:1000 \
  -v myapp_data:/app/data \
  myapp:latest

# สำหรับ docker-compose.yml
services:
  app:
    image: myapp:latest
    user: "1000:1000"
    volumes:
      - myapp_data:/app/data

สำหรับ database images อย่าง MySQL, PostgreSQL หรือ MongoDB ทีม Docker Official Image ออกแบบ entrypoint ให้จัดการ permission อัตโนมัติ ไม่ต้องกำหนดเอง แต่ถ้าสร้าง Image เองต้องระวังเรื่องนี้เป็นพิเศษ

Best Practices สำหรับ Docker Volume ใน Production

เมื่อใช้งาน Docker Volume บน VPS ที่ให้บริการจริง มีแนวปฏิบัติที่ควรทำตามเพื่อให้ระบบมีเสถียรภาพและข้อมูลปลอดภัย

# ตัวอย่าง cron job backup อัตโนมัติ (เพิ่มใน crontab -e)
# Backup ทุกวัน เวลา 02:00 น.
0 2 * * * docker run --rm \
  -v myapp_postgres_data:/source:ro \
  -v /backup/docker:/backup \
  alpine \
  tar czf /backup/postgres_$(date +\%Y\%m\%d).tar.gz \
  -C /source . && \
  find /backup/docker -name "postgres_*.tar.gz" -mtime +30 -delete

คำถามที่พบบ่อย (FAQ)

Docker Volume ต่างจาก Bind Mount อย่างไร

Docker Volume ถูกจัดการโดย Docker Engine เก็บข้อมูลใน /var/lib/docker/volumes/ และพกพาข้ามเครื่องได้ง่าย ส่วน Bind Mount ผูกกับ path บน host โดยตรง เหมาะสำหรับ development ที่ต้องการแก้ไขไฟล์ได้ทันที แต่ขึ้นกับโครงสร้าง directory บน host ในงาน production ควรใช้ Named Volume เป็นหลัก เพราะ Docker จัดการ lifecycle และ permission ให้ และย้าย host ได้ง่ายกว่า

ข้อมูลใน Docker Volume จะหายไปเมื่อ Container ถูกลบหรือไม่

ไม่หาย Volume มีวงจรชีวิตแยกจาก Container โดยสิ้นเชิง แม้จะรัน docker rm -f หรือ docker-compose down ข้อมูลใน Named Volume ยังคงอยู่บน host จนกว่าจะลบ Volume เองด้วย docker volume rm หรือ docker volume prune ดังนั้นการลบ Container จึงปลอดภัยสำหรับข้อมูล แต่ควร backup สม่ำเสมออยู่ดีเพื่อป้องกัน disk failure

วิธี backup ข้อมูลจาก Docker Volume ทำอย่างไร

วิธีมาตรฐานคือรัน Container ชั่วคราวที่ mount volume แล้วสร้าง tar archive เช่น: docker run --rm -v myvolume:/data -v $(pwd):/backup alpine tar czf /backup/backup.tar.gz -C /data . จะได้ไฟล์ backup.tar.gz ในโฟลเดอร์ปัจจุบัน สำหรับ PostgreSQL/MySQL ควรใช้ pg_dump/mysqldump แทนการ backup ไฟล์ raw เพื่อความสอดคล้องของข้อมูล

ควรใช้ Volume หรือ tmpfs สำหรับข้อมูลชั่วคราวใน Container

ถ้าข้อมูลไม่ต้องการ persistence ระหว่าง restart และต้องการความเร็วสูงสุด ให้ใช้ tmpfs mount แทน Volume เพราะเก็บใน RAM ไม่เขียน disk แต่ถ้า Container restart แล้วข้อมูลหายได้รับไม่ได้ ให้ใช้ Named Volume เสมอ tmpfs เหมาะกับ session cache, temp files ที่ regenerate ได้ ส่วน Volume เหมาะกับ database, user uploads, config files ที่ต้องคงอยู่

VPS KVM ประสิทธิภาพสูงของ AsiaGB

AsiaGB VPS Root Access เต็มรูปแบบ Docker MySQL Python Node.js เริ่มต้น 500 บาท/เดือน

ดูแพ็กเกจ VPS