# Docker: จากการพัฒนาสู่การใช้งานจริง > คู่มือ Docker ฉบับสมบูรณ์สำหรับการ containerize แอปพลิเคชัน Dockerfile, Docker Compose, multi-stage build และการ deploy สู่ production พร้อมตัวอย่างจริง - Published: 2026-01-21 - Updated: 2026-04-10 - Author: SharpSkill - Tags: docker, containerization, devops, docker compose, deployment - Reading time: 18 min --- Docker ปฏิวัติวิธีการพัฒนา ทดสอบ และ deploy แอปพลิเคชัน ด้วยการห่อหุ้มแอปพลิเคชันและ dependency ทั้งหมดไว้ใน container แบบพกพา Docker ขจัดปัญหาที่รู้จักกันดีว่า "ทำงานได้บนเครื่องของฉัน" และรับประกันความสอดคล้องในทุกสภาพแวดล้อม คู่มือนี้ครอบคลุมเส้นทางทั้งหมดตั้งแต่ Dockerfile แรกจนถึงการ deploy สู่ production > **Docker ในปี 2026** > > Docker Desktop 5.x นำเสนอการปรับปรุงประสิทธิภาพอย่างมาก รวมถึงการรองรับ containerd แบบ native การจัดการทรัพยากรที่ได้รับการปรับแต่ง และการผสานรวมกับ Kubernetes อย่างไร้รอยต่อ Image แบบ multi-architecture (ARM/x86) กลายเป็นมาตรฐานในปัจจุบัน ## พื้นฐานของ Containerization Container คือหน่วยซอฟต์แวร์น้ำหนักเบาที่รวม code, runtime, system library และการตั้งค่าต่างๆ ไว้ด้วยกัน ต่างจาก virtual machine ที่ virtualize hardware ตรงที่ container ใช้ kernel ของระบบ host ร่วมกัน ทำให้เริ่มต้นได้เร็วกว่าและใช้ทรัพยากรน้อยกว่า ```bash # terminal # Docker installation on Ubuntu sudo apt update sudo apt install -y docker.io # Add user to docker group (avoids sudo) sudo usermod -aG docker $USER # Verify installation docker --version # Docker version 26.1.0, build 1234567 # First container: downloads image and runs docker run hello-world ``` คำสั่งนี้ดาวน์โหลด image `hello-world` จาก Docker Hub และเริ่มต้น container ที่แสดงข้อความยืนยัน ```bash # terminal # List running containers docker ps # List all containers (including stopped) docker ps -a # List downloaded images docker images # Remove a container docker rm # Remove an image docker rmi ``` คำสั่งพื้นฐานเหล่านี้จัดการวงจรชีวิตของ container และ image ## การสร้าง Dockerfile แรก Dockerfile ประกอบด้วยคำสั่งสำหรับสร้าง Docker image แต่ละคำสั่งสร้าง layer ใน image สุดท้าย ทำให้สามารถ cache และนำกลับมาใช้ใหม่ได้ ```dockerfile # Dockerfile # Base image: Node.js 22 on Alpine Linux (lightweight) FROM node:22-alpine # Set working directory in the container WORKDIR /app # Copy dependency files first (cache optimization) COPY package*.json ./ # Install dependencies RUN npm ci --only=production # Copy source code COPY . . # Expose port (documentation) EXPOSE 3000 # Startup command CMD ["node", "server.js"] ``` ลำดับของคำสั่งมีความสำคัญอย่างยิ่งต่อการ optimize cache ไฟล์ที่เปลี่ยนแปลงไม่บ่อย (package.json) ควรถูก copy ก่อน source code ```bash # terminal # Build image with a tag docker build -t my-app:1.0 . # Run the container docker run -d -p 3000:3000 --name my-app-container my-app:1.0 # Check logs docker logs my-app-container # Access container shell docker exec -it my-app-container sh ``` flag `-d` รัน container ในโหมด background, `-p` map port 3000 ของ container ไปยัง port 3000 ของ host > **Alpine vs Debian** > > Image Alpine มีขนาดเล็กกว่ามาก (ประมาณ 5 MB เทียบกับ 120 MB ของ Debian) อย่างไรก็ตาม Alpine ใช้ musl libc แทน glibc ซึ่งอาจทำให้เกิดความไม่เข้ากันกับ native dependency บางตัว เมื่อเกิดปัญหา แนะนำให้ใช้ image ที่ใช้ Debian (node:22-slim) ## Multi-stage Build สำหรับ Production Multi-stage build สร้าง production image ที่ได้รับการ optimize โดยแยกสภาพแวดล้อม build ออกจากสภาพแวดล้อม runtime เฉพาะ artifact ที่จำเป็นเท่านั้นที่จะถูกรวมใน image สุดท้าย ```dockerfile # Dockerfile.production # ============================================ # Stage 1: Build # ============================================ FROM node:22-alpine AS builder WORKDIR /app # Copy and install dependencies (including devDependencies) COPY package*.json ./ RUN npm ci # Copy source code COPY . . # Build the application (TypeScript, bundling, etc.) RUN npm run build # ============================================ # Stage 2: Production # ============================================ FROM node:22-alpine AS production # Non-root user for security RUN addgroup -g 1001 -S nodejs && \ adduser -S nodejs -u 1001 WORKDIR /app # Copy only necessary files from builder stage COPY --from=builder /app/dist ./dist COPY --from=builder /app/node_modules ./node_modules COPY --from=builder /app/package.json ./ # Switch to non-root user USER nodejs # Environment variables ENV NODE_ENV=production ENV PORT=3000 EXPOSE 3000 # Startup command CMD ["node", "dist/server.js"] ``` วิธีนี้ลดขนาด image สุดท้ายได้อย่างมาก โดยไม่รวม build tool, devDependencies และไฟล์ source ```bash # terminal # Build with specific file docker build -f Dockerfile.production -t my-app:production . # Compare image sizes docker images | grep my-app # my-app production abc123 150MB # my-app 1.0 def456 450MB ``` การลดขนาดสามารถถึง 60-70% ขึ้นอยู่กับโปรเจกต์ ช่วยปรับปรุงเวลา deploy และลด attack surface ## Docker Compose สำหรับ Orchestration ในเครื่อง Docker Compose ทำให้การจัดการแอปพลิเคชันหลาย container ง่ายขึ้น ไฟล์ YAML ประกาศ service ทั้งหมด การตั้งค่า และ dependency ของมัน ```yaml # docker-compose.yml version: "3.9" services: # Main application app: build: context: . dockerfile: Dockerfile ports: - "3000:3000" environment: - NODE_ENV=development - DATABASE_URL=postgresql://postgres:secret@db:5432/myapp - REDIS_URL=redis://cache:6379 volumes: # Mount source code for hot-reload - ./src:/app/src - ./package.json:/app/package.json depends_on: db: condition: service_healthy cache: condition: service_started networks: - app-network # PostgreSQL database db: image: postgres:16-alpine environment: POSTGRES_USER: postgres POSTGRES_PASSWORD: secret POSTGRES_DB: myapp volumes: # Data persistence - postgres_data:/var/lib/postgresql/data # Initialization script - ./init.sql:/docker-entrypoint-initdb.d/init.sql ports: - "5432:5432" healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 5s timeout: 5s retries: 5 networks: - app-network # Redis cache cache: image: redis:7-alpine ports: - "6379:6379" volumes: - redis_data:/data command: redis-server --appendonly yes networks: - app-network # Named volumes for persistence volumes: postgres_data: redis_data: # Dedicated network for isolation networks: app-network: driver: bridge ``` Service สื่อสารกันโดยใช้ชื่อ (db, cache) ผ่านเครือข่ายภายในของ Docker Healthcheck รับประกันว่า dependency พร้อมใช้งานก่อนที่แอปพลิเคชันจะเริ่มต้น ```bash # terminal # Start all services docker compose up -d # View logs from all services docker compose logs -f # Logs from a specific service docker compose logs -f app # Stop and remove containers docker compose down # Removal including volumes (caution: data loss) docker compose down -v # Rebuild after Dockerfile changes docker compose up -d --build ``` ## การจัดการ Secret และตัวแปรสภาพแวดล้อม การจัดการ secret อย่างปลอดภัยเป็นสิ่งสำคัญยิ่งใน production Docker มีหลายวิธีการขึ้นอยู่กับบริบท ```yaml # docker-compose.override.yml (development only) version: "3.9" services: app: env_file: - .env.development environment: - DEBUG=true ``` สำหรับ production Docker secrets ให้ความปลอดภัยที่สูงขึ้น ```yaml # docker-compose.production.yml version: "3.9" services: app: build: context: . dockerfile: Dockerfile.production secrets: - db_password - api_key environment: - NODE_ENV=production - DATABASE_PASSWORD_FILE=/run/secrets/db_password - API_KEY_FILE=/run/secrets/api_key secrets: db_password: file: ./secrets/db_password.txt api_key: file: ./secrets/api_key.txt ``` โค้ดแอปพลิเคชันอ่าน secret จากไฟล์ที่ mount ไว้ ```javascript // config/secrets.js const fs = require('fs'); const path = require('path'); // Utility function to read Docker secrets function readSecret(secretName) { const secretPath = `/run/secrets/${secretName}`; // Check if secret file exists if (fs.existsSync(secretPath)) { return fs.readFileSync(secretPath, 'utf8').trim(); } // Fallback to classic environment variables const envVar = secretName.toUpperCase(); return process.env[envVar]; } module.exports = { databasePassword: readSecret('db_password'), apiKey: readSecret('api_key'), }; ``` วิธีนี้ป้องกันไม่ให้ secret ถูกเปิดเผยในตัวแปรสภาพแวดล้อมหรือ Docker image ## การ Optimize Docker Image หลายเทคนิคช่วยลดขนาด image และปรับปรุงประสิทธิภาพ ```dockerfile # Dockerfile.optimized FROM node:22-alpine AS base # Install necessary tools in a single layer RUN apk add --no-cache \ dumb-init \ && rm -rf /var/cache/apk/* # ============================================ # Stage: Dependencies # ============================================ FROM base AS deps WORKDIR /app # Copy only lock files for caching COPY package.json package-lock.json ./ # Install with mounted npm cache (BuildKit) RUN --mount=type=cache,target=/root/.npm \ npm ci --only=production # ============================================ # Stage: Builder # ============================================ FROM base AS builder WORKDIR /app COPY package.json package-lock.json ./ RUN --mount=type=cache,target=/root/.npm \ npm ci COPY . . RUN npm run build # ============================================ # Stage: Production # ============================================ FROM base AS production # Image metadata LABEL maintainer="team@example.com" LABEL version="1.0" LABEL description="Production-ready Node.js application" # Non-root user RUN addgroup -g 1001 -S nodejs && \ adduser -S nodejs -u 1001 WORKDIR /app # Copy production dependencies COPY --from=deps --chown=nodejs:nodejs /app/node_modules ./node_modules # Copy build output COPY --from=builder --chown=nodejs:nodejs /app/dist ./dist COPY --from=builder --chown=nodejs:nodejs /app/package.json ./ USER nodejs ENV NODE_ENV=production # dumb-init as PID 1 for signal handling ENTRYPOINT ["dumb-init", "--"] CMD ["node", "dist/server.js"] ``` การใช้ `dumb-init` รับประกันการจัดการสัญญาณ Unix อย่างถูกต้อง ทำให้ container สามารถ shutdown ได้อย่างสมบูรณ์ ```bash # terminal # Enable BuildKit for advanced features export DOCKER_BUILDKIT=1 # Build with cache and detailed output docker build --progress=plain -t my-app:optimized . # Analyze image layers docker history my-app:optimized # Detailed image inspection docker inspect my-app:optimized ``` > **ความปลอดภัยของ Image** > > Image ควรถูกสแกนหาช่องโหว่เป็นประจำโดยใช้เครื่องมืออย่าง Trivy หรือ Snyk Base image ต้องได้รับการอัปเดตเป็นประจำเพื่อรวม security patch ## เครือข่าย Docker ขั้นสูง Docker มี network driver หลายประเภทสำหรับกรณีการใช้งานที่แตกต่างกัน ```yaml # docker-compose.networking.yml version: "3.9" services: # Publicly accessible frontend frontend: build: ./frontend ports: - "80:80" networks: - frontend-network - backend-network # API accessible only from frontend api: build: ./api networks: - backend-network - database-network # No external ports exposed # Isolated database database: image: postgres:16-alpine networks: - database-network # Accessible only by API networks: frontend-network: driver: bridge backend-network: driver: bridge internal: true # No internet access database-network: driver: bridge internal: true ``` การตั้งค่านี้แยก service ตามหลักการ least privilege ฐานข้อมูลเข้าถึงได้เฉพาะจาก API เท่านั้น ```bash # terminal # Inspect Docker networks docker network ls # Details of a specific network docker network inspect app-network # Create a custom network docker network create --driver bridge --subnet 172.28.0.0/16 custom-network # Connect a container to an existing network docker network connect custom-network my-container ``` ## Volume และการเก็บรักษาข้อมูล Docker volume รักษาข้อมูลเกินกว่าวงจรชีวิตของ container ```yaml # docker-compose.volumes.yml version: "3.9" services: app: image: my-app:latest volumes: # Named volume for persistent data - app_data:/app/data # Bind mount for development - ./uploads:/app/uploads:rw # Read-only mount for configuration - ./config:/app/config:ro backup: image: alpine volumes: # Access same volume for backups - app_data:/data:ro - ./backups:/backups command: | sh -c "tar czf /backups/backup-$$(date +%Y%m%d).tar.gz /data" volumes: app_data: driver: local driver_opts: type: none device: /path/to/host/data o: bind ``` ความแตกต่างระหว่าง named volume และ bind mount เป็นสิ่งสำคัญ: volume ถูกจัดการโดย Docker ในขณะที่ bind mount ใช้ filesystem ของ host โดยตรง ```bash # terminal # List volumes docker volume ls # Inspect a volume docker volume inspect app_data # Remove orphaned volumes docker volume prune # Backup a volume docker run --rm -v app_data:/data -v $(pwd):/backup alpine \ tar czf /backup/volume-backup.tar.gz /data ``` ## การ Deploy สู่ Production กระบวนการ deploy ที่แข็งแกร่งประกอบด้วย build, test และ push ไปยัง registry ```bash # deploy.sh #!/bin/bash set -e # Variables REGISTRY="registry.example.com" IMAGE_NAME="my-app" VERSION=$(git describe --tags --always) echo "Building version: $VERSION" # Build production image docker build \ -f Dockerfile.production \ -t $REGISTRY/$IMAGE_NAME:$VERSION \ -t $REGISTRY/$IMAGE_NAME:latest \ --build-arg BUILD_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ") \ --build-arg VERSION=$VERSION \ . # Security scan echo "Running security scan..." docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \ aquasec/trivy image $REGISTRY/$IMAGE_NAME:$VERSION # Push to registry echo "Pushing to registry..." docker push $REGISTRY/$IMAGE_NAME:$VERSION docker push $REGISTRY/$IMAGE_NAME:latest echo "Deployment complete: $REGISTRY/$IMAGE_NAME:$VERSION" ``` สำหรับ server deployment ไฟล์ compose สำหรับ production แยกต่างหากจะปรับแต่งการตั้งค่า ```yaml # docker-compose.prod.yml version: "3.9" services: app: image: registry.example.com/my-app:latest restart: always deploy: replicas: 3 resources: limits: cpus: "0.5" memory: 512M reservations: cpus: "0.25" memory: 256M update_config: parallelism: 1 delay: 10s failure_action: rollback healthcheck: test: ["CMD", "curl", "-f", "http://localhost:3000/health"] interval: 30s timeout: 10s retries: 3 start_period: 40s logging: driver: "json-file" options: max-size: "10m" max-file: "3" ``` การตั้งค่านี้กำหนดการจัดสรรทรัพยากร กลยุทธ์การอัปเดต และ healthcheck สำหรับการ deploy ที่เชื่อถือได้ ```bash # terminal # Production deployment with Docker Compose docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d # Zero-downtime update (rolling update) docker compose -f docker-compose.yml -f docker-compose.prod.yml pull docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d --no-deps app # Rollback if issues occur docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d --no-deps \ --scale app=0 && \ docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d --no-deps app ``` ## การ Monitor และ Debug Container การ monitor container เป็นสิ่งจำเป็นใน production ```bash # terminal # Real-time statistics for all containers docker stats # Statistics for a specific container with custom format docker stats my-app --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}" # Inspect processes in a container docker top my-app # Real-time Docker events docker events --filter container=my-app # Copy files from/to a container docker cp my-app:/app/logs/error.log ./error.log ``` สำหรับการ debug เชิงลึก มีเทคนิคหลายอย่างให้ใช้ ```bash # terminal # Interactive shell in a running container docker exec -it my-app sh # Execute a single command docker exec my-app cat /app/config/settings.json # Start a container in debug mode docker run -it --rm --entrypoint sh my-app:latest # Inspect environment variables docker exec my-app printenv # Analyze logs with filters docker logs my-app --since 1h --tail 100 | grep ERROR ``` ```yaml # docker-compose.monitoring.yml version: "3.9" services: app: # ... existing configuration labels: - "prometheus.scrape=true" - "prometheus.port=3000" - "prometheus.path=/metrics" prometheus: image: prom/prometheus:latest ports: - "9090:9090" volumes: - ./prometheus.yml:/etc/prometheus/prometheus.yml - prometheus_data:/prometheus command: - '--config.file=/etc/prometheus/prometheus.yml' - '--storage.tsdb.retention.time=15d' grafana: image: grafana/grafana:latest ports: - "3001:3000" volumes: - grafana_data:/var/lib/grafana environment: - GF_SECURITY_ADMIN_PASSWORD=secret volumes: prometheus_data: grafana_data: ``` stack monitoring นี้ช่วยให้สามารถเก็บรวบรวมและแสดงผล metric ของ container ได้ ## สรุป Docker เปลี่ยนแปลงวงจรการพัฒนาโดยรับประกันความสอดคล้องในทุกสภาพแวดล้อม Containerization นำมาซึ่งความพกพา การแยกส่วน และความสามารถในการทำซ้ำ คุณสมบัติที่จำเป็นสำหรับแอปพลิเคชันสมัยใหม่ ### Docker Checklist สำหรับ Production - ✅ Multi-stage build สำหรับ image ที่ได้รับการ optimize - ✅ ใช้ user ที่ไม่ใช่ root ใน container - ✅ Healthcheck ถูกตั้งค่าสำหรับทุก service - ✅ Secret ถูกจัดการผ่าน Docker secrets หรือตัวแปรสภาพแวดล้อมที่ปลอดภัย - ✅ จำกัดทรัพยากร (CPU, หน่วยความจำ) ที่กำหนดไว้ - ✅ Volume สำหรับการเก็บรักษาข้อมูลที่สำคัญ - ✅ Logging แบบรวมศูนย์พร้อม file rotation - ✅ สแกนความปลอดภัยของ image ก่อน deploy - ✅ กลยุทธ์การอัปเดตแบบ zero-downtime - ✅ เครือข่ายแยกระหว่าง service การเชี่ยวชาญ Docker เป็นทักษะพื้นฐานของนักพัฒนาสมัยใหม่ทุกคน ตั้งแต่สภาพแวดล้อมในเครื่องจนถึงการ deploy สู่ production Docker กำหนดมาตรฐาน workflow และทำให้การดำเนินงานง่ายขึ้น แนวคิดที่นำเสนอที่นี่เป็นรากฐานที่แข็งแกร่งสำหรับการสำรวจ Kubernetes และการ orchestrate container ในระดับใหญ่ --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/th/blog/devops/docker-from-development-to-production