多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

Grafana image render 告警截图、/render 接口与排错

Grafana image render 告警截图、/render 接口与排错 凌晨两点被电话叫起来看告警通知里躺着一行数字和一句CPU 使用率超过阈值没有任何曲线。运维群里有人回了一句给我看一眼图我只能爬起来开电脑登 Grafana。这种事发生两回之后我就把 Grafana 的 image render 彻底配了一遍。这个功能说白了就是让 Grafana 把面板当成网页截成 PNG然后塞进告警通知、PDF 报表或者任何你想要图的地方。听着简单真到设置这一步坑比想象的多渲染器单独跑一个容器、回调地址配错就白屏、镜像里没装中文字体出来全是方框、面板一多就超时。这篇就把我踩过的这些东西按顺序捋一遍从部署形态选型、[rendering]配置段逐项说明一直到接口调用、告警带图、中文乱码排查和资源估算。不管你是刚装完 Grafana 想给告警加张图还是已经被渲染超时折腾了几天应该都能在里面找到对得上的那一段。1. 出图之前先想清楚Grafana 的 image render 到底渲染了什么1.1 会用到它的三类真实场景第一类是告警通知。这是绝大多数人配 image render 的唯一原因。Grafana 的 Unified Alerting 在触发告警时可以让渲染器把出问题的那块面板截一张图随通知一起发出去。收到告警的人不用登录 Grafana直接在邮件或者 IM 里就能看到曲线拐点在哪个时间点判断是瞬时抖动还是持续劣化。这个体验的提升是质变的尤其是在半夜。第二类是定期报表。每周一早上把过去七天的核心指标拼成一份 PDF发给业务方或者写进周报。Grafana 企业版自带 Reporting 功能但社区版没有很多人是自己写脚本调/render接口批量出图再用 Python 的图片库或者 HTML 模板拼成 PDF。我做过几个这类脚本后面会讲批量出图时资源该怎么算。第三类是把图嵌到别的地方。比如自建的一个运维门户、一个内部文档站甚至是一个静态的巡检报告页。这时候你需要的是稳定拿到一张确定尺寸、确定时间范围的 PNG而不是每次打开都去连数据源重新查询。图片是快照性质的东西反而比 iframe 更可控。这三类场景对渲染的要求其实不一样告警追求快和稳报表追求一致性和批量吞吐嵌入追求尺寸精确。想清楚自己属于哪一类后面选部署形态的时候能少走弯路。1.2 渲染为什么要甩给一个独立进程很多人第一次配置时会疑惑Grafana 自己不就是个 Web 服务吗为什么不能顺手把页面截了原因是渲染的本质是启动一个浏览器打开一个 URL等页面完全画完截图。Grafana 的后端是 Go 写的它不跑浏览器。而浏览器这东西内存占用大、崩溃概率不低、还需要一堆系统依赖字体、图形库、沙箱把它塞进 Grafana 主进程里等于给一个本来很轻的指标服务绑上了一颗不定时炸弹。所以从设计上渲染被拆成了两个角色Grafana 负责拼出一个内部 URL带着面板 ID、时间范围、主题这些参数然后把这个 URL 交给渲染器渲染器用一个无头浏览器打开这个 URL等页面渲染完成把截图数据回传给 Grafana。这条链路里最关键的一点是渲染器需要能反过来访问 Grafana。不是 Grafana 单向调用渲染器就完事了渲染器得能打开http://你的grafana地址/...这个页面。这一点没想清楚配置十有八九会失败我把它放在第 2 章单独讲。顺带说一句正因为是打开真实页面再截图你在面板里看到的任何东西——阈值线、注释、变量选择器的当前值、暗色主题——都会被原样带进图片。这也意味着如果你用了一个依赖浏览器端插件的面板比如某些需要 Canvas 或者 WebGL 的图表它的渲染行为会和浏览器里看到的一致不会有服务端画不出来的问题。1.3 两代渲染器PhantomJS 时代和 Chromium 时代老版本的grafana-image-renderer插件基于 PhantomJS。PhantomJS 是个老引擎对现代 CSS 和 JavaScript 的支持有限很多新的图表库在上面会画歪或者直接空白。如果你在网上翻到一篇几年前的教程里面提到grafana-cli plugins install grafana-image-renderer然后在[rendering]里写mode phantomjs之类的那些内容基本已经过期了。现在的渲染器是用 Go 重写的底层换成无头 Chromium。这个变化带来的直接好处是前端用什么画的截图就长什么样一致性问题基本消失。代价是镜像变大了内存占用也上去了一个 Chromium 进程吃几百兆是常态。所以如果你在资源很紧张的小机器上跑 Grafana渲染器要单独算资源别指望它不占地方。还有一个版本细节渲染器插件和 Grafana 主版本之间是有兼容性要求的新版本的 Grafana 用太老的渲染器会报错反过来也一样。部署的时候把两个镜像的 tag 都显式写死别用latest这是血泪教训——某次我图省事用了latest半夜自动拉了一个新版本第二天所有告警图片全变成了 500。后面所有环境我都写具体版本号。2. 部署形态怎么选本地插件、独立容器还是远端服务2.1 三种形态的对比渲染器现在有三种跑法选错了会带来一堆无谓的排错时间。第一种是本机安装插件把渲染器的二进制装到 Grafana 所在的机器上Grafana 直接调用本地进程。这种方式在裸机部署、只有一台机器的时候最省事不需要额外配置 URL也不需要处理网络连通性。缺点是渲染器和 Grafana 抢同一份 CPU 和内存一次渲染跑飞可能把整个 Grafana 拖慢。第二种是独立容器渲染器跑在自己的容器里和 Grafana 通过 Docker 网络互通。这是我最推荐的默认方案隔离性好资源可以单独限制升级也互不影响。第三种是远端渲染服务渲染器部署在另一台机器上通过 HTTP 被 Grafana 访问。适合 Grafana 本身是多副本、需要集中渲染能力的场景或者想把渲染这类重活从主节点上彻底剥离出去。代价是要处理认证、HTTPS、跨网络的回调可达性。形态适用场景主要优点主要麻烦本地插件单机裸机部署无需网络配置抢占主进程资源升级耦合独立容器绝大多数容器化部署隔离、可限资源、易升级需要配 URL 和网络远端服务多副本 Grafana、集中渲染资源集中、可横向扩展认证、HTTPS、回调可达性2.2 一套可以直接抄的 compose 组合下面这套是我在中小规模环境里用得最多的一组Grafana 和渲染器在同一个 compose 网络里通过服务名互相访问。注意渲染器的端口只在内部暴露不映射到宿主机避免被人随便调用。services: grafana: image: grafana/grafana:11.1.0 ports: - 3000:3000 environment: - GF_RENDERING_SERVER_URLhttp://renderer:8081/render - GF_RENDERING_CALLBACK_URLhttp://grafana:3000/ - GF_RENDERING_RENDER_TIMEOUT60 - GF_RENDERING_RENDERER_TOKENchange-me-please - GF_SECURITY_ALLOW_EMBEDDINGtrue volumes: - grafana-data:/var/lib/grafana depends_on: - renderer renderer: image: grafana/grafana-image-renderer:3.11.6 environment: - ENABLE_METRICStrue - HTTP_HOST0.0.0.0 - HTTP_PORT8081 - AUTH_TOKENchange-me-please - RENDERING_IGNORE_HTTPS_ERRORSfalse expose: - 8081 - 9090 shm_size: 512m volumes: grafana-data:这里有几个参数值得说明。GF_RENDERING_CALLBACK_URL用的是服务名grafana不是localhost因为渲染器在另一个容器里localhost指向的是它自己。这是新手最常见的错误配完之后日志里会出现连接被拒绝然后你会以为是渲染器没起来。shm_size给到 512M 是因为 Chromium 对共享内存有需求默认的 64M 在渲染稍微复杂一点的面板时经常崩这个在第 6 章会细说。AUTH_TOKEN和GF_RENDERING_RENDERER_TOKEN必须一致这是两个容器之间的共享密钥防止渲染接口被随便调用。GF_SECURITY_ALLOW_EMBEDDINGtrue在某些场景下是必须的尤其是当渲染器需要以匿名或者低权限身份访问 Grafana 页面时。开这个开关意味着 Grafana 允许被 iframe 嵌套如果你对安全性有要求可以改成通过带 API Token 的 URL 来渲染但那会导致 URL 变长且需要管理 Token 生命周期。我更倾向于内网环境直接开 embedding 加内网隔离。2.3 callback_url渲染服务回头找 Grafana 的那条链路这条链路值得单独拎出来说因为它是配置看起来全对但就是不出图的头号原因。整个渲染流程是这样的Grafana 收到一个渲染请求拼出内部 URL比如http://grafana:3000/render/d-solo/abc123/panel?orgId1panelId4width1000height500把这个 URL 通过server_url发给渲染器渲染器拿到 URL 后用它自己的网络环境去访问这个地址把页面打开、截图、返回 PNG。所以渲染器必须能解析并访问callback_url对应的主机名。在 Docker Compose 里服务名是天然可解析的在 Kubernetes 里用 Service 名在裸机部署渲染器但 Grafana 在容器里的场景就得填宿主机的实际 IP 或者域名。我见过最隐蔽的一个坑是Grafana 配的root_url是https://grafana.example.com而callback_url写的是http://grafana:3000/两套地址混用导致渲染出来的页面里某些绝对路径的资源请求走的是外网域名在渲染器所在的网络里解析不了结果是页面框架出来了但图表是空的。解决办法是让callback_url和 Grafana 内部实际监听的地址严格一致不要和外部访问域名混着用。如果你不确定渲染器能不能访问到最直接的验证方式是进渲染器容器里打一条命令docker exec -it renderer sh -c wget -qO- http://grafana:3000/api/health返回 JSON 里的database: ok就说明链路是通的。这一步比看任何日志都快。搞不清楚状态的时候先验证这一步再往下查。3. grafana.ini 的 [rendering] 段到底填什么3.1 server_url 与 callback_url 的写法差异[rendering]段里最核心的就两个参数。server_url指向渲染器的渲染接口通常以/render结尾比如http://renderer:8081/render。注意它不是一个裸的主机地址很多人在这一步漏掉了/render后缀结果 Grafana 把请求打到了根路径渲染器返回 404日志里只有一句语焉不详的 render failed。写配置的时候多敲几个字符能省下半小时排查。callback_url指向 Grafana 自己的根地址必须以/结尾。这个必须以斜杠结尾的细节也是有讲究的——渲染器在拼接内部 URL 时是拿callback_url加路径如果少了斜杠拼出来的地址就会变成http://grafana:3000render/d-solo/...这种地址连解析都过不去。我在第一次配置时就犯过这个错日志里刷的都是 URL 解析失败盯着配置文件看了半天才发现少了一个斜杠。如果你是用环境变量覆盖配置注意命名规则是GF_RENDERING_SERVER_URL这种形式全大写下划线。用环境变量和直接改 ini 文件优先级上环境变量更高如果两边都配了改 ini 不生效是正常的别怀疑人生。3.2 超时、并发、日志这几个参数render_timeout是渲染单个请求的超时时间默认值在我印象里是 30 秒。这个值对不同环境的合适取值差别很大。简单面板一两秒就出图含大量数据点的图表可能需要十几秒如果面板里还带了topk之类的聚合查询、数据源响应又慢30 秒经常不够用。我一般会调到 60同时配合接口上的timeout参数做单次覆盖。但要注意超时调大不是万能药。超时时间设成 120 秒看着很保险实际后果是当渲染器真的卡住时一个请求会占住一份资源整整两分钟并发几个这样的请求渲染器就直接被打满了。所以我更倾向于适度超时60 秒左右加告警监控而不是把超时当保险丝。concurrent_render_request_limit控制 Grafana 侧允许同时发起的渲染请求数默认大概是 30。这个数字要和渲染器自身的并发能力对齐。渲染器内部通常用一个浏览器实例池来服务请求池子有多大直接决定它能扛多少并发。Grafana 这边的限制如果远大于渲染器的池子大小请求会在渲染器那边排队表现出来就是大量超时。我的经验是把 Grafana 侧的并发限制压到比渲染器池子略小一点让排队发生在可控的这一侧。verbose_logging默认关闭排查阶段建议打开能看到渲染请求的详细过程包括实际请求的 URL、耗时、错误堆栈。排查完记得关掉日志量很大长期开着容易把磁盘写满尤其是告警频繁的环境一次告警风暴能刷出几万行。3.3 反向代理和 HTTPS 下的两个隐藏开关Grafana 挂在 Nginx 或者 Ingress 后面是很常见的部署方式这种场景下渲染会多出两个隐藏开关。第一个是[security]段里的cookie_samesite。当渲染器访问的地址和 Grafana 对外暴露的域名不同源时默认的SameSite策略可能导致渲染请求携带不上会话 Cookie结果是渲染出来的页面是登录页或者空白页。把它设成none通常能解决但前提是整条链路走 HTTPS否则浏览器会拒绝这个设置。第二个是allow_embedding。渲染器本质上是以一个浏览者的身份打开页面某些版本里如果这个开关是关的页面会因为拒绝被嵌套而渲染失败。这个开关在部署形态那一章已经提到过放在这里是为了强调它是HTTPS 反代场景的常见补丁。另外如果你的 Grafana 用的是自签名证书渲染器访问时会在 TLS 校验上失败这时候要么把 CA 证书挂进渲染器容器要么打开RENDERING_IGNORE_HTTPS_ERRORStrue先让流程跑通。后者属于权宜之计生产环境更推荐把证书链配好否则渲染链路上会一直有个校验被跳过的隐患。4. 手动调 /render 接口把任意面板变成图片4.1 路径规则和必填参数理解了配置之后手动调一次接口能帮你快速判断问题出在配置还是在渲染器。这个接口在 Grafana 侧是公开的路径分两种单面板渲染用/render/d-solo/:uid/:slug整块看板渲染用/render/d/:uid/:slug。:uid是看板的唯一标识在 URL 里能看到:slug是看板标题转成的短横线形式它其实只是个装饰随便写点什么都行不影响渲染结果。必填参数有三个orgId指定组织 ID单组织环境下通常是 1panelId指定要渲染哪块面板可以在面板编辑界面的 URL 或者面板的 JSON 模型里找到width和height决定输出图片的像素尺寸。少一个都会报错报错信息有时候并不明确所以调接口之前先用浏览器打开一次这个 URL 确认能出图再往脚本里搬。4.2 常用可选参数对照表除了必填项还有一堆参数会直接影响出图效果我把常用的整理成表配的时候对着查就行。参数作用典型值备注from起始时间now-6h / 1700000000000支持相对和绝对时间to结束时间now / 1700003600000与 from 配合使用width图片宽度1000受渲染器最大宽度限制height图片高度500太小会被压缩变形timeout单次超时秒数60不能超过服务端配置上限theme主题light / dark影响背景色和文字色scale缩放倍数1 / 2 / 32 以上适合高清场景kiosk隐藏界面元素1 / tv去掉侧边栏和顶栏tz时区Asia/Shanghai影响时间轴显示scale这个参数值得单独说。默认是 1出图是标准分辨率。设成 2 相当于 2 倍图在邮件客户端里放大看会很清晰代价是图片体积大约翻两倍多渲染时间也会变长。做告警通知时我通常用 1因为大部分 IM 会压缩图片高清没意义做 PDF 报表时用 2打印出来线条更锐利。kiosk1也是告警场景的常客。开着它渲染出来的图不会带上左侧导航和顶部工具栏图片里全是有效信息不会浪费高度在界面元素上。如果你发现出图里顶部有一大片空白或者深色条多半是没开 kiosk。4.3 认证、服务账号与一条能跑通的命令/render是个需要认证的接口匿名访问默认是不放行的。最规范的做法是建一个服务账号生成一个 Token在请求头里带上。早期的做法是用 API Key现在新版 Grafana 更推荐服务账号 Token权限可以控制在只读。curl -H Authorization: Bearer glsa_xxxxxxxxxxxxxxxx \ -o panel.png \ http://localhost:3000/render/d-solo/abcd1234/my-dashboard?orgId1panelId4width1000height500fromnow-6htonowkiosk1themedark跑通这条命令之后再回头看告警配置思路会清晰很多。如果这条命令返回的是一张登录页截图说明认证没生效检查 Token 有没有过期或者权限对不对如果返回的是 JSON 格式的错误通常能在 message 字段里看到具体原因如果返回的是空白图那多半是时间范围和数据源的问题而不是渲染器的问题。这里有个经验排查顺序永远是从接口往上查而不是从告警往下查。告警链路里套了太多层告警规则、联系点、渲染器、通知渠道从最上面的现象往下推会越查越乱从最底层的手动接口往上推每一步都能得到明确的是或否。4.4 整看板渲染和单面板渲染的差别单面板渲染d-solo和整看板渲染d不只是数量差别。整看板渲染会把所有面板按布局拼在一张长图里如果看板有十几个面板渲染时间和内存占用都会显著上升很容易触发超时。而且整看板渲染时那些依赖变量选择器的面板会按变量默认值渲染如果默认值不是你想看的那个出来的图和预期不符。我自己的习惯是告警场景一律用单面板渲染只截出问题的那一块图片小、速度快、信息聚焦。报表场景才用整看板渲染而且会提前把报表专用的看板做精简——去掉那些探索性质的、依赖交互的面板只留下要长期出图的那几块。这个为出图单独做一个看板的做法看起来有点笨但能省掉大量为什么报错图片和实际不符的沟通成本。整看板渲染时记得带上kiosktv它会隐藏更多界面元素比kiosk1更彻底。另外整看板渲染的panelId参数可以省略但如果带上就只会渲染那一块面板效果和d-solo类似区别是它保留看板的整体上下文。5. 让告警通知里带上图5.1 Unified Alerting 的截图开关在哪渲染器配好、手动接口跑通之后最后一步是把图片接进告警。Grafana 的 Unified Alerting 里有两个地方要开。第一处在配置文件里[unified_alerting.screenshots]段有一个开关控制是否自动为告警截图还有一个参数控制并发截图数量。这个并发数不要设太大每个截图都是一次完整的浏览器渲染设成 5 已经能应付大多数场景设成 20 在告警风暴时会把渲染器直接打爆。另外还有一个开关控制图片是否上传到 Alertmanager如果不上传图片只存在 Grafana 本地通知里就带不上。第二处在联系点Contact Point的配置里。每个联系点都有一个是否上传图片的选项需要单独勾选。这里有个容易忽略的点不是所有类型的联系点都支持带图邮件、Slack 这类支持得比较好某些 webhook 类型的接收端需要自己处理图片字段。配完之后建议用测试按钮发一条确认收到的通知里确实有图别等真实告警来了才发现没开。5.2 图片怎么落到 Alertmanager 和外部通道开启上传之后图片会随告警一起发给 AlertmanagerAlertmanager 把图片暂存在内存里通知模板里通过一个特殊的字段引用它。这意味着图片不落盘重启 Alertmanager 之后就没了。如果你需要长期保存告警图片比如做合规留档得自己在通知链路的末端处理比如用一个 webhook 接收端把图片转存到对象存储。图片在 Alertmanager 里占的是内存而且它有一个总的体积上限。如果你的看板出图是 2 倍图、尺寸又大几十条告警同时带图很容易触到这个上限表现是部分告警的通知里没有图或者 Alertmanager 报错。解决办法很简单告警用的图把scale降回 1width控制在一千像素以内height控制在四五百像素。告警图是给人快速看一眼趋势的不是用来做汇报的尺寸上没必要奢侈。另外Alertmanager 对同一张图的引用是按告警指纹来的多条告警如果指向同一块面板的同一个时间范围图片是共享的不会重复占内存。这一点在配置模板的时候可以利用把相似告警的截图时间和范围对齐能省不少资源。5.3 告警风暴时的图片体积控制真正把渲染打爆的从来不是正常告警而是风暴。某次一个下游依赖抖动三分钟内触发了四百多条告警每条都要截图渲染器直接进了排队状态最后大部分告警的通知里图片都是空的还顺带把 Grafana 的接口响应拖慢了。后来我做了三件事。第一件是给渲染器加了资源上限内存和 CPU 都设成硬限制宁可渲染失败也不要影响 Grafana 主服务。第二件是在告警规则层面做了聚合把同一类抖动合并成一条告警从源头减少截图数量。第三件是在通知模板里加了兜底如果通知里没有图片就在正文里附上直接跳转到 Grafana 的链接让收到告警的人至少有路可走。第三件事看着不起眼但实际价值很高。图片是个锦上添花的东西链路长、依赖多不可能保证百分之百成功。做告警设计的时候永远要假设图片可能缺失给一个降级路径。这也是我从那次风暴里学到的最实用的一课。6. 中文方框、超时、崩溃完整的排查链路6.1 第一步永远是确认渲染服务本身活着遇到出图异常很多人第一反应是翻 Grafana 的日志这其实绕远了。正确的第一步是直接确认渲染器这个服务还在不在、能不能响应。渲染器的镜像通常在启动时会打印监听地址和端口容器日志里能看到它有没有正常起来。更直接的方式是进容器打一个健康检查请求很多版本提供了健康检查端点返回 200 就说明服务本身没问题。同时看一眼渲染器的资源使用情况。Chromium 的内存是出了名的能涨如果容器内存设得比较紧进程可能被 OOM Killer 干掉然后自动重启表现出来就是时而能出图时而不能。这种情况在 Grafana 日志里往往只看到请求失败看不到根因只有去docker inspect看容器的重启次数才发现端倪。我遇到过一次容器两小时重启了十七次全程没人发现直到告警图片开始大面积缺失才查出来。6.2 中文显示成方框字体缺失的定位与修复这是中文环境里最典型的坑症状很好认图片能正常出但所有中文字符都变成了一个个空心方框俗称豆腐块英文和数字完全正常。根因是渲染器镜像里没有装中文字体Chromium 找不到能画中文的字形只能画占位符。确认方法很简单进渲染器容器看一下系统里装了哪些字体docker exec -it renderer fc-list | grep -i -E noto|cjk|wqy如果没有输出那就是没装。修复方式是构建一个基于官方镜像的衍生镜像把中文字体装进去。注意要先确认基础镜像是哪个发行版是 Alpine 还是 Debian包管理器不一样。FROM grafana/grafana-image-renderer:3.11.6 # Debian 系基础镜像 USER root RUN apt-get update \ apt-get install -y --no-install-recommends fonts-noto-cjk \ rm -rf /var/lib/apt/lists/* USER 472如果是 Alpine 基础镜像把apt-get换成apk add --no-cache font-noto-cjk就行。这里有个细节USER 472是官方镜像里运行渲染器的非 root 用户 ID装完字体记得切回去否则容器会以 root 身份跑多一层不必要的安全风险。另外装完字体之后要清缓存重启容器光重启可能不够Chromium 的字体缓存有时候会留在临时目录里。装好之后再跑一次渲染中文应该就正常了。如果还是方框检查一下是不是用了自定义的字体名而系统里没有对应字体——面板里如果指定了某个具体的中文字体最好改成通用字体族让它自动回退到 Noto。6.3 超时从日志时间戳反推是哪个面板拖后腿超时排查的关键是定位到具体是哪块面板慢。开启渲染器的详细日志之后每次渲染请求都会留下记录包括实际访问的 URL 和耗时。URL 里的panelId直接告诉你慢的是哪一块。把出问题的面板单独调一次接口用curl加-w参数打出总耗时就能确认是不是它的问题。面板慢通常有三个原因。一是查询本身慢数据源响应就要十几秒这种情况得去优化查询渲染侧无能为力。二是数据点太多一个面板画了几十万个点浏览器渲染这些 SVG 元素会非常吃力解决办法是在查询里降采样或者用更合适的可视化类型。三是面板里嵌了外部资源比如图片、自定义的 JS这些资源在渲染器所在的网络里加载慢或者加载不出来会一直等到超时。第三种最隐蔽因为浏览器里打开可能只是稍微慢一点渲染器那边就直接超了。我一般会把超时时间设成业务能接受的阈值然后对超过阈值一半耗时的面板做专项优化。单靠调大超时是把问题往后推不是解决。6.4 Chromium 起不来与共享内存前面提到过shm_size这个参数的来龙去脉值得说清楚。Chromium 用共享内存来做进程间通信Docker 默认给容器的/dev/shm只有 64MB对大一点的页面来说不够用结果是浏览器渲染到一半崩溃。日志里通常会有比较明确的提示指向共享内存不足或者渲染进程异常退出。解决方式有两种一是给容器加shm_size我一般给 512M够大部分场景二是给 Chromium 传启动参数让它改用磁盘做临时存储。渲染器支持通过环境变量传额外的浏览器启动参数参数之间用逗号分隔。第二种方式的好处是不用调 Docker 配置坏处是性能略差因为落到了磁盘上。还有一类崩溃和沙箱有关。在权限受限的容器环境里比如某些加固过的运行时Chromium 的沙箱机制可能启动不了表现是渲染器容器反复重启或者所有请求都失败。这种情况下的权宜处理是关掉沙箱但要在心里清楚这是降低了隔离级别只应该在内网、受控的环境里用。6.5 500 错误背后是数据源找不到有一类渲染失败很具有迷惑性渲染器日志一切正常Grafana 侧返回 500但没有任何明显的渲染错误。翻日志能翻到类似数据源未找到或者查询升级失败之类的信息。这种情况的根因不在渲染器而在看板或者告警规则本身。典型场景是某个数据源被删掉了或者改了 UID而引用它的面板定义还在渲染时页面加载不出来渲染器等不到页面就绪最终返回错误。这在从旧版本升级过来、或者做过数据源迁移的环境里特别常见。排查方法是把出问题的面板单独打开看能不能正常显示如果浏览器里也是报错那问题就在数据源和渲染无关。还有一种情况是面板引用的数据源在当前组织下没有权限。渲染时用的是服务账号或者匿名身份权限比你自己登录时低浏览器里你看得到是因为你有权限渲染器看不到是因为它没有。这种情况的特征是我看着好好的机器渲染就空白。解决办法是给渲染用的身份补上对应数据源的查看权限或者用带 Token 的 URL 来渲染。7. 资源规划与批量出图的取舍7.1 内存和并发不是线性关系很多人做容量规划时会简单地算一个渲染请求占 300MB那十并发就是 3GB。实际上 Chromium 的多个页面可以共享一部分进程和内存十个并发占用的内存往往比线性估算小但同时它对 CPU 的消耗是非线性的——渲染是 CPU 密集操作并发数超过物理核心数之后每个请求的耗时都会明显拉长最终表现是内存还有富余但全部超时。我的经验值是这样的给渲染器分配的内存至少 1GB 起步中小规模环境 2GB 比较稳妥CPU 方面按每个并发大致需要一个核心来估但实际配置时把上限设在核心数的 1.5 倍左右因为渲染过程中有等待数据源的空档期。真正要盯的指标是渲染请求的 P95 耗时这个值开始持续上升就说明并发到了瓶颈该加资源或者该减少截图数量了。7.2 渲染缓存到底缓存了什么新版渲染器有缓存机制理解它缓存什么很重要。它缓存的主要是浏览器层面的一些资源比如静态文件、脚本这样连续渲染多个面板时不用反复加载这些公共资源。它不缓存查询结果也就是说每次渲染都会重新跑一遍数据查询。这一点在批量出图时特别关键。如果你要出一个过去三十天、每天一张的日报三十次渲染就是三十次完整的查询数据源会被打三十遍。我的做法是把要出图的看板的时间范围固定下来先让数据源把这批查询跑暖再开始渲染如果数据源支持还可以把查询结果缓存打开。更彻底的做法是干脆不走渲染直接从数据源拉数据用图表库自己画但这只适合图表样式简单的场景。7.3 什么时候该放弃实时渲染最后说一个判断标准。渲染是个够用就好的能力它不是所有出图需求的正确答案。如果出现下面几种情况我会考虑换方案出图频率很高比如每分钟一次、图表样式很简单就是折线或者柱状、对图片一致性要求极高像素级可控。这几种场景下用脚本直接查数据、用图表库生成图片比走完整的浏览器渲染链路稳定得多也快得多。渲染真正不可替代的是所见即所得——你在 Grafana 里调好的样式、阈值线、注释、多轴组合能原样出现在图片里。这个价值在复杂面板上是压倒性的但在简单图表上就是杀鸡用牛刀。我现在的做法是分两档复杂的诊断类面板走渲染简单的趋势图走脚本生成两套并用资源上反而更宽裕。我把渲染器从latest改成固定版本号之后告警图片的失败率从每周几次降到了几乎为零把中文字体装进去之后再也没有人问为什么图上是方框。这两个改动加起来花的时间不到半小时换来的稳定性提升却是长期的。如果你现在正被这些问题困扰先去干这两件事剩下的调优可以慢慢来。
返回列表