Docker สำหรับนักพัฒนา: Containers, Compose, และ Workflow จริงในการทำงาน
คู่มือปฏิบัติเกี่ยวกับแนวคิด Docker, แนวทางที่ดีที่สุดสำหรับ Dockerfile, และการใช้ Docker Compose เพื่อรัน development environment แบบหลาย service
"มันทำงานได้บนเครื่องฉันนะ" คือประโยคที่จุดชนวนความล้มเหลวในการ deploy มานับพัน Docker แก้ปัญหานี้ด้วยการบรรจุแอปพลิเคชันและ dependency ทั้งหมดของคุณลงใน container — สภาพแวดล้อมที่แยกตัวและสร้างซ้ำได้ ซึ่งทำงานเหมือนกันทุกที่ ไม่ว่าจะเป็น laptop หรือ production
Containers vs. virtual machines
| Virtual Machine | Container | |
|---|---|---|
| การแยกตัว | Full OS | ระดับ Process |
| เวลาเริ่มต้น | หลายนาที | หลายวินาที |
| ขนาด | GBs | MBs |
| Overhead | สูง (hypervisor) | แทบไม่มี |
| การใช้งาน | แยก OS เต็มรูปแบบ | บรรจุ App |
Container แชร์ host kernel แต่แยก filesystem, process และ networking ออกจากกัน นั่นจึงเป็นเหตุผลที่มันเบามาก
แนวคิดหลักของ Docker
Image — พิมพ์เขียวแบบอ่านอย่างเดียวสำหรับ container ลองนึกภาพเหมือนการนิยาม class
Container — instance ที่กำลังรันอยู่ของ image ลองนึกภาพเหมือน object ที่สร้างจาก class
Registry — ระบบจัดเก็บและกระจาย image Docker Hub คือค่าเริ่มต้นแบบสาธารณะ ส่วน GitHub Container Registry และ AWS ECR เป็นทางเลือกที่นิยมใช้กัน
Volume — พื้นที่จัดเก็บข้อมูลถาวรที่ยังคงอยู่แม้ container จะ restart ข้อมูลที่เขียนภายใน container โดยไม่มี volume จะหายไปเมื่อ container หยุดทำงาน
การเขียน Dockerfile ที่ดี
# ใช้ version ที่ระบุชัดเจน — ห้ามใช้ "latest" ใน production
FROM node:20-alpine
# กำหนด working directory
WORKDIR /app
# Copy ไฟล์ dependency ก่อน (ใช้ประโยชน์จาก layer caching)
COPY package.json package-lock.json ./
RUN npm ci --only=production
# Copy โค้ดแอปพลิเคชัน
COPY . .
# Build แอป
RUN npm run build
# รันในฐานะ non-root user เพื่อความปลอดภัย
USER node
# ระบุ port (ไม่ได้ publish)
EXPOSE 3000
# ใช้ exec form เพื่อจัดการ signal อย่างถูกต้อง
CMD ["node", "server.js"]
Layer caching: กุญแจสู่การ build ที่รวดเร็ว
แต่ละคำสั่งใน Dockerfile จะสร้าง layer ขึ้นมา Docker จะ cache layer ไว้ — หากไม่มีการเปลี่ยนแปลงใน layer นั้นหรือ layer ก่อนหน้า Docker จะนำ cache กลับมาใช้
Copy ไฟล์ dependency ก่อนโค้ดแอปพลิเคชัน dependency เปลี่ยนแปลงน้อยกว่าโค้ดของคุณมาก หากคุณ copy ทุกอย่างพร้อมกัน การเปลี่ยนแปลงเพียงบรรทัดเดียวใน index.js จะทำให้ cache การติดตั้ง dependency ใช้ไม่ได้และบังคับให้รัน npm install ใหม่ทั้งหมด
Multi-stage builds
ใช้ multi-stage builds เพื่อให้ production image มีขนาดเล็กลง:
# Stage 1: Build
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Stage 2: Production (ไม่มี dev dependencies, ไม่มี source)
FROM node:20-alpine AS runner
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
USER node
CMD ["node", "dist/server.js"]
image สุดท้ายจะมีเฉพาะผลลัพธ์ที่ build แล้ว — ไม่มี source code, ไฟล์ test หรือ dev dependency
Docker Compose สำหรับการพัฒนาในเครื่อง
Docker Compose จัดการแอปพลิเคชันที่มีหลาย container ไฟล์ docker-compose.yml เพียงไฟล์เดียวกำหนด local stack ทั้งหมดของคุณ:
version: "3.9"
services:
app:
build: .
ports:
- "3000:3000"
environment:
- DATABASE_URL=postgresql://user:pass@db:5432/myapp
depends_on:
db:
condition: service_healthy
volumes:
- .:/app
- /app/node_modules
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
POSTGRES_DB: myapp
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U user"]
interval: 5s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
postgres_data:
สร้าง scaffolding สำหรับ stack ของคุณด้วย Docker Compose Generator ของเรา — เลือก service ที่ต้องการและรับ docker-compose.yml ที่พร้อมใช้งานใน production ได้ในไม่กี่วินาที
คำสั่ง Compose ที่จำเป็น
# เริ่ม service ทั้งหมดในพื้นหลัง
docker compose up -d
# ดู log (แบบ follow mode)
docker compose logs -f app
# รันคำสั่งภายใน container ที่กำลังทำงาน
docker compose exec app sh
# หยุดและลบ container (เก็บ volume ไว้)
docker compose down
# หยุด ลบ container และ volume ด้วย
docker compose down -v
# Rebuild image หลังจากเปลี่ยน Dockerfile
docker compose up -d --build
Environment variables และ secrets
อย่า hardcode credentials ใน Dockerfile หรือไฟล์ Compose ใช้ไฟล์ .env แทน:
# .env (เพิ่มใน .gitignore!)
DATABASE_URL=postgresql://user:pass@db:5432/myapp
REDIS_URL=redis://redis:6379
JWT_SECRET=your-super-secret-key
Compose จะโหลด .env จาก project directory โดยอัตโนมัติ อ้างอิงตัวแปรด้วย ${VARIABLE_NAME} ใน YAML ของคุณ
ใช้ Env Generator ของเรา เพื่อสร้างไฟล์ .env พร้อม default ที่เหมาะสมและ template .env.example สำหรับทีมของคุณ
Networking ใน Docker
Compose จะสร้าง network เริ่มต้นสำหรับ stack ของคุณ service ต่างๆ สามารถเชื่อมต่อถึงกันโดยใช้ ชื่อ service เป็น hostname — นั่นคือเหตุผลที่แอปใช้ db เป็น database host แทนที่จะเป็น localhost
app → connects to "db:5432"
app → connects to "redis:6379"
เฉพาะ port ที่ map ไว้อย่างชัดเจนด้วย ports: เท่านั้นที่เข้าถึงได้จาก host machine
Health checks และลำดับการเริ่มต้น
depends_on จะรอเฉพาะให้ container เริ่มต้น เท่านั้น ไม่ได้รอให้ พร้อมใช้งาน container ของ database เริ่มต้นในไม่กี่วินาที แต่ Postgres อาจต้องการเวลาอีกสักครู่กว่าจะรับการเชื่อมต่อได้ ใช้ healthcheck + condition: service_healthy เพื่อรอจนกว่า service จะพร้อมใช้งานจริงๆ
ข้อควรพิจารณาสำหรับ Production
- ใช้ image tag ที่ระบุชัดเจน —
node:20.11.1-alpineไม่ใช่node:latest - สแกน image หาช่องโหว่ —
docker scout cves myapp:latest - กำหนด resource limits — ป้องกันไม่ให้ container ตัวเดียวดึงทรัพยากรจนตัวอื่นไม่พอใช้
- ใช้ read-only filesystem เท่าที่เป็นไปได้ —
read_only: trueใน Compose - อย่ารันในฐานะ root — เพิ่ม
USER nodeหรือเทียบเท่า - ตั้งค่า Nginx เป็น reverse proxy ไว้หน้าแอปของคุณ — ใช้ Nginx Config Generator ของเรา เพื่อรับ config ที่ผ่านการทดสอบมาแล้ว
Quick reference
# Images
docker images # แสดง image ในเครื่อง
docker pull nginx:alpine # ดาวน์โหลด image
docker rmi myimage # ลบ image
# Containers
docker ps # container ที่กำลังรัน
docker ps -a # container ทั้งหมด
docker stop <id> # หยุดอย่างสุภาพ
docker rm <id> # ลบ container ที่หยุดแล้ว
docker logs <id> -f # ดู log แบบ stream
# Cleanup
docker system prune # ลบทุกสิ่งที่ไม่ได้ใช้
docker volume prune # ลบ volume ที่ไม่ได้ใช้
Docker ขจัดปัญหา bug ที่เกี่ยวกับ environment ทั้งหมด และทำให้การ onboard นักพัฒนาใหม่เป็นเรื่องง่ายมาก เมื่อคุณเชี่ยวชาญพื้นฐานที่นี่แล้ว คุณจะมีรากฐานสำหรับ Kubernetes, CI/CD pipelines และการ deploy บน cloud