Zero-downtime Docker deploys ด้วย health checks

ผมไม่จำเป็นต้องใช้ Kubernetes สำหรับ homelab 3 nodes แต่ผมอยากได้ zero-downtime deploys นี่คือ bash script ที่ใช้ — pull image ใหม่ สลับ container แบบ atomic roll back ถ้า container ใหม่ health check fail

Script

#!/bin/bash
set -euo pipefail

SERVICE="${1:?usage: deploy.sh <service> <image>}"
IMAGE="${2:?usage: deploy.sh <service> <image>}"
COMPOSE_DIR="/srv/compose/${SERVICE}"
OLD_CONTAINER="${SERVICE}_app_1"

cd "$COMPOSE_DIR"

echo "▶ Pulling $IMAGE"
docker pull "$IMAGE"

echo "▶ Starting new container"
IMAGE="$IMAGE" docker compose up -d --no-deps --scale app=2 app

echo "▶ Waiting for health check"
NEW_CONTAINER="${SERVICE}_app_2"
RETRIES=30
until docker inspect --format='{{.State.Health.Status}}' "$NEW_CONTAINER" 2>/dev/null | grep -q healthy; do
  RETRIES=$((RETRIES-1))
  if [ $RETRIES -le 0 ]; then
    echo "✗ Health check failed, rolling back"
    docker compose up -d --no-deps --scale app=1 app
    exit 1
  fi
  sleep 2
done

echo "▶ Stopping old container"
docker compose up -d --no-deps --scale app=1 app

echo "✓ Deployed $SERVICE"

วิธีใช้

./deploy.sh myapp myuser/myapp:v1.2.3

ทำไมไม่ใช้ docker-rollout / kubectl?

  • ง่ายกว่า: ไฟล์เดียว ~30 บรรทัด ไม่มี Go binary
  • ไม่มี state: Stateless nodes รันได้โดยไม่ต้องมี cluster manager
  • Rollback ในตัว: เก็บ container เก่าไว้จนกว่า health check จะผ่าน
  • ตรวจสอบได้: Bash ล้วนทุกขั้นเห็นได้ชัดเจน

ข้อจำกัด (พูดตรง ๆ)

  • Single-node เท่านั้น (โอเคสำหรับ homelab ไม่พอสำหรับ prod ที่ใหญ่)
  • ไม่มี blue/green ข้ามหลาย hosts
  • Health check เป็น HTTP อย่างเดียว — สำหรับ TCP services ต้องขยาย

สำหรับอะไรที่ใหญ่กว่าใช้ Kubernetes สำหรับ homelab หรือ host เดียว อันนี้พอแล้ว