30.9 服务治理平台

一句话

治理平台不是 “又一个控制台”,而是把注册中心、配置中心、容器平台、监控、日志这五个底座封装成 “人和服务打交道的唯一入口”;它的价值不在功能列表有多长,而在每一次操作都有权限、有审计、可回滚——否则它就是全公司权限最大、最没人管的一个后台。

先分清四个东西

系统 回答什么 谁用
注册中心 服务现在有哪些节点活着 服务消费者(机器)
配置中心 配置改成什么值(限流阈值、超时) 服务消费者(机器)
监控 / 追踪 现在好不好、慢在哪 开发/运维(人)
治理平台 谁来改、改成什么、改完怎么回退 开发/运维(人)

🔑 治理平台本身不产生数据,它只是把上面几个底座的写接口包装成 “带权限和审计的操作”。这一条决定了它的设计重点:权限模型与操作记录,比界面美观重要一百倍。

六块功能,各落在哪个底座

功能 具体操作 落到哪个底座
服务管理 服务上下线、节点添加/删除、服务与节点查询 注册中心
服务治理 限流阈值、降级开关、切流量(按 IDC/分组) 配置中心 + 路由规则(规则语法见 30.8 服务路由)
服务监控 服务依赖拓扑图、QPS / AvgTime / P999 追踪系统的拓扑 + 监控系统(Grafana)
问题定位 宏观看哪个服务异常,微观追一次请求的全链路 监控 + 追踪(50.3 链路)
日志查询 按用户请求 ID 或某类请求查日志 ELK / Loki
服务运维 分批发布、扩缩容 容器平台(K8s)

这六块里,只有前两块是 “写操作”(会改变线上行为),后四块是 “读操作”。写操作才是治理平台的风险所在——把它们和读操作放在同一个页面、同一套权限里,是许多事故的起点。

三层结构:Portal / API / DB

┌─────────────── Web Portal(人用的界面)───────────────┐
│ 服务管理   服务治理   服务监控   服务运维(+操作历史) │
└───────────────────────┬───────────────────────────────┘
                        │ 统一 API
┌───────────────────────▼───────────────────────────────┐
│ 添加/删除服务 · 上下线节点 · 限流/降级/切流 · 扩缩容   │
└──────┬────────────┬────────────┬────────────┬─────────┘
       │            │            │            │
   注册中心      配置中心      容器平台      监控/日志
┌──────▼────────────────────────────────────────────────┐
│ DB:用户与权限 · 服务负责人 · 操作记录 · 规则版本      │
└───────────────────────────────────────────────────────┘

三层的职责很清晰,但 DB 那层最容易被砍掉:很多团队第一版只做 “API + 界面”,把权限写死在代码里、把操作记在日志里,结果是 “谁都能改、改完查不到”。

权限模型:三级角色 + 按服务授权

治理平台的功能太强,权限必须细到 “这个人只能改他负责的服务”:

角色 能做什么 给谁
浏览(viewer) 看服务列表、监控、拓扑、日志 全员、值班同学
操作(operator) 对自己负责的服务做限流/降级/上下线/扩缩容 服务负责人
管理员(admin) 跨服务操作、改权限、批量操作 SRE / 架构组(人数应极少)

⚠️ 只做 “能登录 / 不能登录” 的权限,等于没有权限:任何一个人拿到账号,就能把核心服务的限流阈值改成 0。按服务负责人授权是治理平台的第一道闸。

审计与回滚:把 “谁改的” 变成可查的事实

每次写操作必须落一条记录,至少四个字段:谁、对哪个服务、改了什么、从多少改到多少。这条记录同时解决三件事:

用途 怎么用
故障复盘 “这次雪崩前 3 分钟,谁把 order-svc 的限流从 800 调到 3000”
快速回滚 拿上一条记录的旧值改回去,比重新想 “原来是多少” 快一个数量级
责任与合规 审计要求 “生产变更可追溯” 时,这是唯一证据

一段可运行的最小骨架

把 “按服务授权 + 越权拒绝 + 审计 + 回滚” 写成 30 行,能跑就说明模型没问题(本机实测,Python 3.14 / macOS arm64):

ROLES = {"viewer": 0, "operator": 1, "admin": 2}
USERS = {                       # operator 只负责自己那摊服务
    "alice": {"role": "operator", "services": {"user-svc"}},
    "bob":   {"role": "operator", "services": {"order-svc"}},
    "carol": {"role": "admin",    "services": "*"},
}
STATE = {"user-svc": {"limit": 1000}, "order-svc": {"limit": 800}}
AUDIT = []

def apply(user, svc, action, value=None):
    u = USERS.get(user)
    if not u:                                  return f"拒绝:用户 {user} 不存在"
    if ROLES[u["role"]] < ROLES["operator"]:   return f"拒绝:{user} 只有浏览权限"
    if u["services"] != "*" and svc not in u["services"]:
        return f"拒绝:{user} 不负责 {svc}(负责人才能改)"
    before = dict(STATE[svc])
    STATE[svc][action] = value
    AUDIT.append((user, svc, action, before.get(action), value))
    return f"成功:{user} 把 {svc}.{action} 从 {before.get(action)} 改为 {value}"

真实运行结果:

成功:alice 把 user-svc.limit 从 1000 改为 500
拒绝:alice 不负责 order-svc(负责人才能改)
成功:bob 把 order-svc.limit 从 800 改为 2000
成功:carol 把 order-svc.limit 从 2000 改为 3000

审计流水:
  alice  user-svc   limit  1000 → 500
  bob    order-svc  limit  800 → 2000
  carol  order-svc  limit  2000 → 3000

回滚最后一条:order-svc.limit 恢复为 2000

三行就说明了治理平台的三条底线:越权被拒、每次变更留痕、能按记录回滚。界面长什么样是次要的。

最小可用集:先做四件

从 0 搭平台最容易犯的错是 “先把六个模块的界面都做出来”。合理的顺序是:

优先级 做什么 为什么先做
P0 服务与节点列表(读)+ 上下线节点(写) 故障时最常用:摘节点、拉回节点
P0 限流/降级开关(写)+ 操作记录 止血三件套里最快的两个
P1 服务依赖拓扑(读) 定位 “谁拖死谁” 的地图
P1 扩缩容(写,接容器平台) 容量类故障的唯一解
P2 日志查询、链路下钻 通常已有现成系统,做嵌入即可
P2 权限细化到人、审批流 团队变大后必须补,早期可先按服务授权

生产落地:指名 → 可抄 → 可验 → 可退

指名:注册中心用 Nacos / Consul,配置用 Nacos / Apollo,治理与路由规则用 Dubbo Admin(Java 系)或 Istio + Kiali(Mesh 系),监控面板用 Grafana,日志用 ELK / Loki,容器操作用 K8s API;想少自研就先用 Nacos 控制台 + Grafana + Argo Rollouts 拼一个 “够用版”,再逐步收口。

可抄(写操作的最小接口约定,任何语言都适用):

POST /api/services/{svc}/nodes/{ip}:{port}/offline     # 摘节点
POST /api/services/{svc}/limit    {"value": 500}       # 改限流
POST /api/services/{svc}/degrade  {"enable": true}     # 开降级
GET  /api/audit?svc=order-svc&amp;limit=50                 # 审计
POST /api/audit/{id}/rollback                          # 按记录回滚

可验:四个检查,缺一条就说明平台还只是 “控制台”。

检查 合格线
越权操作被拒 operator 改别人的服务,返回 403 且有审计
每次写操作可追溯 能在 1 分钟内查出 “过去 24 小时谁改过这个服务”
能一键回滚 用审计里的旧值恢复,秒级完成
批量操作有保护 一次影响超过 N 个服务时要求二次确认或审批

可退:治理平台本身也会故障,退路是 “平台挂了,线上不受影响”——所有规则必须已经落到注册中心/配置中心里,平台只是写入通道,不是运行时依赖。这一点常被做错:有的平台把路由规则存在自己的 DB 里,平台一挂,服务连调用都找不到节点。

反模式

反模式 后果
权限只做 “能登录/不能登录” 任何人拿到账号就能改核心服务阈值
写操作不记审计 故障复盘时说不清 “谁在什么时候改了什么”
平台把规则存在自己 DB 里 平台故障 → 服务找不到节点,运行时被平台绑死
一次全量下发限流/降级 阈值给错时全站同时生效,没有灰度窗口
治理操作没有二次确认 手滑点错服务,瞬间全站限流为 0
扩缩容按钮无并发保护 两个人同时点,实例数翻倍,成本飙升
把监控大屏和写操作放同一页面 值班同学误触,把 “看一眼” 变成 “改一下”
平台无审批流就开放给全员 变更不可控,等于没有变更管理(40.5 变更成本)

三条铁律

🔑 治理平台是写通道,不是运行时依赖:规则最终落在注册中心/配置中心,平台挂了业务照跑。

🔑 权限按服务授权,操作必须留痕:只做登录态校验的治理平台,是全公司最危险的内部系统。

🔑 每个写操作都要能一键回滚:审计里的 “旧值” 就是回滚按钮,没有旧值的审计等于日志。

自检

Q1 治理平台和注册中心、配置中心是什么关系?它自己该存什么?

▸ 答案:治理平台是前面的统一入口,本身不提供运行时能力——服务发现靠注册中心、配置下发靠配置中心,平台只调用它们的写接口。它自己该存的只有三类:用户与权限、服务负责人、操作记录与规则版本。如果把规则本体存在平台 DB 里,平台故障就会变成业务故障。

Q2 为什么权限要细到 “按服务负责人授权”,只分 “能登录/不能登录” 不行?

▸ 答案:因为治理平台的写操作(限流、降级、下线)能瞬间改变线上行为。只做登录态校验,等于任意一个账号都能把核心服务的限流阈值改成 0。按服务授权把 “能改什么” 收敛到负责人,是唯一可运营的模型;管理员角色应极少人持有。

Q3 审计记录至少要包含哪几个字段?它除了 “合规” 还有什么实际用途?

▸ 答案:谁、对哪个服务、改了什么、从多少改到多少(时间戳与操作 ID 也要)。实际用途有三个:① 故障复盘时还原 “3 分钟前谁改了什么”;② 一键回滚(拿旧值改回去);③ 判断变更与故障的相关性(把变更时刻与错误率曲线对齐)。

Q4 为什么说 “治理平台挂了,线上不能受影响”?

▸ 答案:因为它的定位是写通道:规则写进注册中心/配置中心后,消费者只订阅那两个底座,不依赖平台。如果平台把路由规则或限流阈值存在自己的 DB 里、由 SDK 直连平台读取,那么平台一挂,服务就找不到节点或拿不到阈值——把 “管理面” 变成了 “数据面依赖”。

Q5 最小可用集为什么先做 “服务/节点上下线 + 限流降级 + 操作记录”?

▸ 答案:因为故障时的止血动作只需要这几件:摘节点、拉回节点、改阈值、开降级,而且都必须 “秒级生效 + 可回滚”。拓扑图和日志查询是 “定位用”,可以接现成系统;权限细化与审批流是团队变大后的治理需求,早期可用 “按服务授权” 过渡。

Q6 一次把限流阈值下发给全部服务,风险在哪?

▸ 答案:没有灰度窗口。阈值给错(少一个 0 或多一个 0)会同时影响所有服务,且平台往往缺少 “先 1% 再全量” 的能力。正确做法是按服务/按机房分批下发,每批观察错误率与 QPS,出问题只回滚这一批。

Q7 治理平台为什么要把 “读操作” 和 “写操作” 在界面上分开?

▸ 答案:因为值班场景下最常见的误操作就是 “想看一眼,结果点错了按钮”。把监控、拓扑、日志(读)与限流、降级、下线(写)分区,并对写操作加二次确认,能把 “误触” 的概率降到接近零;这也是很多事故复盘里最廉价的一条改进。

关联