feat(infra): p6 hardening - observability and deploy compose
- 5 NestJS services add /metrics endpoint via app.getHttpAdapter() - prometheus.yml scales to 8 services with rule_files and alertmanager - monitoring compose replaces blackbox with Loki+Promtail - Grafana datasource adds Loki - docker-compose.deploy.yml scales to 11 services - deploy.env.example completes Neo4j/ES/ClickHouse/LLM vars - teacher-bff adds health.controller - CI removes continue-on-error on lint step - teacher-portal lint script changed to eslint src
This commit is contained in:
@@ -86,15 +86,20 @@
|
||||
|
||||
### 1.6 可观测性(OTel + Prometheus + Loki)
|
||||
|
||||
| 场景 | 技术/规则 |
|
||||
| --------------- | -------------------------------------------------------------------------------- |
|
||||
| P1 最小可观测集 | 每服务结构化日志 + `/metrics` + OTel SDK 初始化(不引入完整后端) |
|
||||
| 三支柱 | Logs(pino/winston/zap)+ Metrics(prom-client)+ Traces(OTel SDK) |
|
||||
| traceId 注入 | Gateway 注入 → 服务读取 header → 日志/响应携带 |
|
||||
| P6 完整后端 | Loki(日志)+ Grafana(仪表盘)+ Jaeger(trace)+ Prometheus(metrics) |
|
||||
| Prometheus 指标 | `http_request_duration_seconds`(Histogram)+ `http_requests_total`(Counter) |
|
||||
| 采样策略 | P1 全量 trace,P6 引入采样率降低开销 |
|
||||
| 日志参数顺序 | `log.error({ err: error, userId, traceId }, "操作描述")`,错误对象字段名用 `err` |
|
||||
| 场景 | 技术/规则 |
|
||||
| ----------------- | ------------------------------------------------------------------------------------------------------------------- |
|
||||
| P1 最小可观测集 | 每服务结构化日志 + `/metrics` + OTel SDK 初始化(不引入完整后端) |
|
||||
| 三支柱 | Logs(pino/winston/zap)+ Metrics(prom-client)+ Traces(OTel SDK) |
|
||||
| traceId 注入 | Gateway 注入 → 服务读取 header → 日志/响应携带 |
|
||||
| P6 完整后端 | Loki(日志)+ Grafana(仪表盘)+ Jaeger(trace)+ Prometheus(metrics) |
|
||||
| Prometheus 指标 | `http_request_duration_seconds`(Histogram)+ `http_requests_total`(Counter) |
|
||||
| 采样策略 | P1 全量 trace,P6 引入采样率降低开销 |
|
||||
| 日志参数顺序 | `log.error({ err: error, userId, traceId }, "操作描述")`,错误对象字段名用 `err` |
|
||||
| P6 /metrics 端点 | NestJS 用 `app.getHttpAdapter().get('/metrics', handler)`,不能用 `app.get()`(会被解析为 DI 容器 get) |
|
||||
| P6 prometheus.yml | 8 个应用服务 + MySQL/Redis + node-exporter + prometheus 自身;rule_files 引用 rules.yml;alerting 关联 alertmanager |
|
||||
| P6 Grafana 数据源 | provisioning/datasources 同时声明 Prometheus(默认)和 Loki(uid=loki) |
|
||||
| P6 Promtail 采集 | docker_sd_configs + relabel_configs 仅采集 `edu-*` 前缀容器日志,避免无关日志 |
|
||||
| P6 service label | static_configs.labels.service 给所有抓取目标打服务标签,告警规则按 service 聚合 |
|
||||
|
||||
### 1.7 微前端 Module Federation
|
||||
|
||||
@@ -328,6 +333,7 @@
|
||||
|
||||
| 日期 | 时间 | 模块 | 做了什么 + 学到什么 |
|
||||
| ---------- | ---- | ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 2026-07-09 | 下午 | 全局 | **P6 硬化:可观测性 + 部署 + CI 硬化**:(1) 可观测性栈完善:5 个 NestJS 服务 main.ts 添加 `/metrics` Prometheus 端点(用 `app.getHttpAdapter().get('/metrics', ...)` 绕过 DI 容器 get 方法);prometheus.yml 从 2 个目标扩展到 8 个应用服务 + MySQL/Redis + node-exporter + prometheus 自身 + rule_files + alertmanager 关联;monitoring compose 用 Loki + Promtail 替换未配置的 blackbox-exporter;Grafana datasource 新增 Loki;新建 promtail/config.yml 用 docker_sd_configs 仅采集 `edu-*` 容器日志。(2) 部署 compose 扩展:docker-compose.deploy.yml 从 3 服务扩展到 11 服务(+ iam/teacher-bff/core-edu/content/msg/ai/data-ana/push-gateway),每个服务带 healthcheck + depends_on 条件 + edu-net/edu-shared 双网络;deploy.env.example 补全 Neo4j/ES/ClickHouse/LLM/Kafka 可选依赖配置。(3) teacher-bff 补 health.controller.ts(原缺失 /healthz 导致 deploy depends_on service_healthy 失败)。(4) CI 硬化:移除 lint 步骤的 continue-on-error(ESLint 9 flat config 已配置完成),test 保留 continue-on-error(部分服务无 test 脚本)。**学到**:NestJS `app.get('/metrics')` 会被解析为 DI 容器 `get(typeOrToken)`,必须用 `app.getHttpAdapter().get()` 才能注册 Express 路由;Promtail docker_sd_configs 通过 relabel_configs 的 `regex: '/(edu-.*).*'` 过滤容器名前缀;docker-compose.depends_on.condition: service_healthy 要求被依赖服务必须有 healthcheck 配置,否则启动失败。 |
|
||||
| 2026-07-09 | 下午 | 全局 | **P6 硬化:ESLint 9 flat config 配置**:(1) 根目录创建 `eslint.config.js`(ESLint 9 flat config 格式):用 `typescript-eslint` recommended 规则集 + `@eslint/js` recommended + `eslint-config-prettier` 禁用冲突规则;自定义规则:`no-explicit-any` warn + `no-unused-vars` 允许下划线前缀 + 测试文件放宽。(2) 6 个 TS 服务 package.json lint 脚本从 `eslint src --ext .ts` 改为 `eslint src`(flat config 不需要 --ext)。(3) `lint-staged.config.js` 恢复 `eslint --fix`。(4) 验证:classes/content/msg/core-edu 四服务 lint 全部零错误零警告通过。**学到**:ESLint 9 flat config 用 `tseslint.config()` 工厂函数组装配置数组;`--ext` 参数在 flat config 模式下被移除,ESLint 自动根据 `eslint.config.js` 中的 `files` 匹配;`@typescript-eslint/consistent-type-assertions` 规则选项格式在 v8 中变化(`objectLiteralType` → `objectLiteralTypeAssertions`),配置时需查最新文档。 |
|
||||
| 2026-07-09 | 中午 | msg/push-gateway/ai/api-gateway | **P5 沟通与 AI 阶段三服务完善**:(1) msg 服务修复:database.ts 导出 db 常量;env.ts JWT_SECRET/ES_URL 改 optional 加 DEV_MODE/PUSH_GATEWAY_URL;elasticsearch.ts ES 降级(esClient=null 时 safeIndex/safeSearch 跳过);notifications.service.ts 加 createBatch + listByUserWithPagination + Push Gateway 推送调用(try/catch 降级);新建 msg-init.sql 2 张表。(2) push-gateway 完善:hub.go 重写用 send chan + 单写协程模式修复 gorilla/websocket 并发写竞争;handler.go 加 DEV_MODE dev-token 支持 + broadcast 端点;config.go 加 DevMode/RedisURL。(3) ai 服务完善:config.py 加 openai_api_key/base_url/dev_mode;新建 llm_client.py(httpx 异步调 OpenAI REST API);main.py 加 /ai 前缀 + 降级模式(无 key 返回骨架 + degraded: true)+ /readyz 端点。(4) Gateway 路由扩展:/notifications → msg,/ai → ai 服务。**学到**:gorilla/websocket 不支持并发写,必须用 send chan 串行化所有写入;FastAPI APIRouter prefix 与 Gateway 代理路径要协调(ai 服务加 /ai 前缀,Gateway 代理 /ai/*path);LLM 降级策略统一返回 degraded 标记,调用方据此判断是否路由流量。 |
|
||||
| 2026-07-09 | 上午 | content/api-gateway | **P4 内容分析服务端到端打通**:(1) content 服务系统性修复:database.ts 导出 db 常量;env.ts JWT_SECRET/ES_URL/NEO4J_URL/NEO4J_PASSWORD 改 optional 加 DEV_MODE;neo4j.ts driver 惰性创建+try/catch+connectionTimeout:3000;health/lifecycle 改用 Drizzle;global-error.filter 移除 @types/express 依赖;textbooks.schema 修复 integer→int + 导出 NewTextbook/NewChapter 类型;textbooks.controller 移除 body as any + 加 PUT/DELETE。(2) 新建 3 模块:chapters(CRUD + 按 textbook 查询)、knowledge-points(CRUD + Neo4j 前置依赖图非阻塞查询)、questions(CRUD + 4 种题型校验)。(3) Gateway 路由扩展:textbooks/chapters/knowledge-points/questions 四组路由。(4) 数据库:content-init.sql 4 张表。(5) E2E 验证:POST /textbooks 201 → POST /chapters 201(字段用 order 非 orderNum)→ POST /knowledge-points 201(Neo4j 不可用 MySQL 正常写入)→ POST /questions 201 → GET 各列表 200。**学到**:Drizzle schema TS 字段名与 DB 列名解耦(order→order_num),API 请求体用 TS 字段名;Neo4j 不可用时必须 driver=null(不设 NEO4J_URL),否则每次请求尝试连接拖慢响应;neo4j-driver safeCreateNode 用 try/catch 非阻塞,MySQL 数据始终先落库。 |
|
||||
|
||||
Reference in New Issue
Block a user