feat(deploy): add Ubuntu 26.04 production package
This commit is contained in:
@@ -0,0 +1,223 @@
|
||||
name: lehuo-production
|
||||
|
||||
x-api-environment: &api-environment
|
||||
APP_ENV: production
|
||||
API_PORT: '3000'
|
||||
DATABASE_URL: ${DATABASE_URL:?DATABASE_URL is required}
|
||||
REDIS_URL: ${REDIS_URL:?REDIS_URL is required}
|
||||
SESSION_SECRET: ${SESSION_SECRET:?SESSION_SECRET is required}
|
||||
SESSION_TOKEN_PEPPER: ${SESSION_TOKEN_PEPPER:?SESSION_TOKEN_PEPPER is required}
|
||||
AUDIT_IP_HASH_KEY: ${AUDIT_IP_HASH_KEY:?AUDIT_IP_HASH_KEY is required}
|
||||
FIELD_ENCRYPTION_KEY: ${FIELD_ENCRYPTION_KEY:?FIELD_ENCRYPTION_KEY is required}
|
||||
ADMIN_WEB_ORIGIN: ${ADMIN_WEB_ORIGIN:?ADMIN_WEB_ORIGIN is required}
|
||||
ADMIN_COOKIE_SECURE: 'true'
|
||||
AUTH_SESSION_TTL_SECONDS: ${AUTH_SESSION_TTL_SECONDS:-28800}
|
||||
WECHAT_SESSION_TTL_SECONDS: ${WECHAT_SESSION_TTL_SECONDS:-7200}
|
||||
WECHAT_APP_ID: ${WECHAT_APP_ID:-}
|
||||
WECHAT_APP_SECRET: ${WECHAT_APP_SECRET:-}
|
||||
WECHAT_TEMPLATE_APPLICATION_SUBMITTED: ${WECHAT_TEMPLATE_APPLICATION_SUBMITTED:-}
|
||||
WECHAT_TEMPLATE_APPLICATION_ACCEPTED: ${WECHAT_TEMPLATE_APPLICATION_ACCEPTED:-}
|
||||
WECHAT_TEMPLATE_SERVICE_ARRANGED: ${WECHAT_TEMPLATE_SERVICE_ARRANGED:-}
|
||||
WECHAT_TEMPLATE_SERVICE_COMPLETED: ${WECHAT_TEMPLATE_SERVICE_COMPLETED:-}
|
||||
WECHAT_TEMPLATE_FOLLOW_UP_PENDING: ${WECHAT_TEMPLATE_FOLLOW_UP_PENDING:-}
|
||||
WECHAT_TEMPLATE_ACTIVITY_REGISTRATION_RESULT: ${WECHAT_TEMPLATE_ACTIVITY_REGISTRATION_RESULT:-}
|
||||
NOTIFICATION_WORKER_ENABLED: ${NOTIFICATION_WORKER_ENABLED:-false}
|
||||
OBJECT_STORAGE_ENDPOINT: ${OBJECT_STORAGE_ENDPOINT:?OBJECT_STORAGE_ENDPOINT is required}
|
||||
OBJECT_STORAGE_REGION: ${OBJECT_STORAGE_REGION:-auto}
|
||||
OBJECT_STORAGE_BUCKET: ${OBJECT_STORAGE_BUCKET:?OBJECT_STORAGE_BUCKET is required}
|
||||
OBJECT_STORAGE_ACCESS_KEY: ${OBJECT_STORAGE_ACCESS_KEY:?OBJECT_STORAGE_ACCESS_KEY is required}
|
||||
OBJECT_STORAGE_SECRET_KEY: ${OBJECT_STORAGE_SECRET_KEY:?OBJECT_STORAGE_SECRET_KEY is required}
|
||||
OBJECT_STORAGE_FORCE_PATH_STYLE: 'true'
|
||||
|
||||
services:
|
||||
mysql:
|
||||
image: mysql:8.4.8
|
||||
restart: unless-stopped
|
||||
command:
|
||||
- --character-set-server=utf8mb4
|
||||
- --collation-server=utf8mb4_0900_ai_ci
|
||||
environment:
|
||||
MYSQL_DATABASE: ${MYSQL_DATABASE:?MYSQL_DATABASE is required}
|
||||
MYSQL_USER: ${MYSQL_USER:?MYSQL_USER is required}
|
||||
MYSQL_PASSWORD: ${MYSQL_PASSWORD:?MYSQL_PASSWORD is required}
|
||||
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:?MYSQL_ROOT_PASSWORD is required}
|
||||
TZ: Asia/Shanghai
|
||||
volumes:
|
||||
- mysql-data:/var/lib/mysql
|
||||
networks:
|
||||
- backend
|
||||
healthcheck:
|
||||
test:
|
||||
- CMD-SHELL
|
||||
- MYSQL_PWD=$$MYSQL_ROOT_PASSWORD mysqladmin ping -h 127.0.0.1 -u root --silent
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 12
|
||||
start_period: 40s
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
|
||||
redis:
|
||||
image: redis:7.4-alpine
|
||||
restart: unless-stopped
|
||||
command:
|
||||
- redis-server
|
||||
- --appendonly
|
||||
- 'yes'
|
||||
- --requirepass
|
||||
- ${REDIS_PASSWORD:?REDIS_PASSWORD is required}
|
||||
volumes:
|
||||
- redis-data:/data
|
||||
networks:
|
||||
- backend
|
||||
healthcheck:
|
||||
test: [CMD-SHELL, 'redis-cli -a "$$REDIS_PASSWORD" ping | grep -q PONG']
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 8
|
||||
environment:
|
||||
REDIS_PASSWORD: ${REDIS_PASSWORD:?REDIS_PASSWORD is required}
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
|
||||
minio:
|
||||
image: minio/minio:RELEASE.2025-09-07T16-13-09Z
|
||||
restart: unless-stopped
|
||||
command: server /data
|
||||
environment:
|
||||
MINIO_ROOT_USER: ${MINIO_ROOT_USER:?MINIO_ROOT_USER is required}
|
||||
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:?MINIO_ROOT_PASSWORD is required}
|
||||
MINIO_BROWSER: 'off'
|
||||
MINIO_SERVER_URL: https://${FILES_HOST:?FILES_HOST is required}
|
||||
volumes:
|
||||
- minio-data:/data
|
||||
networks:
|
||||
- backend
|
||||
healthcheck:
|
||||
test: [CMD, curl, -f, http://localhost:9000/minio/health/live]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 8
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
|
||||
minio-init:
|
||||
image: minio/mc:RELEASE.2025-08-13T08-35-41Z
|
||||
restart: 'no'
|
||||
entrypoint: [/bin/sh, /bootstrap/init-minio.sh]
|
||||
environment:
|
||||
MINIO_ROOT_USER: ${MINIO_ROOT_USER:?MINIO_ROOT_USER is required}
|
||||
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:?MINIO_ROOT_PASSWORD is required}
|
||||
OBJECT_STORAGE_BUCKET: ${OBJECT_STORAGE_BUCKET:?OBJECT_STORAGE_BUCKET is required}
|
||||
ADMIN_WEB_ORIGIN: ${ADMIN_WEB_ORIGIN:?ADMIN_WEB_ORIGIN is required}
|
||||
volumes:
|
||||
- ./minio/init-minio.sh:/bootstrap/init-minio.sh:ro
|
||||
networks:
|
||||
- backend
|
||||
depends_on:
|
||||
minio:
|
||||
condition: service_healthy
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
|
||||
api:
|
||||
image: lehuo-api:${IMAGE_TAG:?IMAGE_TAG is required}
|
||||
build:
|
||||
context: ../..
|
||||
dockerfile: apps/api/Dockerfile
|
||||
restart: unless-stopped
|
||||
environment: *api-environment
|
||||
extra_hosts:
|
||||
- ${FILES_HOST:?FILES_HOST is required}:host-gateway
|
||||
expose:
|
||||
- '3000'
|
||||
networks:
|
||||
- backend
|
||||
depends_on:
|
||||
mysql:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
minio-init:
|
||||
condition: service_completed_successfully
|
||||
healthcheck:
|
||||
test:
|
||||
- CMD
|
||||
- node
|
||||
- -e
|
||||
- fetch('http://127.0.0.1:3000/health/live').then(r=>{if(!r.ok)process.exit(1)}).catch(()=>process.exit(1))
|
||||
interval: 15s
|
||||
timeout: 5s
|
||||
retries: 8
|
||||
start_period: 30s
|
||||
read_only: true
|
||||
tmpfs:
|
||||
- /tmp:size=64m,noexec,nosuid
|
||||
cap_drop:
|
||||
- ALL
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
|
||||
migrate:
|
||||
image: lehuo-api:${IMAGE_TAG:?IMAGE_TAG is required}
|
||||
profiles: [tools]
|
||||
command:
|
||||
- ./apps/api/node_modules/.bin/prisma
|
||||
- migrate
|
||||
- deploy
|
||||
- --config
|
||||
- apps/api/prisma.config.ts
|
||||
environment:
|
||||
DATABASE_URL: ${DATABASE_URL:?DATABASE_URL is required}
|
||||
networks:
|
||||
- backend
|
||||
depends_on:
|
||||
mysql:
|
||||
condition: service_healthy
|
||||
read_only: true
|
||||
tmpfs:
|
||||
- /tmp:size=64m,noexec,nosuid
|
||||
cap_drop:
|
||||
- ALL
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
|
||||
web:
|
||||
image: lehuo-admin:${IMAGE_TAG:?IMAGE_TAG is required}
|
||||
build:
|
||||
context: ../..
|
||||
dockerfile: apps/admin-web/Dockerfile
|
||||
args:
|
||||
VITE_API_BASE_URL: https://${PUBLIC_HOST:?PUBLIC_HOST is required}
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
PUBLIC_HOST: ${PUBLIC_HOST:?PUBLIC_HOST is required}
|
||||
FILES_HOST: ${FILES_HOST:?FILES_HOST is required}
|
||||
NGINX_ENVSUBST_FILTER: ^(PUBLIC_HOST|FILES_HOST)$$
|
||||
ports:
|
||||
- '80:80'
|
||||
- '443:443'
|
||||
volumes:
|
||||
- ${TLS_CERT_PATH:?TLS_CERT_PATH is required}:/etc/nginx/tls/fullchain.pem:ro
|
||||
- ${TLS_KEY_PATH:?TLS_KEY_PATH is required}:/etc/nginx/tls/privkey.pem:ro
|
||||
networks:
|
||||
- backend
|
||||
- edge
|
||||
depends_on:
|
||||
api:
|
||||
condition: service_healthy
|
||||
read_only: true
|
||||
tmpfs:
|
||||
- /var/cache/nginx:size=32m,noexec,nosuid
|
||||
- /var/run:size=4m,noexec,nosuid
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
|
||||
networks:
|
||||
backend:
|
||||
edge:
|
||||
|
||||
volumes:
|
||||
mysql-data:
|
||||
minio-data:
|
||||
redis-data:
|
||||
Reference in New Issue
Block a user