
1. 从一次面板迁移翻车说起Grafana 在监控链路里到底管什么第一次把 Grafana 面板从测试环境搬到生产环境时我的预期是导出 JSON、导入、收工实际结果是所有图表变成一片空白日志里反复刷着一行failed to upgrade legacy queries datasource im7_otuvz was not found。那一刻我才意识到Grafana 面板本身不存数据它只是一堆查询语句加展示配置的组合真正决定这张图能不能出结果的是背后那条数据源 - 查询 - 展示的完整链路。任何一个环节的标识对不上面板就是一张好看的废纸。先把定位说清楚。Prometheus 负责采集和存储时间序列数据它提供了 PromQL 这个查询语言和一套 HTTP 接口Grafana 不采集数据它做的事是连接数据源、把查询结果画出来、再加上告警和权限这一层。很多人把这两个东西混在一起讲导致排查问题时方向跑偏——Grafana 上图表不显示你去查 Prometheus 的采集目标查半天发现采集完全正常问题其实在 Grafana 的数据源 UID 或者时间范围设置上。1.1 Grafana 与 Prometheus 的职责边界用一句话概括Prometheus 管数据从哪来、怎么存Grafana 管数据怎么看、怎么看懂。能力PrometheusGrafana指标采集核心能力基于 pull 模型抓取不做采集时序存储内置 TSDB默认本地磁盘不存储指标数据查询语言PromQL转发 PromQL自身不解析可视化只有简单的 Graph 页面核心能力多数据源统一展示告警规则在 Prometheus 侧求值支持 Grafana 托管告警与展示 Prometheus 规则通知发送只负责把告警推给 Alertmanager可做联系人、通知策略也可对接外部 Alertmanager这张表的意义在于划清排查边界。比如告警没收到这件事可能是 Prometheus 规则没触发、可能是 Alertmanager 路由没匹配上、也可能是 Grafana 侧联系人配置错了。把链路拆成三段逐段验证比盲目改配置快得多。1.2 什么时候该上 Grafana什么时候一个 curl 就够了我见过不少人一上来就搭全套结果只有三台机器、五个指标维护成本比收益还高。判断标准其实很朴素指标数量少于 20 个、看的人只有你自己直接curl localhost:9090/api/v1/query?queryup或者用 Prometheus 自带的 Graph 页面就够需要多人共享、需要按团队分权限、需要历史趋势对比、需要把告警发到群里这时候 Grafana 的价值才体现出来一旦出现不同数据源要放在一张大屏上的需求比如同时看 Prometheus 的指标、数据库的连接数、日志系统的错误量Grafana 的多数据源能力就是刚需。提示Grafana 本身对硬件要求不高2 核 4G 跑几十个仪表盘完全够用。真正吃资源的是 Prometheus 的 TSDB 和 Grafana 里那些一次查 30 天原始数据的面板。这套组合最常见的落地场景就是业务服务暴露/metrics接口Prometheus 定时抓取Grafana 做展示层Alertmanager 做通知层。整条链路跑通之后日常新增一个监控项的工作量基本就是改一个规则文件和加一个面板边际成本很低。2. Docker 环境下 Prometheus 与 Grafana 的镜像选型与部署用 Docker 部署这套组合是现在最主流的方式好处是环境隔离干净、版本切换方便、迁移时打包带走就行。但镜像标签怎么选、数据怎么持久化、配置文件怎么挂载这三点没想清楚后面全是坑。2.1 镜像标签别用 latest官方镜像的latest标签会随上游更新而变动某次docker compose pull之后版本悄悄从 9.x 跳到 10.x仪表盘布局和告警配置格式都可能变。我在测试环境就吃过一次亏Grafana 从 9 升到 10 之后老版本的部分配置项改了默认值触发了一堆告警抖动。推荐做法是锁定具体版本docker pull prom/prometheus:v2.51.2 docker pull prom/alertmanager:v0.27.0 docker pull grafana/grafana-oss:10.4.2关于grafana/grafana和grafana/grafana-oss的区别简单说前者在 2021 年之后变成了包含部分企业特性的构建后者是纯开源版。自建场景下用grafana-oss更干净功能对个人和中小团队完全够用。内网环境没法直连镜像仓库时用docker save/docker load做离线搬运# 在有网的机器上导出 docker save -o grafana-10.4.2.tar grafana/grafana-oss:10.4.2 docker save -o prometheus-2.51.2.tar prom/prometheus:v2.51.2 # 拷贝到目标机器后导入 docker load -i grafana-10.4.2.tar docker load -i prometheus-2.51.2.tar这里有个细节导出时最好把prom/prometheus:v2.51.2这种带标签的完整名字写全否则导入后镜像名会变成none编排文件里再引用就找不到。另外docker save出来的 tar 包体积会比较大Grafana 镜像通常几百 MB用gzip压一下能省一半空间。2.2 docker-compose 编排文件的逐行拆解下面这份编排文件是我在几个项目里反复用过的版本字段不多但每个都有用处version: 3.8 services: prometheus: image: prom/prometheus:v2.51.2 container_name: prometheus restart: unless-stopped volumes: - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro - ./prometheus/rules:/etc/prometheus/rules:ro - prometheus-data:/prometheus command: - --config.file/etc/prometheus/prometheus.yml - --storage.tsdb.path/prometheus - --storage.tsdb.retention.time15d - --web.enable-lifecycle ports: - 9090:9090 networks: - monitor alertmanager: image: prom/alertmanager:v0.27.0 container_name: alertmanager restart: unless-stopped volumes: - ./alertmanager/alertmanager.yml:/etc/alertmanager/alertmanager.yml:ro - alertmanager-data:/alertmanager ports: - 9093:9093 networks: - monitor grafana: image: grafana/grafana-oss:10.4.2 container_name: grafana restart: unless-stopped environment: GF_SECURITY_ADMIN_USER: admin GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_PASSWORD} GF_USERS_ALLOW_SIGN_UP: false GF_INSTALL_PLUGINS: volumes: - ./grafana/provisioning:/etc/grafana/provisioning:ro - ./grafana/dashboards:/var/lib/grafana/dashboards:ro - grafana-data:/var/lib/grafana ports: - 3000:3000 depends_on: - prometheus networks: - monitor volumes: prometheus-data: grafana-data: alertmanager-data: networks: monitor: driver: bridge几个值得展开的点--web.enable-lifecycle开启之后改完prometheus.yml不用重启容器直接curl -X POST http://localhost:9090/-/reload就能热加载。这个参数在生产环境很实用重启 Prometheus 会中断采集虽然时间很短但在告警密集的时段容易造成误报。GF_SECURITY_ADMIN_PASSWORD用环境变量注入而不是写死在编排文件里配合.env文件管理。.env记得加进.gitignore我见过有人把带密码的编排文件直接推到公开仓库虽说不是生产环境但习惯一旦坏了很难改回来。三个数据卷prometheus-data、grafana-data、alertmanager-data是命脉。grafana-data里存着grafana.db所有仪表盘、用户、数据源配置都在里面这个卷丢了等于整个 Grafana 归零。所以备份策略里这个卷的优先级要排在最高。2.3 配置文件挂载的三个常见失误第一个失误挂载目录写成文件路径。把./prometheus/prometheus.yml挂到/etc/prometheus目录结果容器启动直接报错说找不到配置文件。正确做法要么挂单个文件路径要么把整个目录挂进去两者不能混。第二个失误只读挂载忘了加:ro然后容器里的进程写坏了宿主机上的文件。配置类挂载都建议加:ro反正你本来也不打算让容器改配置。第三个失误改了配置文件忘了同步 reload。Prometheus 的规则文件、Alertmanager 的路由配置改完之后都要走一次 reload 或者重启光改文件是没用的。我习惯在改完配置后跑一遍promtool check config和promtool check rules语法错误在容器外就能拦下来比看日志快得多docker run --rm -v $(pwd)/prometheus:/etc/prometheus \ prom/prometheus:v2.51.2 \ promtool check config /etc/prometheus/prometheus.yml3. datasource was not found 报错的完整排查链路回到开头那个问题。这行报错在 Grafana 8 之后特别常见理解它的成因基本上就理解了 Grafana 数据源引用机制的演进。3.1 报错现场还原从测试环境导出仪表盘 JSON导入到生产环境之后页面正常渲染但每个面板都显示 No data同时 Grafana 日志里不断出现loggercontext userId1 orgId1 unameadmin msgfailed to upgrade legacy queries errordatasource im7_otuvz was not found这里的关键信息是中间那串im7_otuvz。它看起来像随机字符串实际上是源环境里那个 Prometheus 数据源的uid。Grafana 7 之前面板 JSON 里引用数据源用的是名字datasource: PrometheusGrafana 8 引入了 uid 机制因为同一个组织里可以存在多个同名数据源靠名字引用会产生歧义。新版面板 JSON 变成这样datasource: { type: prometheus, uid: im7_otuvz }当 Grafana 加载一份老格式的面板时会尝试把字符串形式的数据源引用升级成对象形式——先按名字找找不到再按 uid 找。如果目标环境里既没有同名数据源也没有这个 uid 的数据源升级就失败查询发不出去面板自然空白。这就是整个报错的来龙去脉。3.2 逐层排查从 uid 到数据源注册表排查按这个顺序走基本十分钟内能定位第一步确认目标环境里到底有哪些数据源。打开Connections - Data sources看列表里 Prometheus 数据源的 uid 是什么。也可以在 Grafana 里直接调 APIcurl -s -H Authorization: Bearer $GRAFANA_TOKEN \ http://localhost:3000/api/datasources | jq .[] | {name, uid, type}输出的 uid 如果和报错里的im7_otuvz不一致问题就确认了。第二步确认是部分面板还是全部面板受影响。如果只有个别面板报错说明这些面板硬编码了某个特定 uid而其他面板用的是变量引用能自动解析。这种情况下单独改那几个面板就行。第三步确认是不是导入时选了外部共享格式。导出面板时 Grafana 提供一个 Export for sharing externally 选项勾上之后 JSON 里的数据源会被替换成${DS_PROMETHEUS}这种占位符导入时弹窗让你手动选数据源。如果你用的是这个选项但导入时选了不指定或者关掉了弹窗占位符就没被替换也会出现找不到数据源的情况。3.3 修复方案与预防手段修复思路有三个层次按从快到慢排列方案一把目标环境的数据源 uid 改成和源环境一致。这是最彻底的做法。如果用 provisioning 方式管理数据源直接在 YAML 里显式声明 uidapiVersion: 1 datasources: - name: Prometheus type: prometheus uid: im7_otuvz access: proxy url: http://prometheus:9090 isDefault: true jsonData: timeInterval: 15s httpMethod: POSTuid这个字段是手动指定的不指定的话 Grafana 会自动生成一个随机值每次重建数据源都不一样——这正是迁移时 uid 对不上的根源。只要把 uid 固定下来写进 provisioning 文件同一个面板 JSON 就能在任何环境直接导入使用。方案二批量替换面板 JSON 里的 uid。导出的 JSON 是纯文本用sed或jq改一遍再导入# 把旧 uid 全部替换成新 uid sed -i s/im7_otuvz/newuid123/g dashboard.json # 或者用 jq 递归处理所有 datasource 字段 jq (.panels[]?.datasource.uid) | newuid123 dashboard.json fixed.json这个方法适合一次性迁移几十个面板的场景。注意jq那条只能处理顶层 panels 数组嵌套的行面板、重复面板要用递归写法才彻底。方案三改用模板变量。在仪表盘设置里定义变量DS_PROMETHEUS类型选 Datasource然后所有面板的数据源都引用${DS_PROMETHEUS}。这样切换环境时只要在仪表盘顶部下拉框里选一次数据源全盘生效datasource: { type: prometheus, uid: ${DS_PROMETHEUS} }这是我最推荐的做法尤其是需要长期维护的仪表盘。代价是前期要把已有面板改一遍但改完之后迁移再也不用操心 uid 的事。提示如果仪表盘里既有 Prometheus 又有 Loki 这类多数据源就定义多个变量命名上区分开比如DS_PROM_METRICS、DS_PROM_LOGS别都叫DS_PROMETHEUS。4. 面板复制与整盘迁移三种粒度的实操对比Grafana 拷贝整个面板这个需求出现的频率非常高但面板这个词在不同语境下指的东西不一样。有人指的是单个图表有人指的是整个 Dashboard还有人想连文件夹结构一起搬。粒度不同操作方式完全不同。4.1 三种复制粒度与适用场景粒度操作入口适用场景需要注意单个面板面板标题 - Edit - Panel JSON复用某个现成的查询和样式粘贴后必须改id和gridPos整个仪表盘仪表盘设置 - JSON Model同环境内复制一份改改名称重复会被拒绝改title跨实例迁移导出 JSON 或走 API测试到生产、旧环境到新环境数据源 uid 和变量要对齐文件夹批量迁移API 脚本几十个仪表盘整体搬迁分页、限流、失败重试单个面板的复制最容易出问题。在面板编辑页找到 Panel JSON复制整段 JSON到新面板里粘贴。这里有个坑JSON 里的id字段是面板在当前仪表盘内的编号粘贴时如果原id已经存在Grafana 可能会报错或行为异常。稳妥做法是粘贴前把id删掉或者改成null让 Grafana 自己分配同时把gridPos的x、y调到你想要的位置否则新面板会叠在老面板上面。4.2 用 API 做批量迁移手动导出导入适合三五个面板超过二十个就必须上脚本。Grafana 的 Dashboard API 很规整读和写各一个接口# 读取按 uid 拉取仪表盘 curl -s -H Authorization: Bearer $SRC_TOKEN \ http://old-grafana:3000/api/dashboards/uid/abc123 | jq . dash.json # 写入注意 payload 要用 dashboard 字段包裹 curl -s -X POST -H Authorization: Bearer $DST_TOKEN \ -H Content-Type: application/json \ -d $(jq {dashboard: (.dashboard | del(.id) | .id null), overwrite: false, folderUid: target-folder} dash.json) \ http://new-grafana:3000/api/dashboards/db几个实操细节值得记下来读取接口返回的是一个信封结构真正的仪表盘内容在dashboard字段里外层还有meta字段记录文件夹、版本、权限等信息。写回时如果直接把整个返回体丢过去会失败必须重新包一层{ dashboard, folderUid, overwrite }。overwrite设为false时如果目标环境已存在同名仪表盘接口会返回 412 错误设为true则会覆盖。批量迁移我倾向先用false跑一遍看哪些重名人工处理完再跑第二遍。请求头里的 Token 建议用 Service Account Token 而不是 API Key。API Key 是旧机制Grafana 9 之后主推 Service Account权限粒度更细可以只给dashboards:read或dashboards:write泄露了损失也有限。批量迁移时还要注意限流和分页。搜索接口GET /api/search?typedash-dblimit100page1默认每页返回数量有限几百个仪表盘要循环翻页。另外短时间内大量写入会让 Grafana 的 sqlite 数据库压力上来脚本里加个sleep 0.2之类的间隔会稳很多。4.3 迁移后必须检查的四件事迁移不是导入成功就完了下面四项每次都要过一遍第一数据源引用。回到第 3 节讲的 uid 问题导入后随便点开一个面板看有没有 No data。第二模板变量。有些仪表盘用了label_values()这类查询变量变量依赖数据源返回的标签名。如果目标环境的数据源虽然通了但指标标签不一样变量下拉框会是空的整个面板的筛选就废了。检查方式是点开变量下拉框看有没有正常列出选项。第三时间范围和刷新间隔。源环境的面板可能设了最近 7 天的时间范围搬到新环境后如果数据只保留 3 天图表前半段就是空白。刷新间隔也一样测试环境设了 5 秒自动刷新生产环境几百个面板一起刷会把 Prometheus 打满。第四告警规则关联。Grafana 8 之后面板可以绑定告警规则迁移时规则不会跟着走。遗漏了这点你会以为监控还在实际上告警早就断了。检查方式是进Alerting - Alert rules看有没有规则引用已经不存在的仪表盘或面板。5. 把告警接到 Alertmanager分工、配置与自测Grafana 上能看数据之后下一步自然就是出问题得有人知道。告警这块最容易混乱的地方在于Grafana 和 Prometheus 都能做告警Alertmanager 又能被两边同时使用搞不清谁在什么时候起作用。5.1 Grafana 托管告警与 Prometheus 规则的分工先给结论如果已经在用 Prometheus规则优先写在 Prometheus 侧。原因有三个。规则文件和指标定义放在一起版本管理简单改一条规则提交一个 commit 就完事。Grafana 的告警规则存在数据库里虽然也能通过 provisioning 文件管理但多了一层抽象。Prometheus 的规则用promtool check rules就能做语法校验CI 里加一步即可Grafana 规则没有这么成熟的命令行校验工具。最后Prometheus 规则可以被多个 Grafana 实例共同展示而 Grafana 托管告警是跟实例绑定的。Grafana 托管告警更适合这些场景需要跨数据源做告警比如 Prometheus 的指标加上 MySQL 的查询结果一起判断、团队没有独立维护 Prometheus 的人、需要 Grafana 的多维告警能力比如按标签动态生成告警实例。5.2 两边接 Alertmanager 的配置差异Prometheus 侧的配置在prometheus.yml里加一段alerting: alertmanagers: - static_configs: - targets: - alertmanager:9093 rule_files: - /etc/prometheus/rules/*.yml规则文件示例一条 CPU 使用率过高的告警groups: - name: host-alerts rules: - alert: HighCpuUsage expr: 100 - (avg by(instance) (rate(node_cpu_seconds_total{modeidle}[5m])) * 100) 85 for: 10m labels: severity: warning team: infra annotations: summary: 实例 {{ $labels.instance }} CPU 持续高于 85% description: 当前值 {{ $value | printf \%.1f\ }}%已持续 10 分钟。for: 10m是关键参数它表示表达式连续成立 10 分钟才真正触发告警。没有这个字段指标瞬间抖动一下就会发通知一晚上能把你手机震没电。Grafana 侧接外部 Alertmanager在Alerting - Alertmanager页面能看到当前配置的实例列表。通过 provisioning 文件配置更规范放在provisioning/alerting/alertmanager.ymlapiVersion: 1 alertmanagers: - name: external-am url: http://alertmanager:9093 timeout: 10s配好之后Prometheus 规则触发的告警会出现在 Grafana 的Alerting - Active alerts页面里来源标记为 Prometheus。这是很多人不知道的一个便利点用 Grafana 统一看告警列表比来回切两个界面舒服得多。5.3 路由、分组、静默的实战参数Alertmanager 的配置文件看起来复杂核心就四块route: receiver: default-receiver group_by: [alertname, instance] group_wait: 30s group_interval: 5m repeat_interval: 4h routes: - match: severity: critical receiver: oncall-receiver group_wait: 10s repeat_interval: 1h receivers: - name: default-receiver webhook_configs: - url: http://notice-bridge:8060/webhook - name: oncall-receiver webhook_configs: - url: http://notice-bridge:8060/webhook inhibit_rules: - source_match: severity: critical target_match: severity: warning equal: [alertname, instance]参数含义逐个说清楚group_by决定哪些告警被合并成一条通知。按alertname和instance分组意味着同一台机器上的同类告警会打包发送避免一次故障刷出几十条消息。group_wait是首次通知前的等待时间。设成 30 秒的意思是新告警产生后先等 30 秒看有没有同类告警一起进来凑一批再发。设太短会碎片化设太长则通知延迟。group_interval控制同一组告警有新成员加入时的发送间隔通常设 5 分钟。repeat_interval是重复提醒周期。设 4 小时的含义是如果告警一直没恢复每 4 小时再提醒一次。生产环境别设太短我见过设 10 分钟的值班同事直接把这个群免打扰了效果适得其反。inhibit_rules是抑制规则上面这段的意思是如果同一实例同一告警名已经产生了 critical 级别的告警那么对应的 warning 级别告警就不发了。避免大问题和小问题一起响让值班的人聚焦在最严重的那条上。提示inhibit_rules里equal字段的标签匹配是精确匹配标签名写错了不会报错只会静默失效。配完之后一定要构造真实场景验证别只看配置语法没问题就上线。5.4 告警链路的三段自测方法配置完不验证等于没配。我习惯把链路拆成三段分别测第一段规则求值。打开 Prometheus 的/alerts页面看规则是否处于inactive/pending/firing状态。如果表达式本身有问题这里会直接显示错误信息。想快速验证一条规则能不能触发可以临时把阈值改得极低比如 1看状态是否立刻从 pending 变 firing验证完再改回去。第二段告警推送。访问 Alertmanager 的/api/v2/alerts接口看 firing 状态的告警有没有进来。这一步能确认 Prometheus 到 Alertmanager 的网络和配置是通的。第三段通知发送。用 Alertmanager 的amtool手动造一条告警amtool alert add alertnameTestAlert severitycritical instancetest-host \ --alertmanager.urlhttp://localhost:9093如果通知能正常收到说明路由和接收端配置没问题。这是最省事的验证方式不用真去制造一次故障。三段都通了才算是真正的告警链路可用。我在几个项目里都是按这个顺序排查能省掉大量翻日志的时间。6. 长期运行后的性能调优与运维习惯搭起来容易跑得久还稳当是另一回事。Grafana 这类工具的问题通常不是突然爆发的而是随着仪表盘数量、查询复杂度、数据保留周期慢慢累积出来的。6.1 查询侧的几个低成本优化用$__rate_interval替代硬编码的[5m]。Grafana 提供了这个内置变量它会根据面板的时间范围和数据源的最小采集间隔自动算出一个合适的时间窗口。好处是在看 1 小时和看 7 天的时候查询用的步长自动适配既不会因为窗口太小导致数据点过密也不会因为窗口太大丢掉细节。开启 Max data points 限制。面板设置里的Max data points控制单个图表最多返回多少个数据点默认是自动计算。如果一个面板横跨 30 天还想要秒级精度Prometheus 得吐出几十万个点浏览器直接卡死。手动限制在 500 到 1000 之间形状基本不受影响性能差别是数量级的。把高频使用的复杂查询落成 recording rules。如果某个仪表盘每次加载都要现算一个涉及十几个标签聚合的表达式把这部分提前在 Prometheus 里算好存成新指标Grafana 直接查这个新指标。代价是占一点存储换来的是面板加载从几秒降到几百毫秒。关掉不必要的自动刷新。大屏展示类的仪表盘设 10 秒或者 30 秒刷新就够了没必要跟 Prometheus 的采集间隔完全对齐。生产环境里几十个面板同时以 5 秒间隔查询Prometheus 的查询压力会明显上升。6.2 版本管理与备份的落地做法Grafana 的仪表盘如果只存在数据库里出问题时就只能靠备份文件恢复看不到变更历史。我现在的做法是双轨并行第一条轨道是 provisioning。把长期维护的核心仪表盘导出成 JSON放进 Git 仓库通过provisioning/dashboards的 provider 配置自动加载apiVersion: 1 providers: - name: core-dashboards orgId: 1 folder: Core type: file disableDeletion: false updateIntervalSeconds: 30 options: path: /var/lib/grafana/dashboardsdisableDeletion: false这个设置要注意它允许 Grafana 删除 provisioning 目录里已经不存在的仪表盘。如果你希望 Git 是唯一事实来源设成false如果允许有人直接在界面里改设成true更安全否则界面上的临时修改会被文件同步覆盖掉。第二条轨道是定期备份grafana.db。这个文件在grafana-data卷的/var/lib/grafana目录下用一个定时任务每天复制一份到对象存储或者另一台机器上。sqlite 数据库在运行中直接复制可能拿到不一致的快照稳妥点用sqlite3 grafana.db .backup /backup/grafana-$(date %F).db这种方式。6.3 常见问题速查表跑久了会遇到的典型问题整理成表方便对照现象常见原因处理方式面板全空白日志有 datasource not found数据源 uid 不匹配固定 uid 或用模板变量个别面板无数据其他正常该面板硬编码了旧 uid单独修改该面板 JSON变量下拉框为空查询变量依赖的标签不存在检查目标环境指标标签导入仪表盘报 412同名仪表盘已存在改名或设置 overwrite告警不触发for时间过长或阈值过高查/alerts页面的状态告警只有一条没有后续repeat_interval过长按业务调整到 1 到 4 小时首页加载慢单屏面板数量过多拆分仪表盘限制数据点容器重启后配置丢失数据卷未挂载或挂错检查 volume 映射最后分享一个我踩过的坑。有次迁移完之后一切正常过了两周同事反馈某个仪表盘少了几个面板。查了半天才发现Grafana 里有人直接在界面上编辑过那个仪表盘但 provisioning 的updateIntervalSeconds到了之后文件版本把它覆盖回去了。界面上的改动看着保存成功了实际上撑不过 30 秒。这类问题的根源是谁是权威数据源这件事没说清楚——要么全走 Git要么全走界面混着用一定会打架。我现在统一按 Git 管核心资产、界面管临时探索的方式来做并且明确告诉团队改核心面板要提 MR不要直接在界面上动。