Docker Swarm: คู่มือการใช้งานฉบับสมบูรณ์

Docker Swarm คืออะไร

Docker Swarm เป็น Container Orchestration Platform ที่พัฒนาโดย Docker เพื่อใช้ในการจัดการและประสานงาน Docker containers หลายๆ ตัวบนเครื่องหลายๆ เครื่อง (cluster) โดย Swarm จะช่วยให้เราสามารถ:

ข้อดีของ Docker Swarm


สถาปัตยกรรมของ Docker Swarm

graph TB
    subgraph "Docker Swarm Cluster"
        LB[Load Balancer]
        
        subgraph "Manager Nodes"
            M1[Manager 1
Leader] M2[Manager 2] M3[Manager 3] end subgraph "Worker Nodes" W1[Worker 1] W2[Worker 2] W3[Worker 3] W4[Worker 4] end subgraph "Services" S1[Service A
3 replicas] S2[Service B
2 replicas] end end M1 -.Raft Consensus.-> M2 M2 -.Raft Consensus.-> M3 M3 -.Raft Consensus.-> M1 M1 -->|Schedule Tasks| W1 M1 -->|Schedule Tasks| W2 M2 -->|Schedule Tasks| W3 M3 -->|Schedule Tasks| W4 LB -->|Route Traffic| S1 LB -->|Route Traffic| S2 S1 -.->|Runs on| W1 S1 -.->|Runs on| W2 S1 -.->|Runs on| W3 S2 -.->|Runs on| W3 S2 -.->|Runs on| W4

องค์ประกอบหลัก

  1. Node: เครื่องแต่ละเครื่องใน Swarm cluster

  2. Service: นิยามของ application ที่ต้องการรัน

  3. Task: Container instance ของ service แต่ละตัว

  4. Stack: กลุ่มของ services ที่เกี่ยวข้องกัน (deploy ด้วย docker-compose.yml)


การติดตั้งและเริ่มต้นใช้งาน

Prerequisites

ต้องมี Docker Engine ติดตั้งอยู่แล้ว (version 1.12 ขึ้นไป)

# ตรวจสอบ Docker version
docker --version

สร้าง Swarm Cluster

ขั้นตอนที่ 1: เริ่มต้น Swarm Mode (Manager Node)

# บนเครื่อง Manager
docker swarm init --advertise-addr <MANAGER-IP>

ตัวอย่าง:

docker swarm init --advertise-addr 192.168.1.100

ผลลัพธ์:

Swarm initialized: current node (xyz123) is now a manager.

To add a worker to this swarm, run the following command:

    docker swarm join --token SWMTKN-1-xxx... 192.168.1.100:2377

To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.

ขั้นตอนที่ 2: เพิ่ม Worker Nodes

# บนเครื่อง Worker แต่ละเครื่อง
docker swarm join --token <WORKER-TOKEN> <MANAGER-IP>:2377

ตัวอย่าง:

docker swarm join --token SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-1awxwuwd3z9j1z3puu7rcgdbx 192.168.1.100:2377

ขั้นตอนที่ 3: เพิ่ม Manager Nodes (ถ้าต้องการ High Availability)

# ดู join token สำหรับ manager
docker swarm join-token manager

# บนเครื่อง Manager ใหม่
docker swarm join --token <MANAGER-TOKEN> <MANAGER-IP>:2377

ตรวจสอบ Cluster

# ดูรายชื่อ nodes ทั้งหมด
docker node ls

ตัวอย่างผลลัพธ์:

ID                            HOSTNAME   STATUS    AVAILABILITY   MANAGER STATUS
xyz123abc *                   manager1   Ready     Active         Leader
abc456def                     worker1    Ready     Active         
def789ghi                     worker2    Ready     Active         

คำสั่ง Docker Swarm พื้นฐาน

การจัดการ Swarm

1. ตรวจสอบข้อมูล Swarm

# ดูข้อมูล Swarm
docker info

# ดูข้อมูลโดยละเอียด
docker swarm inspect

2. Update Swarm Configuration

# Update task history retention
docker swarm update --task-history-limit 3

# Update dispatcher heartbeat
docker swarm update --dispatcher-heartbeat 5s

3. ออกจาก Swarm

# บน Worker Node
docker swarm leave

# บน Manager Node (บังคับ)
docker swarm leave --force

การจัดการ Nodes

1. ดูข้อมูล Nodes

# ดูรายชื่อ nodes
docker node ls

# ดูข้อมูลโดยละเอียดของ node
docker node inspect <NODE-ID>

# ดูแบบ pretty format
docker node inspect --pretty <NODE-ID>

2. Update Node

# เปลี่ยน availability
docker node update --availability drain <NODE-ID>
# drain: ไม่รับ tasks ใหม่และย้าย tasks ปัจจุบันออก
# active: รับ tasks ปกติ
# pause: ไม่รับ tasks ใหม่แต่เก็บ tasks ปัจจุบันไว้

# เพิ่ม label
docker node update --label-add type=database <NODE-ID>
docker node update --label-add environment=production <NODE-ID>

# เปลี่ยน role
docker node promote <NODE-ID>    # เปลี่ยนเป็น Manager
docker node demote <NODE-ID>     # เปลี่ยนเป็น Worker

3. ลบ Node

# ลบ node ออกจาก cluster (รัน on Manager)
docker node rm <NODE-ID>

# บังคับลบ (ถ้า node offline)
docker node rm --force <NODE-ID>

การจัดการ Join Tokens

# ดู worker token
docker swarm join-token worker

# ดู manager token
docker swarm join-token manager

# สร้าง token ใหม่ (rotate)
docker swarm join-token --rotate worker
docker swarm join-token --rotate manager

การจัดการ Services

Service คือหัวใจหลักของ Docker Swarm ที่ใช้ในการ deploy applications

สร้าง Service

1. Service พื้นฐาน

# สร้าง service แบบง่าย
docker service create --name web nginx:latest

# สร้างพร้อม replicas
docker service create \
  --name web \
  --replicas 3 \
  nginx:latest

2. Service พร้อม Port Publishing

docker service create \
  --name web \
  --replicas 3 \
  --publish published=80,target=80 \
  nginx:latest

# รูปแบบสั้น
docker service create \
  --name web \
  --replicas 3 \
  -p 80:80 \
  nginx:latest

3. Service พร้อม Environment Variables

docker service create \
  --name myapp \
  --replicas 2 \
  --env MYSQL_ROOT_PASSWORD=secret \
  --env MYSQL_DATABASE=mydb \
  mysql:8.0

4. Service พร้อม Resource Constraints

docker service create \
  --name web \
  --replicas 3 \
  --limit-cpu 0.5 \
  --limit-memory 256M \
  --reserve-cpu 0.25 \
  --reserve-memory 128M \
  nginx:latest

5. Service พร้อม Placement Constraints

# รันเฉพาะ nodes ที่มี label environment=production
docker service create \
  --name web \
  --replicas 3 \
  --constraint node.labels.environment==production \
  nginx:latest

# รันเฉพาะ Manager nodes
docker service create \
  --name monitoring \
  --constraint node.role==manager \
  prometheus:latest

# รันเฉพาะ nodes ที่มี hostname เฉพาะ
docker service create \
  --name db \
  --constraint node.hostname==worker1 \
  postgres:13

6. Global Service (รันทุก Node)

docker service create \
  --name monitoring-agent \
  --mode global \
  monitoring-agent:latest

ดูข้อมูล Services

# ดูรายชื่อ services ทั้งหมด
docker service ls

# ดูข้อมูลโดยละเอียด
docker service inspect <SERVICE-NAME>
docker service inspect --pretty <SERVICE-NAME>

# ดู tasks ของ service
docker service ps <SERVICE-NAME>

# ดู logs
docker service logs <SERVICE-NAME>
docker service logs -f <SERVICE-NAME>  # follow mode
docker service logs --tail 100 <SERVICE-NAME>

Update Service

1. Scale Service

# เพิ่มจำนวน replicas
docker service scale web=5

# scale หลาย services พร้อมกัน
docker service scale web=5 api=3 worker=10

2. Update Image

# update image version
docker service update --image nginx:1.21 web

# update พร้อม rolling update config
docker service update \
  --image nginx:1.21 \
  --update-delay 10s \
  --update-parallelism 2 \
  web

3. Update Environment Variables

docker service update \
  --env-add NEW_VAR=value \
  --env-rm OLD_VAR \
  myapp

4. Update Port Mapping

docker service update \
  --publish-add published=8080,target=80 \
  web

docker service update \
  --publish-rm 8080 \
  web

5. Update Resources

docker service update \
  --limit-cpu 1.0 \
  --limit-memory 512M \
  web

6. Rollback Service

# rollback ไปเวอร์ชันก่อนหน้า
docker service rollback web

# update พร้อม rollback config
docker service update \
  --image nginx:1.21 \
  --rollback-delay 5s \
  --rollback-monitor 10s \
  --rollback-max-failure-ratio 0.2 \
  web

ลบ Service

# ลบ service
docker service rm <SERVICE-NAME>

# ลบหลาย services
docker service rm service1 service2 service3

Docker Stack

Docker Stack เป็นวิธีการ deploy หลาย services พร้อมกันโดยใช้ไฟล์ docker-compose.yml แบบเดียวกับ Docker Compose แต่เพิ่มความสามารถสำหรับ Swarm mode

Docker Compose File สำหรับ Stack

ตัวอย่างที่ 1: Web Application พื้นฐาน

สร้างไฟล์ docker-compose.yml:

version: '3.8'

services:
  web:
    image: nginx:latest
    ports:
      - "80:80"
    deploy:
      replicas: 3
      update_config:
        parallelism: 1
        delay: 10s
      restart_policy:
        condition: on-failure
        delay: 5s
        max_attempts: 3
    networks:
      - webnet

  api:
    image: myapi:latest
    environment:
      - DATABASE_URL=postgres://db:5432/mydb
    deploy:
      replicas: 2
      placement:
        constraints:
          - node.role == worker
      resources:
        limits:
          cpus: '0.5'
          memory: 256M
        reservations:
          cpus: '0.25'
          memory: 128M
    networks:
      - webnet
      - backend

  db:
    image: postgres:13
    environment:
      - POSTGRES_PASSWORD_FILE=/run/secrets/db_password
      - POSTGRES_DB=mydb
    volumes:
      - db-data:/var/lib/postgresql/data
    deploy:
      replicas: 1
      placement:
        constraints:
          - node.labels.type == database
    secrets:
      - db_password
    networks:
      - backend

networks:
  webnet:
    driver: overlay
  backend:
    driver: overlay

volumes:
  db-data:

secrets:
  db_password:
    external: true

ตัวอย่างที่ 2: Microservices Application

สร้างไฟล์ microservices-stack.yml:

version: '3.8'

services:
  # Frontend Service
  frontend:
    image: myapp/frontend:v1.0
    ports:
      - "80:80"
      - "443:443"
    deploy:
      replicas: 3
      update_config:
        parallelism: 1
        delay: 10s
        order: start-first
      rollback_config:
        parallelism: 1
        delay: 5s
      labels:
        - "traefik.enable=true"
        - "traefik.http.routers.frontend.rule=Host(`example.com`)"
    configs:
      - source: nginx_config
        target: /etc/nginx/nginx.conf
    networks:
      - frontend

  # API Gateway
  gateway:
    image: myapp/gateway:v1.0
    ports:
      - "8080:8080"
    environment:
      - SERVICE_DISCOVERY=consul
      - LOG_LEVEL=info
    deploy:
      replicas: 2
      placement:
        constraints:
          - node.role == worker
      resources:
        limits:
          cpus: '1.0'
          memory: 512M
    networks:
      - frontend
      - backend

  # User Service
  user-service:
    image: myapp/user-service:v1.0
    environment:
      - DATABASE_HOST=user-db
      - REDIS_HOST=redis
      - JWT_SECRET_FILE=/run/secrets/jwt_secret
    deploy:
      replicas: 3
      update_config:
        parallelism: 1
        delay: 10s
      restart_policy:
        condition: on-failure
        max_attempts: 3
        window: 120s
    secrets:
      - jwt_secret
    networks:
      - backend
      - database

  # Order Service
  order-service:
    image: myapp/order-service:v1.0
    environment:
      - DATABASE_HOST=order-db
      - MESSAGE_QUEUE=rabbitmq
    deploy:
      replicas: 2
      labels:
        - "app=order-service"
        - "tier=backend"
    networks:
      - backend
      - database

  # Payment Service
  payment-service:
    image: myapp/payment-service:v1.0
    environment:
      - PAYMENT_GATEWAY=stripe
      - API_KEY_FILE=/run/secrets/stripe_key
    deploy:
      replicas: 2
      placement:
        constraints:
          - node.labels.security == high
    secrets:
      - stripe_key
    networks:
      - backend

  # User Database
  user-db:
    image: postgres:13
    environment:
      - POSTGRES_DB=users
      - POSTGRES_PASSWORD_FILE=/run/secrets/user_db_password
    volumes:
      - user-db-data:/var/lib/postgresql/data
    deploy:
      replicas: 1
      placement:
        constraints:
          - node.labels.type == database
      restart_policy:
        condition: on-failure
    secrets:
      - user_db_password
    networks:
      - database

  # Order Database
  order-db:
    image: postgres:13
    environment:
      - POSTGRES_DB=orders
      - POSTGRES_PASSWORD_FILE=/run/secrets/order_db_password
    volumes:
      - order-db-data:/var/lib/postgresql/data
    deploy:
      replicas: 1
      placement:
        constraints:
          - node.labels.type == database
    secrets:
      - order_db_password
    networks:
      - database

  # Redis Cache
  redis:
    image: redis:6-alpine
    deploy:
      replicas: 1
      placement:
        constraints:
          - node.role == worker
    networks:
      - backend

  # RabbitMQ Message Queue
  rabbitmq:
    image: rabbitmq:3-management
    ports:
      - "15672:15672"  # Management UI
    environment:
      - RABBITMQ_DEFAULT_USER=admin
      - RABBITMQ_DEFAULT_PASS_FILE=/run/secrets/rabbitmq_password
    deploy:
      replicas: 1
    secrets:
      - rabbitmq_password
    networks:
      - backend

  # Monitoring - Prometheus
  prometheus:
    image: prom/prometheus:latest
    ports:
      - "9090:9090"
    configs:
      - source: prometheus_config
        target: /etc/prometheus/prometheus.yml
    deploy:
      replicas: 1
      placement:
        constraints:
          - node.role == manager
    networks:
      - monitoring

  # Monitoring - Grafana
  grafana:
    image: grafana/grafana:latest
    ports:
      - "3000:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD_FILE=/run/secrets/grafana_password
    deploy:
      replicas: 1
      placement:
        constraints:
          - node.role == manager
    secrets:
      - grafana_password
    networks:
      - monitoring

networks:
  frontend:
    driver: overlay
    attachable: true
  backend:
    driver: overlay
  database:
    driver: overlay
    internal: true  # ไม่สามารถเข้าถึงจากภายนอกได้
  monitoring:
    driver: overlay

volumes:
  user-db-data:
  order-db-data:

secrets:
  jwt_secret:
    external: true
  stripe_key:
    external: true
  user_db_password:
    external: true
  order_db_password:
    external: true
  rabbitmq_password:
    external: true
  grafana_password:
    external: true

configs:
  nginx_config:
    external: true
  prometheus_config:
    external: true

คำสั่งจัดการ Stack

1. Deploy Stack

# deploy stack
docker stack deploy -c docker-compose.yml mystack

# deploy พร้อม prune services เก่า
docker stack deploy -c docker-compose.yml --prune mystack

# deploy จากหลายไฟล์
docker stack deploy \
  -c docker-compose.yml \
  -c docker-compose.prod.yml \
  mystack

ตัวอย่างการ Deploy แบบละเอียด:

# ขั้นตอนที่ 1: สร้าง secrets
echo "mysecretpassword" | docker secret create db_password -
echo "jwt_secret_key_here" | docker secret create jwt_secret -

# ขั้นตอนที่ 2: สร้าง configs
docker config create nginx_config nginx.conf

# ขั้นตอนที่ 3: เพิ่ม labels ให้ nodes
docker node update --label-add type=database worker1
docker node update --label-add security=high worker2

# ขั้นตอนที่ 4: deploy stack
docker stack deploy -c docker-compose.yml production

# ตรวจสอบสถานะ
docker stack ps production

2. ดูข้อมูล Stack

# ดูรายชื่อ stacks
docker stack ls

# ดู services ใน stack
docker stack services mystack

# ดู tasks/containers ใน stack
docker stack ps mystack

# ดูแบบละเอียด (รวม stopped tasks)
docker stack ps --no-trunc mystack
docker stack ps --no-resolve mystack

3. Update Stack

# แก้ไข docker-compose.yml แล้ว deploy ใหม่
docker stack deploy -c docker-compose.yml mystack

# Docker จะทำ rolling update อัตโนมัติตาม update_config

4. ลบ Stack

# ลบ stack ทั้งหมด
docker stack rm mystack

# จะลบ services, networks, secrets, configs ที่สร้างโดย stack
# แต่จะไม่ลบ volumes โดยอัตโนมัติ

Stack Deployment Workflow

graph TD
    A[เตรียม docker-compose.yml] --> B[สร้าง Secrets/Configs]
    B --> C[กำหนด Node Labels
ถ้าจำเป็น] C --> D[docker stack deploy] D --> E{Deploy สำเร็จ?} E -->|Yes| F[Services Running] E -->|No| G[ตรวจสอบ Logs] G --> H[แก้ไขปัญหา] H --> D F --> I[Monitor Services] I --> J{ต้องการ Update?} J -->|Yes| K[แก้ไข docker-compose.yml] K --> D J -->|No| I F --> L{ต้องการลบ?} L -->|Yes| M[docker stack rm] M --> N[ลบ Volumes
ถ้าจำเป็น]

การจัดการ Networks และ Volumes

Networks

Docker Swarm ใช้ overlay networks เพื่อให้ containers บน nodes ต่างๆ สื่อสารกันได้

1. สร้าง Overlay Network

# สร้าง overlay network พื้นฐาน
docker network create --driver overlay mynetwork

# สร้างพร้อม subnet
docker network create \
  --driver overlay \
  --subnet 10.0.0.0/24 \
  --gateway 10.0.0.1 \
  mynetwork

# สร้าง encrypted network
docker network create \
  --driver overlay \
  --opt encrypted \
  secure-network

# สร้าง attachable network (สำหรับ standalone containers)
docker network create \
  --driver overlay \
  --attachable \
  frontend-network

2. ดูข้อมูล Networks

# ดูรายชื่อ networks
docker network ls

# ดูข้อมูลโดยละเอียด
docker network inspect mynetwork

3. เชื่อม Service กับ Network

# เชื่อม service เข้ากับ network
docker service update --network-add mynetwork web

# ถอด service ออกจาก network
docker service update --network-rm mynetwork web

4. ลบ Network

docker network rm mynetwork

Volumes

1. สร้าง Volume

# สร้าง volume พื้นฐาน
docker volume create myvolume

# สร้างพร้อม driver options
docker volume create \
  --driver local \
  --opt type=nfs \
  --opt o=addr=192.168.1.200,rw \
  --opt device=:/path/to/dir \
  nfs-volume

2. ดูข้อมูล Volumes

# ดูรายชื่อ volumes
docker volume ls

# ดูข้อมูลโดยละเอียด
docker volume inspect myvolume

3. ใช้ Volume กับ Service

# mount volume เข้ากับ service
docker service create \
  --name db \
  --mount type=volume,source=db-data,target=/var/lib/postgresql/data \
  postgres:13

# หรือใช้ syntax แบบสั้น
docker service create \
  --name db \
  --mount source=db-data,target=/var/lib/postgresql/data \
  postgres:13

4. ลบ Volume

# ลบ volume
docker volume rm myvolume

# ลบ volumes ที่ไม่ได้ใช้งาน
docker volume prune

Secrets และ Configs

Secrets

Secrets ใช้สำหรับเก็บข้อมูลที่เป็นความลับ เช่น passwords, API keys, certificates

1. สร้าง Secret

# สร้างจาก stdin
echo "mysecretpassword" | docker secret create db_password -

# สร้างจากไฟล์
docker secret create db_password ./password.txt

# สร้างจาก environment variable
echo "$MY_PASSWORD" | docker secret create db_password -

2. ดูข้อมูล Secrets

# ดูรายชื่อ secrets
docker secret ls

# ดูข้อมูลโดยละเอียด (ไม่แสดงค่าจริง)
docker secret inspect db_password

3. ใช้ Secret กับ Service

docker service create \
  --name db \
  --secret db_password \
  --env POSTGRES_PASSWORD_FILE=/run/secrets/db_password \
  postgres:13

# secret จะถูก mount ที่ /run/secrets/<secret_name>

4. Update Secret

# ไม่สามารถ update secret ได้โดยตรง
# ต้องสร้างใหม่และ update service

# สร้าง secret ใหม่
echo "newsecretpassword" | docker secret create db_password_v2 -

# update service ให้ใช้ secret ใหม่
docker service update \
  --secret-rm db_password \
  --secret-add source=db_password_v2,target=db_password \
  db

5. ลบ Secret

# ลบ secret (ต้องไม่มี service ใช้งานอยู่)
docker secret rm db_password

Configs

Configs ใช้สำหรับเก็บไฟล์ configuration ที่ไม่เป็นความลับ

1. สร้าง Config

# สร้างจากไฟล์
docker config create nginx_config nginx.conf

# สร้างจาก stdin
cat <<EOF | docker config create app_config -
{
  "database": {
    "host": "db",
    "port": 5432
  }
}
EOF

2. ดูข้อมูล Configs

# ดูรายชื่อ configs
docker config ls

# ดูข้อมูลโดยละเอียด
docker config inspect nginx_config

# ดูเนื้อหา config
docker config inspect --pretty nginx_config

3. ใช้ Config กับ Service

docker service create \
  --name web \
  --config source=nginx_config,target=/etc/nginx/nginx.conf \
  nginx:latest

# กำหนด permissions
docker service create \
  --name web \
  --config source=nginx_config,target=/etc/nginx/nginx.conf,mode=0440 \
  nginx:latest

4. Update Config

# สร้าง config ใหม่
docker config create nginx_config_v2 nginx-updated.conf

# update service
docker service update \
  --config-rm nginx_config \
  --config-add source=nginx_config_v2,target=/etc/nginx/nginx.conf \
  web

5. ลบ Config

docker config rm nginx_config

ตัวอย่างการใช้งานจริง

โปรเจค 1: WordPress + MySQL Stack

ขั้นตอนที่ 1: เตรียม Secrets

# สร้าง secrets สำหรับ passwords
echo "wordpress_db_password" | docker secret create wp_db_password -
echo "mysql_root_password" | docker secret create mysql_root_password -

ขั้นตอนที่ 2: สร้าง docker-compose.yml

version: '3.8'

services:
  wordpress:
    image: wordpress:latest
    ports:
      - "80:80"
    environment:
      - WORDPRESS_DB_HOST=mysql:3306
      - WORDPRESS_DB_USER=wordpress
      - WORDPRESS_DB_PASSWORD_FILE=/run/secrets/wp_db_password
      - WORDPRESS_DB_NAME=wordpress
    secrets:
      - wp_db_password
    deploy:
      replicas: 2
      update_config:
        parallelism: 1
        delay: 10s
      restart_policy:
        condition: on-failure
    networks:
      - wordpress-network

  mysql:
    image: mysql:8.0
    environment:
      - MYSQL_ROOT_PASSWORD_FILE=/run/secrets/mysql_root_password
      - MYSQL_DATABASE=wordpress
      - MYSQL_USER=wordpress
      - MYSQL_PASSWORD_FILE=/run/secrets/wp_db_password
    secrets:
      - mysql_root_password
      - wp_db_password
    volumes:
      - mysql-data:/var/lib/mysql
    deploy:
      replicas: 1
      placement:
        constraints:
          - node.labels.type == database
    networks:
      - wordpress-network

networks:
  wordpress-network:
    driver: overlay

volumes:
  mysql-data:

secrets:
  wp_db_password:
    external: true
  mysql_root_password:
    external: true

ขั้นตอนที่ 3: เพิ่ม Label ให้ Node

# กำหนด node สำหรับ database
docker node update --label-add type=database worker1

ขั้นตอนที่ 4: Deploy Stack

docker stack deploy -c docker-compose.yml wordpress

ขั้นตอนที่ 5: ตรวจสอบสถานะ

# ดู services
docker stack services wordpress

# ดู tasks
docker stack ps wordpress

# ดู logs
docker service logs wordpress_wordpress
docker service logs wordpress_mysql

ขั้นตอนที่ 6: Scale WordPress

# เพิ่ม replicas
docker service scale wordpress_wordpress=5

# หรือ update stack file แล้ว deploy ใหม่

โปรเจค 2: Complete E-commerce Platform

สถาปัตยกรรม

graph TB
    subgraph "Frontend Layer"
        LB[Load Balancer
Traefik] WEB[Web Frontend
React - 3 replicas] ADMIN[Admin Panel
Vue - 2 replicas] end subgraph "API Layer" GW[API Gateway
2 replicas] AUTH[Auth Service
3 replicas] PROD[Product Service
3 replicas] ORDER[Order Service
2 replicas] PAY[Payment Service
2 replicas] end subgraph "Data Layer" REDIS[(Redis Cache)] PDB[(Product DB
PostgreSQL)] ODB[(Order DB
PostgreSQL)] UDB[(User DB
PostgreSQL)] end subgraph "Message Queue" MQ[RabbitMQ] WORKER[Background Workers
3 replicas] end LB -->|Route| WEB LB -->|Route| ADMIN WEB -->|API Calls| GW ADMIN -->|API Calls| GW GW --> AUTH GW --> PROD GW --> ORDER GW --> PAY AUTH --> UDB AUTH --> REDIS PROD --> PDB PROD --> REDIS ORDER --> ODB PAY --> MQ MQ --> WORKER WORKER --> ODB

ขั้นตอนการ Deploy

1. สร้าง Secrets และ Configs

# Database passwords
echo "user_db_pass123" | docker secret create user_db_password -
echo "product_db_pass123" | docker secret create product_db_password -
echo "order_db_pass123" | docker secret create order_db_password -

# API Keys
echo "stripe_sk_test_xxx" | docker secret create stripe_api_key -
echo "jwt_secret_key_xxx" | docker secret create jwt_secret -
echo "admin_rabbitmq_pass" | docker secret create rabbitmq_password -

# Application Configs
docker config create traefik_config traefik.yml
docker config create redis_config redis.conf

2. สร้าง docker-compose.yml

version: '3.8'

services:
  # Load Balancer / Reverse Proxy
  traefik:
    image: traefik:v2.9
    command:
      - "--api.insecure=true"
      - "--providers.docker.swarmMode=true"
      - "--providers.docker.exposedByDefault=false"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
    ports:
      - "80:80"
      - "443:443"
      - "8080:8080"  # Traefik dashboard
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    configs:
      - source: traefik_config
        target: /etc/traefik/traefik.yml
    deploy:
      replicas: 1
      placement:
        constraints:
          - node.role == manager
    networks:
      - frontend

  # Web Frontend
  web-frontend:
    image: ecommerce/frontend:v1.0
    environment:
      - API_GATEWAY_URL=http://api-gateway:8080
      - NODE_ENV=production
    deploy:
      replicas: 3
      labels:
        - "traefik.enable=true"
        - "traefik.http.routers.frontend.rule=Host(`shop.example.com`)"
        - "traefik.http.services.frontend.loadbalancer.server.port=80"
      update_config:
        parallelism: 1
        delay: 10s
        order: start-first
      resources:
        limits:
          cpus: '0.5'
          memory: 256M
    networks:
      - frontend

  # Admin Panel
  admin-panel:
    image: ecommerce/admin:v1.0
    environment:
      - API_GATEWAY_URL=http://api-gateway:8080
    deploy:
      replicas: 2
      labels:
        - "traefik.enable=true"
        - "traefik.http.routers.admin.rule=Host(`admin.example.com`)"
        - "traefik.http.services.admin.loadbalancer.server.port=80"
    networks:
      - frontend

  # API Gateway
  api-gateway:
    image: ecommerce/gateway:v1.0
    environment:
      - AUTH_SERVICE=auth-service:8080
      - PRODUCT_SERVICE=product-service:8080
      - ORDER_SERVICE=order-service:8080
      - PAYMENT_SERVICE=payment-service:8080
    deploy:
      replicas: 2
      labels:
        - "traefik.enable=true"
        - "traefik.http.routers.gateway.rule=Host(`api.example.com`)"
        - "traefik.http.services.gateway.loadbalancer.server.port=8080"
      resources:
        limits:
          cpus: '1.0'
          memory: 512M
    networks:
      - frontend
      - backend

  # Auth Service
  auth-service:
    image: ecommerce/auth-service:v1.0
    environment:
      - DATABASE_HOST=user-db
      - DATABASE_PORT=5432
      - DATABASE_NAME=users
      - REDIS_HOST=redis
      - JWT_SECRET_FILE=/run/secrets/jwt_secret
    secrets:
      - jwt_secret
      - user_db_password
    deploy:
      replicas: 3
      update_config:
        parallelism: 1
        delay: 10s
      restart_policy:
        condition: on-failure
        max_attempts: 3
    networks:
      - backend
      - database

  # Product Service
  product-service:
    image: ecommerce/product-service:v1.0
    environment:
      - DATABASE_HOST=product-db
      - DATABASE_PORT=5432
      - DATABASE_NAME=products
      - REDIS_HOST=redis
      - CACHE_TTL=3600
    secrets:
      - product_db_password
    deploy:
      replicas: 3
      resources:
        limits:
          cpus: '0.75'
          memory: 512M
    networks:
      - backend
      - database

  # Order Service
  order-service:
    image: ecommerce/order-service:v1.0
    environment:
      - DATABASE_HOST=order-db
      - DATABASE_PORT=5432
      - DATABASE_NAME=orders
      - MESSAGE_QUEUE_HOST=rabbitmq
    secrets:
      - order_db_password
      - rabbitmq_password
    deploy:
      replicas: 2
    networks:
      - backend
      - database
      - messagequeue

  # Payment Service
  payment-service:
    image: ecommerce/payment-service:v1.0
    environment:
      - STRIPE_API_KEY_FILE=/run/secrets/stripe_api_key
      - MESSAGE_QUEUE_HOST=rabbitmq
    secrets:
      - stripe_api_key
      - rabbitmq_password
    deploy:
      replicas: 2
      placement:
        constraints:
          - node.labels.security == high
    networks:
      - backend
      - messagequeue

  # User Database
  user-db:
    image: postgres:13
    environment:
      - POSTGRES_DB=users
      - POSTGRES_USER=userapp
      - POSTGRES_PASSWORD_FILE=/run/secrets/user_db_password
    volumes:
      - user-db-data:/var/lib/postgresql/data
    secrets:
      - user_db_password
    deploy:
      replicas: 1
      placement:
        constraints:
          - node.labels.type == database
      restart_policy:
        condition: on-failure
    networks:
      - database

  # Product Database
  product-db:
    image: postgres:13
    environment:
      - POSTGRES_DB=products
      - POSTGRES_USER=productapp
      - POSTGRES_PASSWORD_FILE=/run/secrets/product_db_password
    volumes:
      - product-db-data:/var/lib/postgresql/data
    secrets:
      - product_db_password
    deploy:
      replicas: 1
      placement:
        constraints:
          - node.labels.type == database
    networks:
      - database

  # Order Database
  order-db:
    image: postgres:13
    environment:
      - POSTGRES_DB=orders
      - POSTGRES_USER=orderapp
      - POSTGRES_PASSWORD_FILE=/run/secrets/order_db_password
    volumes:
      - order-db-data:/var/lib/postgresql/data
    secrets:
      - order_db_password
    deploy:
      replicas: 1
      placement:
        constraints:
          - node.labels.type == database
    networks:
      - database

  # Redis Cache
  redis:
    image: redis:6-alpine
    configs:
      - source: redis_config
        target: /usr/local/etc/redis/redis.conf
    command: redis-server /usr/local/etc/redis/redis.conf
    deploy:
      replicas: 1
      placement:
        constraints:
          - node.role == worker
    networks:
      - backend

  # RabbitMQ
  rabbitmq:
    image: rabbitmq:3-management
    environment:
      - RABBITMQ_DEFAULT_USER=admin
      - RABBITMQ_DEFAULT_PASS_FILE=/run/secrets/rabbitmq_password
    secrets:
      - rabbitmq_password
    deploy:
      replicas: 1
    networks:
      - messagequeue

  # Background Workers
  worker:
    image: ecommerce/worker:v1.0
    environment:
      - MESSAGE_QUEUE_HOST=rabbitmq
      - DATABASE_HOST=order-db
    secrets:
      - rabbitmq_password
      - order_db_password
    deploy:
      replicas: 3
      resources:
        limits:
          cpus: '0.5'
          memory: 256M
    networks:
      - messagequeue
      - database

  # Monitoring - Prometheus
  prometheus:
    image: prom/prometheus:latest
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.path=/prometheus'
    volumes:
      - prometheus-data:/prometheus
    deploy:
      replicas: 1
      placement:
        constraints:
          - node.role == manager
    networks:
      - monitoring

  # Monitoring - Grafana
  grafana:
    image: grafana/grafana:latest
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin123
    volumes:
      - grafana-data:/var/lib/grafana
    deploy:
      replicas: 1
      labels:
        - "traefik.enable=true"
        - "traefik.http.routers.grafana.rule=Host(`monitoring.example.com`)"
        - "traefik.http.services.grafana.loadbalancer.server.port=3000"
      placement:
        constraints:
          - node.role == manager
    networks:
      - monitoring
      - frontend

networks:
  frontend:
    driver: overlay
    attachable: true
  backend:
    driver: overlay
  database:
    driver: overlay
    internal: true
  messagequeue:
    driver: overlay
  monitoring:
    driver: overlay

volumes:
  user-db-data:
  product-db-data:
  order-db-data:
  prometheus-data:
  grafana-data:

secrets:
  jwt_secret:
    external: true
  stripe_api_key:
    external: true
  user_db_password:
    external: true
  product_db_password:
    external: true
  order_db_password:
    external: true
  rabbitmq_password:
    external: true

configs:
  traefik_config:
    external: true
  redis_config:
    external: true

3. เตรียม Nodes

# กำหนด labels สำหรับ nodes
docker node update --label-add type=database worker1
docker node update --label-add type=database worker2
docker node update --label-add security=high worker3

4. Deploy Stack

# deploy
docker stack deploy -c docker-compose.yml ecommerce

# ตรวจสอบ
watch docker stack ps ecommerce

5. Monitoring และ Maintenance

# ดู logs แบบ real-time
docker service logs -f ecommerce_product-service

# ตรวจสอบ resource usage
docker stats

# ดูสถานะ services
docker stack services ecommerce

# scale services
docker service scale ecommerce_product-service=5
docker service scale ecommerce_auth-service=5

Best Practices

1. High Availability

# ใช้ Manager nodes จำนวนคี่ (3, 5, 7)
# เพื่อการ consensus ที่ดี

# ตัวอย่าง: cluster ขนาดกลาง
- 3 Manager nodes
- 5+ Worker nodes

2. Resource Management

# กำหนด resource limits และ reservations เสมอ
deploy:
  resources:
    limits:
      cpus: '1.0'
      memory: 512M
    reservations:
      cpus: '0.5'
      memory: 256M

3. Health Checks

# กำหนด health checks สำหรับ services
services:
  api:
    image: myapi:latest
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

4. Rolling Updates

# กำหนด update config ที่เหมาะสม
deploy:
  update_config:
    parallelism: 2          # update ทีละ 2 tasks
    delay: 10s              # delay ระหว่าง batches
    failure_action: rollback # rollback ถ้า fail
    monitor: 60s            # monitor เวลา 60s
    max_failure_ratio: 0.2  # ยอมให้ fail ได้ 20%
    order: start-first      # start ตัวใหม่ก่อนปิดตัวเก่า

5. Placement Strategies

# ใช้ placement constraints อย่างชาญฉลาด
deploy:
  placement:
    constraints:
      - node.role == worker
      - node.labels.type == database
      - node.hostname != old-server
    preferences:
      - spread: node.labels.datacenter  # กระจายตาม datacenter

6. Security

# ใช้ secrets สำหรับข้อมูลลับทุกตัว
# ไม่ควรใส่ passwords ใน environment variables

# ใช้ encrypted networks
docker network create --opt encrypted mynetwork

# จำกัดการเข้าถึง internal networks
networks:
  database:
    driver: overlay
    internal: true  # ไม่สามารถเข้าถึงจากภายนอกได้

7. Logging และ Monitoring

# ใช้ logging driver
services:
  app:
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

# Deploy monitoring stack
- Prometheus สำหรับ metrics
- Grafana สำหรับ visualization
- ELK Stack สำหรับ log aggregation

8. Backup Strategy

# Backup volumes เป็นประจำ
docker run --rm \
  -v volume_name:/volume \
  -v $(pwd):/backup \
  alpine tar czf /backup/backup.tar.gz /volume

# Backup swarm configurations
docker config ls -q | xargs -I {} sh -c 'docker config inspect {} > config-{}.json'
docker secret ls -q | xargs -I {} sh -c 'docker secret inspect {} > secret-{}.json'

9. Network Isolation

# แยก networks ตามชั้น
networks:
  frontend:      # สำหรับ public-facing services
    driver: overlay
    attachable: true
    
  backend:       # สำหรับ internal services
    driver: overlay
    
  database:      # สำหรับ databases เท่านั้น
    driver: overlay
    internal: true

10. Documentation

# เขียน documentation ใน docker-compose.yml
# ใช้ comments อธิบาย

# เก็บ version history
# ใช้ Git สำหรับ version control

# สร้าง README.md สำหรับแต่ละ stack

การแก้ปัญหาทั่วไป

ปัญหา 1: Service ไม่ขึ้น

# ตรวจสอบ logs
docker service logs <service-name>

# ดู tasks ที่ fail
docker service ps <service-name> --no-trunc

# ตรวจสอบ events
docker events

# ดู node availability
docker node ls

ปัญหา 2: Network ไม่สามารถเชื่อมต่อได้

# ตรวจสอบ overlay network
docker network ls
docker network inspect <network-name>

# ทดสอบ connectivity
docker run --rm --network <network-name> alpine ping <service-name>

# restart docker daemon บน nodes ที่มีปัญหา
sudo systemctl restart docker

ปัญหา 3: Volume mount ไม่ได้

# ตรวจสอบ volume
docker volume ls
docker volume inspect <volume-name>

# ตรวจสอบ permissions
docker run --rm -v <volume>:/data alpine ls -la /data

ปัญหา 4: Secret/Config ไม่ถูกต้อง

# ตรวจสอบ secret
docker secret ls
docker secret inspect <secret-name>

# update service ด้วย secret ใหม่
docker service update \
  --secret-rm old_secret \
  --secret-add new_secret \
  <service-name>

ปัญหา 5: Rolling Update ติด

# rollback service
docker service rollback <service-name>

# ตรวจสอบ update status
docker service inspect --pretty <service-name>

# force update
docker service update --force <service-name>

สรุป

Docker Swarm เป็นเครื่องมือที่ทรงพลังสำหรับการจัดการ containers แบบ cluster ที่มีความสามารถในการ:

Deploy และ Scale applications ได้ง่ายและรวดเร็ว
High Availability ด้วย automatic failover
Load Balancing อัตโนมัติ
Rolling Updates โดยไม่มี downtime
Security ด้วย built-in TLS และ secrets management
Service Discovery อัตโนมัติ

การใช้งาน Docker Swarm จะช่วยให้การ deploy และจัดการ applications ของคุณมีความน่าเชื่อถือและปรับขนาดได้ง่ายขึ้น โดยเฉพาะเมื่อใช้ร่วมกับ Docker Stack ที่ทำให้การจัดการ multi-service applications เป็นเรื่องง่าย

แหล่งข้อมูลเพิ่มเติม