Docker เปลี่ยนวิธีที่เราสร้าง ส่งมอบ และรันซอฟต์แวร์ แทนที่จะ "ทำงานได้บนเครื่องของฉัน" Docker รับประกันว่าแอปพลิเคชันของคุณจะทำงานเหมือนกันทุกที่ - บนแล็ปท็อปของคุณ, บนเครื่องของเพื่อนร่วมงาน, ใน CI/CD และใน production ในคู่มือนี้ เราจะเริ่มจากศูนย์ไปจนถึงการ deploy แอปพลิเคชันจริง
Docker คืออะไร?
Docker เป็นแพลตฟอร์มที่แพ็กเกจแอปพลิเคชันของคุณและ dependency ทั้งหมดลงในหน่วยมาตรฐานที่เรียกว่า คอนเทนเนอร์ คอนเทนเนอร์เป็นกระบวนการที่แยกตัวและเบา ซึ่งใช้เคอร์เนลของ OS โฮสต์ร่วมกัน แต่มีระบบไฟล์ เครือข่าย และพื้นที่กระบวนการของตัวเอง
คอนเทนเนอร์ vs เครื่องเสมือน
| ด้าน | คอนเทนเนอร์ | เครื่องเสมือน |
|---|---|---|
| การเริ่มต้น | วินาที | นาที |
| ขนาด | MB | GB |
| OS | ใช้เคอร์เนลโฮสต์ร่วมกัน | OS แขกเต็มรูปแบบ |
| การแยกตัว | ระดับกระบวนการ | ระดับฮาร์ดแวร์ |
| ประสิทธิภาพ | เกือบเท่า native | มี overhead จาก hypervisor |
| ความหนาแน่น | หลายร้อยต่อโฮสต์ | หลายสิบต่อโฮสต์ |
ติดตั้ง Docker
# macOS brew install --cask docker # Ubuntu/Debian curl -fsSL https://get.docker.com | sh sudo usermod -aG docker $USER # Verify installation docker --version docker run hello-world
แนวคิดหลัก
อิมเมจ
อิมเมจเป็นเทมเพลตแบบอ่านอย่างเดียวที่มีคำสั่งสำหรับสร้างคอนเทนเนอร์ คิดว่ามันเป็นสแน็ปช็อตของแอปพลิเคชันและสภาพแวดล้อมของมัน
# Pull an image from Docker Hub docker pull node:20-alpine # List local images docker images # Remove an image docker rmi node:20-alpine
คอนเทนเนอร์
คอนเทนเนอร์เป็นอินสแตนซ์ที่กำลังทำงานของอิมเมจ คุณสามารถสร้าง เริ่ม หยุด และลบคอนเทนเนอร์ได้
# Run a container docker run -d --name my-app -p 3000:3000 node:20-alpine # List running containers docker ps # List all containers (including stopped) docker ps -a # Stop a container docker stop my-app # Remove a container docker rm my-app # View logs docker logs my-app # Execute a command inside a running container docker exec -it my-app sh
การเขียน Dockerfile
Dockerfile เป็นไฟล์ข้อความที่มีคำสั่งสำหรับสร้างอิมเมจ แต่ละคำสั่งจะสร้างเลเยอร์หนึ่ง
Dockerfile พื้นฐานสำหรับแอป Node.js
# Use an official Node.js runtime as base image FROM node:20-alpine # Set working directory WORKDIR /app # Copy package files first (better caching) COPY package.json package-lock.json ./ # Install dependencies RUN npm ci --only=production # Copy application code COPY . . # Expose the port the app runs on EXPOSE 3000 # Command to run the application CMD ["node", "server.js"]
สร้างและรัน
# Build the image docker build -t my-node-app . # Run the container docker run -d -p 3000:3000 my-node-app # Visit http://localhost:3000
Multi-Stage Build
Multi-stage build รักษาอิมเมจ production ให้เล็กโดยแยกสภาพแวดล้อมการ build ออกจาก runtime
# Stage 1: Build FROM node:20-alpine AS builder WORKDIR /app COPY package.json package-lock.json ./ RUN npm ci COPY . . RUN npm run build # Stage 2: Production FROM node:20-alpine AS runner WORKDIR /app COPY /app/dist ./dist COPY /app/node_modules ./node_modules COPY /app/package.json ./ EXPOSE 3000 CMD ["node", "dist/server.js"]
สิ่งนี้สร้างอิมเมจที่มีเฉพาะผลลัพธ์ที่คอมไพล์แล้วและ dependency ของ production - ไม่มีซอร์สโค้ด, ไม่มี dependency ของการพัฒนา, ไม่มีเครื่องมือ build
ตัวอย่าง Multi-Stage สำหรับ Next.js
FROM node:20-alpine AS deps WORKDIR /app COPY package.json package-lock.json ./ RUN npm ci FROM node:20-alpine AS builder WORKDIR /app COPY /app/node_modules ./node_modules COPY . . RUN npm run build FROM node:20-alpine AS runner WORKDIR /app ENV NODE_ENV=production COPY /app/public ./public COPY /app/.next/standalone ./ COPY /app/.next/static ./.next/static EXPOSE 3000 CMD ["node", "server.js"]
วอลุ่ม: ข้อมูลถาวร
ตามค่าเริ่มต้น ข้อมูลภายในคอนเทนเนอร์จะหายไปเมื่อคอนเทนเนอร์ถูกลบ วอลุ่มแก้ปัญหานี้
# Create a named volume docker volume create my-data # Run with a volume docker run -d -v my-data:/app/data my-app # Bind mount (map host directory to container) docker run -d -v $(pwd)/data:/app/data my-app # List volumes docker volume ls
เครือข่าย
Docker สร้างเครือข่ายแบบแยกตัวเพื่อให้คอนเทนเนอร์สื่อสารกัน
# Create a custom network docker network create my-network # Run containers on the same network docker run -d --name api --network my-network my-api docker run -d --name db --network my-network postgres:16 # Containers can reach each other by name # From "api" container: postgres://db:5432
Docker Compose
Docker Compose ให้คุณกำหนดและรันแอปพลิเคชันหลายคอนเทนเนอร์ด้วยไฟล์ YAML เดียว
docker-compose.yml
services: api: build: ./api ports: - "3000:3000" environment: - DATABASE_URL=postgres://user:pass@db:5432/mydb depends_on: - db db: image: postgres:16-alpine environment: - POSTGRES_USER=user - POSTGRES_PASSWORD=pass - POSTGRES_DB=mydb volumes: - pgdata:/var/lib/postgresql/data ports: - "5432:5432" redis: image: redis:7-alpine ports: - "6379:6379" volumes: pgdata:
คำสั่ง
# Start all services docker compose up -d # View logs docker compose logs -f # Stop all services docker compose down # Rebuild and restart docker compose up -d --build # Scale a service docker compose up -d --scale api=3
.dockerignore
เหมือนกับ .gitignore ไฟล์นี้ป้องกันไฟล์ที่ไม่จำเป็นไม่ให้ถูกคัดลอกเข้าไปในอิมเมจ
node_modules .git .env *.md .next dist coverage
แนวปฏิบัติที่ดีที่สุดสำหรับ Production
1. ใช้อิมเมจฐานที่เล็ก
# Bad: 1GB+ FROM node:20 # Good: ~180MB FROM node:20-alpine
2. อย่ารันเป็น Root
FROM node:20-alpine RUN addgroup -S app && adduser -S app -G app USER app WORKDIR /home/app COPY . .
3. ใช้แท็กอิมเมจที่เฉพาะเจาะจง
# Bad: can change unexpectedly FROM node:latest # Good: pinned version FROM node:20.11-alpine3.19
4. ใช้ประโยชน์จาก Build Cache
จัดลำดับคำสั่ง Dockerfile จากที่เปลี่ยนน้อยที่สุดไปมากที่สุด:
FROM node:20-alpine WORKDIR /app # These change rarely - cached COPY package.json package-lock.json ./ RUN npm ci --only=production # This changes often - not cached COPY . .
5. Health Check
HEALTHCHECK \ CMD wget -qO- http://localhost:3000/health || exit 1
6. ใช้ตัวแปรสภาพแวดล้อม
ENV NODE_ENV=production ENV PORT=3000
สรุปคำสั่ง Docker ที่ใช้บ่อย
# Images docker build -t name:tag . # Build image docker images # List images docker rmi image_name # Remove image docker image prune # Remove unused images # Containers docker run -d -p 3000:3000 image # Run detached docker ps # List running docker stop container_name # Stop docker rm container_name # Remove docker logs -f container_name # Follow logs docker exec -it container sh # Shell into container # Compose docker compose up -d # Start services docker compose down # Stop services docker compose logs -f # Follow all logs docker compose ps # List services # Cleanup docker system prune -a # Remove everything unused
จาก Docker สู่ Kubernetes
Docker จัดการคอนเทนเนอร์แต่ละตัว เมื่อคุณต้อง orchestrate คอนเทนเนอร์หลายร้อยตัวข้ามหลายเซิร์ฟเวอร์ คุณต้องการ Kubernetes Docker และ Kubernetes เป็นส่วนเสริมของกัน:
- Docker: สร้างและรันคอนเทนเนอร์
- Kubernetes: orchestrate คอนเทนเนอร์ในระดับใหญ่ (การจัดตาราง, การปรับขนาด, การซ่อมแซมอัตโนมัติ)
หากคุณสนใจขั้นตอนถัดไป ลองอ่านบทความของผมเกี่ยวกับ Introduction to Kubernetes
สรุป
Docker เป็นทักษะพื้นฐานสำหรับนักพัฒนาสมัยใหม่ มันขจัดความไม่สอดคล้องกันของสภาพแวดล้อม ทำให้การ deploy ง่ายขึ้น และเป็นรากฐานสำหรับการ orchestrate คอนเทนเนอร์ด้วย Kubernetes เริ่มต้นด้วย Dockerfile อย่างง่าย ย้ายไปยัง Docker Compose สำหรับแอปหลายบริการ และนำ multi-stage build และแนวปฏิบัติด้านความปลอดภัยที่ดีที่สุดมาใช้เมื่อคุณเติบโต
วิธีที่ดีที่สุดในการเรียนรู้ Docker คือการทำ containerize โปรเจกต์ที่คุณกำลังทำอยู่ เริ่มวันนี้