實務範例:使用 Docker 部署 Web 應用
我們已經學會了 Docker 的各個核心組件。現在,我們要將這些零散的知識拼湊起來,進行一次完整的實戰:將一個常見的 Web 應用程式(前端 + 後端 + 資料庫)部署到 Linux 伺服器上。
專案架構概覽
本實戰案例將包含以下三個組件:
- Frontend: React 應用程式 (經由 Nginx 執行)。
- Backend: Node.js/Python API。
- 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
第三步:遠端伺服器拉取並部署
- 使用 Docker Context(推薦)或是 SSH 登入伺服器。
- 在伺服器上準備好
docker-compose.yaml。 - 執行部署:
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.yaml 或 Dockerfile 中。
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 |