突破性能瓶颈!Grafana 自动生成图表的深度优化实战

开发 前端
Grafana Image Renderer 插件依赖 Chromium 来渲染图像,渲染每张图片时会启动一个 Chromium 实例,这些实例可能会占用较多的 CPU 和内存资源。当多个渲染任务同时进行时,如果服务器的资源不足,就可能导致渲染失败。

引言

我们上篇文章讲解了如何生成 Grafana Dashboard 的 PDF 文档,这篇文章我们来讨论和实施下我们的相关优化,这个对于我们后面的自动化生成 PDF 很重要。

开始

Grafana Image Renderer 插件依赖 Chromium 来渲染图像,渲染每张图片时会启动一个 Chromium 实例,这些实例可能会占用较多的 CPU 和内存资源。当多个渲染任务同时进行时,如果服务器的资源不足,就可能导致渲染失败。

我们先简单了解下什么是 Chromium:

什么是 Chromium?

• Chromium 是由 Google 维护的一个开源项目,最初发布于 2008 年。

• 它是一个浏览器内核和平台,提供了网页渲染和浏览的核心功能。

• Chromium 的目标:提供一个快速、稳定和安全的浏览体验。

就比如这个,当时做好之后,把链接给同事们访问体验下,结果访问量太大,直接给它干不行了,对于这样的结果,我肯定不满意,所以,就直接给它上优化方案!!!

图片 图片

图片图片

那么在这之前,这个问题到底是谁引发的呢,是 Grafana 呢,还是 Grafana-image-render 呢,还是 Grafana-reporter 呢?

大家可以提前思考下这个问题。

揭秘环节

你需要修改 Grafana 的配置,而不是 Grafana Image Renderer 插件本身的配置。因为 "Concurrent server side render limit reached" 错误是由 Grafana 的渲染请求并发限制引起的,而这个限制是通过 Grafana 主配置来管理的。

Grafana 控制并发渲染请求

• Grafana 处理图像渲染时,会限制同时发起的渲染任务数量,这个限制通过 grafana.ini 或环境变量进行管理。

• Grafana Image Renderer 插件只是负责处理具体的渲染任务,但受 Grafana 的限制约束。

Image Renderer 插件无独立配置

• Image Renderer 插件本身不对并发请求施加额外限制,它只在收到 Grafana 发起的请求时开始渲染任务。

具体的原因

并发渲染限制

• Grafana Image Renderer 默认有并发渲染限制(通常是 5 个并发任务),超过限制后会抛出此错误。

• 在渲染多个报告或多个图表时,如果任务数超过限制,就会导致渲染失败。

系统资源不足

• Grafana Image Renderer 插件依赖 Chromium 运行环境,它可能占用较多的 CPU 和内存资源。如果资源不足,也可能出现渲染失败。

长时间任务或延迟

• 渲染时间过长(复杂的仪表板或大数据量)可能导致 Grafana 达到超时,无法继续处理后续任务。

开始解决

这边直接修改 Grafana 的配置文件,添加优化环境变量:

k edit sts kube-prometheus-stack-grafana -nmonitoring
·····
            env:
              - name: GF_RENDERING_CONCURRENT_RENDER_REQUEST_LIMIT
                value: "10"
·····

我们再看一下:

图片 图片

图片图片

可以看到,这个优化参数给的有点少,这回直接加到 70 :

k edit sts kube-prometheus-stack-grafana -nmonitoring
·····
            env:
              - name: GF_RENDERING_CONCURRENT_RENDER_REQUEST_LIMIT
                value: "70"
·····

但是这显然不够的,只优化 Grafana,对于之后的访问量是不够的,所以,这边又参考了很多的资料,对 Grafana-image-render 和相关组件进行了更深层次的优化:

分为四部分:

• Grafana-image-render Resources 的优化

• Grafana-image-render 配置文件的优化

• Grafana-image-render 副本的优化

• 相关组件的 Ingress 的优化

Grafana-image-render 的 Resources 的优化

k edit sts grafana-image-renderer -nmonitoring
·····
resources:
  limits:
    cpu: "2"          # 限制 CPU 使用的上限
    memory: "2Gi"     # 限制内存使用的上限
  requests:
    cpu: "1"          # 最低需要的 CPU
    memory: "1Gi"     # 最低需要的内存
·····

副本的优化

k edit sts grafana-image-renderer -nmonitoring
·····
spec:
  replicas: 3
·····

Ingress 的优化

这边的 Ingress 也尝试优化下,优化的有 Grafana 还有 Grafana-image-render 的 Ingress 配置文件,都加上:

nginx.ingress.kubernetes.io/proxy-read-timeout: "300"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "300"
    nginx.ingress.kubernetes.io/limit-connections: "50"

Grafana-image-render 的参数优化

启用 Chromium 的无沙盒模式

禁用 Chromium 的沙盒模式。这可以减少权限问题和资源使用,同时加速渲染进程,但需要确保你的环境是可信任的:

args:
  - "--no-sandbox"

但是这边的这个参数优化失败了

图片

图片图片

错误信息表明:--no-sandbox 参数可能被传递到了容器内部,但由于路径或参数格式错误导致无法识别。

所以这边这个优化参数,需要改进下了,这边去了 Grafana 的官网[1]。

就找到了这个配置文件,既然不能直接在 YAML 文件添加,那就把文件挂载进去:

{
  "service": {
    "host": null,
    "port": 8081,
    "protocol": "http",
    "certFile": "",
    "certKey": "",

    "metrics": {
      "enabled": true,                           // 启用性能指标,便于监控性能瓶颈
      "collectDefaultMetrics": true,
      "requestDurationBuckets": [1, 5, 7, 9, 11, 13, 15] // 精简桶数,减少不必要的性能开销
    },

    "logging": {
      "level": "info",
      "console": {
        "json": true,
        "colorize": false
      }
    },

    "security": {
      "authToken": "-"                          // 保持默认值,但可以根据需要加强认证
    }
  },
  "rendering": {
    "chromeBin": null,
    "args": ["--no-sandbox", "--disable-gpu", "--disable-dev-shm-usage"], // 添加 --disable-dev-shm-usage 以优化内存
    "ignoresHttpsErrors": false,

    "timezone": null,                          // 设置为您实际应用的时区以减少渲染时间问题
    "acceptLanguage": "en-US",                 // 设置默认语言以避免语言渲染问题
    "width": 1200,                             // 根据实际需要调整默认宽度
    "height": 800,                             // 根据实际需要调整默认高度
    "deviceScaleFactor": 2,                    // 提升分辨率,但保持适当值,避免性能损失
    "maxWidth": 3840,                          // 增加最大宽度以支持高分辨率渲染
    "maxHeight": 2160,                         // 增加最大高度
    "maxDeviceScaleFactor": 3,                 // 减小 scale factor,避免超高分辨率导致性能问题
    "pageZoomLevel": 1,                        // 保持默认值
    "headed": false,                           // 保持 headless 模式
    "mode": "default",
    "emulateNetworkConditions": false,         // 保持默认值,不模拟网络条件
    "clustering": {
      "monitor": true,                         // 启用监控集群,以便检测集群性能
      "mode": "browser",                       // 使用 browser 模式
      "maxConcurrency": 10,                    // 提升最大并发数(根据服务器性能调整)
      "timeout": 60                            // 增加超时时间以处理复杂图表
    },

    "verboseLogging": false,
    "dumpio": false,
    "timingMetrics": true                      // 启用时间指标以分析性能瓶颈
  }
}

1.服务端优化

• 启用性能监控 (metrics.enabled: true): 开启服务端性能监控,可以通过 Grafana 或其他工具查看渲染器的性能表现,识别瓶颈。

2.渲染器参数调整

• 禁用 GPU (--disable-gpu): 对于 headless 模式的 Chromium,GPU 通常没有意义,禁用它可以节省资源。

• 启用 --disable-dev-shm-usage: 此参数可以解决共享内存不足的问题,尤其是当容器在 Kubernetes 中运行时。

• 合理调整 maxWidth 和 maxHeight: 避免渲染过大的图表分辨率,过高的值可能导致渲染时间过长。

• 提升并发 (maxConcurrency): 在 clustering 配置中提升并发上限,视服务器性能(CPU 和内存)增加值,例如 5 → 10。

• 增加超时 (timeout): 对于复杂图表,可能需要更长时间完成渲染,因此增加超时时间到 60 秒。

3.时间和语言设置

• 明确时区 (timezone): 如果您的图表涉及时间数据,指定一个明确的时区(如 "timezone": "UTC" 或 "Asia/Shanghai") 以减少渲染时间的不确定性。

• 默认语言 (acceptLanguage): 指定默认语言,如 en-US,减少渲染器加载不必要语言资源的时间。

4.内存与分辨率

• 默认分辨率优化 (width, height): 根据实际需求调整为 1200x800,避免渲染器超出必要范围。

• maxDeviceScaleFactor 降低到 3: 将 scale factor 限制到 3,避免过高分辨率耗费过多资源。

5.调整日志与监控

• 启用集群监控 (clustering.monitor: true): 启用后可以帮助您监控渲染器的资源分配和性能表现。

• 启用时间指标 (timingMetrics: true): 启用后可以记录任务的详细耗时数据,便于优化。

注意,如果你这边要把文件挂载进去的话,需要把相应的这些注释和没有用的去除掉,上面只是为了让大家理解每一个参数的作用,我们这里需要用下面这个:

{
  "service": {
    "host": null,
    "port": 8081,
    "protocol": "http",
    "certFile": "",
    "certKey": "",
    "metrics": {
      "enabled": true,
      "collectDefaultMetrics": true,
      "requestDurationBuckets": [1, 5, 7, 9, 11, 13, 15]
    },
    "logging": {
      "level": "info",
      "console": {
        "json": true,
        "colorize": false
      }
    },
    "security": {
      "authToken": "-"
    }
  },
  "rendering": {
    "chromeBin": null,
    "args": ["--no-sandbox", "--disable-gpu", "--disable-dev-shm-usage"],
    "ignoresHttpsErrors": false,
    "timezone": "Asia/Shanghai",
    "acceptLanguage": "zh-CN",
    "width": 1200,
    "height": 800,
    "deviceScaleFactor": 2,
    "maxWidth": 3840,
    "maxHeight": 2160,
    "maxDeviceScaleFactor": 3,
    "pageZoomLevel": 1,
    "headed": false,
    "mode": "default",
    "emulateNetworkConditions": false,
    "clustering": {
      "monitor": true,
      "mode": "browser",
      "maxConcurrency": 70,
      "timeout": 60
    },
    "verboseLogging": false,
    "dumpio": false,
    "timingMetrics": true
  }
}

创建一个 ConfigMap:

k create cm grafana-image-render-config --from-file=config.json -nmonitoring

然后修改我们的部署文件,重新部署,以下的这个文件,也是我们最终的一个优化好的 YAML 文件:

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: grafana-image-renderer
  namespace: monitoring
  labels:
    app: grafana-image-renderer
spec:
  serviceName: grafana-image-renderer-headless
  replicas: 3
  selector:
    matchLabels:
      app: grafana-image-renderer
  template:
    metadata:
      labels:
        app: grafana-image-renderer
    spec:
      containers:
        - name: renderer
          image: grafana/grafana-image-renderer:latest
          ports:
            - containerPort: 8081
          env:
            - name: RENDERER_CALLBACK_URL
              value: "https://grafana.example.com" # Grafana 服务地址
          resources:
            limits:
              cpu: "2"       # 限制 CPU 使用的上限
              memory: "2Gi"  # 限制内存使用的上限
            requests:
              cpu: "1"       # 最低需要的 CPU
              memory: "1Gi"  # 最低需要的内存
          volumeMounts:              # 添加 volumeMount 挂载 ConfigMap
            - name: renderer-config
              mountPath: /usr/src/app/config.json # 挂载到容器内的路径
              subPath: config.json     # 只挂载 ConfigMap 中的 config.json 文件
      volumes:                        # 定义 ConfigMap volume
        - name: renderer-config
          configMap:
            name: grafana-image-renderer-config # 引用 ConfigMap 的名称

这一波优化,让它从船员直接晋升到了船长。

总结

以上就是我们的本篇文章,希望大家多多支持,接下来还会出更多优质文章,大家敬请期待。

如果你这边还有什么好玩的好用的想法,可以联系我。

如果你这边还有什么更好的优化方案或者……

引用链接

[1] 官网: https://grafana.com/docs/grafana/latest/setup-grafana/image-rendering/

责任编辑:武晓燕 来源: 云原生运维圈
相关推荐

2024-01-25 16:19:27

2009-07-02 15:55:03

2023-09-07 14:04:58

计算机CPU内存

2023-08-02 09:28:28

计算机性能CPU

2012-02-13 09:53:16

Marvell整合式芯片88EM8183 IC

2023-10-05 09:03:18

OpenSwoolePHP模型

2013-05-22 13:06:14

aerohive协同控制WLAN

2019-08-13 08:32:14

MySQL数据库性能调优

2020-04-22 11:11:48

Decoder性能应用

2009-04-20 08:51:50

MySQL查询优化数据库

2024-03-01 12:19:00

接口性能优化

2022-05-17 09:02:30

前端性能优化

2017-03-29 14:44:20

网络性能优化

2024-02-19 08:31:10

SoraAIOpenAI

2021-07-05 08:58:17

Golang分布式性能

2013-07-04 08:47:55

华为FusionCube融合一体机

2023-04-17 08:04:15

Redis性能内存

2023-05-31 06:49:54

图表查询数据查询
点赞
收藏

51CTO技术栈公众号