本文承接從零開始的 Docker 容器化指南 ,主要討論如何實現全自動化的 Docker 部署流程。透過整合 GitHub Actions、Docker Hub 和自動化工具,我們可以實現程式碼推送後自動更新生產環境的目標。
目錄
自動化部署概述 完整的自動化部署流程包含以下步驟:
開發者推送程式碼到 GitHub
GitHub Actions 自動執行測試和建置
建立新的 Docker 映像檔並推送至 Docker Hub
生產環境自動偵測並更新容器
健康檢查確認部署狀態
部署流程圖 graph TD
A[開發者推送程式碼] --> B[GitHub Actions 觸發]
B --> C[執行測試]
C --> D[建立 Docker 映像檔]
D --> E[推送至 Docker Hub]
E --> F[生產環境更新]
F --> G[健康檢查]
G -->|成功| H[部署完成]
G -->|失敗| I[自動回滾]
GitHub Actions 設定 1. 基礎設定 建立 .github/workflows/deploy.yml 檔案:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 name: Deploy to Production on: push: branches: [ main ] workflow_dispatch: jobs: deploy: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Cache Docker layers uses: actions/cache@v4 with: path: /tmp/.buildx-cache key: ${{ runner.os }}-buildx-${{ github.sha }} restore-keys: | ${{ runner.os }}-buildx- - name: Login to Docker Hub uses: docker/login-action@v3 with: username: ${{ secrets.DOCKER_HUB_USERNAME }} password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} - name: Build and push uses: docker/build-push-action@v6 with: context: . push: true tags: | user/myapp:latest user/myapp:${{ github.sha }} cache-from: type=local,src=/tmp/.buildx-cache cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max - name: Move cache run: | rm -rf /tmp/.buildx-cache mv /tmp/.buildx-cache-new /tmp/.buildx-cache
2. 部署到伺服器 添加遠端部署步驟:
1 2 3 4 5 6 7 8 9 - name: Deploy to Production uses: appleboy/ssh-action@v1.0.3 with: host: ${{ secrets.SERVER_HOST }} username: ${{ secrets.SERVER_USER }} key: ${{ secrets.SERVER_SSH_KEY }} script: | cd /path/to/project ./deploy.sh ${{ github.sha }}
自動更新容器設定 1. Watchtower 設定 Watchtower 可以自動監測和更新 Docker 容器:
1 2 3 4 5 6 7 8 9 10 11 12 13 services: watchtower: image: containrrr/watchtower volumes: - /var/run/docker.sock:/var/run/docker.sock environment: - WATCHTOWER_CLEANUP=true - WATCHTOWER_LABEL_ENABLE=true - WATCHTOWER_NOTIFICATION_URL=slack://hook:xxxx-yyyy-zzzz@webhook labels: - "com.centurylinklabs.watchtower.enable=true" command: --interval 300
2. 容器標籤設定 為需要自動更新的服務加上標籤:
1 2 3 4 services: api: labels: - "com.centurylinklabs.watchtower.enable=true"
零停機部署策略 1. 藍綠部署 建立部署腳本 deploy.sh:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 #!/bin/bash APP_NAME="myapp" BLUE_PORT="8080" GREEN_PORT="8081" PROXY_PORT="80" VERSION=$1 ACTIVE_COLOR=$(docker ps --filter "name=${APP_NAME} -" --format "{{.Names}}" | grep -o 'blue\|green' ) if [ "$ACTIVE_COLOR " == "blue" ]; then NEW_COLOR="green" NEW_PORT=$GREEN_PORT else NEW_COLOR="blue" NEW_PORT=$BLUE_PORT fi echo "Starting $NEW_COLOR deployment..." docker-compose -f docker-compose.$NEW_COLOR .yml up -d --build sleep 10if curl -f "http://localhost:$NEW_PORT /health" ; then echo "Switching traffic to $NEW_COLOR deployment (port $NEW_PORT )" OLD_COLOR=$ACTIVE_COLOR export ACTIVE_COLOR=$NEW_COLOR export ACTIVE_PORT=$NEW_PORT docker-compose -f docker-compose.proxy.yml up -d --force-recreate if [ -n "$OLD_COLOR " ]; then docker-compose -f docker-compose.$OLD_COLOR .yml down fi echo "Deployment successful!" else echo "Health check failed! Rolling back..." docker-compose -f docker-compose.$NEW_COLOR .yml down exit 1 fi
2. 健康檢查設定 在服務中設定健康檢查:
1 2 3 4 5 6 7 8 services: api: healthcheck: test: ["CMD" , "curl" , "-f" , "http://localhost/health" ] interval: 30s timeout: 10s retries: 3 start_period: 40s
備份與還原機制 1. 自動備份設定 建立備份腳本 backup.sh:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #!/bin/bash BACKUP_DIR="/backups" TIMESTAMP=$(date +%Y%m%d_%H%M%S) mkdir -p $BACKUP_DIR cp docker-compose.yml $BACKUP_DIR /docker-compose-$TIMESTAMP .ymldocker-compose exec -T db sh -c 'mysqldump -u root -p"$MYSQL_ROOT_PASSWORD" --all-databases' > $BACKUP_DIR /db-$TIMESTAMP .sql find $BACKUP_DIR -type f -mtime +7 -delete
2. 還原流程 建立還原腳本 restore.sh:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 #!/bin/bash if [ -z "$1 " ]; then echo "Please provide backup timestamp" exit 1 fi TIMESTAMP=$1 BACKUP_DIR="/backups" if [ ! -f "$BACKUP_DIR /docker-compose-$TIMESTAMP .yml" ] || [ ! -f "$BACKUP_DIR /db-$TIMESTAMP .sql" ]; then echo "Backup files not found" exit 1 fi docker-compose down cp $BACKUP_DIR /docker-compose-$TIMESTAMP .yml docker-compose.ymldocker-compose up -d docker-compose exec -T db sh -c "mysql -u root -p\"$MYSQL_ROOT_PASSWORD \"" < $BACKUP_DIR /db-$TIMESTAMP .sql
監控與告警設定 1. Prometheus 設定 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 services: prometheus: image: prom/prometheus volumes: - ./prometheus.yml:/etc/prometheus/prometheus.yml ports: - "9090:9090" grafana: image: grafana/grafana ports: - "3000:3000" depends_on: - prometheus
2. 告警設定 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 alerting: alertmanagers: - static_configs: - targets: - alertmanager:9093 rule_files: - alert.rules.yml scrape_configs: - job_name: 'docker' static_configs: - targets: ['host.docker.internal:9323' ]
故障排除與回滾流程 1. 常見問題處理
問題
優先確認項目
映像檔拉取失敗
Registry 憑證是否過期(docker login);網路是否可達 Docker Hub
容器啟動失敗
docker logs <container> 查啟動錯誤;環境變數是否齊全
健康檢查失敗
服務實際監聽埠是否正確;start_period 是否太短
資料庫連線問題
容器 network 是否同一個;連線字串主機名是否用服務名稱而非 localhost
2. 回滾流程 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #!/bin/bash VERSION=$1 if [ -z "$VERSION " ]; then echo "Please specify version to rollback" exit 1 fi export APP_VERSION=$VERSION docker-compose pull docker-compose down docker-compose up -d
最佳實踐建議
版本標籤 :映像檔同時打 latest 與 git sha 標籤,方便回滾到任意 commit。定期執行 docker image prune 清理無用層。
Actions 版本釘牢 :所有 uses: 鎖定至 major 版本(如 @v4)或 commit SHA,避免上游靜默破壞工作流。
Secrets 不落地 :資料庫密碼、Registry token 只放 GitHub Secrets 或 Vault,不寫進 compose 檔。
映像檔瘦身 :多階段建置(multi-stage build)分離 build 與 runtime,runtime stage 改用 -slim 或 distroless 基底映像檔,通常可將映像檔大小砍半以上。
快取層序 :Dockerfile 把最少變動的層(安裝套件)放前、最常變動的層(複製應用程式碼)放後,BuildKit 快取命中率更高。
結論 上述流程把「推程式碼 → 測試 → 建置 → 部署 → 監控」串成一條無人值守的管線。實際落地時,藍綠切換腳本需要三個對應的 compose 檔(blue / green / proxy),Watchtower Slack 通知需要用 shoutrrr 格式的 URL,回滾腳本需要在 docker-compose pull 之前先 export 版本變數,Prometheus 的 alert.rules.yml 也需要自行定義閾值——這些都是直接抄範例會踩到的坑,對照本文的注解調整即可。
參考資源