chundev
日期:2026-03-16 場景:一個主應用(Node.js)+ 一個 Worker(Python)共用 DB,部署在同一台 server
兩個不同語言、不同 repo 的服務,用同一個 docker-compose.yml 部署。關鍵技巧:build.context 指向隔壁目錄,共用 Docker network 讓服務互打內部 API。
你有兩個獨立 repo:
~/projects/
├── app/ # 主應用(Node.js, Next.js)
│ ├── Dockerfile
│ ├── docker-compose.prod.yml ← compose 放這裡
│ └── .env.production
└── worker/ # Worker(Python)
└── Dockerfile
需求:
docker-compose.yml 的 build.context 可以指向任意路徑,不限於當前目錄:
services:
postgres:
image: postgres:17-alpine
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready"]
interval: 10s
app:
build: . # 當前 repo
depends_on:
postgres:
condition: service_healthy
ports:
- "127.0.0.1:3050:3050"
environment:
DATABASE_URL: postgresql://user:pass@postgres:5432/mydb
worker:
build:
context: ../worker # ← 指向隔壁 repo
dockerfile: Dockerfile
depends_on:
app:
condition: service_healthy
environment:
API_URL: http://app:3050/api # ← Docker 內部網路,用 service name
volumes:
pgdata:
| 概念 | 說明 |
|---|---|
build.context: ../worker |
compose 會把 ../worker 整個目錄送給 Docker daemon 當作 build context |
http://app:3050 |
同一個 compose 的 services 自動在同一個 network,用 service name 當 hostname |
depends_on + condition |
確保 app 健康後 worker 才啟動 |
/home/deploy/
├── app/
│ ├── docker-compose.prod.yml
│ ├── .env.production # 所有環境變數(兩個服務共用)
│ └── ...(app 原始碼)
├── worker/
│ ├── Dockerfile
│ └── ...(worker 原始碼)
└── certs/ # 敏感憑證(volume 掛載)
部署指令:
cd /home/deploy/app
docker compose -f docker-compose.prod.yml --env-file .env.production up -d --build
--build 會根據 build.context 自動 build 兩個 image。
Worker 通常不是常駐服務,而是定時執行一次。有三種方式:
Server 的 crontab 定時用 docker compose run 觸發:
# /etc/crontab 或 crontab -e
30 8 * * 1-5 cd /home/deploy/app && docker compose -f docker-compose.prod.yml --env-file .env.production run --rm worker
run --rm:建立一個臨時 container,跑完自動刪除restart: "no",避免 Docker 自動重啟Worker Dockerfile 裝 cron,自己排程:
FROM python:3.12-slim
RUN apt-get update && apt-get install -y cron
COPY crontab /etc/cron.d/worker-cron
RUN chmod 0644 /etc/cron.d/worker-cron && crontab /etc/cron.d/worker-cron
CMD ["cron", "-f"]
restart: unless-stopped(常駐)主應用的排程器在指定時間 docker exec 或呼叫 API 觸發 worker:
// PM2 ecosystem.config.cjs
{
name: 'trigger-worker',
script: 'bash',
args: '-c "docker compose run --rm worker"',
cron_restart: '30 8 * * 1-5',
}
| 策略 | 複雜度 | 常駐? | 依賴 host? |
|---|---|---|---|
| A. Host Crontab | 低 ⭐ | ❌ | ✅ |
| B. Container Cron | 中 | ✅ | ❌ |
| C. App 觸發 | 中 | ❌ | ⚠️ 需 docker socket |
同一個 docker-compose.yml 的 services 自動加入同一個 bridge network:
app ←→ postgres # app 用 postgres:5432 連 DB
worker ←→ app # worker 用 app:3050 打 API
worker ←→ postgres # worker 也能直接連 DB
不需要 expose port 到 host。ports: "127.0.0.1:3050:3050" 是給外部(Nginx)用的,內部直接用 service name。
Worker 打 App 的 API 時,用 Token 驗證避免外部濫用:
# .env.production
EXECUTOR_API_TOKEN=random-secret-token
# app 檢查 Authorization header
# worker 帶上 Bearer token
絕對不要 把憑證、密鑰打包進 Docker image。用 volume 掛載:
services:
worker:
volumes:
- /home/deploy/certs:/app/certs:ro # :ro = 唯讀
environment:
CA_CERT_PATH: /app/certs/cert.pfx
這樣 image 裡沒有敏感檔案,可以安全推到 registry。
# 1. Clone 兩個 repo
ssh deploy@server
cd /home/deploy
git clone git@github.com:you/app.git
git clone git@github.com:you/worker.git
# 2. 設定環境變數
cp app/.env.example app/.env.production
vim app/.env.production # 填入所有變數
# 3. 放置憑證
mkdir -p certs
scp local/cert.pfx deploy@server:/home/deploy/certs/
# 4. Build + 啟動
cd app
docker compose -f docker-compose.prod.yml --env-file .env.production up -d --build
# 5. 設定 crontab
crontab -e
# 加入:30 8 * * 1-5 cd /home/deploy/app && docker compose -f docker-compose.prod.yml --env-file .env.production run --rm worker >> /home/deploy/worker/logs/cron.log 2>&1
# 只更新 app
cd /home/deploy/app && git pull
docker compose -f docker-compose.prod.yml --env-file .env.production up -d --build app
# 只更新 worker
cd /home/deploy/worker && git pull
docker compose -f docker-compose.prod.yml --env-file .env.production build worker
# 下次 cron 觸發時自動用新 image
# 兩個都更新
cd /home/deploy/app && git pull
cd /home/deploy/worker && git pull
cd /home/deploy/app
docker compose -f docker-compose.prod.yml --env-file .env.production up -d --build
../worker 目錄?確保目錄結構正確:
ls /home/deploy/
# 應該看到 app/ 和 worker/ 在同一層
http://app:3050?docker compose psdocker network ls + docker network inspect0.0.0.0:3050 而非 127.0.0.1:3050docker compose run vs docker compose up?| 指令 | 用途 |
|---|---|
up -d |
啟動常駐服務(app, postgres) |
run --rm |
執行一次性任務(worker),完成後自動清除 container |
exec |
在已運行的 container 中執行指令 |
# 如果用 crontab,log 在指定的檔案
tail -f /home/deploy/worker/logs/cron.log
# 如果用 docker compose run,即時看到 stdout
docker compose -f docker-compose.prod.yml run --rm worker
# 查看歷史 container 的 log
docker logs tsm-executor
Server
┌─────────────────────────────────────────────┐
│ Docker Network (bridge) │
│ │
│ ┌──────────┐ ┌──────────┐ ┌───────────┐ │
│ │ postgres │←─│ app │←─│ worker │ │
│ │ :5432 │ │ :3050 │ │ (cron) │ │
│ └──────────┘ └────┬─────┘ └───────────┘ │
│ │ │
└─────────────────────┼────────────────────────┘
│ 127.0.0.1:3050
┌─────┴─────┐
│ Nginx │ ← HTTPS
└───────────┘
↕
Internet