Files
Edu/infra/docker-compose.dev-apps.yml
SpecialX 682f323bad chore(infra): docker dev-apps stack + apollo-router v1.45 compat
- 新增 docker-compose.dev-apps.yml:7 个应用服务容器化
  (iam/classes/core-edu/content/msg/push-gateway/api-gateway)
  + apollo-router override
- 新增 apollo-router dev-apps-entrypoint.sh / dev-apps-supergraph.yaml
  仅等待 5 个子图(iam/core-edu/content/msg/config-service)
  避免等待未启动的 ai/data-ana
- router.yaml:移除 v1.45 不兼容键
  (require_manifest/manifest_path/max_cost/max_batch_size)
  移除 ELv2 受限特性(persisted_queries/limits.max_depth)
  自托管无 GraphOS license
- docker-compose.yml:config-service 添加 NODE_ENV=production
  修复 pino-pretty devDep 误用
  apollo-router 添加 APOLLO_PERSISTED_QUERIES env
- services/{classes,core-edu,content,config-service}/Dockerfile
  重写为 repo 根 context + shared-ts/shared-proto 工作区依赖
  + --ignore-scripts
  修复 ERR_PNPM_WORKSPACE_PKG_NOT_FOUND 与 ERR_PNPM_IGNORED_BUILDS
- api-gateway healthcheck:wget --spider 改为 wget -O /dev/null
  修复 busybox 对 200+body 误报 exit 8
2026-07-22 11:35:02 +08:00

327 lines
12 KiB
YAML
Raw Permalink 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.
# ============================================================
# Docker Compose Override本地开发态应用服务v2.1
#
# 用途:将 8 个应用服务iam/classes/core-edu/content/msg/api-gateway/push-gateway/iam也以容器方式运行
# 与 infra/docker-compose.yml 中的基础设施MySQL/Redis/Kafka/Apollo Router/portal-shell共用 default 网络。
#
# 启动方式:
# docker compose -f infra/docker-compose.yml -f infra/docker-compose.dev-apps.yml --profile p3 up -d
#
# 与 infra/docker-compose.yml 的关系:
# - 同 name: edu-full共用 project 命名空间与 default 网络)
# - 应用服务通过容器名edu-mysql/edu-redis/edu-kafka访问基础设施
# - apollo-router 已在 infra/docker-compose.yml 定义,此处不重复
# - config-service 已在 infra/docker-compose.yml 定义,此处不重复
#
# 注意:本文件只补充 docker-compose.yml 中缺失的 7 个应用服务(不含已定义的 config-service/apollo-router/portal-shell
# ============================================================
name: edu-full
services:
# ============================================================
# IAM 服务NestJS端口 3002 + gRPC 50052
# 依赖MySQL用户/角色/权限表、Redis缓存、KafkaOutbox 事件)
# RS256 密钥:由 infra/keys/iam-{private,public}.pem 挂载(首次启动需生成,见 scripts/gen-iam-keys.ps1
# ============================================================
iam:
build:
context: ..
dockerfile: services/iam/Dockerfile
container_name: edu-iam
restart: unless-stopped
environment:
PORT: "3002"
GRPC_PORT: "50052"
DATABASE_URL: mysql://edu:${MYSQL_PASSWORD:-changeme}@edu-mysql:3306/next_edu_cloud
REDIS_URL: redis://edu-redis:6379
KAFKA_BROKERS: kafka:29092
KAFKA_CLIENT_ID: iam-service
IAM_PRIVATE_KEY_PATH: /keys/iam-private.pem
IAM_PUBLIC_KEY_PATH: /keys/iam-public.pem
JWT_ISSUER: ${JWT_ISSUER:-next-edu-cloud}
JWT_AUDIENCE: ${JWT_AUDIENCE:-next-edu-cloud}
JWT_KEY_ID: iam-rs256-v1
ACCESS_TOKEN_TTL: "15m"
REFRESH_TOKEN_TTL_DAYS: "7"
COOKIE_SECURE: "auto"
OTEL_EXPORTER_OTLP_ENDPOINT: ${OTEL_EXPORTER_OTLP_ENDPOINT:-http://edu-jaeger:4318}
LOG_LEVEL: ${LOG_LEVEL:-info}
NODE_ENV: production
# DEV_MODE=true 让子图 middleware 跳过鉴权,允许 apollo-router rover introspect 子图
# 外网请求经 api-gatewayDEV_MODE=falseRS256 验签)入站,子图在 docker 内网可信
DEV_MODE: "true"
ports:
- "3002:3002"
- "50052:50052"
volumes:
- ../infra/keys:/keys:ro
depends_on:
mysql:
condition: service_healthy
redis:
condition: service_started
healthcheck:
test: ["CMD", "wget", "--quiet", "--spider", "http://localhost:3002/healthz"]
interval: 15s
timeout: 5s
start_period: 30s
retries: 10
# ============================================================
# Classes 服务NestJS端口 3001
# 依赖MySQL、Redis、Kafka可选
# ============================================================
classes:
build:
context: ..
dockerfile: services/classes/Dockerfile
container_name: edu-classes
restart: unless-stopped
environment:
PORT: "3001"
DATABASE_URL: mysql://edu:${MYSQL_PASSWORD:-changeme}@edu-mysql:3306/next_edu_cloud
REDIS_URL: redis://edu-redis:6379
KAFKA_BROKERS: kafka:29092
JWT_SECRET: ${JWT_SECRET:-p1-dev-secret-change-in-production}
JWT_ISSUER: ${JWT_ISSUER:-next-edu-cloud}
OTEL_EXPORTER_OTLP_ENDPOINT: ${OTEL_EXPORTER_OTLP_ENDPOINT:-http://edu-jaeger:4318}
LOG_LEVEL: ${LOG_LEVEL:-info}
NODE_ENV: production
# DEV_MODE=true 让子图 middleware 跳过鉴权,允许 apollo-router rover introspect 子图
DEV_MODE: "true"
ports:
- "3001:3001"
depends_on:
mysql:
condition: service_healthy
redis:
condition: service_started
healthcheck:
test: ["CMD", "wget", "--quiet", "--spider", "http://localhost:3001/healthz"]
interval: 15s
timeout: 5s
start_period: 30s
retries: 10
# ============================================================
# Core-Edu 服务NestJS端口 3004 + gRPC 50054
# 依赖MySQL、Redis、KafkaOutbox 事件)
# ============================================================
core-edu:
build:
context: ..
dockerfile: services/core-edu/Dockerfile
container_name: edu-core-edu
restart: unless-stopped
environment:
PORT: "3004"
GRPC_PORT: "50054"
DATABASE_URL: mysql://edu:${MYSQL_PASSWORD:-changeme}@edu-mysql:3306/next_edu_cloud
REDIS_URL: redis://edu-redis:6379
KAFKA_BROKERS: kafka:29092
OTEL_EXPORTER_OTLP_ENDPOINT: ${OTEL_EXPORTER_OTLP_ENDPOINT:-http://edu-jaeger:4318}
LOG_LEVEL: ${LOG_LEVEL:-info}
NODE_ENV: production
# DEV_MODE=true 让子图 middleware 跳过鉴权,允许 apollo-router rover introspect 子图
DEV_MODE: "true"
ports:
- "3004:3004"
- "50054:50054"
depends_on:
mysql:
condition: service_healthy
redis:
condition: service_started
healthcheck:
test: ["CMD", "wget", "--quiet", "--spider", "http://localhost:3004/healthz"]
interval: 15s
timeout: 5s
start_period: 30s
retries: 10
# ============================================================
# Content 服务NestJS端口 3005 + gRPC 50055
# 依赖MySQL、Redis、KafkaOutbox、Neo4j可选降级、ES可选降级
# ============================================================
content:
build:
context: ..
dockerfile: services/content/Dockerfile
container_name: edu-content
restart: unless-stopped
environment:
PORT: "3005"
GRPC_PORT: "50055"
DATABASE_URL: mysql://edu:${MYSQL_PASSWORD:-changeme}@edu-mysql:3306/next_edu_cloud
REDIS_URL: redis://edu-redis:6379
KAFKA_BROKERS: kafka:29092
NEO4J_URL: bolt://neo4j:7687
NEO4J_USER: neo4j
NEO4J_PASSWORD: ${NEO4J_PASSWORD:-changeme}
ES_URL: http://elasticsearch:9200
OTEL_EXPORTER_OTLP_ENDPOINT: ${OTEL_EXPORTER_OTLP_ENDPOINT:-http://edu-jaeger:4318}
LOG_LEVEL: ${LOG_LEVEL:-info}
NODE_ENV: production
# DEV_MODE=true 让子图 middleware 跳过鉴权,允许 apollo-router rover introspect 子图
DEV_MODE: "true"
ports:
- "3005:3005"
- "50055:50055"
depends_on:
mysql:
condition: service_healthy
redis:
condition: service_started
healthcheck:
test: ["CMD", "wget", "--quiet", "--spider", "http://localhost:3005/healthz"]
interval: 15s
timeout: 5s
start_period: 30s
retries: 10
# ============================================================
# Msg 服务NestJS端口 3007 + gRPC 50057
# 依赖MySQL、Redis、Kafka、ES可选、push-gatewayHTTP /internal/push
# ============================================================
msg:
build:
context: ..
dockerfile: services/msg/Dockerfile
container_name: edu-msg
restart: unless-stopped
environment:
PORT: "3007"
GRPC_PORT: "50057"
DATABASE_URL: mysql://edu:${MYSQL_PASSWORD:-changeme}@edu-mysql:3306/next_edu_cloud
REDIS_URL: redis://edu-redis:6379
KAFKA_BROKERS: kafka:29092
ES_URL: http://elasticsearch:9200
PUSH_GATEWAY_URL: http://push-gateway:8081
PUSH_INTERNAL_TOKEN: ${PUSH_INTERNAL_TOKEN:-edu-internal-token}
OTEL_EXPORTER_OTLP_ENDPOINT: ${OTEL_EXPORTER_OTLP_ENDPOINT:-http://edu-jaeger:4318}
LOG_LEVEL: ${LOG_LEVEL:-info}
NODE_ENV: production
# DEV_MODE=true 让子图 middleware 跳过鉴权,允许 apollo-router rover introspect 子图
DEV_MODE: "true"
ports:
- "3007:3007"
- "50057:50057"
depends_on:
mysql:
condition: service_healthy
redis:
condition: service_started
push-gateway:
condition: service_healthy
healthcheck:
test: ["CMD", "wget", "--quiet", "--spider", "http://localhost:3007/healthz"]
interval: 15s
timeout: 5s
start_period: 30s
retries: 10
# ============================================================
# Push-Gateway 服务Go端口 8081
# 依赖RedisPub/Sub backplane、JWKSiam 公钥验签)
# ============================================================
push-gateway:
build:
context: ..
dockerfile: services/push-gateway/Dockerfile
container_name: edu-push-gateway
restart: unless-stopped
environment:
PUSH_GATEWAY_PORT: "8081"
JWT_SECRET: ${JWT_SECRET:-p1-dev-secret-change-in-production}
REDIS_URL: redis://edu-redis:6379
PUSH_INTERNAL_TOKEN: ${PUSH_INTERNAL_TOKEN:-edu-internal-token}
JWKS_URL: http://iam:3002/v1/iam/.well-known/jwks.json
WS_ALLOWED_ORIGINS: ${WS_ALLOWED_ORIGINS:-http://localhost:3000,http://localhost:4000,http://localhost:4001,http://localhost:4002,http://localhost:4003,http://localhost:4010}
MAX_CONNS_PER_USER: "5"
HEARTBEAT_INTERVAL_SECONDS: "30"
OTEL_EXPORTER_OTLP_ENDPOINT: ${OTEL_EXPORTER_OTLP_ENDPOINT:-http://edu-jaeger:4318}
DEV_MODE: "false"
ports:
- "8081:8081"
depends_on:
redis:
condition: service_started
healthcheck:
test: ["CMD", "wget", "-q", "-O", "/dev/null", "http://localhost:8081/healthz"]
interval: 15s
timeout: 5s
start_period: 10s
retries: 5
# ============================================================
# API-Gateway 服务Go端口 8080
# 依赖iamJWKS、apollo-router、所有后端服务HTTP 路由代理)
# ============================================================
api-gateway:
build:
context: ..
dockerfile: services/api-gateway/Dockerfile
container_name: edu-api-gateway
restart: unless-stopped
environment:
API_GATEWAY_PORT: "8080"
ENV: production
JWT_SECRET: ${JWT_SECRET:-p1-dev-secret-change-in-production}
JWT_ISSUER: ${JWT_ISSUER:-next-edu-cloud}
JWT_AUDIENCE: ${JWT_AUDIENCE:-next-edu-cloud}
IAM_JWKS_URL: http://iam:3002/v1/iam/.well-known/jwks.json
DEV_MODE: "false"
CLASSES_SERVICE_URL: http://classes:3001
IAM_SERVICE_URL: http://iam:3002
APOLLO_ROUTER_URL: http://apollo-router:3000
CORE_EDU_SERVICE_URL: http://core-edu:3004
CONTENT_SERVICE_URL: http://content:3005
MSG_SERVICE_URL: http://msg:3007
CORS_ORIGINS: ${CORS_ORIGINS:-http://localhost:3000,http://localhost:4000,http://localhost:4001,http://localhost:4002,http://localhost:4003,http://localhost:4010}
OTEL_EXPORTER_OTLP_ENDPOINT: ${OTEL_EXPORTER_OTLP_ENDPOINT:-http://edu-jaeger:4318}
LOG_LEVEL: ${LOG_LEVEL:-info}
ports:
- "8080:8080"
depends_on:
iam:
condition: service_healthy
apollo-router:
condition: service_healthy
healthcheck:
test: ["CMD", "wget", "-q", "-O", "/dev/null", "http://localhost:8080/healthz"]
interval: 15s
timeout: 5s
start_period: 10s
retries: 5
# ============================================================
# Apollo Router override覆盖 docker-compose.yml 中的同名服务)
#
# 覆盖原因:默认 entrypoint.sh 会等待 ai/data-ana 子图(用户未启动),
# 默认 supergraph.yaml 包含 ai/data-ana 子图rover compose 会失败)。
#
# 覆盖内容:
# - 挂载 dev-apps-supergraph.yaml仅 iam/core-edu/content/msg/config 5 个子图)
# - 挂载 dev-apps-entrypoint.sh仅等待 5 个子图)
# - 添加 depends_on 5 个子图health check 通过后才启动 router
# ============================================================
apollo-router:
volumes:
# 追加覆盖默认 supergraph.yaml 与 entrypoint.sh
- ./apollo-router/dev-apps-supergraph.yaml:/dist/supergraph.yaml:ro
- ./apollo-router/dev-apps-entrypoint.sh:/dist/entrypoint.sh:ro
# 保留原 pq-manifest 挂载(来自 docker-compose.yml
- ../apps/portal-shell/public/pq-manifest.json:/etc/apollo-router/pq-manifest.json:ro
depends_on:
iam:
condition: service_healthy
config-service:
condition: service_healthy
core-edu:
condition: service_healthy
content:
condition: service_healthy
msg:
condition: service_healthy