實務範例:使用 Docker 部署 Web 應用

我們已經學會了 Docker 的各個核心組件。現在,我們要將這些零散的知識拼湊起來,進行一次完整的實戰:將一個常見的 Web 應用程式(前端 + 後端 + 資料庫)部署到 Linux 伺服器上。

專案架構概覽

本實戰案例將包含以下三個組件:

  1. Frontend: React 應用程式 (經由 Nginx 執行)。
  2. Backend: Node.js/Python API。
  3. Database: PostgreSQL。

準備 Docker 設定檔

Backend: Dockerfile

使用多階段建置以確保安全性與體積:

# Build stage
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .

# Run stage
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app ./
# 使用非 root 使用者執行
USER node
CMD ["node", "server.js"]

全域: docker-compose.yaml

定義服務間的網路與依賴關係:

services:
  api:
    build: ./backend
    environment:
      DATABASE_URL: postgres://user:password@db:5432/mydb
    depends_on:
      - db
    networks:
      - app-net

  frontend:
    image: nginx:alpine
    ports:
      - '80:80'
    volumes:
      - ./frontend/dist:/usr/share/nginx/html
    networks:
      - app-net

  db:
    image: postgres:15-alpine
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password
      POSTGRES_DB: mydb
    volumes:
      - db-data:/var/lib/postgresql/data
    networks:
      - app-net

volumes:
  db-data:

networks:
  app-net:

部署流程:從本地到伺服器

第一步:在本地驗證

執行 docker compose up -d 並確保瀏覽器能正常存取功能。

第二步:推送映像檔至倉庫

docker build -t mikelee/my-api:v1 ./backend
docker push mikelee/my-api:v1

第三步:遠端伺服器拉取並部署

  1. 使用 Docker Context(推薦)或是 SSH 登入伺服器。
  2. 在伺服器上準備好 docker-compose.yaml
  3. 執行部署:
    docker compose up -d
    

生產環境的進階配置

為了讓你的部署達到「工業等級」,建議追加以下設定:

A. 自動修復與重啟

docker-compose.yaml 為關鍵服務加入 restart 原則,確保容器在當掉或伺服器重啟時能自動恢復。

services:
  api:
    restart: always

B. 日誌自動清理

防止硬碟被容器日誌塞爆:

services:
  api:
    logging:
      driver: 'json-file'
      options:
        max-size: '10m'
        max-file: '3'

C. SSL 憑證 (HTTPS)

這通常不會寫在應用的容器中,而是建議在前端容器的前面加一個 Reverse Proxy (反向代理)

  • Nginx Proxy Manager (推薦):提供圖形化介面,適合新手管理 SSL。
  • Traefik:雲原生架構的最佳選擇,能自動偵測 Docker 容器並申請 Let's Encrypt 憑證。

環境變數與金鑰管理

在生產環境中,我們絕對不能將敏感資訊(如資料庫密碼、API Key)直接寫在 docker-compose.yamlDockerfile 中。

A. 使用 .env 檔案

Docker Compose 會自動讀取目錄下的 .env 檔案,並將變數注入到 YAML 中:

# .env 檔案
DB_PASSWORD=super_secret_password_123
API_KEY=your_api_key_here

docker-compose.yaml 中引用:

services:
  api:
    environment:
      - DATABASE_PASSWORD=${DB_PASSWORD}
務必將 .env 加入 .gitignore,不要將其推送到 Git 倉庫。

B. 使用 env_file 大規模管理

當環境變數很多時,可以在 Compose 中直接掛載整個檔案:

services:
  api:
    env_file:
      - ./config/production.env

CI/CD 自動化部署 (GitHub Actions)

手動登入伺服器下指令既低效又容易出錯。現代化的流程是透過 CI/CD 自動化。

以下是一個簡單的 GitHub Actions 範例 (.github/workflows/deploy.yml):

name: Deploy to Production

on:
  push:
    branches: [main]

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      # 1. 登入 Docker Hub
      - name: Login to Docker Hub
        uses: docker/login-action@v2
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}

      # 2. 建置並推送到倉庫
      - name: Build and Push
        run: |
          docker build -t mikelee/my-api:latest ./backend
          docker push mikelee/my-api:latest

      # 3. 透過 SSH 部署到遠端伺服器
      - name: Deploy via SSH
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: ${{ secrets.SERVER_USER }}
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          script: |
            cd /app/my-project
            docker compose pull
            docker compose up -d

維運與清理:保持系統整潔

Docker 運行久了會產生大量的虛身(Dangling Images)與無用的數據卷,這可能會塞爆硬碟。

A. 定期清理指令

建議定期執行以下指令來回收空間:

# 清理所有沒在使用的容器、網路與無標籤映像檔
docker system prune -f

# 同時清理沒在使用的數據卷 (小心使用!)
docker system prune -a --volumes

B. 自動化清理

你可以設定一個 Cron job 每週定期清理一次:

0 3 * * 0 docker system prune -af

常見部署問題排查

徵兆可能原因排查指令
網頁連不到服務埠號沒開或被防火牆擋住telnet localhost [PORT]
資料庫連線失敗depends_on 沒考慮到資料庫啟動時間docker compose logs db
權限不足 (Permission Denied)數據卷目錄權限不正確docker exec -it [ID] ls -l
伺服器變很慢某個容器發生記憶體洩漏docker stats