Files
danyang-match/infrastructure/docker-compose.prod.yml
T
WorkBuddy 69ebf33e18
CI/CD Pipeline / Backend Build & Test (push) Canceled after 0s
CI/CD Pipeline / Admin Web Build (push) Canceled after 0s
CI/CD Pipeline / Mini Program Compile Check (push) Canceled after 0s
CI/CD Pipeline / Migration Validation (push) Canceled after 0s
CI/CD Pipeline / Test Shell Guard (Phase 23 Gate) (push) Canceled after 0s
CI/CD Pipeline / UI Contracts (source-structure gates) (push) Canceled after 0s
CI/CD Pipeline / E2E (live stack) (push) Canceled after 0s
release: freeze demo v1.1.0 2026-10-19
2026-10-19 20:36:22 +08:00

204 lines
7.4 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.
# Production Docker Compose
# 生产环境配置,敏感信息通过 .env 文件注入,不进 Git
services:
postgres:
image: postgres:16-alpine
container_name: danyang-match-postgres
environment:
POSTGRES_DB: ${DB_NAME:-danyang_match}
POSTGRES_USER: ${DB_USER:-danyang}
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
ports:
- "127.0.0.1:5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
- ./backups:/backups
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${DB_USER:-danyang} -d ${DB_NAME:-danyang_match}"]
interval: 10s
timeout: 5s
retries: 5
networks:
- danyang-net
secrets:
- db_password
restart: unless-stopped
redis:
image: redis:7-alpine
container_name: danyang-match-redis
command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD:?请设置REDIS_PASSWORD}
volumes:
- redis_data:/data
healthcheck:
test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD:?请设置REDIS_PASSWORD}", "ping"]
interval: 10s
timeout: 5s
retries: 5
networks:
- danyang-net
restart: unless-stopped
seaweedfs:
# 同 docker-compose.yml:对象存储已从 MinIO 换成 SeaweedFS。
# 生产换的动力比 CI 更强 —— MinIO 仓库已归档、不再产出安全补丁,
# 而这里存的是身份证与实名材料。详见 docker-compose.yml 中的完整说明。
# 版本与本地/CI 一致(钉 4.47),避免各环境跑不同实现。
image: chrislusf/seaweedfs:4.47
container_name: danyang-match-seaweedfs
# 必须覆盖 entrypoint:镜像 ENTRYPOINT 是 /usr/bin/weed,只写 command 会被追加成
# `weed sh -c ...` → `weed: unknown subcommand "sh"`(本地实测踩到)。
entrypoint: ["sh", "-c"]
command:
- |
cat > /tmp/s3cfg.json <<CFG
{"identities":[{"name":"app","credentials":[{"accessKey":"$${MINIO_ACCESS_KEY}","secretKey":"$${MINIO_SECRET_KEY}"}],"actions":["Admin","Read","Write","List","Tagging"]}]}
CFG
# -volume.max=100:bucket 名即 collection,默认上限 8 个 volume 只够一个 bucket
# (第二个桶写入会 InternalError)。详见 docker-compose.yml 中的说明。
exec weed server -s3 -s3.port=8333 -s3.config=/tmp/s3cfg.json -dir=/data -volume.max=100
environment:
MINIO_ACCESS_KEY: ${MINIO_ACCESS_KEY:?请设置MINIO_ACCESS_KEY}
MINIO_SECRET_KEY: ${MINIO_SECRET_KEY:?请设置MINIO_SECRET_KEY}
volumes:
- seaweedfs_data:/data
networks:
- danyang-net
healthcheck:
# 与本地一致:鉴权开启后匿名列桶返回 403,故 200/403 都算健康。
test: ["CMD-SHELL", "code=$$(curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:8333/); [ \"$$code\" = 200 ] || [ \"$$code\" = 403 ]"]
interval: 5s
timeout: 5s
retries: 24
restart: unless-stopped
storage-init:
# SeaweedFS 不会自动建桶,PutObject 到不存在的 bucket 直接失败。
# 生产同样必须预建 public / private,否则照片与实名材料全部上传不了。
# 走 filer 目录接口,无需签名,复用同一镜像的 curl。幂等:201 新建 / 409 已存在。
image: chrislusf/seaweedfs:4.47
container_name: danyang-match-storage-init
depends_on:
seaweedfs:
condition: service_healthy
entrypoint:
- sh
- -c
- |
for b in public private; do
code=$$(curl -s -o /dev/null -w '%{http_code}' "http://seaweedfs:8888/buckets/$$b/" || echo 000)
if [ "$$code" = "200" ]; then echo "bucket $$b: already exists"; continue; fi
i=0
while [ $$i -lt 30 ]; do
code=$$(curl -s -o /dev/null -w '%{http_code}' -X POST "http://seaweedfs:8888/buckets/$$b/" || echo 000)
case "$$code" in 200|201|409) break ;; esac
i=$$((i+1)); sleep 2
done
echo "bucket $$b: create -> $$code"
done
networks:
- danyang-net
restart: "no"
backend:
build:
context: ../backend
dockerfile: Dockerfile
container_name: danyang-match-backend
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
seaweedfs:
condition: service_healthy
storage-init:
condition: service_completed_successfully
environment:
SPRING_PROFILES_ACTIVE: docker,production
SPRING_CONFIG_IMPORT: configtree:/run/secrets/
DB_HOST: postgres
DB_PORT: 5432
DB_NAME: ${DB_NAME:-danyang_match}
DB_USER: ${DB_USER:-danyang}
REDIS_HOST: redis
REDIS_PORT: 6379
REDIS_PASSWORD: ${REDIS_PASSWORD:?请设置REDIS_PASSWORD}
# 变量名仍为 MINIO_*(服务端配置键未改),实际指向 SeaweedFS 的 S3 网关。
MINIO_ENDPOINT: http://seaweedfs:8333
# 客户端直传 presigned URL 用的「对外可达」地址。
# 生产 S3 网关当前未 host-publish(客户端经 nginx/ingress 访问),故缺省仍是容器名
# —— 即「presigned host 对外可达」缺陷在生产仍待配置 ingress 后才彻底解决。
# 一旦生产 S3 网关暴露了对外地址,把该变量设成那个地址即可,无需再改代码。
MINIO_PUBLIC_ENDPOINT: ${MINIO_PUBLIC_ENDPOINT:-http://seaweedfs:8333}
MINIO_ACCESS_KEY: ${MINIO_ACCESS_KEY:?请设置MINIO_ACCESS_KEY}
MINIO_SECRET_KEY: ${MINIO_SECRET_KEY:?请设置MINIO_SECRET_KEY}
JWT_ACCESS_SECRET: ${JWT_ACCESS_SECRET:?请设置JWT_ACCESS_SECRET}
JWT_REFRESH_SECRET: ${JWT_REFRESH_SECRET:?请设置JWT_REFRESH_SECRET}
CHECKIN_TOKEN_SECRET: ${CHECKIN_TOKEN_SECRET:?请设置CHECKIN_TOKEN_SECRET}
ENCRYPTION_KEY: ${ENCRYPTION_KEY:?请设置ENCRYPTION_KEY}
WECHAT_APP_ID: ${WECHAT_APP_ID:?请设置WECHAT_APP_ID}
WECHAT_APP_SECRET: ${WECHAT_APP_SECRET:?请设置WECHAT_APP_SECRET}
VERIFICATION_PROVIDER: ${VERIFICATION_PROVIDER:?请设置VERIFICATION_PROVIDER}
SPRING_SECURITY_FALLBACK_PASSWORD: ${SPRING_SECURITY_FALLBACK_PASSWORD:?请设置SPRING_SECURITY_FALLBACK_PASSWORD}
FEATURES_APPEAL_ENABLED: ${FEATURES_APPEAL_ENABLED:-false}
ports:
- "127.0.0.1:8080:8080"
networks:
- danyang-net
secrets:
- source: db_password
target: spring.datasource.password
restart: unless-stopped
deploy:
resources:
limits:
memory: 512M
reservations:
memory: 256M
admin-web:
build:
context: ../admin-web
dockerfile: Dockerfile
container_name: danyang-match-admin
depends_on:
- backend
ports:
# 管理端端口统一 13520(与 admin-web/vite.config.ts 的开发端口一致,
# 由 ui-tests/ui-document-baseline-contracts.mjs 钉住三处一致)。
# 仍绑定 127.0.0.1:对外访问走 nginx 的 80/443 + /admin/,此处仅供本机直连排查。
- "127.0.0.1:13520:80"
networks:
- danyang-net
restart: unless-stopped
nginx:
image: nginx:alpine
container_name: danyang-match-nginx
depends_on:
- backend
- admin-web
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx-production.conf:/etc/nginx/nginx.conf:ro
- ./certs:/etc/nginx/certs:ro
networks:
- danyang-net
restart: unless-stopped
volumes:
postgres_data:
redis_data:
seaweedfs_data:
networks:
danyang-net:
driver: bridge
secrets:
db_password:
file: ./secrets/db_password.txt