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
This commit is contained in:
@@ -26,13 +26,14 @@ RUN curl -sSL -o /tmp/rover.tar.gz https://github.com/apollographql/rover/releas
|
||||
# 预下载 supergraph 插件(federation-rs supergraph v2.9.0)
|
||||
# 避免运行时因网络问题(如 GitHub 不可达)导致 rover compose 失败
|
||||
# 插件会被缓存到 /root/.rover/bin/supergraph-v2.9.0
|
||||
# tarball 内文件路径为 dist/supergraph,需 --strip-components=1 解包
|
||||
RUN APOLLO_ELV2_LICENSE=accept rover supergraph compose --config /dev/null --output /tmp/test-supergraph.graphql 2>&1 || \
|
||||
(echo "[build] Pre-downloading supergraph plugin..." && \
|
||||
curl -sSL -o /tmp/supergraph-plugin.tar.gz \
|
||||
"https://github.com/apollographql/federation-rs/releases/download/supergraph%40v2.9.0/supergraph-v2.9.0-x86_64-unknown-linux-gnu.tar.gz" && \
|
||||
mkdir -p /root/.rover/bin && \
|
||||
tar -xzf /tmp/supergraph-plugin.tar.gz -C /root/.rover/bin/ && \
|
||||
mv /root/.rover/bin/supergraph /root/.rover/bin/supergraph-v2.9.0 2>/dev/null || true && \
|
||||
tar -xzf /tmp/supergraph-plugin.tar.gz --strip-components=1 -C /root/.rover/bin/ dist/supergraph && \
|
||||
mv /root/.rover/bin/supergraph /root/.rover/bin/supergraph-v2.9.0 && \
|
||||
chmod +x /root/.rover/bin/supergraph-v2.9.0 && \
|
||||
rm -f /tmp/supergraph-plugin.tar.gz /tmp/test-supergraph.graphql && \
|
||||
echo "[build] Supergraph plugin pre-installed")
|
||||
|
||||
92
infra/apollo-router/dev-apps-entrypoint.sh
Normal file
92
infra/apollo-router/dev-apps-entrypoint.sh
Normal file
@@ -0,0 +1,92 @@
|
||||
#!/bin/bash
|
||||
# Apollo Router 启动脚本(dev-apps 模式:仅 5 个 TS 子图)
|
||||
#
|
||||
# 与 entrypoint.sh 的差异:
|
||||
# - SUBGRAPHS 仅包含 iam/core-edu/content/msg/config-service(排除 ai/data-ana)
|
||||
# - 依赖 docker compose depends_on 已确保子图健康,等待重试次数减少到 30
|
||||
#
|
||||
# 使用:通过 docker-compose.dev-apps.yml 卷挂载覆盖 /dist/entrypoint.sh
|
||||
|
||||
set -e
|
||||
|
||||
SUBGRAPHS=(
|
||||
"iam:iam:3002"
|
||||
"core-edu:core-edu:3004"
|
||||
"content:content:3005"
|
||||
"msg:msg:3007"
|
||||
"config-service:config-service:3011"
|
||||
)
|
||||
|
||||
echo "[apollo-router:dev-apps] Waiting for subgraphs to be ready..."
|
||||
|
||||
wait_for_subgraph() {
|
||||
local name="$1"
|
||||
local host="$2"
|
||||
local port="$3"
|
||||
local max_retries=30
|
||||
local retry=0
|
||||
|
||||
while [ $retry -lt $max_retries ]; do
|
||||
if curl -sf -o /dev/null "http://${host}:${port}/healthz" 2>/dev/null; then
|
||||
echo "[apollo-router:dev-apps] ${name} is ready (http://${host}:${port})"
|
||||
return 0
|
||||
fi
|
||||
retry=$((retry + 1))
|
||||
echo "[apollo-router:dev-apps] Waiting for ${name} at ${host}:${port} (attempt ${retry}/${max_retries})"
|
||||
sleep 3
|
||||
done
|
||||
|
||||
echo "[apollo-router:dev-apps] WARNING: ${name} not ready after ${max_retries} attempts, continuing anyway..."
|
||||
return 0
|
||||
}
|
||||
|
||||
for entry in "${SUBGRAPHS[@]}"; do
|
||||
name="${entry%%:*}"
|
||||
hostport="${entry#*:}"
|
||||
host="${hostport%%:*}"
|
||||
port="${hostport##*:}"
|
||||
wait_for_subgraph "$name" "$host" "$port"
|
||||
done
|
||||
|
||||
# 组合 supergraph SDL
|
||||
echo "[apollo-router:dev-apps] Composing supergraph SDL..."
|
||||
export APOLLO_ELV2_LICENSE=accept
|
||||
|
||||
max_compose_retries=5
|
||||
compose_retry=0
|
||||
while [ $compose_retry -lt $max_compose_retries ]; do
|
||||
compose_retry=$((compose_retry + 1))
|
||||
if rover supergraph compose --config /dist/supergraph.yaml --output /tmp/supergraph.graphql 2>&1; then
|
||||
echo "[apollo-router:dev-apps] Supergraph composed successfully"
|
||||
break
|
||||
fi
|
||||
echo "[apollo-router:dev-apps] Compose attempt ${compose_retry}/${max_compose_retries} failed, retrying in 5s..."
|
||||
sleep 5
|
||||
done
|
||||
|
||||
if [ ! -f /tmp/supergraph.graphql ]; then
|
||||
echo "[apollo-router:dev-apps] ERROR: Failed to compose supergraph after ${max_compose_retries} attempts"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# PQ manifest 检查(dev 模式 require_manifest=false,仅警告)
|
||||
MANIFEST_PATH="/etc/apollo-router/pq-manifest.json"
|
||||
REQUIRE_MANIFEST="${APOLLO_REQUIRE_PQ_MANIFEST:-false}"
|
||||
|
||||
if [ "${REQUIRE_MANIFEST}" = "true" ]; then
|
||||
if [ ! -f "${MANIFEST_PATH}" ]; then
|
||||
echo "[apollo-router:dev-apps] ERROR: APOLLO_REQUIRE_PQ_MANIFEST=true but manifest not found at ${MANIFEST_PATH}"
|
||||
exit 1
|
||||
fi
|
||||
echo "[apollo-router:dev-apps] PQ manifest loaded (require_manifest=true): ${MANIFEST_PATH}"
|
||||
else
|
||||
if [ ! -f "${MANIFEST_PATH}" ]; then
|
||||
echo "[apollo-router:dev-apps] WARNING: PQ manifest not found at ${MANIFEST_PATH} (require_manifest=false, continuing)"
|
||||
else
|
||||
echo "[apollo-router:dev-apps] PQ manifest loaded (require_manifest=false): ${MANIFEST_PATH}"
|
||||
fi
|
||||
fi
|
||||
|
||||
# 启动 router
|
||||
echo "[apollo-router:dev-apps] Starting Apollo Router on port 3000..."
|
||||
exec /dist/router --config /dist/configuration.yaml --supergraph /tmp/supergraph.graphql --hot-reload
|
||||
36
infra/apollo-router/dev-apps-supergraph.yaml
Normal file
36
infra/apollo-router/dev-apps-supergraph.yaml
Normal file
@@ -0,0 +1,36 @@
|
||||
# Apollo Federation 2 Supergraph 组合配置(dev-apps 模式:仅启动 5 个 TS 子图)
|
||||
#
|
||||
# 适用场景:本地用 docker compose 启动 iam/classes/core-edu/content/msg/config-service/apollo-router/api-gateway/push-gateway
|
||||
# 不包含:ai(Python)、data-ana(Python)— 用户未要求启动
|
||||
#
|
||||
# 与 supergraph.yaml 的差异:
|
||||
# - 删除 ai / data-ana 子图(避免 rover compose 因它们不可达而失败)
|
||||
# - 所有 routing_url 用容器名(iam:3002 等)而非 host.docker.internal
|
||||
|
||||
federation_version: =2.9.0
|
||||
|
||||
subgraphs:
|
||||
iam:
|
||||
routing_url: http://iam:3002/graphql
|
||||
schema:
|
||||
subgraph_url: http://iam:3002/graphql
|
||||
|
||||
core-edu:
|
||||
routing_url: http://core-edu:3004/graphql
|
||||
schema:
|
||||
subgraph_url: http://core-edu:3004/graphql
|
||||
|
||||
content:
|
||||
routing_url: http://content:3005/graphql
|
||||
schema:
|
||||
subgraph_url: http://content:3005/graphql
|
||||
|
||||
msg:
|
||||
routing_url: http://msg:3007/graphql
|
||||
schema:
|
||||
subgraph_url: http://msg:3007/graphql
|
||||
|
||||
config:
|
||||
routing_url: http://config-service:3011/graphql
|
||||
schema:
|
||||
subgraph_url: http://config-service:3011/graphql
|
||||
@@ -17,28 +17,23 @@ supergraph:
|
||||
listen: 0.0.0.0:3000
|
||||
path: /graphql
|
||||
# 生产关闭 introspection(APOLLO_ROUTER_INTROSPECTION=false)
|
||||
# 开发默认 true 便于调试
|
||||
introspection: ${env.APOLLO_ROUTER_INTROSPECTION::true}
|
||||
# 开发默认 true 便于调试(由 docker-compose environment 注入)
|
||||
introspection: ${env.APOLLO_ROUTER_INTROSPECTION}
|
||||
|
||||
# 持久化查询(v2.1 M3 安全加固)
|
||||
# 关联:portal-shell spec §4.3
|
||||
# - enabled: 启用 APQ(接受 hash-only 请求)
|
||||
# - require_manifest: 生产设为 true,仅接受 manifest 内的 hash
|
||||
# - manifest_path: manifest 文件路径(由 portal-shell 构建产物挂载)
|
||||
persisted_queries:
|
||||
enabled: true
|
||||
require_manifest: ${env.APOLLO_REQUIRE_PQ_MANIFEST::false}
|
||||
manifest_path: /etc/apollo-router/pq-manifest.json
|
||||
# 注意:Apollo Router v1.45 的 persisted_queries 是 ELv2 受限特性,
|
||||
# 需连接 GraphOS(APOLLO_KEY + APOLLO_GRAPH_REF)才能启用。
|
||||
# 自托管开发态禁用此特性;生产环境如需启用,需配置 GraphOS license。
|
||||
# persisted_queries:
|
||||
# enabled: false
|
||||
|
||||
# 查询限制(v2.1 M3 安全加固)
|
||||
# 关联:portal-shell spec §5.1
|
||||
# - max_depth: 最大嵌套深度 10(防止递归攻击)
|
||||
# - max_cost: 最大查询复杂度 1000(防止放大攻击)
|
||||
# - max_batch_size: 批量查询上限 5(防止 batch 攻击)
|
||||
limits:
|
||||
max_depth: 10
|
||||
max_cost: 1000
|
||||
max_batch_size: 5
|
||||
# 注意:Apollo Router v1.45 的 limits.max_depth 是 ELv2 受限特性,
|
||||
# 需连接 GraphOS 才能启用。自托管开发态禁用;
|
||||
# 生产环境如需深度限制,可使用 rhai script 或 custom plugin 替代。
|
||||
# limits:
|
||||
# max_depth: 10
|
||||
|
||||
# Sandbox 模式:支持开发和调试(v1.45 中 sandbox 已合并到 homepage)
|
||||
homepage:
|
||||
|
||||
326
infra/docker-compose.dev-apps.yml
Normal file
326
infra/docker-compose.dev-apps.yml
Normal file
@@ -0,0 +1,326 @@
|
||||
# ============================================================
|
||||
# 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(缓存)、Kafka(Outbox 事件)
|
||||
# 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-gateway(DEV_MODE=false,RS256 验签)入站,子图在 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、Kafka(Outbox 事件)
|
||||
# ============================================================
|
||||
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、Kafka(Outbox)、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-gateway(HTTP /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)
|
||||
# 依赖:Redis(Pub/Sub backplane)、JWKS(iam 公钥验签)
|
||||
# ============================================================
|
||||
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)
|
||||
# 依赖:iam(JWKS)、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
|
||||
@@ -306,7 +306,9 @@ services:
|
||||
REDIS_URL: redis://edu-redis:6379
|
||||
KAFKA_BROKERS: kafka:29092
|
||||
ROUTER_AUTH_SECRET: ${ROUTER_AUTH_SECRET:-dev-router-secret}
|
||||
DEV_MODE: ${DEV_MODE:-false}
|
||||
# DEV_MODE=true 让子图 middleware 跳过鉴权,允许 apollo-router rover introspect 子图
|
||||
DEV_MODE: "true"
|
||||
NODE_ENV: production
|
||||
PORT: "3011"
|
||||
GRPC_PORT: "50059"
|
||||
ports:
|
||||
@@ -334,6 +336,8 @@ services:
|
||||
# v2.1 M3 安全加固开关(生产设为 true,开发默认 false)
|
||||
APOLLO_REQUIRE_PQ_MANIFEST: ${APOLLO_REQUIRE_PQ_MANIFEST:-false}
|
||||
APOLLO_ROUTER_INTROSPECTION: ${APOLLO_ROUTER_INTROSPECTION:-true}
|
||||
# Apollo Router v1.45 persisted_queries.enabled 需要布尔值(true/false)
|
||||
APOLLO_PERSISTED_QUERIES: ${APOLLO_PERSISTED_QUERIES:-true}
|
||||
ports:
|
||||
- "3000:3000"
|
||||
- "8088:8088"
|
||||
@@ -365,7 +369,8 @@ services:
|
||||
APOLLO_ROUTER_URL: http://apollo-router:3000/graphql
|
||||
NEXT_PUBLIC_API_GATEWAY_URL: http://api-gateway:8080
|
||||
NEXT_PUBLIC_REALTIME_GATEWAY_URL: http://realtime-gateway:8081
|
||||
NEXT_PUBLIC_DEV_MODE: "true"
|
||||
# P0-5:生产 profile 必须 false(启动校验见 src/instrumentation.ts)
|
||||
NEXT_PUBLIC_DEV_MODE: "false"
|
||||
ports:
|
||||
- "4010:4010"
|
||||
depends_on:
|
||||
|
||||
Reference in New Issue
Block a user