- 新增 Dockerfile.prebuilt / scripts/push-build.sh:本机构建、只上传产物,服务器零编译 - deploy.sh 增加 --prebuilt(自动 --no-pull + 产物校验),并保留 --no-cache - compose 支持 BACKEND_DOCKERFILE / WEB_DOCKERFILE 切换;预构建时用 prebuilt 变体 - init-db.sh / scripts/backup-db.sh 改为 docker exec 1Panel 的 Postgres 容器 - WEB_DIR 默认改为 ../frontend_v2;nginx 切换文件改到 .deploy/ 避免弄脏工作区 - 新增 .gitattributes 固定脚本与配置为 LF
145 lines
4.9 KiB
YAML
145 lines
4.9 KiB
YAML
# 生产部署:蓝绿双槽 + 常驻代理,支持零停机发布
|
||
#
|
||
# 服务器目录(必须同级):
|
||
# /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:-}
|
||
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}
|
||
|
||
# 复用 1Panel 现有 Postgres(宿主 5432)。host-gateway 让容器经宿主访问它。
|
||
DB_HOST: host.docker.internal
|
||
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
|
||
extra_hosts:
|
||
- "host.docker.internal:host-gateway"
|
||
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:
|
||
# ---- 数据库:复用 1Panel 现有 Postgres(连接信息见 .env 的 DB_HOST/DB_PORT/DB_USER/DB_PASSWORD)----
|
||
|
||
# ---- 常驻代理:整个发布过程中不重建,切换只靠 reload ----
|
||
proxy:
|
||
image: nginx:1.27-alpine
|
||
restart: unless-stopped
|
||
ports:
|
||
# 只绑宿主回环:80/443 由 1Panel OpenResty 占用,外层反代到 127.0.0.1:${HTTP_PORT}
|
||
- "127.0.0.1:${HTTP_PORT:-8080}: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: .
|
||
# 默认在容器内编译;本地预构建时由 deploy.sh --prebuilt 切到 Dockerfile.prebuilt
|
||
dockerfile: ${BACKEND_DOCKERFILE:-Dockerfile}
|
||
args:
|
||
GOPROXY: ${GOPROXY:-https://goproxy.cn,direct}
|
||
|
||
backend_b:
|
||
<<: *backend-base
|
||
|
||
# ---- 前端双槽(只跑 Astro node,nginx 在 proxy 里)----
|
||
web_a:
|
||
<<: *web-base
|
||
build:
|
||
context: ${WEB_DIR:-../test}
|
||
# 默认在容器内 npm ci + astro build;本地预构建时切到 Dockerfile.prebuilt
|
||
dockerfile: ${WEB_DOCKERFILE:-Dockerfile}
|
||
args:
|
||
# 走宿主回环的代理 18091,由代理转发到活跃后端槽
|
||
BASE_API_SSG: http://127.0.0.1:18091
|
||
SSG_TOKEN: ${SSG_TOKEN:-}
|
||
network: host
|
||
|
||
web_b:
|
||
<<: *web-base
|
||
|
||
volumes:
|
||
uploads:
|