2026-09-22 10:54:24 +08:00
|
|
|
|
# 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
|
|
|
|
|
|
|
2026-09-22 14:26:48 +08:00
|
|
|
|
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
|
2026-09-22 10:54:24 +08:00
|
|
|
|
environment:
|
2026-09-22 14:26:48 +08:00
|
|
|
|
MINIO_ACCESS_KEY: ${MINIO_ACCESS_KEY:?请设置MINIO_ACCESS_KEY}
|
|
|
|
|
|
MINIO_SECRET_KEY: ${MINIO_SECRET_KEY:?请设置MINIO_SECRET_KEY}
|
2026-09-22 10:54:24 +08:00
|
|
|
|
volumes:
|
2026-09-22 14:26:48 +08:00
|
|
|
|
- seaweedfs_data:/data
|
2026-09-22 10:54:24 +08:00
|
|
|
|
networks:
|
|
|
|
|
|
- danyang-net
|
2026-09-22 14:26:48 +08:00
|
|
|
|
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
|
2026-09-22 10:54:24 +08:00
|
|
|
|
restart: unless-stopped
|
|
|
|
|
|
|
2026-09-22 14:26:48 +08:00
|
|
|
|
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"
|
|
|
|
|
|
|
2026-09-22 10:54:24 +08:00
|
|
|
|
backend:
|
|
|
|
|
|
build:
|
|
|
|
|
|
context: ../backend
|
|
|
|
|
|
dockerfile: Dockerfile
|
|
|
|
|
|
container_name: danyang-match-backend
|
|
|
|
|
|
depends_on:
|
|
|
|
|
|
postgres:
|
|
|
|
|
|
condition: service_healthy
|
|
|
|
|
|
redis:
|
|
|
|
|
|
condition: service_healthy
|
2026-09-22 14:26:48 +08:00
|
|
|
|
seaweedfs:
|
|
|
|
|
|
condition: service_healthy
|
|
|
|
|
|
storage-init:
|
|
|
|
|
|
condition: service_completed_successfully
|
2026-09-22 10:54:24 +08:00
|
|
|
|
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}
|
2026-09-22 14:26:48 +08:00
|
|
|
|
# 变量名仍为 MINIO_*(服务端配置键未改),实际指向 SeaweedFS 的 S3 网关。
|
|
|
|
|
|
MINIO_ENDPOINT: http://seaweedfs:8333
|
2026-09-22 10:54:24 +08:00
|
|
|
|
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:
|
2026-09-22 14:26:48 +08:00
|
|
|
|
seaweedfs_data:
|
2026-09-22 10:54:24 +08:00
|
|
|
|
|
|
|
|
|
|
networks:
|
|
|
|
|
|
danyang-net:
|
|
|
|
|
|
driver: bridge
|
|
|
|
|
|
|
|
|
|
|
|
secrets:
|
|
|
|
|
|
db_password:
|
|
|
|
|
|
file: ./secrets/db_password.txt
|