Files
backend/docker-compose.prod.yml
T
34047007@qq.com 3c85ded216
CI / backend (push) Waiting to run
CI / frontend (push) Waiting to run
feat: 生产部署准备 — 构建加速 + 24迁移链分批脚本 + 搜索性能优化
- Dockerfile 腾讯源 + BuildKit 缓存(backend pip / frontend npm ci),gitea 锁 1.27.1
- .dockerignore 纳入版本管理(防密钥进镜像)
- 新增 migrate_prod.sh:24 个迁移分 4 批执行,含镜像新鲜度 + DB 起点校验
- cb07d6b1df01 移除 search_tsv 回填(延迟到 g0h1i2j3k4l5 统一全量回填)
- 模型 journal_iso/volume/issue/pages → Text(对应迁移 95c18ebf31e4 / 55105f0bb1d7)
- 搜索优化:tsvector 主路径 + COUNT 截断(10000) + pub_date 排序,admin 聚合单查询
- statement_timeout 30s→60s
2026-08-10 00:33:16 +08:00

292 lines
7.6 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# SciLit 生产环境 Docker Compose
#
# 使用方式:
# cp .env.example .env # 填入实际值
# docker compose -f docker-compose.prod.yml --env-file .env up -d
#
# HTTPS 终止建议使用上游反向代理(Caddy / Nginx / ALB),
# 前端 Nginx 容器本身仅提供 HTTP 服务。
version: "3.9"
networks:
scilit:
driver: bridge
services:
postgres:
image: pgvector/pgvector:pg16
networks:
- scilit
environment:
POSTGRES_DB: scilit
POSTGRES_USER: scilit
POSTGRES_PASSWORD: ${PG_PASSWORD}
ports: []
volumes:
- pgdata:/var/lib/postgresql/data
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U scilit"]
interval: 10s
timeout: 5s
retries: 5
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
redis:
image: redis:7-alpine
networks:
- scilit
command: redis-server --requirepass ${REDIS_PASSWORD}
ports: []
volumes:
- redisdata:/data
restart: unless-stopped
healthcheck:
test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"]
interval: 10s
timeout: 5s
retries: 5
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
elasticsearch:
image: elasticsearch:8.11.0
networks:
- scilit
environment:
discovery.type: single-node
xpack.security.enabled: "false"
"ES_JAVA_OPTS": "-Xms1g -Xmx1g"
bootstrap.memory_lock: "true"
ports: []
volumes:
- esdata:/usr/share/elasticsearch/data
restart: unless-stopped
ulimits:
memlock:
soft: -1
hard: -1
healthcheck:
test: ["CMD-SHELL", "curl -s http://localhost:9200/_cluster/health | grep -qE 'green|yellow'"]
interval: 15s
timeout: 10s
retries: 5
start_period: 30s
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
minio:
image: minio/minio:latest
command: server /data --console-address ":9001"
networks:
- scilit
environment:
MINIO_ROOT_USER: ${MINIO_ROOT_USER:-minioadmin}
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:-minioadmin}
ports: []
volumes:
- miniodata:/data
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "curl -sf http://localhost:9000/minio/health/live || exit 1"]
interval: 15s
timeout: 5s
retries: 5
start_period: 10s
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
migrate:
build: ./backend
networks:
- scilit
command: alembic -c alembic/alembic.ini upgrade head
environment:
DATABASE_URL: postgresql+asyncpg://scilit:${PG_PASSWORD}@postgres:5432/scilit
JWT_SECRET: ${JWT_SECRET}
SPECIALTY: ${SPECIALTY:-oncology}
DEBUG: "false"
depends_on:
postgres:
condition: service_healthy
restart: "no"
backend:
build: ./backend
networks:
- scilit
# ENTRYPOINT 自动执行迁移,CMD 启动 uvicorn4 workers + proxy-headers
# 无需额外 command
environment:
DATABASE_URL: postgresql+asyncpg://scilit:${PG_PASSWORD}@postgres:5432/scilit
REDIS_URL: redis://default:${REDIS_PASSWORD}@redis:6379
ES_URL: http://elasticsearch:9200
S3_ENDPOINT: ${S3_ENDPOINT}
S3_ACCESS_KEY: ${S3_ACCESS_KEY}
S3_SECRET_KEY: ${S3_SECRET_KEY}
S3_BUCKET: ${S3_BUCKET:-scilit-files}
COS_SECRET_ID: ${COS_SECRET_ID:-}
COS_SECRET_KEY: ${COS_SECRET_KEY:-}
COS_REGION: ${COS_REGION:-ap-guangzhou}
COS_BUCKET: ${COS_BUCKET:-scilit-files}
JWT_SECRET: ${JWT_SECRET}
SPECIALTY: ${SPECIALTY:-oncology}
DEBUG: "false"
SMTP_HOST: ${SMTP_HOST}
SMTP_PORT: ${SMTP_PORT:-587}
SMTP_USER: ${SMTP_USER}
SMTP_PASSWORD: ${SMTP_PASSWORD}
SMTP_FROM: ${SMTP_FROM}
SENTRY_DSN: ${SENTRY_DSN}
AI_API_KEY: ${AI_API_KEY}
AI_BASE_URL: ${AI_BASE_URL}
AI_MODEL: ${AI_MODEL}
PUBMED_API_KEY: ${PUBMED_API_KEY}
CORS_ORIGINS: ${CORS_ORIGINS}
PUBLIC_BASE_URL: ${PUBLIC_BASE_URL}
ports:
- "127.0.0.1:8000:8000"
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
elasticsearch:
condition: service_started
minio:
condition: service_healthy
migrate:
condition: service_completed_successfully
healthcheck:
test: ["CMD-SHELL", "curl -sf http://localhost:8000/health || exit 1"]
interval: 15s
timeout: 5s
retries: 3
start_period: 20s
restart: unless-stopped
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
worker:
build: ./backend
networks:
- scilit
# 通过 CMD 覆写默认的 uvicorn 启动,改为 ARQ worker(异步任务消费)
# 迁移由独立的 migrate 服务完成(depends_on 确保顺序)
command: arq app.tasks.worker.WorkerSettings
healthcheck:
test: ["CMD-SHELL", "grep -q arq /proc/1/cmdline"]
interval: 30s
timeout: 5s
start_period: 30s
retries: 3
environment:
DATABASE_URL: postgresql+asyncpg://scilit:${PG_PASSWORD}@postgres:5432/scilit
REDIS_URL: redis://default:${REDIS_PASSWORD}@redis:6379
ES_URL: http://elasticsearch:9200
S3_ENDPOINT: ${S3_ENDPOINT}
S3_ACCESS_KEY: ${S3_ACCESS_KEY}
S3_SECRET_KEY: ${S3_SECRET_KEY}
S3_BUCKET: ${S3_BUCKET:-scilit-files}
COS_SECRET_ID: ${COS_SECRET_ID:-}
COS_SECRET_KEY: ${COS_SECRET_KEY:-}
COS_REGION: ${COS_REGION:-ap-guangzhou}
COS_BUCKET: ${COS_BUCKET:-scilit-files}
JWT_SECRET: ${JWT_SECRET}
SPECIALTY: ${SPECIALTY:-oncology}
DEBUG: "false"
SMTP_HOST: ${SMTP_HOST}
SMTP_PORT: ${SMTP_PORT:-587}
SMTP_USER: ${SMTP_USER}
SMTP_PASSWORD: ${SMTP_PASSWORD}
SMTP_FROM: ${SMTP_FROM}
AI_API_KEY: ${AI_API_KEY}
AI_BASE_URL: ${AI_BASE_URL}
AI_MODEL: ${AI_MODEL}
PUBMED_API_KEY: ${PUBMED_API_KEY}
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
minio:
condition: service_healthy
migrate:
condition: service_completed_successfully
restart: unless-stopped
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
frontend:
build:
context: ./frontend
dockerfile: Dockerfile.prod
networks:
- scilit
environment:
VITE_API_BASE: /api/v1
ports:
- "80:80"
volumes:
- /var/log/scilit/nginx:/var/log/nginx
depends_on:
backend:
condition: service_started
restart: unless-stopped
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
gitea:
image: gitea/gitea:1.27.1-rootless
restart: unless-stopped
networks:
- scilit
volumes:
- gitea_data:/var/lib/gitea
environment:
GITEA__database__DB_TYPE: postgres
GITEA__database__HOST: postgres:5432
GITEA__database__NAME: gitea
GITEA__database__USER: gitea
GITEA__database__PASSWD: gitea_pass_2026
GITEA__server__DOMAIN: 123.207.9.209
GITEA__server__HTTP_PORT: 3000
GITEA__server__ROOT_URL: http://123.207.9.209:3000
GITEA__server__START_SSH_SERVER: "true"
GITEA__server__SSH_DOMAIN: 123.207.9.209
GITEA__server__SSH_PORT: 2222
GITEA__server__SSH_LISTEN_PORT: 22
ports:
- "3000:3000"
- "2222:22"
volumes:
pgdata:
redisdata:
esdata:
miniodata:
gitea_data: