使用openresty+lua来实现grafana中自动切换实时群集/历史群集对应的vmselect

news/2025/9/30 18:00:45/文章来源:https://www.cnblogs.com/ahfuzhang/p/19121084

使用openresty+lua来实现grafana中自动切换实时群集/历史群集对应的vmselect

作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢!

  • cnblogs博客
  • zhihu
  • Github
  • 公众号:一本正经的瞎扯

我曾设计了这样的 VictoriaMetrics 中的实时群集和历史群集:

image

see: deploy_VictoriaMetrics_cluster

期待的效果是:

  • 实时群集存储最近 7 天的数据,保障足够快足够可靠,提供告警查询和当前的系统监控。
    • 为了保障实时群集的稳定性,通过牺牲存储时间来减少存储的数据
  • 历史群集提供长周期的(例如半年),且降采样的数据的存储
    • 以低成本的方式提供长周期的数据查询

部署完成后,我部署了一个新的 vmselect 节点,来连接到所有的实时群集和历史群集的vmselect节点上。
可是当我执行如下的查询时,发现数据是正确数据的两倍:

sum by (path) (increase(http_request_total{job="myApp"}[1m]))

很明显, sum() 时,把实时群集和历史群集中同样的 time series 上的值加了两次。
研究过 dedup 的源码,并未发现明显问题。
无奈,只能通过别的办法绕过去。

于是想到:如果能够自动发现用户查询的时间范围,当用户查询七天以内时转发到实时群集,而查询超过七天就转到历史群集,那么就不用把历史群集和实时群集混合在一起了。
下面是这个思路的详细解决办法:

image

部署 openresty 的 deployment 的代码如下:

# openresty.yaml# nginx 的配置文件放在 configMap 中
apiVersion: v1
kind: ConfigMap
metadata:name: openresty-config
data:# nginx 的 配置文件nginx.conf: |worker_processes  1;events {worker_connections  1024;}http {access_log /dev/stdout;error_log /dev/stderr warn;lua_package_path "/usr/local/openresty/nginx/lua/?.lua;;";upstream realtime {server vmselect-realtime:8481;  # 实时群集的 vmselect}upstream historical {server vmselect-historical:8481;  # 历史群集的 vmselect}server {listen 8401;location /select/0/prometheus/api/v1/query_range {  # 核心是修改 query_range 这条 apicontent_by_lua_file /usr/local/openresty/nginx/lua/router.lua;}# 其它所有路径默认走 realtimelocation / {proxy_pass http://realtime;}location @toRealtime {proxy_pass http://realtime;}location @toHistorical {proxy_pass http://historical;}}}# lua 脚本的代码router.lua: |-- 时间值要支持三种格式:数值,字符串,grafana中的简写local function parse_start(val)if not val then return nil endlocal num = tonumber(val)if num thenif num > 1e12 thenreturn math.floor(num / 1000)elsereturn numendendlocal year, mon, day, hour, min, sec =val:match("^(%d+)%-(%d+)%-(%d+)T(%d+):(%d+):(%d+)")if year thenreturn os.time({year = tonumber(year),month = tonumber(mon),day = tonumber(day),hour = tonumber(hour),min = tonumber(min),sec = tonumber(sec)})endlocal num, unit = val:match("^([%-]?%d+)([smhdw])$")if num and unit thennum = tonumber(num)local seconds = 0if unit == "s" then seconds = numelseif unit == "m" then seconds = num * 60elseif unit == "h" then seconds = num * 3600elseif unit == "d" then seconds = num * 86400elseif unit == "w" then seconds = num * 7 * 86400endreturn ngx.time() + secondsendreturn nilendlocal args = ngx.req.get_uri_args()local is_post = (ngx.req.get_method() == "POST")local post_args = {}if is_post thenngx.req.read_body()post_args = ngx.req.get_post_args()for k,v in pairs(post_args) doargs[k] = vendendlocal start = parse_start(args["start"])local now = ngx.time()local days = 7  -- 这里设定一个七天的范围:七天以内在实时群集查询,超过七天在历史群集查询local n_days_ago = now - days*24*3600local step = "300s"  -- 当查询历史群集时,使用历史群集的降采样后的间隔,即 5 分钟if start ~= nil thenif start > n_days_ago thenreturn ngx.exec("@toRealtime")elseif is_post thenpost_args["step"] = steplocal body_tbl = {}for k,v in pairs(post_args) dotable.insert(body_tbl, ngx.escape_uri(k) .. "=" .. ngx.escape_uri(v))endlocal new_body = table.concat(body_tbl, "&")ngx.req.set_body_data(new_body)elseargs["step"] = stepngx.req.set_uri_args(args)endreturn ngx.exec("@toHistorical")endelsereturn ngx.exec("@toRealtime")end
---
# 这里是部署 openresty 的 deployment
apiVersion: apps/v1
kind: Deployment
metadata:name: openresty
spec:replicas: 1selector:matchLabels:app: openrestytemplate:metadata:labels:app: openrestyspec:containers:- name: openrestyimage: openresty/openresty:1.27.1.2-alpineports:- containerPort: 8401volumeMounts:- name: configmountPath: /usr/local/openresty/nginx/conf/nginx.confsubPath: nginx.conf- name: configmountPath: /usr/local/openresty/nginx/lua/router.luasubPath: router.luacommand: ["/usr/local/openresty/bin/openresty"]args: ["-g", "daemon off;", "-c", "/usr/local/openresty/nginx/conf/nginx.conf"]volumes:- name: configconfigMap:name: openresty-config
---
apiVersion: v1
kind: Service
metadata:name: openresty
spec:selector:app: openrestyports:- protocol: TCPport: 8401targetPort: 8401type: ClusterIP

通过命令行部署:

KUBECONFIG=~/my-test-k8s.yaml kubectl apply -f ./openresty.yaml -n my-namespace

通过 grafana 创建新的数据源,或者可以使用命令查询:

curl -G "http://127.0.0.1:8401/select/0/prometheus/api/v1/query_range?start=-7d" -v

可以通过 header X-Server-Hostname 观察数据由哪个服务返回。

Have Fun. 😃

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/923116.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

上海明鹏建设集团有限公司网站销售管理软件有哪些

来源丨TechTalks作者丨Ben Dickson编译丨科技行者人类级别的表现、人类级别的精度……在开发AI系统的企业中,我们经常会听到这类表述,其指向范围则涵盖人脸识别、物体检测,乃至问题解答等各个方面。随着机器学习与深度学习的不断进步&#xf…

有哪些可以做兼职翻译的网站wordpress循环分类子分类与文章

文章目录 1. 定义2. 应用场景3. 代码实现结语 策略模式(Strategy Pattern)是一种行为型设计模式,定义了一系列算法,并将每个算法封装起来,使它们可以互相替换。策略模式允许客户端在运行时选择算法的具体实现&#xff…

庆阳网站建设做标书的视频网站

httpModules 与 httpHandlers ASP.NET对请求处理的过程:当请求一个*.aspx文件的时候,这个请求会被inetinfo.exe进程截获,它判断文件的后缀(aspx)之后,将这个请求转交给ASPNET_ISAPI.dll,ASPNET_…

【python】根据给定的协议,解析一个hexdump 字符串

一、场景在于客户进行协议交互时,对方给出了协议和一串hex dump字符串, 通过python脚本解析接口的各个字段二、struct库的介绍 官方的文档 https://docs.python.org/zh-cn/3/library/struct.html 2.1了解大序端和小序…

spring boot 切面的机制和作用原理

spring boot 切面的机制和作用原理切面(Aspect)的核心机制就是拦截(Interception),但切面的概念比单纯的拦截更加丰富和系统化。 让我用一个更准确的比喻来解释: 🎯 拦截 vs 切面特性 单纯的拦截(Interceptio…

wordpress首页显示文章数量深圳谷歌seo培训班

引言 ChatGPT4相比于ChatGPT3.5,有着诸多不可比拟的优势,比如图片生成、图片内容解析、GPTS开发、更智能的语言理解能力等,但是在国内使用GPT4存在网络及充值障碍等问题,如果您对ChatGPT4.0感兴趣,可以私信博主为您解决账号和环境…

建设银行重庆市分行官方网站vs 2008 手机网站开发

目录 概述概念适用场景结构类图 衍化过程业务需求基本的数据访问程序工厂方法实现数据访问程序抽象工厂实现数据访问程序简单工厂改进抽象工厂使用反射抽象工厂反射配置文件衍化过程总结 常见问题总结 概述 概念 抽象工厂模式是一种创建型设计模式,它提供了一种将相…

读博期间的工作节奏与身心状态管理经验总结

7-9三个月里连着处理了很多事情的ddl,总结下来是写了两篇论文的大修,一篇会议还有一篇论文的小修。总算是强撑到告一段落,惯例的做一些复盘和总结。 如题,主要是想做两方面问题的分析,第一个是如何有效的管理工作…

郑州网站顾问热狗网淮安市交通建设局网站

文章目录 1. 创建索引2. 插入模拟数据Painless 脚本的基本特点:Painless 脚本的常见用途1. 脚本查询和过滤示例:基于脚本的查询 2. 脚本字段示例:脚本字段 3. 聚合中的脚本示例:脚本聚合 4. 文档更新中的脚本示例:文档…

【Rust GUI开发入门】编写一个本地音乐播放器(7. 制作歌词显示面板) - Jordan

目的是要制作一个这样的面板显示歌词:水平布局:左边30%显示专辑封面 右边70%显示歌词歌词仍然使用ListView来构建,跟前文的歌曲列表一样,代码如下: export component LyricsPanel inherits Window {in property &…

长沙做网站美工的公司网站制度建设模板

当web Service 和 Manifest 被浏览器安装完后,正常情况下,浏览器会提醒用户可将网页安装到桌面。但是通常浏览器都会限制提醒。这时候我们需要手动唤醒浏览器询问用户是否安装到桌面 beforeinstallprompt beforeinstallprompt 事件 window.addEventListe…

做塑料的网站名字潍坊企业网站制作

使用 Redis 可以优化性能,但是存在 Redis 的数据和数据库同步的问题,这是我们需要关注的问题。假设两个业务逻辑都是在操作数据库的同一条记录,而 Redis 和数据库不一致。 Redis 和数据库不一致 在图中,T1 时刻以键 key1 保存数…

深圳商城网站设计公司数字化营销模式及特点

YOLO TT100K: 基于YOLO训练的交通标志检测模型 在原始代码基础上: 修改数据加载类,支持CoCo格式(使用cocoapi);修改数据增强;validation增加mAP计算;修改anchor; 注: 实验开启weig…

html5 自适应网站燕郊建设局网站

目录 打包/解包 作用 zip -r选项 unzip -d选项 如果不使用递归压缩 -l / -v选项 tar 介绍 选项 示例 打包/解包 作用 使多个文件变成一个文件,不易造成数据缺失使下载时间变短 zip 将目录或文件压缩成zip格式 -r选项 递归式压缩某目录及其所有子目录中的文件 如果不…

基于内容可信空间的医疗行业机遇研究报告

基于内容可信空间的医疗行业机遇研究报告pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "M…

建立网站的费用襄阳网站seo

文章目录 概述一、app应用安装白名单核心代码二、app应用安装白名单核心功能分析三、代码实战1.先导入所需要的包2.添加获取白名单方法3.添加限制白名单方法4.上层使用PS:查看当前白名单 总结 概述 在13.0系统rom定制化开发中,客户需求要实现应用安装白名单功能&am…

2025 年智能门锁厂家最新推荐榜单:涵盖高端 / 猫眼 / 家用 / 人脸 / 续航 / 掌静脉等多类型,优质厂家推荐助你选对产品

随着智能家居普及,智能门锁成为家庭安全刚需,但当前市场乱象频发。众多品牌涌入导致产品质量悬殊,部分产品存在指纹识别失误、续航缩水、安全漏洞等问题,甚至有小品牌为降低成本省去核心防护技术,让消费者陷入 “…

2025 年成型机厂商最新权威推荐排行榜:冷弯 / 光伏支架 / 门业等设备企业精度耐用性测评底樑/光伏支架/C型钢/彩钢瓦/快速门成型机厂商推荐

在当前制造业加速升级的背景下,成型机作为建材、门业、新能源等领域的核心生产装备,其品质直接决定下游企业的生产效率与产品竞争力。然而,市场上成型机厂商数量众多,产品质量参差不齐:部分厂商缺乏核心技术,设备…

个人建什么样的网站怎么做网页快照

文章目录 Rust语言之属性宏(Attribute Macro)derive Rust语言之属性宏(Attribute Macro)derive 属性宏是一种基于属性的宏,用于修改、扩展或注解 Rust 代码。它们通常用于为函数、结构体、枚举、模块等添加元数据或自…

自己做的网站点进去很卡即时聊天app开发

摘 要:随着电力需求的不断增加,电力系统供电可靠性要求越来越高,许多供电系统已具备两回或多回供电线路。备用电源自动投入装置可以有效提高供电的可靠性,该类装置能够在工作电源因故障断开后,自动且迅速地将备用电源投…