From f322e66ea2a36fcdbe1eb1c69baec01355dc89f7 Mon Sep 17 00:00:00 2001 From: TangJia025 <574451426@qq.com> Date: Thu, 17 Sep 2026 15:34:36 +0800 Subject: [PATCH 1/5] =?UTF-8?q?docs(design):=20=E4=B8=AD=E5=BF=83=E7=9B=91?= =?UTF-8?q?=E6=8E=A7=E5=8E=BB=20Operator=20=E5=8C=96=20+=20HA=20=E7=BB=93?= =?UTF-8?q?=E8=AE=BA=E4=BF=AE=E6=AD=A3=20+=20istio=20HTTPS=20=E5=85=A5?= =?UTF-8?q?=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 依据 backlog#2062 的落地决策(完全原生、不用 Operator、3 个手写 chart)修订设计文档: - §3.1 / §4.7.1 / §4.7.2 / §4.8.1:kube-prometheus-stack → 3 个自研手写 Helm chart; 采集目标 ServiceMonitor → kubernetes_sd_configs(不依赖 CRD) - §4.7.2 新增「为什么 `replicas: 2` 不是 HA」:单 URL + 2 副本是分片, 两副本各持不完整数据 → 规则各自评估 → 告警不可信;一期定 replicas: 1, 二期换 VictoriaMetrics,并附二期组件去留表 - §4.8.1 新增「为什么不用 Operator」(CI 在 kind 上 ct install 无 CRD、用不上 声明式选目标、少一层升级面),补 tcpSocket 探针、web.config.file 轮换、 istio-injection: disabled 等易漏点;补 1 个 prometheus 自采 job 的有意偏差说明 - §4.7.4 / §4.8.2 / §4.9.1 / §4.9.2:istio gateway 按 host 区分(替代「复用 ELB、 端口分开」);Service 名改 community-*;Grafana 数据源补 basicAuth 与 裸 $VAR 的 provisioning 插值陷阱 - §6:R20 由「待验证」改为「已定论」;新增 R21(中心落点/共存未定 + AppProject sourceRepos 不在 Git 里) - 文档内 Open-Infra-Ops/helm-chart-value → opensourceways/helm-chart-value §3.1.1 存量系统图(L145-174)按原指示不动。 Co-Authored-By: Claude Code --- ...00\346\234\257\350\256\276\350\256\241.md" | 137 +++++++++++++----- ...33\345\272\246\350\277\275\350\270\252.md" | 4 +- 2 files changed, 103 insertions(+), 38 deletions(-) diff --git "a/docs/\345\276\256\346\234\215\345\212\241\345\217\257\350\247\202\346\265\213\346\200\247\345\273\272\350\256\276\346\212\200\346\234\257\350\256\276\350\256\241.md" "b/docs/\345\276\256\346\234\215\345\212\241\345\217\257\350\247\202\346\265\213\346\200\247\345\273\272\350\256\276\346\212\200\346\234\257\350\256\276\350\256\241.md" index 06609cf..04d30fe 100644 --- "a/docs/\345\276\256\346\234\215\345\212\241\345\217\257\350\247\202\346\265\213\346\200\247\345\273\272\350\256\276\346\212\200\346\234\257\350\256\276\350\256\241.md" +++ "b/docs/\345\276\256\346\234\215\345\212\241\345\217\257\350\247\202\346\265\213\346\200\247\345\273\272\350\256\276\346\212\200\346\234\257\350\256\276\350\256\241.md" @@ -263,8 +263,8 @@ flowchart LR APP -->|"obs-sdk 指标 · Prometheus 文本"| MET["GET /metrics"] OUT --> LA["log-agent
(节点 DaemonSet)"] LA --> LTS["LTS"] - MET --> SM["ServiceMonitor"] - SM --> PROM["Prometheus Agent
(--agent,本地只抓不存)"] + MET --> SD["抓取发现
kubernetes_sd_configs"] + SD --> PROM["Prometheus Agent
(--agent,本地只抓不存)"] PROM -->|"remote_write"| CTR["中心 Prometheus
(独立中心集群)"] ``` @@ -274,7 +274,7 @@ flowchart LR | 能力 | 选型 | 说明 | | --- | --- | --- | | 日志存储 | **LTS**——**日志组按集群划分:每集群 1 个 `k8s-log-{集群ID}`,共 17 个** | 不自建 ES;容器日志写入 `stdout-{集群ID}` 日志流。**各 Region 自持,无跨日志组检索能力**(该限制决定了下方的日志告警形态) | -| 指标存储 | **自建中心 Prometheus**(kube-prometheus-stack):17 集群 Agent(`--agent`,只抓不存)`remote_write` 汇聚到**1 个中心实例** | 不自研 SDK,**但自建底座**——理由与形态见 **§4.7**。原「AOM Prometheus for CCE」路线**已弃用**(官方确认多实例聚合不支持跨 Region) | +| 指标存储 | **自建中心 Prometheus**(**手写 Helm chart,不用 Operator**):17 集群 Agent(`--agent`,只抓不存)`remote_write` 汇聚到**1 个中心实例** | 不自研 SDK,**但自建底座**——理由与形态见 **§4.7**。原「AOM Prometheus for CCE」路线**已弃用**(官方确认多实例聚合不支持跨 Region) | | 日志大盘 | **自建 Grafana(同一实例)挂 LTS 日志数据源 ×17** | 与指标大盘同屏、**跨账号免多登**(§4.6.4)。✅ **接入日志流已确认可行**(2026-09-14 华为云,前提是流已结构化) | | 指标大盘 | **自建 Grafana(1 个实例)挂中心 Prometheus**。**独立 Deployment、与中心 Prometheus 同集群**(§4.7.4) | 替代原「AOM 托管 Grafana ×4」——一个实例即可覆盖全部 17 集群(§4.7.4 / §4.6.4)。**只做看板,不进告警链路**(见下行注释) | | 日志告警 | **各 Region 本地 LTS 内逐流配置(17 条)**,出口经 SMN 统一到订阅端 | 因 LTS **无跨流查询**,一条规则无法绑定多个日志流,**明确接受「无跨集群联合告警」**。**SMN 仅日志告警仍用**(Region 级资源),「统一」落在各 Region 主题**订阅同一接收端**,而非同一主题 | @@ -514,7 +514,7 @@ with _context.bind(community="openEuler", request_id="req-123"): - namespace 推不出:`openEuler` 的 prod-02 与 test 都叫 `robot-openeuler`;`OpenUBMC` 的 prod 与 test 都叫 `robot-openubmc`。 - 集群名推不出:它是 ArgoCD 规范名,**pod 内无法自省**。 -**注入方式**:在 `Open-Infra-Ops/helm-chart-value` 各部署目录的 `values.yaml` 里,给对应 deployment 加: +**注入方式**:在 `opensourceways/helm-chart-value` 各部署目录的 `values.yaml` 里,给对应 deployment 加: ```yaml env: @@ -599,7 +599,7 @@ with _context.bind(community="openEuler", request_id="req-123"): #### 4.7.1 指标采集 -**形态**:各集群部署 **Prometheus Agent**(`--agent`,**本地只抓不存**,本地只有 WAL),以 ServiceMonitor 选目标、抓各服务 `/metrics`,再 `remote_write` 到中心。形态照搬资源块(见 §4.7.2),但**独立部署、不复用其实例**——资源块 Agent 覆盖的是 CI 集群,与服务块 prod 集群基本不重叠;资源块的价值是**形态先例与配置模板**。 +**形态**:各集群部署 **Prometheus Agent**(`--agent`,**本地只抓不存**,本地只有 WAL),以 `kubernetes_sd_configs` / `static_configs` 选目标(**不依赖 ServiceMonitor CRD**)、抓各服务 `/metrics`,再 `remote_write` 到中心。形态照搬资源块(见 §4.7.2),但**独立部署、不复用其实例**——资源块 Agent 覆盖的是 CI 集群,与服务块 prod 集群基本不重叠;资源块的价值是**形态先例与配置模板**。 **⚠️ 跨 Region 链路是本方案的唯一硬门槛**(各集群 Agent → 中心): @@ -609,13 +609,43 @@ with _context.bind(community="openEuler", request_id="req-123"): #### 4.7.2 指标存储 -**形态:1 个独立中心 Prometheus 集群**(kube-prometheus-stack,放独立中心集群)。17 个集群的 Agent 经 `remote_write` 汇入同一实例,`enableRemoteWriteReceiver: true`。 +**形态:1 个独立中心 Prometheus 集群**(**自研手写 Helm chart,不用 Operator**,放独立中心集群)。17 个集群的 Agent 经 `remote_write` 汇入同一实例,启动参数开 `--web.enable-remote-write-receiver`。 | 项 | 决定 | | --- | --- | -| **HA** | `replicas: 2` + pod 反亲和(资源块当前为 `1`,其 values 注释已写明「正式上线时恢复 `replicas: 2`」)。**它是单点,挂了全网指标断线** | +| **HA** | ⚠️ **`replicas: 2` 在这里不是 HA,是分片**——详见下方。**一期 `replicas: 1`**,靠 StatefulSet 分钟级重建 + Agent WAL 兜(RTO 必须 < 2h,超出部分永久丢失);二期换 VictoriaMetrics(把接收端本身做成集群)。**它是单点,挂了全网指标断线** | | **保留期** | 一期**本地存储 30d,不上 VictoriaMetrics**——先跑一两个月看真实 series 增长再定,避免过早引入额外组件 | -| **安全** | `enableRemoteWriteReceiver: true` + 公网端口 = **拿到 basic auth 就能往中心写任意指标**。生产至少要做:强凭据 + 轮换、源 IP 白名单 / 安全组收敛、只放行 `/api/v1/write`。**独立集群的好处正是把风险面收敛到这一个集群上** | +| **安全** | 开了 `--web.enable-remote-write-receiver` 就**拿到 basic auth 便能往中心写任意指标**。落到配置:**istio HTTPS 入口**(TLS 在网关终止,公网不出现任何明文端口)+ **`--web.config.file` 强制 basic auth**(每业务集群一个用户)+ 只放行 `POST /api/v1/write`(其余路径 404)+ 强凭据轮换。**独立集群的好处正是把风险面收敛到这一个集群上** | + +**为什么 `replicas: 2` 不是 HA**(本次决策,回写自原「`replicas: 2` + pod 反亲和」的写法): + +单台 Prometheus 是**单机 TSDB**——没有分片,也没有查询期聚合。PromQL 只在**本进程内**的本地 TSDB 上执行,两个 Prometheus 实例之间互不知晓对方存在。这与 ES 的模型根本不同:ES 把索引切成 shard 分散到多节点,查询时协调节点 fan-out、各自本地执行、再 reduce/merge,所以 ES 加节点 = 加容量且**查询自动跨分片**;Prometheus 既没有「协调节点」也没有「shard」这两个抽象。 + +(易混点:Prometheus 语境下的 "sharding" 指的是**抓取侧**分片——用 `hashmod` relabel 把 target 分给不同实例去抓,不是存储/查询侧的分片;分完之后查询仍只能落在一台上。唯一的跨实例查询机制是 `remote_read`,但它是「显式配置 + `read_recent` 默认 false」的受限语义,不是自动 scatter-gather。生产上的标准答案是外挂 Thanos Querier / VictoriaMetrics / Mimir——它们做的正是 ES 协调节点做的事。) + +**反过来说,这也解释了为什么「中心天然汇聚、无需额外聚合层」成立**:汇聚发生在**写入时**——17 个 Agent 的 `remote_write` 全推到同一个 URL,数据进库时就已经在一个 TSDB 里。**汇聚在存储时,不在查询时**,这是 Prometheus 与 ES/LTS 的本质分野。(日志侧才是 ES 式模型:物理分裂成 17 个日志流,查询时靠 Grafana 多数据源拼装,§4.6.4 的「必选筛选」正是给这个打的补丁。) + +当 Agent 的 `remote_write` 只有一个 URL 时,**两个副本各自持有不完整的数据**,且不是干净的「各一半」:`remote_write` 走 HTTP keep-alive,TCP 连接被 conntrack 钉在某个 pod 上,同时 `queue_config` 的 shards 按积压动态增减并发连接——所以分布介于「某 Agent 整条流全落在一个 pod」到「大致均分」之间,**且随时间漂移**(shard 扩缩、连接回收、pod 重启都会重新洗牌)。唯一不变的是:**任一副本都不保证持有全集,且偏斜程度我们控制不了。** + +后果分两层,第二层更要害: + +1. **Grafana 查询抖动**:打 Service 会拿到两个不同的不完整结果。 +2. **告警不可信**:两副本**各自**用自己不完整的数据独立评估规则——`rate()` / `sum()` 偏离真值;`absent()` 之类规则在缺数据的副本上**误报**;两副本的评估结果互相不一致。而中心告警「一条规则覆盖全部集群」这个收益的根基,正是它必须可信。 + +⚠️ **一个必须说清的边界**:**告警链路的可用性 = Prometheus 的可用性**,不是 Alertmanager 的。**规则评估在 Prometheus 里**,Prometheus 挂了就不会产生新告警——Alertmanager 的 `replicas: 2` 只保证「有告警产生时通知不丢」(它的 HA 是货真价实的,见 §4.7.3),这两件事不要混。 + +**要让 `replicas: 2` 真正成立,只能让每个 Agent 写两份(fan-out 到两个端点)。** 代价不止「流量翻倍」:Agent 出网流量 ×2、中心入口带宽 ×2、**磁盘 ×2**(两副本各存全量)、Agent 侧 WAL 队列 ×2。**且查询层(Thanos Querier / VictoriaMetrics)不能替代 fan-out**——单 URL + 查询层 = 无损的「分片并集」,Grafana 不抖了,但任一副本挂掉仍丢它那一半,可用性没解决。**本方案一期不走这条**:`replicas: 2` 这件事本就不该用 Prometheus 做,单机 TSDB 不是适合当「17 集群汇聚点」的形态;VictoriaMetrics 则把接收端本身做成集群(`vminsert` 无状态多副本 + `vmstorage` 按 series hash 复制 + `vmselect`),一次解决 HA、水平扩容与 >30d 保留期,故定为二期。 + +**二期换 VM 时的组件去留**(沉没成本只有「中心 Prometheus 本体」这一个 chart 及其 values): + +| 组件 | 二期 | +| --- | --- | +| `charts/prometheus` 本体(StatefulSet + prometheus.yml + 规则文件 + PVC) | ❌ 退役,被 `vmcluster` + `vmalert` 取代 | +| 规则文件**内容** | ✅ 复用(`vmalert` 直接读 Prometheus 格式) | +| `charts/alertmanager` | ✅ **保留**——VM 生态的告警路由仍用 Alertmanager(`vmalert` 只评估规则,分组/去重/通知仍在 Alertmanager) | +| `charts/grafana` | ✅ 保留,只改数据源 URL | +| istio VirtualService、web-config 鉴权、Vault/Secret 组织 | ✅ 原样复用,只换后端 Service 名 | +| 业务集群 Agent(`--agent`) | ✅ **完全不动**——`remote_write` 是通用协议,推给 `vminsert` 一样收 | **为什么弃用 AOM 路线**: @@ -651,11 +681,11 @@ with _context.bind(community="openEuler", request_id="req-123"): | 设计点 | 说明 | | --- | --- | | **「独立」的价值在生命周期解耦,不在物理位置** | 独立 Deployment 已让 Grafana 的升级 / 重启 / 回滚不碰 Prometheus,反之亦然——这是真正需要的隔离 | -| **同集群走集群内 svc 查询** | `prometheus-kube-prometheus-prometheus.infra-monitoring.svc:9090`,零网络成本、零延迟。Grafana 是查询密集型无状态服务,离 Prometheus 越近越好 | +| **同集群走集群内 svc 查询** | `community-prometheus.infra-monitoring.svc.cluster.local:9090`,零网络成本、零延迟。Grafana 是查询密集型无状态服务,离 Prometheus 越近越好 | | **故障域与告警解耦** | 关键在**告警用 Alertmanager、不用 Grafana Alerting**——Grafana 挂了只是看不了图,**告警不受影响** | | **不单开一个集群** | 多一份集群底座成本,还**新增一条跨集群链路**(Grafana → Prometheus) | -**两点可照抄资源块**:Grafana PVC 只要 **10Gi**(dashboard 走 ConfigMap provisioning,PVC 只存状态);**和 Prometheus 复用同一个 ELB、端口分开**,省一个 ELB。 +**两点可照抄资源块**:Grafana PVC 只要 **10Gi**(dashboard 走 ConfigMap provisioning,PVC 只存状态);**和 Prometheus 复用同一个 istio gateway 的 ELB、按 host 区分**(`grafana.osinfra.cn` vs `prometheus.osinfra.cn`),省一个 ELB。——这比「同一个 ELB、端口分开」更好:**TLS 统一在网关终止,公网不再出现任何明文端口**。 **面板筛选策略**:`community` **默认全选、不强制**——中心 Prometheus 存储已聚合(全部 17 集群汇入同一实例),不筛选无查询放大问题;`community` 是低基数 label,`sum by (community)` 成本可忽略。**必选会禁掉横向对比,而横向对比恰是指标大盘最有价值之处**;担心误读时在面板标题显示当前筛选值即可。 @@ -684,41 +714,73 @@ with _context.bind(community="openEuler", request_id="req-123"): ### 4.8 Prometheus 中心集群与 Agent 搭建 -指标底座要搭**两处**:一处**中心**(1 个),一处**每个业务集群**(17 个)。都走 **kustomize + ArgoCD**——目录组织照抄资源块:`base/` 放公共清单,`config-for-<集群>/` 放该集群的 patch 与 Secret,一份 Application 对应一个集群。 +指标底座要搭**两处**:一处**中心**(1 个),一处**每个业务集群**(17 个)。**两处的交付方式不同**: + +- **中心(1 处)**:**3 个自研手写 Helm chart**(Prometheus / Alertmanager / Grafana),走 **ArgoCD + git path 直读**——chart 来自 `opensourceways/helm-charts` 的 `charts/*`,values 来自 `opensourceways/helm-chart-value` 的 `common/*/prod/`。**不走 SWR OCI**:那条链路靠**手动 Jenkins job** 发布、凭据只在 Jenkins 里,Git 中不可见,无法自动化。 +- **业务集群 Agent(17 处)**:仍走 **kustomize + ArgoCD**——目录组织照抄资源块:`base/` 放公共清单,`config-for-<集群>/` 放该集群的 patch 与 Secret,一份 Application 对应一个集群。 #### 4.8.1 中心集群(1 处) -**位置**:**独立中心集群**(新申请,不与任何业务集群复用),ns `infra-monitoring`——与 Alertmanager、Grafana(§4.9)同 ns,走集群内 DNS。 +**位置**:ns `infra-monitoring`,与 Alertmanager、Grafana(§4.9)同 ns,走集群内 DNS。⚠️ 中心落在**哪个**集群尚未定案——设计原文写「独立中心集群(新申请)」,而实现放在 `infra-cn4-x86-common-cluster`(微服务部署最多的 Region),该集群的 `infra-monitoring` ns **已有资源块的监控栈**(Prometheus + Alertmanager + Grafana + Pushgateway)。本方案用 **`community-` 前缀**规避资源名冲突(资源块的 Grafana Deployment 就叫 `grafana`)。**「共存还是替换」需与运维定案**——两个 ArgoCD 管同一个 ns 会互相打架(见 §6 R21)。 + +**组件:3 个自研手写 Helm chart,不用 Operator。** + +``` +charts/prometheus/ StatefulSet + Service + headless Service + ConfigMap(prometheus.yml) + + ConfigMap(规则) + Secret(web-config) + SA + VirtualService + Namespace +charts/alertmanager/ StatefulSet + Service + headless Service + ConfigMap(alertmanager.yml + *.tmpl) + + Secret(SMTP) + SA + VirtualService +charts/grafana/ Deployment + Service + PVC + ConfigMap(数据源/provider/大盘) + Secret(admin) + SA + VirtualService +``` -**组件**:一个 `kube-prometheus-stack` 出齐 Prometheus + Alertmanager + Operator,**关掉 chart 自带的其他组件**——`grafana.enabled: false`(Grafana 独立部署,§4.9)、`kubeStateMetrics` / `nodeExporter` / `kubeApiServer` / `kubelet` / `kubeControllerManager` / `kubeScheduler` / `kubeProxy` / `kubeEtcd` 全关(基础设施指标复用云上,§1.3 G5),`defaultRules` 里对应规则一并禁用。 +**为什么不用 Operator**(本次决策): -| values 关键项 | 取值 | 理由 | +1. **CI 硬约束**:chart 仓的 CI 是 `ct install` 在 **kind** 上**真实安装**,而 kind **没有任何 CRD**。Operator 模式意味着 ServiceMonitor / PrometheusRule 这些 CRD 必须可被 guard 掉、空 values 下渲染 0 个对象才过——CRD 依赖是纯负担。 +2. **用不上**:「声明式选目标」这个 Operator 的核心价值我们用不到——采集侧用 `kubernetes_sd_configs` 就够(§4.8.2),规则用普通 ConfigMap 挂载即可。 +3. **少一层升级/兼容面**:Operator 的版本矩阵、admission webhook、CRD 升级都是额外的长期负担。 + +代价是 HA、配置 reload、PVC 生命周期要自己写——本方案已逐条给出对策(HA 见 §4.7.2 的 fan-out 结论;reload 用 `checksum/config` 注解触发滚动 + `--web.enable-lifecycle` 供手动 `POST /-/reload`;PVC 用 StatefulSet `volumeClaimTemplates`)。**注意这是相对 Operator 模式的一处认知迁移**:Operator 下每个副本各自跑**完整**的 scrape loop 抓同一批 target,所以副本是全量副本、只需查询层去重;改成 Agent **push** 之后,「每个副本自己抓全量」这个前提消失了——这正是 §4.7.2 那条 HA 结论的根因。 + +**values 关键项**: + +| 关键项 | 取值 | 理由 | | --- | --- | --- | -| `prometheusSpec.replicas` | **2** + pod 反亲和 | 中心是单点,挂了全网指标断线(§4.7.2) | -| `retention` | **30d** | 一期本地存储,先看真实 series 增长再定;>30d 才考虑 VictoriaMetrics | -| `scrapeInterval` | 60s | 与 Agent 侧一致,§4.7.2 的容量公式按此口径 | -| `enableRemoteWriteReceiver` | **true** | 接收 17 集群 Agent 的写入 | -| `enableFeatures` / `tsdb.outOfOrderTimeWindow` | `out-of-order-ingestion` / `10m` | 跨 Region 写入乱序是常态,不开会丢点 | -| `storageSpec` | `storageClassName: csi-disk`、**100Gi** | 规格按 §4.7.2 公式核算后回填 | -| `service.type` | `LoadBalancer` + ELB ID | 对外入口,供各集群 Agent 写入 | +| `prometheus.replicaCount` | **1** | 一期不上 2——单 URL + 2 副本是分片不是 HA(§4.7.2) | +| `prometheus.image` | `.../prometheus:v3.11.3` | 镜像钉版本 | +| `prometheus.config` 里 `storage.tsdb.retention` | `{time: 30d, size: 90GB}` | **不用启动参数**——`--storage.tsdb.retention.*` 在 v3 已标 `[DEPRECATED]`,官方要求写进配置文件;`size` 兜底,100Gi 盘留余量给 WAL / compaction | +| `prometheus.config` 里 `storage.tsdb.out_of_order_time_window` | `10m` | 跨 Region 写入乱序是常态,不开会丢点。⚠️ **不是** `--enable-feature` 的值——合法值里没有 `out-of-order-ingestion`,只能走配置文件的这个键 | +| `prometheus.config` 里 `global.external_labels` | **刻意留空** | 会盖到全部业务集群的告警上,破坏 `group_by: [cluster, alertname]` | +| `prometheus.persistence` | `csi-disk` / RWO / **100Gi** | 规格按 §4.7.2 公式核算后回填(≈140 万 active series) | +| `--web.enable-remote-write-receiver` | 开启 | 接收 17 集群 Agent 的写入。⚠️ **v3 正确写法是独立 flag**;`--enable-feature=remote-write-receiver` 已删 | +| `--web.config.file` | 指向挂载进来的 `web-config.yaml` | 强制 basic auth,**每业务集群一个用户** | +| `prometheus.resources` | requests 2 / 8Gi、limits 4 / 16Gi | 与资源块对齐 | +| `alertmanager.replicaCount` | **2** | Alertmanager 的 HA 是**真的**(gossip 共享告警状态、同一告警只通知一次),故本期就上 2(§4.7.2) | +| `grafana.strategy` | `Recreate` | PVC 是 RWO,Deployment 上滚动更新会卡 Multi-Attach | + +**对文档「中心组件全关」的有意偏差**:原文说 kubeStateMetrics / nodeExporter / kubeApiServer 等全关,指的是**不采集群基础设施组件**(§1.3 G5),**不等于「连自己都不采」**。本方案**保留 1 个 `prometheus` 自采 job**——接收端的健康状况全在 `prometheus_remote_storage_samples_total` / `..._failed_total` / `prometheus_tsdb_storage_blocks_bytes` 上,没有它就无法回答「Agent 到底写进来了没 / 盘快满没满」,而这正是 R2 / R3 / R20 的唯一观测手段。 **入口与安全**(§4.7.2 的安全项落到配置): -- ⚠️ **不照搬资源块的公网明文 HTTP**——资源块当前是 `http://:9090` + Basic Auth,而其 TLS 清单(`prometheus-remote-write-certificate` / `-ingress`)在 `kustomization.yaml` 中**被注释、标为 Phase 2**。本方案**搭建时就要上 HTTPS**(§4.7.1),不留这个尾巴。 -- 凭据走 Secret 挂**文件**(`password_file`),不进 values 明文;配合源 IP 白名单 / 安全组收敛,**只放行 `/api/v1/write`**。 +- ⚠️ **不照搬资源块的公网明文 HTTP**——资源块当前是 `http://:9090` + Basic Auth,而其 TLS 清单在 `kustomization.yaml` 中**被注释、标为 Phase 2**。本方案**搭建时就要上 HTTPS**(§4.7.1),不留这个尾巴。 +- **复用集群现成的 istio**(`istio-system/istio-gateway-https`,已终止 TLS、泛域名证书走 Vault),`VirtualService` 只放行 `POST /api/v1/write`,**其余路径一律 404**——UI 与 API 不对公网暴露。选 istio 的理由:TLS 终止能力已存在且自动化,**不需要申请证书、不需要新 ELB**;`VirtualService` 天然支持精确 path 匹配,而 ELB 注解做不到。 +- 凭据(整个 `web-config.yaml`,含 17 个 bcrypt hash)走 **Vault → Secret 挂文件**,不进 values 明文、不进 Git。**轮换的最大优势**:`web.config.file` 是**每个 HTTP 请求都重读**的,改 Vault **即刻生效、无需 reload 或重启**;轮换顺序必须是「先加新 hash(新旧并存)→ 再逐集群改 Agent → 最后摘旧 hash」。 +- ⚠️ **开了 web auth 后探针必须用 `tcpSocket`**:`--web.config.file` 的 basic auth 在 exporter-toolkit 层包裹**整个** HTTP server,`/-/healthy` / `/-/ready` 同样受保护,而 k8s 的 `httpGet` 探针无法携带凭据(prometheus/prometheus#9166)。三个 chart 一律用 `tcpSocket`。 +- ⚠️ **`infra-monitoring` namespace 必须带 `istio-injection: disabled`**,否则 Prometheus / Grafana 会被注入 sidecar,在 STRICT mTLS 下出 gateway → pod 握手问题。`CreateNamespace=true` 建的 ns **不带这个 label**,故由 chart 渲染 Namespace 并打上 label。 - 把暴露面收敛到这一个集群,正是独立中心相对 AOM 的好处(§4.7.2)。 -**PrometheusRule** 与 **Alertmanager 配置**(含邮件模板 / 接收人)也集中在中心定义,一条规则覆盖全部 17 集群,`group_by: [cluster, alertname]` 区分来源(§4.7.3)。 +**规则文件**(替代 Operator 的 `PrometheusRule`)与 **Alertmanager 配置**(含邮件模板 / 接收人)也集中在中心定义,以 ConfigMap 挂载(规则体积大、变更节奏不同,单独一份 values 文件),一条规则覆盖全部 17 集群,`group_by: [cluster, alertname]` 区分来源(§4.7.3)。 #### 4.8.2 各业务集群 Agent(17 处) **形态**:`prometheus --agent`(**本地只抓不存**,只有 WAL),`scrape` 完 `remote_write` 到中心。每个集群一套,用 `config-for-<集群>/` 打 patch 注入本集群的 `cluster` label 与写入凭据。 +⚠️ **`--agent` 在 v3 是独立 flag**,**不是** `--enable-feature` 的取值——资源块那套 `--enable-feature=...` 的写法不要往这里套。 + | 配置项 | 做法 | | --- | --- | -| **采集目标** | ServiceMonitor 选各服务 `/metrics`(按 ns / label 选,**服务侧无需改配置**);基础设施指标仍复用云上,Agent 不重复抓 | +| **采集目标** | `kubernetes_sd_configs`(按 ns / label relabel)选各服务 `/metrics`——**服务侧无需改配置,也不依赖 ServiceMonitor CRD**;基础设施指标仍复用云上,Agent 不重复抓 | | **来源标注** | `cluster` label 是告警与大屏 `group_by` 的依据,在 Agent 侧统一打上 | -| **写入地址** | 中心 Prometheus 的写入端点,**HTTPS**(见 §4.8.1) | +| **写入地址** | `https://prometheus.osinfra.cn/api/v1/write`(istio HTTPS 入口,见 §4.8.1) | | **鉴权** | `basic_auth` + `password_file` 挂 Secret,**每集群一套凭据**——跨账号场景各带各的,与账号归属无关(§4.7.2) | | **写入瘦身** | 用 `write_relabel_configs` drop 无用 label(资源块的做法是 drop `container` / `container_id` / `uid`)——直接省中心磁盘与跨 Region 流量 | | **资源限制** | 常驻但轻量,按集群规模给 requests / limits | @@ -727,7 +789,7 @@ with _context.bind(community="openEuler", request_id="req-123"): #### 4.8.3 搭建顺序 -1. 中心集群起 `kube-prometheus-stack`(Prometheus + Alertmanager),开 `enableRemoteWriteReceiver`; +1. 中心集群起 **3 个手写 chart**(Prometheus + Alertmanager + Grafana,§4.8.1),Prometheus 开 `--web.enable-remote-write-receiver`; 2. **先把跨 Region 网络打通再铺 Agent**——它是本方案唯一硬门槛(§4.7.1),链路不通则后面全白做; 3. 选 **1 个集群**试点 Agent:验证 `remote_write` 写入、`cluster` label、带宽与容量; 4. 批量铺 17 集群(脚本化生成 kustomize 目录 / ArgoCD Application); @@ -741,26 +803,28 @@ with _context.bind(community="openEuler", request_id="req-123"): #### 4.9.1 部署 -清单组成照抄资源块 `monitoring/grafana/`,用 kustomize 管: +**用 Helm chart `charts/grafana` 管**(与其他两件套同源、同交付方式,§4.8),values 在 `common/grafana/prod/`: -| 清单 | 作用 | 资源块取值(可直接参照) | +| 对象 | 作用 | 取值 | | --- | --- | --- | | `deployment.yaml` | 实例本体 | `replicas: 1`、`strategy: Recreate`(PVC 是 RWO)、`runAsNonRoot`、**镜像钉版本** | -| `pvc.yaml` | 只存 Grafana 自身状态 | **10Gi**——dashboard 走 ConfigMap provisioning,不占 PVC(§4.7.4) | -| `service.yaml` | 入口 | `LoadBalancer`,**复用中心 Prometheus 的那个 ELB、端口分开**,省一个 ELB | -| `grafana-datasource.yaml` | 数据源 provisioning | 见 §4.9.2 | -| `grafana-dashboard-provider.yaml` | dashboard 加载器 | 从 `/etc/grafana/dashboards/json` 读;`disableDeletion: true` + `editable: false` | -| admin Secret | 管理员密码 | Secret 注入,不进 values | +| `pvc.yaml` | 只存 Grafana 自身状态 | **10Gi**——dashboard 走 provisioning 挂载,不占 PVC(§4.7.4)。⚠️ Deployment 用不了 `volumeClaimTemplates`,PVC 是独立对象 | +| `service.yaml` + `virtualservice.yaml` | 入口 | 集群内 Service + **istio VirtualService**(`grafana.osinfra.cn`,`prefix: /`);与 Prometheus **复用同一个 gateway 的 ELB、按 host 区分**(§4.7.4) | +| `configmap.yaml`(数据源) | 数据源 provisioning | 见 §4.9.2 | +| `configmap.yaml`(provider) | dashboard 加载器 | 从 `/var/lib/grafana/dashboards` 读;`folder: Community` | +| admin Secret | 管理员密码 | Vault → Secret 注入,不进 values | #### 4.9.2 数据源 | 数据源 | 类型 | 接入方式 | | --- | --- | --- | -| **中心 Prometheus** | prometheus | 集群内 DNS(`http://prometheus-kube-prometheus-prometheus.infra-monitoring.svc:9090`)、`access: proxy`、`isDefault: true`——零网络成本、零延迟(§4.7.4) | +| **中心 Prometheus** | prometheus | 集群内 DNS(`http://community-prometheus.infra-monitoring.svc.cluster.local:9090`)、`access: proxy`、`isDefault: true`——零网络成本、零延迟(§4.7.4)。⚠️ **必须带 `basicAuth` 凭据**:Prometheus 侧开了 `--web.config.file`(§4.8.1),读也要认证;口令走环境变量 `$PROM_BASIC_AUTH_PASSWORD` 从 Vault Secret 注入 | | **LTS 日志流 ×17** | 华为云 LTS 插件 | **每个数据源绑一条日志流、各带一套 AK/SK**(§4.6.4)——正因为凭证是数据源级的,一个实例才能横跨 N 个账号、**免多登** | > ⚠️ LTS 侧的前提是**日志流已配结构化解析**(§4.6.2)——该前提不成立时,数据源挂上了也查不出结构化字段。 +> ⚠️ **数据源口令必须写「裸 `$VAR`」形式,不要写 `${VAR}`**。Grafana 的 provisioning 会**先替换 `${VAR}`、再做一次 `$VAR` 展开**——口令里若含 `$`(强口令几乎必然含,bcrypt hash 更是遍地 `$`),`${VAR}` 会被二次展开截断。官方文档给的例子:`PASSWORD=Pa$sw0rd` 时 `$PASSWORD` → `Pa$sw0rd`,而 `${PASSWORD}` → `Pa`。**症状是 Grafana 拿着被截断的口令去查 Prometheus,静默 401**——面板一片「no data」,日志里不显眼。 + #### 4.9.3 看板 **dashboard 用代码管**:JSON 放 Git,经 `configMapGenerator` 生成 ConfigMap 挂进容器——**改看板 = 提 PR**,可 review、可回滚。资源块放了 2 块(`CI-Network-Overview` / `Pod-Network-Traffic`),本方案要做的: @@ -852,7 +916,8 @@ flowchart LR | R6 | Python / Node / Java 未打 tag,Java 进私服路径未验证 | 消费方无法按版本引用 | 随各语言首次接入服务时一并处理 | | R8 | Java SDK 的 logstash-logback-encoder / jackson-core 在 SDK 内为 `provided` | 接入服务运行时必须自带,否则日志不可用 | 接入文档中明确要求,或改为传递依赖 | | R14 | **同一日志流内并存两套格式**:obs-sdk 契约行与 `robot-framework-lib` 的 logrus JSON(后者无 `service` / `env` / `community`,且 `level: fatal`、`time` 秒精度均与契约冲突)(§4.6.2) | LTS 侧的检索 / 聚合 / 告警规则要同时兼容两种格式;且**不是过渡期现象**——框架那 121 处打点会长期保留,两套格式的共存是稳态 | 明确 LTS 侧怎么处理这两套(分别配解析?云端做字段归一化?只对 obs-sdk 行做索引、logrus 行仅全文检索?)。**这一点未定之前,「结构化解析配得上」仍不成立** | -| R20 | **中心集群的 HA 未验证**:`replicas: 2` + pod 反亲和下的去重与告警行为(§4.7.2) | 中心是单点,挂了全网指标断线 | 试点期验证。若将来上 VictoriaMetrics,必须同时配 `-dedup.minScrapeInterval`,否则两个副本各写一份、**数据翻倍** | +| R20 | ~~**中心集群的 HA 未验证**~~ → **【2026-09-17 已定论】单 URL + `replicas: 2` 是分片、不是 HA**:Prometheus 是单机 TSDB,两副本互不知晓、各自持不完整数据,**告警不可信**(§4.7.2) | 若按原设计上 2 副本,会得到「看似有冗余、实则规则评估各自不准」的最坏形态——`rate()` / `sum()` 偏离真值、`absent()` 误报,而**中心告警是「一条规则覆盖全部集群」这个收益的根基** | **已定论:一期 `replicas: 1`**(靠 StatefulSet 分钟级重建 + Agent WAL 兜);`replicas: 2` 要真成立**只能靠 Agent 双端点 fan-out**(出网流量与磁盘各 ×2),本期不做。⚠️ **两副本也不得加区分性 `external_labels`**(会破坏 `group_by: [cluster, alertname]`)。二期换 VictoriaMetrics(接收端本身成集群);换 VM 时若做副本,必须配 `-dedup.minScrapeInterval`,否则各写一份、**数据翻倍** | +| R21 | **中心落在哪个集群、与资源块监控栈「共存还是替换」未定案**——设计原文写「独立中心集群(新申请)」,实现放在 `infra-cn4-x86-common-cluster`(§4.8.1);且该集群的 AppProject `sourceRepos` 白名单**不在 Git 里**(带外创建),无法核实 | ①**替换** = 资源块监控栈下线;**共存** = 两个 ArgoCD 管同一个 ns,会互相打架。②白名单不含 `helm-charts.git` 则三个 Application 一定报 `is not permitted in project`;若提交 `project.yaml` 时漏写现有源,会**整体覆盖**并打断现存 9 个 App(7 个 Helm + 2 个 Kustomize) | ①与运维定案「共存 or 替换」,本方案用 `community-` 前缀先把资源名冲突规避掉。②**顺序不能反**:先 `kubectl get appproject infra-cn4-x86-common-cluster -n argocd -o yaml` 读出真实白名单,再提交**并集**(现有 ∪ `helm-charts.git` ∪ `helm-chart-value.git`) | --- diff --git "a/docs/\350\277\233\345\272\246\350\277\275\350\270\252.md" "b/docs/\350\277\233\345\272\246\350\277\275\350\270\252.md" index 9fcfa1b..006e52c 100644 --- "a/docs/\350\277\233\345\272\246\350\277\275\350\270\252.md" +++ "b/docs/\350\277\233\345\272\246\350\277\275\350\270\252.md" @@ -20,14 +20,14 @@ | obs-sdk-node(10 UT) | ✅ 代码完成 | 0.5(打 tag) | | 试点接入:`robot-universal-review` 日志([#2161](https://github.com/opensourceways/backlog/issues/2161)) | 🔄 代码 [PR #90](https://github.com/opensourceways/robot-universal-review/pull/90) 已开;preview 环境已验证日志契约 | 0.5(评审 + 入 test 环境) | | 试点接入:`robot-universal-review` 指标([#2162](https://github.com/opensourceways/backlog/issues/2162)) | 📋 待办 | 1.5 | -| 部署侧 `OBS_*` 注入(helm-chart-value,21 个目录 = 13 prod + 8 test/preview) | 🔄 进行中:8 个 test/preview 已提 [PR #1428](https://github.com/Open-Infra-Ops/helm-chart-value/pull/1428)(待评审);13 个 prod 待办,含 `common/` 两个目录 community 取值待 owner 确认 | 2~3(含跨团队确认) | +| 部署侧 `OBS_*` 注入(helm-chart-value,21 个目录 = 13 prod + 8 test/preview) | 🔄 进行中:8 个 test/preview 已提 [PR #1428](https://github.com/Open-Infra-Ops/helm-chart-value/pull/1428)(待评审);13 个 prod 待办,含 `common/` 两个目录 community 取值待 owner 确认。⚠️ **该仓正从 `Open-Infra-Ops` 迁往 `opensourceways`——此后新工作一律提交 `opensourceways/helm-chart-value`;本 PR 是否需在新仓重开待定** | 2~3(含跨团队确认) | | **小计** | | **8~9.5** | ## 2. 子任务 B —— 底座搭建与大盘告警(#2062) | 项 | 状态 | 预估人天 | | --- | --- | --- | -| 中心 Prometheus 集群搭建(kube-prometheus-stack + HA + 存储 + Alertmanager) | 📋 待办 | 5~8 | +| 中心 Prometheus 集群搭建(**3 个手写 Helm chart** + HA + 存储 + Alertmanager + **istio HTTPS 入口**) | 🔄 进行中:3 个 chart + values + ArgoCD Application 已本地就绪并通过渲染验证,待评审 | 5~8 | | 各集群 Prometheus Agent 铺开(17 集群)+ 采集链路验证 | 📋 待办 | 4~6 | | **跨 Region 网络打通**(CC / 专线 / 公网 HTTPS 三选一)+ 安全加固 | 📋 待办 | 3~6 | | 自建 Grafana 部署(独立 Deployment + 持久化 + 入口) | 📋 待办 | 2~3 | From 342cc0b93c9a2a56c83d022f7321512a0f8435e6 Mon Sep 17 00:00:00 2001 From: TangJia025 <574451426@qq.com> Date: Thu, 17 Sep 2026 16:44:34 +0800 Subject: [PATCH 2/5] =?UTF-8?q?docs(design):=20=E4=B8=AD=E5=BF=83=E6=94=B9?= =?UTF-8?q?=E8=90=BD=20infra-monitoring-community=EF=BC=88=E5=90=8C?= =?UTF-8?q?=E9=9B=86=E7=BE=A4=E4=B8=8D=E5=90=8C=20ns=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit U1 定案:中心落在 infra-cn4-x86-common-cluster,ns 新建 infra-monitoring-community,与 ascend 资源块的 infra-monitoring 同集群、不同 ns,两套完全独立。 - §4.8.1 位置段重写:说明「复用集群」相对原文「新申请独立集群」的有意改动, 并以对照表列出两套的数据域 / 组件 / 告警出口差异 - §4.8.1 补「明确不做 Pushgateway」:服务侧都是长驻进程、无 push 场景; 仅当纳入 CronJob 形态服务时才需要,届时须先解决多集群 label 归属 - §4.7.4 / §4.8.1 / §4.9.2 的 FQDN 与 ns 名同步 - §6:R21 结案(落点已定,保留容量配额这一剩余风险); 原 R21 后半段的 AppProject 白名单问题拆出为新的 R22(仍开放,硬阻断) Co-Authored-By: Claude Code --- ...00\346\234\257\350\256\276\350\256\241.md" | 27 +++++++++++++++---- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git "a/docs/\345\276\256\346\234\215\345\212\241\345\217\257\350\247\202\346\265\213\346\200\247\345\273\272\350\256\276\346\212\200\346\234\257\350\256\276\350\256\241.md" "b/docs/\345\276\256\346\234\215\345\212\241\345\217\257\350\247\202\346\265\213\346\200\247\345\273\272\350\256\276\346\212\200\346\234\257\350\256\276\350\256\241.md" index 04d30fe..1c138af 100644 --- "a/docs/\345\276\256\346\234\215\345\212\241\345\217\257\350\247\202\346\265\213\346\200\247\345\273\272\350\256\276\346\212\200\346\234\257\350\256\276\350\256\241.md" +++ "b/docs/\345\276\256\346\234\215\345\212\241\345\217\257\350\247\202\346\265\213\346\200\247\345\273\272\350\256\276\346\212\200\346\234\257\350\256\276\350\256\241.md" @@ -681,7 +681,7 @@ with _context.bind(community="openEuler", request_id="req-123"): | 设计点 | 说明 | | --- | --- | | **「独立」的价值在生命周期解耦,不在物理位置** | 独立 Deployment 已让 Grafana 的升级 / 重启 / 回滚不碰 Prometheus,反之亦然——这是真正需要的隔离 | -| **同集群走集群内 svc 查询** | `community-prometheus.infra-monitoring.svc.cluster.local:9090`,零网络成本、零延迟。Grafana 是查询密集型无状态服务,离 Prometheus 越近越好 | +| **同集群走集群内 svc 查询** | `community-prometheus.infra-monitoring-community.svc.cluster.local:9090`,零网络成本、零延迟。Grafana 是查询密集型无状态服务,离 Prometheus 越近越好 | | **故障域与告警解耦** | 关键在**告警用 Alertmanager、不用 Grafana Alerting**——Grafana 挂了只是看不了图,**告警不受影响** | | **不单开一个集群** | 多一份集群底座成本,还**新增一条跨集群链路**(Grafana → Prometheus) | @@ -721,7 +721,21 @@ with _context.bind(community="openEuler", request_id="req-123"): #### 4.8.1 中心集群(1 处) -**位置**:ns `infra-monitoring`,与 Alertmanager、Grafana(§4.9)同 ns,走集群内 DNS。⚠️ 中心落在**哪个**集群尚未定案——设计原文写「独立中心集群(新申请)」,而实现放在 `infra-cn4-x86-common-cluster`(微服务部署最多的 Region),该集群的 `infra-monitoring` ns **已有资源块的监控栈**(Prometheus + Alertmanager + Grafana + Pushgateway)。本方案用 **`community-` 前缀**规避资源名冲突(资源块的 Grafana Deployment 就叫 `grafana`)。**「共存还是替换」需与运维定案**——两个 ArgoCD 管同一个 ns 会互相打架(见 §6 R21)。 +**位置**:**`infra-cn4-x86-common-cluster`**(北京 cn4,微服务部署最多的 Region),ns **`infra-monitoring-community`**——与 Alertmanager、Grafana(§4.9)同 ns,走集群内 DNS。 + +⚠️ **这里相对设计原文有一处有意的改动**:原文写「独立中心集群(**新申请**,不与任何业务集群复用)」,本次改为**复用 `infra-cn4-x86-common-cluster`**——网络、istio 网关、泛域名证书(Vault)都现成,且它是微服务部署最多的 Region。该集群**已有资源块的监控栈**,但在**另一个 ns**: + +| | 资源块监控栈(ns `infra-monitoring`) | 本方案(ns `infra-monitoring-community`) | +| --- | --- | --- | +| **数据域** | CI 集群:拨测 CronJob + Pushgateway + 12 个 CI 集群 Agent | 服务块:17 个 prod 集群的微服务 `/metrics` | +| **组件** | Prometheus + Alertmanager + Pushgateway,**外加 Operator 及 8 个 `*.monitoring.coreos.com` CRD** | Prometheus + Alertmanager + Grafana,**无 Operator、无 CRD** | +| **告警出口** | 邮件:CI 拨测 / 共享盘 / 账号余额… | 邮件:微服务指标。**两者内容不重叠**——运维会收到两个来源的邮件,属预期,不是重复告警 | + +**为什么独立 ns 而不是共用 `infra-monitoring`**:①两个 ArgoCD 共管同一个 Namespace 对象会互相打架——资源块那两个 Application 是 `automated: prune + selfHeal`,我们改的 label 会被它改回去;②资源块的 Prometheus 是 `type: LoadBalancer`、占着共享 ELB `8625d057-…`(就是 §4.7.1 点名的那条公网明文通道),共用 ns 还得处理端口与安全组;③独立 ns 后两套**互不管理对方对象**,**并且完全不碰资源块的 Operator 与其 CRD**——这是「不用 Operator」的又一重收益。 + +`community-` 前缀**不是为避重名**(不同 ns 本就不会撞),而是让运维在 `kubectl get po -A` 时一眼区分两套。 + +⚠️ **新增的容量约束**:同一集群里再放一套(requests 2 CPU / 8Gi + 100Gi EVS),需核实节点池与 EVS 配额(见 §6 R21)。 **组件:3 个自研手写 Helm chart,不用 Operator。** @@ -733,6 +747,8 @@ charts/alertmanager/ StatefulSet + Service + headless Service + ConfigMap(ale charts/grafana/ Deployment + Service + PVC + ConfigMap(数据源/provider/大盘) + Secret(admin) + SA + VirtualService ``` +**明确不做 Pushgateway**:资源块那套里有 Pushgateway,是因为它的**拨测 CronJob** 跑完即弃、指标没处挂,只能 push。本方案的服务侧都是**长驻进程、自己有 `/metrics`**,没有 push 场景,故**一期不部署**。将来若纳入 CronJob 形态的服务(跑完即弃、`/metrics` 随 pod 消失),再单独补一个 chart——它的体量很小,但届时必须先解决「多个集群的 CronJob 往同一个 Pushgateway 推」的 label 归属与去重。 + **为什么不用 Operator**(本次决策): 1. **CI 硬约束**:chart 仓的 CI 是 `ct install` 在 **kind** 上**真实安装**,而 kind **没有任何 CRD**。Operator 模式意味着 ServiceMonitor / PrometheusRule 这些 CRD 必须可被 guard 掉、空 values 下渲染 0 个对象才过——CRD 依赖是纯负担。 @@ -765,7 +781,7 @@ charts/grafana/ Deployment + Service + PVC + ConfigMap(数据源/provide - **复用集群现成的 istio**(`istio-system/istio-gateway-https`,已终止 TLS、泛域名证书走 Vault),`VirtualService` 只放行 `POST /api/v1/write`,**其余路径一律 404**——UI 与 API 不对公网暴露。选 istio 的理由:TLS 终止能力已存在且自动化,**不需要申请证书、不需要新 ELB**;`VirtualService` 天然支持精确 path 匹配,而 ELB 注解做不到。 - 凭据(整个 `web-config.yaml`,含 17 个 bcrypt hash)走 **Vault → Secret 挂文件**,不进 values 明文、不进 Git。**轮换的最大优势**:`web.config.file` 是**每个 HTTP 请求都重读**的,改 Vault **即刻生效、无需 reload 或重启**;轮换顺序必须是「先加新 hash(新旧并存)→ 再逐集群改 Agent → 最后摘旧 hash」。 - ⚠️ **开了 web auth 后探针必须用 `tcpSocket`**:`--web.config.file` 的 basic auth 在 exporter-toolkit 层包裹**整个** HTTP server,`/-/healthy` / `/-/ready` 同样受保护,而 k8s 的 `httpGet` 探针无法携带凭据(prometheus/prometheus#9166)。三个 chart 一律用 `tcpSocket`。 -- ⚠️ **`infra-monitoring` namespace 必须带 `istio-injection: disabled`**,否则 Prometheus / Grafana 会被注入 sidecar,在 STRICT mTLS 下出 gateway → pod 握手问题。`CreateNamespace=true` 建的 ns **不带这个 label**,故由 chart 渲染 Namespace 并打上 label。 +- ⚠️ **`infra-monitoring-community` namespace 必须带 `istio-injection: disabled`**,否则 Prometheus / Grafana 会被注入 sidecar,在 STRICT mTLS 下出 gateway → pod 握手问题。`CreateNamespace=true` 建的 ns **不带这个 label**,故由 chart 渲染 Namespace 并打上 label。 - 把暴露面收敛到这一个集群,正是独立中心相对 AOM 的好处(§4.7.2)。 **规则文件**(替代 Operator 的 `PrometheusRule`)与 **Alertmanager 配置**(含邮件模板 / 接收人)也集中在中心定义,以 ConfigMap 挂载(规则体积大、变更节奏不同,单独一份 values 文件),一条规则覆盖全部 17 集群,`group_by: [cluster, alertname]` 区分来源(§4.7.3)。 @@ -818,7 +834,7 @@ charts/grafana/ Deployment + Service + PVC + ConfigMap(数据源/provide | 数据源 | 类型 | 接入方式 | | --- | --- | --- | -| **中心 Prometheus** | prometheus | 集群内 DNS(`http://community-prometheus.infra-monitoring.svc.cluster.local:9090`)、`access: proxy`、`isDefault: true`——零网络成本、零延迟(§4.7.4)。⚠️ **必须带 `basicAuth` 凭据**:Prometheus 侧开了 `--web.config.file`(§4.8.1),读也要认证;口令走环境变量 `$PROM_BASIC_AUTH_PASSWORD` 从 Vault Secret 注入 | +| **中心 Prometheus** | prometheus | 集群内 DNS(`http://community-prometheus.infra-monitoring-community.svc.cluster.local:9090`)、`access: proxy`、`isDefault: true`——零网络成本、零延迟(§4.7.4)。⚠️ **必须带 `basicAuth` 凭据**:Prometheus 侧开了 `--web.config.file`(§4.8.1),读也要认证;口令走环境变量 `$PROM_BASIC_AUTH_PASSWORD` 从 Vault Secret 注入 | | **LTS 日志流 ×17** | 华为云 LTS 插件 | **每个数据源绑一条日志流、各带一套 AK/SK**(§4.6.4)——正因为凭证是数据源级的,一个实例才能横跨 N 个账号、**免多登** | > ⚠️ LTS 侧的前提是**日志流已配结构化解析**(§4.6.2)——该前提不成立时,数据源挂上了也查不出结构化字段。 @@ -917,7 +933,8 @@ flowchart LR | R8 | Java SDK 的 logstash-logback-encoder / jackson-core 在 SDK 内为 `provided` | 接入服务运行时必须自带,否则日志不可用 | 接入文档中明确要求,或改为传递依赖 | | R14 | **同一日志流内并存两套格式**:obs-sdk 契约行与 `robot-framework-lib` 的 logrus JSON(后者无 `service` / `env` / `community`,且 `level: fatal`、`time` 秒精度均与契约冲突)(§4.6.2) | LTS 侧的检索 / 聚合 / 告警规则要同时兼容两种格式;且**不是过渡期现象**——框架那 121 处打点会长期保留,两套格式的共存是稳态 | 明确 LTS 侧怎么处理这两套(分别配解析?云端做字段归一化?只对 obs-sdk 行做索引、logrus 行仅全文检索?)。**这一点未定之前,「结构化解析配得上」仍不成立** | | R20 | ~~**中心集群的 HA 未验证**~~ → **【2026-09-17 已定论】单 URL + `replicas: 2` 是分片、不是 HA**:Prometheus 是单机 TSDB,两副本互不知晓、各自持不完整数据,**告警不可信**(§4.7.2) | 若按原设计上 2 副本,会得到「看似有冗余、实则规则评估各自不准」的最坏形态——`rate()` / `sum()` 偏离真值、`absent()` 误报,而**中心告警是「一条规则覆盖全部集群」这个收益的根基** | **已定论:一期 `replicas: 1`**(靠 StatefulSet 分钟级重建 + Agent WAL 兜);`replicas: 2` 要真成立**只能靠 Agent 双端点 fan-out**(出网流量与磁盘各 ×2),本期不做。⚠️ **两副本也不得加区分性 `external_labels`**(会破坏 `group_by: [cluster, alertname]`)。二期换 VictoriaMetrics(接收端本身成集群);换 VM 时若做副本,必须配 `-dedup.minScrapeInterval`,否则各写一份、**数据翻倍** | -| R21 | **中心落在哪个集群、与资源块监控栈「共存还是替换」未定案**——设计原文写「独立中心集群(新申请)」,实现放在 `infra-cn4-x86-common-cluster`(§4.8.1);且该集群的 AppProject `sourceRepos` 白名单**不在 Git 里**(带外创建),无法核实 | ①**替换** = 资源块监控栈下线;**共存** = 两个 ArgoCD 管同一个 ns,会互相打架。②白名单不含 `helm-charts.git` 则三个 Application 一定报 `is not permitted in project`;若提交 `project.yaml` 时漏写现有源,会**整体覆盖**并打断现存 9 个 App(7 个 Helm + 2 个 Kustomize) | ①与运维定案「共存 or 替换」,本方案用 `community-` 前缀先把资源名冲突规避掉。②**顺序不能反**:先 `kubectl get appproject infra-cn4-x86-common-cluster -n argocd -o yaml` 读出真实白名单,再提交**并集**(现有 ∪ `helm-charts.git` ∪ `helm-chart-value.git`) | +| R21 | ~~**中心落在哪个集群、与资源块监控栈「共存还是替换」未定案**~~ → **【2026-09-17 已定案】中心落在 `infra-cn4-x86-common-cluster`,ns `infra-monitoring-community`;与资源块的 `infra-monitoring` 同集群、不同 ns,两套完全独立**(§4.8.1) | 剩余风险:①同一集群再放一套(requests 2 CPU / 8Gi + 100Gi EVS),**节点池与 EVS 配额未核实**;②运维会收到两个来源的告警邮件(内容不重叠,属预期) | ①实施前核实集群容量。②把两套监控的职责边界告知运维。**ArgoCD 抢管对象的冲突已消除**——两套各管各的 ns | +| R22 | **`infra-cn4-x86-common-cluster` 的 AppProject `sourceRepos` 白名单不在 Git 里**——该目录下恰好 9 个 yaml、**没有 `project.yaml`**,但 9 个 Application 都写着 `project: infra-cn4-x86-common-cluster`,说明该 AppProject 只存在于 ArgoCD 集群里(**带外创建**),白名单**无法从 Git 核实** | 白名单不含 `helm-charts.git` 时,三个 Application 一定报 `is not permitted in project`;而若提交 `project.yaml` 时漏写现有源,运维的 `kubectl apply` 会**整体覆盖**,打断现存 9 个 App(7 个 Helm + 2 个 Kustomize) | **顺序不能反**:先 `kubectl get appproject infra-cn4-x86-common-cluster -n argocd -o yaml` 读出真实白名单,再提交**并集**(现有 ∪ `helm-charts.git` ∪ `helm-chart-value.git`)。本机无集群访问,**此项待集群侧执行** | --- From 28fc90b11e4d8b5ebe40e60611e3f7b3ceba0297 Mon Sep 17 00:00:00 2001 From: TangJia025 <574451426@qq.com> Date: Thu, 17 Sep 2026 16:48:48 +0800 Subject: [PATCH 3/5] =?UTF-8?q?docs(design):=20R22=20=E7=BB=93=E6=A1=88?= =?UTF-8?q?=E2=80=94=E2=80=94AppProject=20=E7=99=BD=E5=90=8D=E5=8D=95?= =?UTF-8?q?=E7=BB=8F=E8=BF=90=E7=BB=B4=E7=A1=AE=E8=AE=A4=EF=BC=8C=E6=97=A0?= =?UTF-8?q?=E9=9C=80=E6=8F=90=E4=BA=A4=20project.yaml?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 与运维确认:三个 Application 按现有配置即可正常拉取 helm-charts.git / helm-chart-value.git,不存在访问问题。 故不提交 project.yaml:该 AppProject 是带外创建的真实定义, 从 Git 覆盖它只会引入不一致,也消除了「漏写现有源会整体覆盖、 打断现存 9 个 App」这一风险。 Co-Authored-By: Claude Code --- ...\256\276\346\212\200\346\234\257\350\256\276\350\256\241.md" | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git "a/docs/\345\276\256\346\234\215\345\212\241\345\217\257\350\247\202\346\265\213\346\200\247\345\273\272\350\256\276\346\212\200\346\234\257\350\256\276\350\256\241.md" "b/docs/\345\276\256\346\234\215\345\212\241\345\217\257\350\247\202\346\265\213\346\200\247\345\273\272\350\256\276\346\212\200\346\234\257\350\256\276\350\256\241.md" index 1c138af..ee867f0 100644 --- "a/docs/\345\276\256\346\234\215\345\212\241\345\217\257\350\247\202\346\265\213\346\200\247\345\273\272\350\256\276\346\212\200\346\234\257\350\256\276\350\256\241.md" +++ "b/docs/\345\276\256\346\234\215\345\212\241\345\217\257\350\247\202\346\265\213\346\200\247\345\273\272\350\256\276\346\212\200\346\234\257\350\256\276\350\256\241.md" @@ -934,7 +934,7 @@ flowchart LR | R14 | **同一日志流内并存两套格式**:obs-sdk 契约行与 `robot-framework-lib` 的 logrus JSON(后者无 `service` / `env` / `community`,且 `level: fatal`、`time` 秒精度均与契约冲突)(§4.6.2) | LTS 侧的检索 / 聚合 / 告警规则要同时兼容两种格式;且**不是过渡期现象**——框架那 121 处打点会长期保留,两套格式的共存是稳态 | 明确 LTS 侧怎么处理这两套(分别配解析?云端做字段归一化?只对 obs-sdk 行做索引、logrus 行仅全文检索?)。**这一点未定之前,「结构化解析配得上」仍不成立** | | R20 | ~~**中心集群的 HA 未验证**~~ → **【2026-09-17 已定论】单 URL + `replicas: 2` 是分片、不是 HA**:Prometheus 是单机 TSDB,两副本互不知晓、各自持不完整数据,**告警不可信**(§4.7.2) | 若按原设计上 2 副本,会得到「看似有冗余、实则规则评估各自不准」的最坏形态——`rate()` / `sum()` 偏离真值、`absent()` 误报,而**中心告警是「一条规则覆盖全部集群」这个收益的根基** | **已定论:一期 `replicas: 1`**(靠 StatefulSet 分钟级重建 + Agent WAL 兜);`replicas: 2` 要真成立**只能靠 Agent 双端点 fan-out**(出网流量与磁盘各 ×2),本期不做。⚠️ **两副本也不得加区分性 `external_labels`**(会破坏 `group_by: [cluster, alertname]`)。二期换 VictoriaMetrics(接收端本身成集群);换 VM 时若做副本,必须配 `-dedup.minScrapeInterval`,否则各写一份、**数据翻倍** | | R21 | ~~**中心落在哪个集群、与资源块监控栈「共存还是替换」未定案**~~ → **【2026-09-17 已定案】中心落在 `infra-cn4-x86-common-cluster`,ns `infra-monitoring-community`;与资源块的 `infra-monitoring` 同集群、不同 ns,两套完全独立**(§4.8.1) | 剩余风险:①同一集群再放一套(requests 2 CPU / 8Gi + 100Gi EVS),**节点池与 EVS 配额未核实**;②运维会收到两个来源的告警邮件(内容不重叠,属预期) | ①实施前核实集群容量。②把两套监控的职责边界告知运维。**ArgoCD 抢管对象的冲突已消除**——两套各管各的 ns | -| R22 | **`infra-cn4-x86-common-cluster` 的 AppProject `sourceRepos` 白名单不在 Git 里**——该目录下恰好 9 个 yaml、**没有 `project.yaml`**,但 9 个 Application 都写着 `project: infra-cn4-x86-common-cluster`,说明该 AppProject 只存在于 ArgoCD 集群里(**带外创建**),白名单**无法从 Git 核实** | 白名单不含 `helm-charts.git` 时,三个 Application 一定报 `is not permitted in project`;而若提交 `project.yaml` 时漏写现有源,运维的 `kubectl apply` 会**整体覆盖**,打断现存 9 个 App(7 个 Helm + 2 个 Kustomize) | **顺序不能反**:先 `kubectl get appproject infra-cn4-x86-common-cluster -n argocd -o yaml` 读出真实白名单,再提交**并集**(现有 ∪ `helm-charts.git` ∪ `helm-chart-value.git`)。本机无集群访问,**此项待集群侧执行** | +| R22 | ~~**`infra-cn4-x86-common-cluster` 的 AppProject `sourceRepos` 白名单不在 Git 里**(带外创建,无法从 Git 核实)~~ → **【2026-09-17 已定案】经与运维确认:三个 Application 按现有配置即可正常拉取,`helm-charts.git` / `helm-chart-value.git` 的访问不存在问题** | ~~白名单不含 `helm-charts.git` 时报 `is not permitted in project`;提交 `project.yaml` 漏写现有源会整体覆盖、打断现存 9 个 App~~ **风险消除** | **无需动作,且刻意不提交 `project.yaml`**——该 AppProject 是带外创建的真实定义,从 Git 覆盖它只会引入不一致。白名单与仓库凭据由运维侧保证 | --- From b64c32ed88b2dbc3f912e95bdbc693daae15bd16 Mon Sep 17 00:00:00 2001 From: TangJia025 <574451426@qq.com> Date: Fri, 18 Sep 2026 17:30:47 +0800 Subject: [PATCH 4/5] =?UTF-8?q?docs(design):=20=E4=BF=AE=E6=AD=A3=E8=87=AA?= =?UTF-8?q?=E9=87=87=20job=20=E7=9A=84=E6=8C=87=E6=A0=87=E5=8F=A3=E5=BE=84?= =?UTF-8?q?=E4=B8=8E=E9=89=B4=E6=9D=83=E5=89=8D=E6=8F=90=EF=BC=88backlog#2?= =?UTF-8?q?062=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 在 infra-cn4-x86-common-cluster / ns infra-monitoring-community 实测踩到两点, 原文都没写对: 1. **指标口径**:中心是**纯接收端**、自己不发 remote_write,所以 prometheus_remote_storage_samples_total / _failed_total 这两个**发送侧**指标 在本地根本不存在,不能拿它们当观测依据,只能看 _in_total 一族。 2. **自采 job 必须带 basic_auth**:basic_auth_users 保护的是**整个 HTTP server** (/metrics 与 /-/healthy 同样在内),不带凭据会拿 401、 up{job="prometheus"} 恒为 0,这些指标一条都进不了 TSDB。 失效形态是「静默为空」而非报错 —— 看起来一切正常,比误报更危险。 配套实现:opensourceways/helm-charts#310 + opensourceways/helm-chart-value#148。 Co-Authored-By: Claude Code --- ...276\346\212\200\346\234\257\350\256\276\350\256\241.md" | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git "a/docs/\345\276\256\346\234\215\345\212\241\345\217\257\350\247\202\346\265\213\346\200\247\345\273\272\350\256\276\346\212\200\346\234\257\350\256\276\350\256\241.md" "b/docs/\345\276\256\346\234\215\345\212\241\345\217\257\350\247\202\346\265\213\346\200\247\345\273\272\350\256\276\346\212\200\346\234\257\350\256\276\350\256\241.md" index ee867f0..c4b3efe 100644 --- "a/docs/\345\276\256\346\234\215\345\212\241\345\217\257\350\247\202\346\265\213\346\200\247\345\273\272\350\256\276\346\212\200\346\234\257\350\256\276\350\256\241.md" +++ "b/docs/\345\276\256\346\234\215\345\212\241\345\217\257\350\247\202\346\265\213\346\200\247\345\273\272\350\256\276\346\212\200\346\234\257\350\256\276\350\256\241.md" @@ -773,7 +773,12 @@ charts/grafana/ Deployment + Service + PVC + ConfigMap(数据源/provide | `alertmanager.replicaCount` | **2** | Alertmanager 的 HA 是**真的**(gossip 共享告警状态、同一告警只通知一次),故本期就上 2(§4.7.2) | | `grafana.strategy` | `Recreate` | PVC 是 RWO,Deployment 上滚动更新会卡 Multi-Attach | -**对文档「中心组件全关」的有意偏差**:原文说 kubeStateMetrics / nodeExporter / kubeApiServer 等全关,指的是**不采集群基础设施组件**(§1.3 G5),**不等于「连自己都不采」**。本方案**保留 1 个 `prometheus` 自采 job**——接收端的健康状况全在 `prometheus_remote_storage_samples_total` / `..._failed_total` / `prometheus_tsdb_storage_blocks_bytes` 上,没有它就无法回答「Agent 到底写进来了没 / 盘快满没满」,而这正是 R2 / R3 / R20 的唯一观测手段。 +**对文档「中心组件全关」的有意偏差**:原文说 kubeStateMetrics / nodeExporter / kubeApiServer 等全关,指的是**不采集群基础设施组件**(§1.3 G5),**不等于「连自己都不采」**。本方案**保留 1 个 `prometheus` 自采 job**——接收端的健康状况全在 `prometheus_remote_storage_highest_timestamp_in_seconds` / `..._samples_in_total` / `prometheus_tsdb_storage_blocks_bytes` 上,没有它就无法回答「Agent 到底写进来了没 / 盘快满没满」,而这正是 R2 / R3 / R20 的唯一观测手段。 + +⚠️ **这条 job 有两个实测踩到的前提**(2026-09-18 在 `infra-cn4-x86-common-cluster` / ns `infra-monitoring-community` 验证): + +1. **指标口径**:中心是**纯接收端**、自己不发 `remote_write`,所以 `prometheus_remote_storage_samples_total` / `_failed_total` 这两个**发送侧**指标在本地**根本不存在**,不能拿它们当观测依据,只能看 `_in_total` 一族。这是「照搬发送端经验」最容易写错的一处。 +2. **该 job 必须带 `basic_auth`**:`--web.config.file` 的 `basic_auth_users` 保护的是**整个 HTTP server**(`/metrics` 与 `/-/healthy` 同样在内,探针只能用 `tcpSocket` 也是同一个原因),而自采走的是普通 HTTP。不带凭据会拿 401 → `up{job="prometheus"}` 恒为 0 → 上面这些指标**一条都进不了 TSDB**。这正是「留了 job 却等于没留」的失效形态:**不报错,静默为空**——比误报更危险,因为它看起来一切正常。 **入口与安全**(§4.7.2 的安全项落到配置): From dc939c970d152c2cac69f043ce6358782ce75e9b Mon Sep 17 00:00:00 2001 From: TangJia025 <574451426@qq.com> Date: Sun, 20 Sep 2026 13:23:10 +0800 Subject: [PATCH 5/5] =?UTF-8?q?docs(design):=20=E5=85=A5=E5=8F=A3=E5=90=88?= =?UTF-8?q?=E5=B9=B6=E4=B8=BA=E5=8D=95=E5=9F=9F=E5=90=8D=20monitoring.osin?= =?UTF-8?q?fra.cn=EF=BC=8CGrafana=20=E8=B5=B0=E5=AD=90=E8=B7=AF=E5=BE=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - §4.7.4:新增「两个 web 面合并成一个域名」——合并的理由(华为云 WAF 逐域名 配置,少一个域名少一份运维申请;istio 一个 host 只认一个 VirtualService, 两个 VS 声明同一 host 不会合并)、代价(公网可达性绑在一起;二期源 IP 白名单失去 host 级隔离)与分工(Prometheus 无需路由前缀,只有 Grafana 需要 serve_from_sub_path)。 - §4.8.1:chart 清单去掉 grafana 的 VirtualService 并说明原因;入口与安全 一节改写为合并域名的路由表,并补「域名要经 WAF 单独申请、证书是 *.osinfra.cn 泛域名故无额外成本」。 - §4.8.2:Agent 写入地址同步为 https://monitoring.osinfra.cn/api/v1/write。 - §4.9.1:Grafana 入口改为「不再自带 VS」+ 子路径环境变量两项,并写明 探针路径为何不用跟着改。 --- ...00\346\234\257\350\256\276\350\256\241.md" | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git "a/docs/\345\276\256\346\234\215\345\212\241\345\217\257\350\247\202\346\265\213\346\200\247\345\273\272\350\256\276\346\212\200\346\234\257\350\256\276\350\256\241.md" "b/docs/\345\276\256\346\234\215\345\212\241\345\217\257\350\247\202\346\265\213\346\200\247\345\273\272\350\256\276\346\212\200\346\234\257\350\256\276\350\256\241.md" index c4b3efe..9e281f2 100644 --- "a/docs/\345\276\256\346\234\215\345\212\241\345\217\257\350\247\202\346\265\213\346\200\247\345\273\272\350\256\276\346\212\200\346\234\257\350\256\276\350\256\241.md" +++ "b/docs/\345\276\256\346\234\215\345\212\241\345\217\257\350\247\202\346\265\213\346\200\247\345\273\272\350\256\276\346\212\200\346\234\257\350\256\276\350\256\241.md" @@ -685,7 +685,14 @@ with _context.bind(community="openEuler", request_id="req-123"): | **故障域与告警解耦** | 关键在**告警用 Alertmanager、不用 Grafana Alerting**——Grafana 挂了只是看不了图,**告警不受影响** | | **不单开一个集群** | 多一份集群底座成本,还**新增一条跨集群链路**(Grafana → Prometheus) | -**两点可照抄资源块**:Grafana PVC 只要 **10Gi**(dashboard 走 ConfigMap provisioning,PVC 只存状态);**和 Prometheus 复用同一个 istio gateway 的 ELB、按 host 区分**(`grafana.osinfra.cn` vs `prometheus.osinfra.cn`),省一个 ELB。——这比「同一个 ELB、端口分开」更好:**TLS 统一在网关终止,公网不再出现任何明文端口**。 +**两点可照抄资源块**:Grafana PVC 只要 **10Gi**(dashboard 走 ConfigMap provisioning,PVC 只存状态);**和 Prometheus 复用同一个 istio gateway 的 ELB**。——这比「同一个 ELB、端口分开」更好:**TLS 统一在网关终止,公网不再出现任何明文端口**。 + +**两个 web 面合并成一个域名**(`monitoring.osinfra.cn`),按**子路径**区分:`/grafana/` 给人看大盘、`/api/v1/write` 给 Agent 写。不拆两个域名的理由有二: + +1. 该 zone 的对外域名统一经**华为云 WAF**、且是**逐域名配置**的(现存域名要么 CNAME 到 `.vip1.huaweicloudwaf.com`,要么 A 记录直指 WAF VIP、响应带 `Server: CloudWAF`)——**少一个域名就少一份运维申请**。 +2. **istio 一个 host+gateway 只认一个 VirtualService**,两个 VS 声明同一 host **不会合并**、只会有一个生效(另一份的路由**静默 404**)。所以合并后该 host 的全部路由(含兜底 404)必须由**一个** chart 声明——本方案放在 `charts/prometheus`,`charts/grafana` 的 `istio.enabled` 保持 `false`。 + +代价要说清:**两个服务的公网可达性被绑在一起**(一条路由写错两个一起挂);二期给 Agent 做源 IP 白名单(§6 R3)时失去 host 级隔离,要退到 path 级 `AuthorizationPolicy`。**Prometheus 侧不需要任何路由前缀改动**——它公网只有 `/api/v1/write` 一个路径,本身就是唯一路径;**只有 Grafana 侧**需要 `serve_from_sub_path`(见 §4.9.1)。 **面板筛选策略**:`community` **默认全选、不强制**——中心 Prometheus 存储已聚合(全部 17 集群汇入同一实例),不筛选无查询放大问题;`community` 是低基数 label,`sum by (community)` 成本可忽略。**必选会禁掉横向对比,而横向对比恰是指标大盘最有价值之处**;担心误读时在面板标题显示当前筛选值即可。 @@ -744,9 +751,11 @@ charts/prometheus/ StatefulSet + Service + headless Service + ConfigMap(pro + ConfigMap(规则) + Secret(web-config) + SA + VirtualService + Namespace charts/alertmanager/ StatefulSet + Service + headless Service + ConfigMap(alertmanager.yml + *.tmpl) + Secret(SMTP) + SA + VirtualService -charts/grafana/ Deployment + Service + PVC + ConfigMap(数据源/provider/大盘) + Secret(admin) + SA + VirtualService +charts/grafana/ Deployment + Service + PVC + ConfigMap(数据源/provider/大盘) + Secret(admin) + SA ``` +⚠️ **`charts/prometheus` 的 VirtualService 声明的是合并域名 `monitoring.osinfra.cn` 的全部路由**(含面向 Grafana 的 `/grafana` 与 `/api/health`,§4.7.4),所以 `charts/grafana` **不再自带 VS**——istio 一个 host 只认一个 VS,两个 chart 各声明一份会互相抢。 + **明确不做 Pushgateway**:资源块那套里有 Pushgateway,是因为它的**拨测 CronJob** 跑完即弃、指标没处挂,只能 push。本方案的服务侧都是**长驻进程、自己有 `/metrics`**,没有 push 场景,故**一期不部署**。将来若纳入 CronJob 形态的服务(跑完即弃、`/metrics` 随 pod 消失),再单独补一个 chart——它的体量很小,但届时必须先解决「多个集群的 CronJob 往同一个 Pushgateway 推」的 label 归属与去重。 **为什么不用 Operator**(本次决策): @@ -783,7 +792,8 @@ charts/grafana/ Deployment + Service + PVC + ConfigMap(数据源/provide **入口与安全**(§4.7.2 的安全项落到配置): - ⚠️ **不照搬资源块的公网明文 HTTP**——资源块当前是 `http://:9090` + Basic Auth,而其 TLS 清单在 `kustomization.yaml` 中**被注释、标为 Phase 2**。本方案**搭建时就要上 HTTPS**(§4.7.1),不留这个尾巴。 -- **复用集群现成的 istio**(`istio-system/istio-gateway-https`,已终止 TLS、泛域名证书走 Vault),`VirtualService` 只放行 `POST /api/v1/write`,**其余路径一律 404**——UI 与 API 不对公网暴露。选 istio 的理由:TLS 终止能力已存在且自动化,**不需要申请证书、不需要新 ELB**;`VirtualService` 天然支持精确 path 匹配,而 ELB 注解做不到。 +- **复用集群现成的 istio**(`istio-system/istio-gateway-https`,已终止 TLS、泛域名证书走 Vault),由 `charts/prometheus` 的 `VirtualService` 统一声明**合并域名** `monitoring.osinfra.cn` 的全部路由:`/api/v1/write`(仅 `POST`)→ 中心 Prometheus,`/grafana` 与 `/api/health` → Grafana,**其余路径一律 404**——Prometheus 的 UI 与查询 API 不对公网暴露。选 istio 的理由:TLS 终止能力已存在且自动化,**不需要申请证书、不需要新 ELB**;`VirtualService` 天然支持精确 path 匹配,而 ELB 注解做不到。 +- ⚠️ **域名本身要单独向运维申请**:该 zone **无泛解析**,对外域名统一经**华为云 WAF** 且**逐域名配置**(§4.7.4)——合并成一个域名就只申一次。证书不成问题:网关用的 `osinfra-cn-tls` 是 `*.osinfra.cn` 泛域名(DigiCert RapidSSL,SAN 含 `*.osinfra.cn` / `osinfra.cn`),任何单层子域都覆盖。 - 凭据(整个 `web-config.yaml`,含 17 个 bcrypt hash)走 **Vault → Secret 挂文件**,不进 values 明文、不进 Git。**轮换的最大优势**:`web.config.file` 是**每个 HTTP 请求都重读**的,改 Vault **即刻生效、无需 reload 或重启**;轮换顺序必须是「先加新 hash(新旧并存)→ 再逐集群改 Agent → 最后摘旧 hash」。 - ⚠️ **开了 web auth 后探针必须用 `tcpSocket`**:`--web.config.file` 的 basic auth 在 exporter-toolkit 层包裹**整个** HTTP server,`/-/healthy` / `/-/ready` 同样受保护,而 k8s 的 `httpGet` 探针无法携带凭据(prometheus/prometheus#9166)。三个 chart 一律用 `tcpSocket`。 - ⚠️ **`infra-monitoring-community` namespace 必须带 `istio-injection: disabled`**,否则 Prometheus / Grafana 会被注入 sidecar,在 STRICT mTLS 下出 gateway → pod 握手问题。`CreateNamespace=true` 建的 ns **不带这个 label**,故由 chart 渲染 Namespace 并打上 label。 @@ -801,7 +811,7 @@ charts/grafana/ Deployment + Service + PVC + ConfigMap(数据源/provide | --- | --- | | **采集目标** | `kubernetes_sd_configs`(按 ns / label relabel)选各服务 `/metrics`——**服务侧无需改配置,也不依赖 ServiceMonitor CRD**;基础设施指标仍复用云上,Agent 不重复抓 | | **来源标注** | `cluster` label 是告警与大屏 `group_by` 的依据,在 Agent 侧统一打上 | -| **写入地址** | `https://prometheus.osinfra.cn/api/v1/write`(istio HTTPS 入口,见 §4.8.1) | +| **写入地址** | `https://monitoring.osinfra.cn/api/v1/write`(istio HTTPS 入口,与 Grafana 合用一个域名,见 §4.7.4 / §4.8.1) | | **鉴权** | `basic_auth` + `password_file` 挂 Secret,**每集群一套凭据**——跨账号场景各带各的,与账号归属无关(§4.7.2) | | **写入瘦身** | 用 `write_relabel_configs` drop 无用 label(资源块的做法是 drop `container` / `container_id` / `uid`)——直接省中心磁盘与跨 Region 流量 | | **资源限制** | 常驻但轻量,按集群规模给 requests / limits | @@ -830,7 +840,8 @@ charts/grafana/ Deployment + Service + PVC + ConfigMap(数据源/provide | --- | --- | --- | | `deployment.yaml` | 实例本体 | `replicas: 1`、`strategy: Recreate`(PVC 是 RWO)、`runAsNonRoot`、**镜像钉版本** | | `pvc.yaml` | 只存 Grafana 自身状态 | **10Gi**——dashboard 走 provisioning 挂载,不占 PVC(§4.7.4)。⚠️ Deployment 用不了 `volumeClaimTemplates`,PVC 是独立对象 | -| `service.yaml` + `virtualservice.yaml` | 入口 | 集群内 Service + **istio VirtualService**(`grafana.osinfra.cn`,`prefix: /`);与 Prometheus **复用同一个 gateway 的 ELB、按 host 区分**(§4.7.4) | +| `service.yaml` | 入口 | 集群内 Service。**本 chart 不再自带 VirtualService**——域名与 Prometheus 合并后(§4.7.4),该 host 的全部路由由 `charts/prometheus` 统一声明,此处 `istio.enabled` 保持 `false` | +| (环境变量) | 子路径 | `GF_SERVER_ROOT_URL=https://monitoring.osinfra.cn/grafana/` + `GF_SERVER_SERVE_FROM_SUB_PATH=true`。**两者必须成对**:`serve_from_sub_path` 要求 `root_url` 带同一子路径,否则静态资源与重定向会链到根路径而 404。⚠️ **探针不用跟着改**——子路径下 `/api/health` **仍在根 `/api` 下**(Grafana 的 `SubPathRedirect` 中间件对 `/api` 前缀明确豁免重定向),且探针直连容器、本就不过网关 | | `configmap.yaml`(数据源) | 数据源 provisioning | 见 §4.9.2 | | `configmap.yaml`(provider) | dashboard 加载器 | 从 `/var/lib/grafana/dashboards` 读;`folder: Community` | | admin Secret | 管理员密码 | Vault → Secret 注入,不进 values |