update
This commit is contained in:
155
docker-compose.yml
Normal file
155
docker-compose.yml
Normal file
@ -0,0 +1,155 @@
|
||||
# 生产部署:蓝绿双槽 + 常驻代理,支持零停机发布
|
||||
#
|
||||
# 服务器目录(必须同级):
|
||||
# /opt/fashion/backend ← 本文件所在(后端仓,部署入口)
|
||||
# /opt/fashion/test ← 前端仓(默认按 ../test 取,可用 .env 的 WEB_DIR 覆盖)
|
||||
#
|
||||
# 浏览器 ──▶ proxy(nginx:80,常驻不重建)
|
||||
# ├─ /api ──▶ backend_a 或 backend_b
|
||||
# ├─ / ──▶ web_a 或 web_b
|
||||
# ├─ 18090(容器内) SSR 取数 → 当前活跃后端
|
||||
# ├─ 127.0.0.1:18091 前端构建期取 SSG
|
||||
# └─ 127.0.0.1:18092 管理后台(SSH 隧道)
|
||||
#
|
||||
# 首次部署:cp .env.example .env → 填密钥 → ./init-db.sh <sql> → ./deploy.sh all
|
||||
# 日常发布:./deploy.sh(只更新后端) / ./deploy.sh all / ./deploy.sh --rollback
|
||||
#
|
||||
# a/b 两个槽共用同一份镜像标签(RELEASE=时间戳-提交号,不可变),
|
||||
# deploy.sh 只重建空闲槽,换完 nginx -s reload 即切换。
|
||||
|
||||
x-backend-base: &backend-base
|
||||
image: fashion-backend:${RELEASE:-latest}
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
SERVER_PORT: 8090
|
||||
SSG_PORT: 8091
|
||||
BACKSTAGE_PORT: 8092
|
||||
GIN_MODE: release
|
||||
|
||||
HASHID_SECRET: ${HASHID_SECRET:?HASHID_SECRET 未设置:复制 .env.example 为 .env 并填写}
|
||||
JWT_SECRET: ${JWT_SECRET:?JWT_SECRET 未设置}
|
||||
SSG_TOKEN: ${SSG_TOKEN:-}
|
||||
CORS_ALLOW_ORIGINS: ${CORS_ALLOW_ORIGINS:-https://xisoa.com,https://www.xisoa.com}
|
||||
|
||||
RATE_LIMIT_ENABLED: ${RATE_LIMIT_ENABLED:-true}
|
||||
RATE_LIMIT_RPS: ${RATE_LIMIT_RPS:-20}
|
||||
RATE_LIMIT_BURST: ${RATE_LIMIT_BURST:-40}
|
||||
|
||||
DB_HOST: db
|
||||
DB_PORT: 5432
|
||||
DB_USER: ${DB_USER:-fashion}
|
||||
DB_PASSWORD: ${DB_PASSWORD:?DB_PASSWORD 未设置}
|
||||
DB_NAME: ${DB_NAME:-fashion}
|
||||
|
||||
INGEST_SECRET: ${INGEST_SECRET:?INGEST_SECRET 未设置}
|
||||
INGEST_TTL: ${INGEST_TTL:-300}
|
||||
INGEST_DOWNLOAD_PROXY: ${INGEST_DOWNLOAD_PROXY:-}
|
||||
INGEST_IMAGE_CONCURRENCY: ${INGEST_IMAGE_CONCURRENCY:-4}
|
||||
|
||||
S4_ENABLED: ${S4_ENABLED:-true}
|
||||
S4_AK: ${S4_AK:?S4_AK 未设置}
|
||||
S4_SK: ${S4_SK:?S4_SK 未设置}
|
||||
S4_BUCKET: ${S4_BUCKET:-toomstudio}
|
||||
S4_ENDPOINT: ${S4_ENDPOINT:-https://s3.bitiful.net}
|
||||
S4_REGION: ${S4_REGION:-cn-east-1}
|
||||
S4_BASE_URL: ${S4_BASE_URL:-}
|
||||
|
||||
VISION_ENABLED: ${VISION_ENABLED:-false}
|
||||
VISION_ENDPOINT: ${VISION_ENDPOINT:-http://127.0.0.1:8900}
|
||||
|
||||
UPLOAD_DIR: /app/uploads
|
||||
volumes:
|
||||
- uploads:/app/uploads
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "-qO-", "http://127.0.0.1:8090/api/health"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 6
|
||||
start_period: 15s
|
||||
|
||||
x-web-base: &web-base
|
||||
image: fashion-web:${RELEASE:-latest}
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
# 容器内的 localhost 是它自己,SSR 取数统一走代理(永远指向活跃后端槽)
|
||||
BASE_API_SSR: http://proxy:18090
|
||||
healthcheck:
|
||||
test: ["CMD", "nc", "-z", "127.0.0.1", "3000"]
|
||||
interval: 10s
|
||||
timeout: 3s
|
||||
retries: 5
|
||||
start_period: 15s
|
||||
|
||||
services:
|
||||
# ---- 数据库:PostgreSQL 16 + pgvector ----
|
||||
db:
|
||||
image: pgvector/pgvector:pg16
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_USER: ${DB_USER:-fashion}
|
||||
POSTGRES_PASSWORD: ${DB_PASSWORD:?DB_PASSWORD 未设置}
|
||||
POSTGRES_DB: ${DB_NAME:-fashion}
|
||||
TZ: Asia/Shanghai
|
||||
volumes:
|
||||
- db_data:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U ${DB_USER:-fashion} -d ${DB_NAME:-fashion}"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
|
||||
# ---- 常驻代理:整个发布过程中不重建,切换只靠 reload ----
|
||||
proxy:
|
||||
image: nginx:1.27-alpine
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "${HTTP_PORT:-80}:80"
|
||||
- "127.0.0.1:18091:18091" # 前端构建期取 SSG
|
||||
- "127.0.0.1:18092:18092" # 管理后台(SSH 隧道)
|
||||
volumes:
|
||||
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
|
||||
# 切换点放在 .deploy/(已被 gitignore),发布时改写它不会弄脏工作区
|
||||
- ./.deploy/nginx:/etc/nginx/active:ro
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "-qO-", "http://127.0.0.1/healthz"]
|
||||
interval: 15s
|
||||
timeout: 3s
|
||||
retries: 5
|
||||
start_period: 5s
|
||||
depends_on:
|
||||
backend_a:
|
||||
condition: service_healthy
|
||||
web_a:
|
||||
condition: service_started
|
||||
|
||||
# ---- 后端双槽 ----
|
||||
backend_a:
|
||||
<<: *backend-base
|
||||
build:
|
||||
context: .
|
||||
args:
|
||||
GOPROXY: ${GOPROXY:-https://goproxy.cn,direct}
|
||||
|
||||
backend_b:
|
||||
<<: *backend-base
|
||||
|
||||
# ---- 前端双槽(只跑 Astro node,nginx 在 proxy 里)----
|
||||
web_a:
|
||||
<<: *web-base
|
||||
build:
|
||||
context: ${WEB_DIR:-../test}
|
||||
args:
|
||||
# 走宿主回环的代理 18091,由代理转发到活跃后端槽
|
||||
BASE_API_SSG: http://127.0.0.1:18091
|
||||
SSG_TOKEN: ${SSG_TOKEN:-}
|
||||
network: host
|
||||
|
||||
web_b:
|
||||
<<: *web-base
|
||||
|
||||
volumes:
|
||||
db_data:
|
||||
uploads:
|
||||
Reference in New Issue
Block a user