feat(infra): 新增生产 Dockerfile 与部署 compose
- api-gateway/Dockerfile: 多阶段构建,golang:1.22-alpine → alpine:3.20,CGO_ENABLED=0 静态编译,非 root 运行 - teacher-portal/Dockerfile: 多阶段构建,node:20-alpine builder → runner,含 HEALTHCHECK - docker-compose.prod.yml: 本地生产编排,3 服务,强制 DEV_MODE=false - docker-compose.deploy.yml: 服务器部署用,镜像来自 Gitea Registry,通过 edu-shared 外部网络连接 MySQL/Redis - deploy.env.example: 部署环境变量模板
This commit is contained in:
105
infra/docker-compose.prod.yml
Normal file
105
infra/docker-compose.prod.yml
Normal file
@@ -0,0 +1,105 @@
|
||||
# 生产环境应用服务编排
|
||||
# 仅包含应用服务(api-gateway / classes / teacher-portal),基础设施请使用 docker-compose.yml 或 K8s
|
||||
#
|
||||
# 使用方式:
|
||||
# 1. 先构建镜像:
|
||||
# docker compose -f infra/docker-compose.prod.yml build
|
||||
# 2. 启动(依赖基础设施已运行):
|
||||
# docker compose -f infra/docker-compose.prod.yml up -d
|
||||
# 3. 查看日志:
|
||||
# docker compose -f infra/docker-compose.prod.yml logs -f
|
||||
#
|
||||
# 前置条件:
|
||||
# - MySQL(:3306)、Redis(:6379)已通过 docker-compose.minimal.yml 或外部方式启动
|
||||
# - .env 文件已配置(DEV_MODE 必须为 false)
|
||||
|
||||
name: edu-prod
|
||||
|
||||
services:
|
||||
api-gateway:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: services/api-gateway/Dockerfile
|
||||
image: edu/api-gateway:latest
|
||||
container_name: edu-api-gateway
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
API_GATEWAY_PORT: ${API_GATEWAY_PORT:-8080}
|
||||
JWT_SECRET: ${JWT_SECRET}
|
||||
JWT_ISSUER: ${JWT_ISSUER:-next-edu-cloud}
|
||||
JWT_AUDIENCE: ${JWT_AUDIENCE:-next-edu-cloud}
|
||||
# 生产环境强制关闭 dev-token 旁路
|
||||
DEV_MODE: "false"
|
||||
CLASSES_SERVICE_URL: http://classes:3001
|
||||
IAM_SERVICE_URL: http://iam:3002
|
||||
TEACHER_BFF_URL: http://teacher-bff:3003
|
||||
OTEL_EXPORTER_OTLP_ENDPOINT: ${OTEL_EXPORTER_OTLP_ENDPOINT:-http://otel-collector:4318}
|
||||
LOG_LEVEL: ${LOG_LEVEL:-info}
|
||||
ports:
|
||||
- "${API_GATEWAY_PORT:-8080}:8080"
|
||||
depends_on:
|
||||
classes:
|
||||
condition: service_healthy
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--quiet", "--spider", "http://localhost:8080/healthz"]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
start_period: 10s
|
||||
retries: 3
|
||||
networks:
|
||||
- edu-net
|
||||
|
||||
classes:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: services/classes/Dockerfile
|
||||
image: edu/classes:latest
|
||||
container_name: edu-classes
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
PORT: 3001
|
||||
DATABASE_URL: ${DATABASE_URL}
|
||||
REDIS_URL: ${REDIS_URL}
|
||||
KAFKA_BROKERS: ${KAFKA_BROKERS:-}
|
||||
OTEL_EXPORTER_OTLP_ENDPOINT: ${OTEL_EXPORTER_OTLP_ENDPOINT:-http://otel-collector:4318}
|
||||
LOG_LEVEL: ${LOG_LEVEL:-info}
|
||||
# classes 连接宿主机 MySQL/Redis 时,host.docker.internal 在 Docker Desktop 上可用
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--quiet", "--spider", "http://localhost:3001/healthz"]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
start_period: 30s
|
||||
retries: 5
|
||||
networks:
|
||||
- edu-net
|
||||
|
||||
teacher-portal:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: apps/teacher-portal/Dockerfile
|
||||
image: edu/teacher-portal:latest
|
||||
container_name: edu-teacher-portal
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
NODE_ENV: production
|
||||
PORT: 3000
|
||||
API_GATEWAY_URL: http://api-gateway:8080
|
||||
ports:
|
||||
- "${TEACHER_PORTAL_PORT:-3000}:3000"
|
||||
depends_on:
|
||||
api-gateway:
|
||||
condition: service_healthy
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--quiet", "--spider", "http://localhost:3000/"]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
start_period: 20s
|
||||
retries: 3
|
||||
networks:
|
||||
- edu-net
|
||||
|
||||
networks:
|
||||
edu-net:
|
||||
driver: bridge
|
||||
Reference in New Issue
Block a user