← กลับ
DockerContainerมือใหม่

Docker คืออะไร? ทำไม Developer ทุกคนต้องรู้จัก

ปัญหา 'in my machine works' จะหายไปถ้าใช้ Docker เริ่มจากศูนย์ในไม่กี่นาที พร้อม Dockerfile ตัวอย่าง

2025-10-08อ่าน 6 นาทีใหม่

ทำไมต้องรู้จัก Docker

คนที่เขียนโค้ดมาสักพักน่าจะเคยเจอเหตุการณ์ทำนองนี้

โค้ดบนเครื่อง dev รันได้ปกติ แต่พอเอาไปลงเครื่องเพื่อนร่วมทีม รันแล้ว error เพราะ Node version ไม่ตรง พอจะ deploy ขึ้น server ก็เจออีก เพราะ server เป็น Ubuntu 20.04 แต่ที่ test คือ macOS

Docker เกิดมาเพื่อแก้ปัญหานี้ — มัน package ทั้งโค้ด ทั้ง dependency ทั้ง OS lib เข้าไปใน "container" เดียว เอาไปรันที่ไหนก็ได้ผลเหมือนกัน

Container กับ VM ต่างกันยังไง

หลายคนสับสนเรื่องนี้ ลองดูภาพเปรียบเทียบ

Virtual Machine (VM): จำลองทั้งคอม รวม OS เต็มๆ ใช้ disk เป็นหลาย GB, RAM เป็น GB, boot ใช้เวลาเป็นนาที

Container: ไม่จำลอง OS แต่ใช้ kernel ของ host แชร์ ตัว container เก็บแค่ app + dependency disk แค่ MB-100MB, RAM น้อยกว่ามาก, start ใช้เวลาวินาที

ภาพง่ายๆ:

┌─────────────────┐  ┌─────────────────┐
│  VM             │  │  Container      │
│  ┌───────────┐  │  │  ┌───────────┐  │
│  │ App       │  │  │  │ App       │  │
│  ├───────────┤  │  │  ├───────────┤  │
│  │ Libraries │  │  │  │ Libraries │  │
│  ├───────────┤  │  │  └───────────┘  │
│  │ Guest OS  │  │  │  (ใช้ kernel ของ│
│  └───────────┘  │  │   host ร่วมกัน)│
│  Hypervisor     │  │  Docker Engine  │
│  Host OS        │  │  Host OS        │
└─────────────────┘  └─────────────────┘

ดังนั้นใน 1 server เราอาจรัน container ได้ 50 ตัว แต่รัน VM ได้แค่ 5 ตัว

คำว่า Image กับ Container

อีก 2 คำที่ต้องแยกให้ออก:

  • Image — template ของ container เปรียบเหมือน "class" ใน OOP
  • Container — instance ที่กำลังรันจาก image เปรียบเหมือน "object"

จาก 1 image คุณสามารถรันได้หลาย container

ติดตั้ง Docker

  • macOS / Windows: ดาวน์โหลด Docker Desktop จาก docker.com
  • Linux: ใช้ official script
curl -fsSL https://get.docker.com | sh

# ให้ user ปกติใช้ docker ได้โดยไม่ต้อง sudo
sudo usermod -aG docker $USER
# logout-login ใหม่

คำสั่งที่ใช้บ่อยสุด

# รัน container จาก image (ดึง image ลงให้อัตโนมัติ)
docker run hello-world

# ดู container ที่กำลังรัน
docker ps

# ดูทั้งที่หยุดด้วย
docker ps -a

# หยุด container
docker stop <container_id_or_name>

# ลบ container
docker rm <container_id_or_name>

# ดู image ทั้งหมด
docker images

# ลบ image
docker rmi <image_id>

# build image จาก Dockerfile ใน folder ปัจจุบัน
docker build -t myapp:1.0 .

# รัน container แบบ detached + map port
docker run -d -p 3000:3000 --name myapp myapp:1.0

# เข้าไปใน container ที่กำลังรัน (เหมือน SSH)
docker exec -it myapp sh

# ดู log
docker logs -f myapp

flag ที่ควรรู้:

  • -d = detached (รัน background)
  • -p 3000:3000 = map port host:container
  • --name xxx = ตั้งชื่อ container
  • -v ./data:/app/data = mount folder จาก host เข้า container
  • -e NODE_ENV=production = set environment variable
  • --rm = ลบ container ทิ้งหลังหยุด

เขียน Dockerfile แรก

สมมติมี Node.js app ในโฟลเดอร์ปัจจุบัน:

# ใช้ image base ที่มี Node 20 อยู่แล้ว
FROM node:20-alpine

# ตั้ง working directory ใน container
WORKDIR /app

# copy package files ก่อน เพื่อ cache dependency layer
COPY package*.json ./
RUN npm ci --omit=dev

# copy ไฟล์ที่เหลือ
COPY . .

# build (สำหรับ Next.js, TypeScript ฯลฯ)
RUN npm run build

# บอก docker ว่า app listen port อะไร (เป็น metadata เฉยๆ)
EXPOSE 3000

# คำสั่งเริ่มต้นเมื่อ container start
CMD ["npm", "start"]

build แล้วรัน:

docker build -t myapp .
docker run -p 3000:3000 myapp

เปิด http://localhost:3000 ได้เลย

เคล็ดลับ Dockerfile ให้ image เล็กและเร็ว

1. ใช้ alpine variantnode:20-alpine ขนาด ~50MB เทียบกับ node:20 ขนาด ~400MB

2. เรียง layer ให้ cache ได้ดี — copy package*.json แยกก่อน copy code ที่เหลือ เพราะ dependency เปลี่ยนน้อยกว่าโค้ด

3. Multi-stage build — แยก stage build กับ stage run ลด image final ได้เยอะ

# Stage 1: build
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# Stage 2: runtime
FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/.next ./.next
COPY --from=builder /app/public ./public
COPY --from=builder /app/package*.json ./
RUN npm ci --omit=dev
EXPOSE 3000
CMD ["npm", "start"]

วิธีนี้ stage runtime ไม่มี source code, devDependencies, build tool ใดๆ ขนาด final อาจลดได้ 60-70%

4. ใช้ .dockerignore — ไม่ให้ copy node_modules, .git, .env เข้า image

node_modules
.next
.git
.env*
*.md
.vscode

Docker Compose สำหรับหลาย service

ถ้า app ต้องใช้ database + cache ด้วย เขียน docker-compose.yml ครั้งเดียว start ทั้งหมดด้วยคำสั่งเดียว:

services:
  app:
    build: .
    ports:
      - "3000:3000"
    environment:
      DATABASE_URL: postgresql://postgres:secret@db:5432/myapp
      REDIS_URL: redis://cache:6379
    depends_on:
      - db
      - cache

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_PASSWORD: secret
      POSTGRES_DB: myapp
    volumes:
      - pgdata:/var/lib/postgresql/data
    ports:
      - "5432:5432"

  cache:
    image: redis:7-alpine
    ports:
      - "6379:6379"

volumes:
  pgdata:
# start ทั้ง 3 service
docker compose up -d

# ดู log รวมทุก service
docker compose logs -f

# หยุดทั้งหมด
docker compose down

# หยุด + ลบ volume (ข้อมูลใน db หาย)
docker compose down -v

ใน compose service สามารถเรียกหากันด้วยชื่อ service ได้เลย เช่น app ติดต่อ db ผ่าน hostname db

ข้อผิดพลาดที่เจอบ่อย

Container กิน disk ทั้งเครื่อง → image, container ที่ไม่ใช้สะสมไปเรื่อย ลบทิ้งบ้าง:

docker system prune -a --volumes

Build ช้ามาก → ตรวจสอบลำดับ COPY ใน Dockerfile, ใช้ multi-stage, ลองเปิด BuildKit (default แล้วใน Docker version ใหม่)

App รันแล้วเจอ "EACCES: permission denied" → user ใน container ไม่มีสิทธิเขียนใน volume ที่ mount มา แก้โดยเพิ่ม USER node ใน Dockerfile หรือ chown folder

ENV variable ไม่เข้า → จำไว้ว่า ENV ใน Dockerfile กับ -e ตอน run คนละเรื่อง production ควรใช้ -e หรือ env_file ใน compose มากกว่า

สรุป

Docker ลดเวลา onboarding คนเข้าทีมจาก 1 วัน เหลือ 10 นาที แค่ docker compose up ก็พร้อมเขียนโค้ด

ถ้าอยากเริ่มจริง — เปิด Dockerfile ของ project ตัวเองดูก่อน ลอง build, run, exec เข้าไปดู ของพวกนี้ต้อง hands-on เท่านั้นถึงจะเข้าใจจริง

← ดูบทความอื่น