pushgateway HA高可用方案

未经本人同意不得转载,若引用请附上原文链接。

项目使用flink来处理kafka中的无界流数据,采用的是flink on yarn的模式部署flink任务。最近做flink任务的监控过程中,踩了一些坑。下面是过程,只想看最终方案的直接拉到最后。

先说一下整体流程,flink官方支持通过配置,将运行过程中的jobManager,taskManager的各项指标推送给pushgateway(后面简称"pgw"),然后prometheus每隔30s从pgw主动拉取数据并存储;最后,grafana以prometheus作为数据源绘制各种指标的图标,同时alertmanager上配置了一些指标计算的表达式,当触发时会产生告警。

现在把重点放在 flink => pgw=> prometheus 这条链路上来

假设有2个flink job,因为是per job的模式,所以会在yarn上起两个flink 集群,每个集群都有1个jobManager和若干个taskManager(假设是2个)。最初的设计如下图

在测试场景中,每个作业只有少量的jobManager和taskManager,一直这样使用没问题。但是当部署到预生产环境时,需要调整作业的并行度,这时每个作业有1个jobManagerh和33个taskManager,作业也有将近10个。运行的过程中,发现pgw总是崩溃,导致指标数据中断。但是pgw本身也不支持调节运行各项参数,也找不到运行过程的打印的日志。怀疑其本身是个内存模型,推送的数据源过多导致数据量太大,内存溢出崩溃。(这里不得不吐槽pgw,挺不好用的)。同时也面临着单点问题(假使pgw能承载无限大的数据,但部署的节点有可能会宕机,导致指标都无法推送出去)。

于是从网上查询相关的解决方案,发现github上有个开源项目GitHub - ning1875/dynamic-sharding: 用动态分片解决pushgateway高可用 单点 HA问题,有一些解决思路。但对于我们来说不适用(公司对开源项目有比较严格的限制,且为此引入一整个分布式中间件consul有些矫枉过正的意思),但其提供的想法给了一些灵感。

最初的想法是使用“多点部署+nginx路由”的方式来解决HA的问题。在每个yarn的节点上全都部署一个pgw,然后将他们的ip+port作为上游资源池。flink侧将nginx节点的某个端口作为pgw的输入点,然后由nginx使用轮询的方式将请求依次发往不同的pgw,这样数据是均匀分布的,不会造成某个节点数据过多程序崩溃。然后prometheus从所有的pgw节点拉取数据,聚合在一起形成完整的数据。这样即使某个pgw节点挂了,但nginx有探活机制,它会将请求发往其他的pgw节点上。当宕机的pgw节点恢复后,nginx也会自动把请求分发给它。“完美”实现高可用。

然后新的问题就产生了,发现grafana上针对同一个作业会生成多个图形。例如JobManager1的指标flink_jobmanager_job_uptime(上报flink作业运行时间的一项指标)会有多个图形。究其原因,正是因为nginx采用轮询的方式发送数据,导致JobManager1的指标数据可能在pgw1上有一部分,也可能在pgw2、pgw3上有一部分。

但prometheus抓取数据时,它在指标里增加了一项instance属性用于区分是从哪个pgw拿到的数据,导致本应该合并的数据没有合并,在promethues上用PromQL查询就会有多条数据(从而grafana上对于同一个指标会有多个图形)。但其实我们是不希望它区分数据从哪个pgw过来的,即对于同一个组件的同一个指标只希望有一条数据。那就需要把instance属性去除或掩盖。通过调研发现,在prometheus.yml里添加字段转换的配置可以实现

- job_name: "flink_monitor"scrape_interval: 30sstatic_configs:- targets: ['xxx.xxx.xxx.xxx:9091']metric_relabel_configs:- source_labels: [instance]target_laebl: 'instance'replacement: ''action: replace

相当于将instance的数值替换为空,这样就解决了多条数据的问题。

但是新的问题又产生了,发现grafana图形虽然只有一个了,但图形不正确。拿上述指标flink_jobmanager_job_uptime为例,它应该是个单调递增的直线,但实际显示它是乱七八糟的线,有上升也有下降,总运行时间怎么可能会下降呢,这不符合常识。

然后通过PromQL查询jobManager1的指标flink_jobmanager_job_uptime,是一条数据,但是,不停的点击查询发现,其数值不是递增的,有时候会变小。

又经过一番研究,才知道虽然instance被抹除了,但实际上prometheus还是从3个pwg节点上拿到了三份数据,表面上显示为一个指标项,但结果有三个。每次查询的时候随机返回一个。

这是jobManager推送指标数据到pgw的时间线,由于nginx的轮询机制,假设30s、60s、90s的指标发往了不同的pgw。而在prometheus的时间线上,就大有文章了。

 由于prometheus是同时从多个pgw获取数据的,第30s时,由于只有pgw1有数据,拿到了指标值30,没问题。第60s时,pgw1和pgw2都有数据,同时拿到后prometheus将两个数据都存了下来,且pgw1返回的还是老数据,此时通过PromQL查询该指标时,你会发现值是在30和60两个值随机返回一个。到了90s时,pgw1、pgw2、pgw3都有了数据,而除了pgw3之外都是老数据,查询时也是随机挑一个返回。我晕~这样就能解释为啥grafana图形是乱的。

这pushgateway也太拉跨了,问你要数据你就给啊,也不看看你那都啥时候的数据了。

行,以解决问题为优先。如果,能把同一个组件(jobManager或taskManager)的数据发往同一个pgw,这样就不会产生多份数据的问题了,某个pgw只会独立的拥有某个jobManager的最新指标数据。我们知道nginx是有通过ip_hash,url_hash的能力,可能让同样的请求分发到同一个目标机器。

那就先研究一下,组件(jobManager或taskManager)上报指标时,它的请求是什么样的呢。通过nginx配置access_log后,打印出Request本身。以下是nginx.conf的部分配置(完整配置往后看),其中$request可以打印出请求url

# 定义日志打印格式
log_format main '$remote_addr - "$request" ''$status $upstream_addr';

通过转发的日志access_log发现,flink会调用以下API推送指标数据给pgw

PUT /metrics/job/flink-metrics112ljlkna02k1l29j210nkns HTTP/1.1

 而中间这串flink-metrics112ljlkna02k1l29j210nkns,就是pgw的Group Key的概念(可以在pgw的前端页面上看到),每个独立的jobManager或taskManager都会拥有自己独一无二的Group Key。那答案就呼之欲出了,不需要解析报文体,请求的uri的唯一变化值就是Group Key,那只通过uri就能区分是哪个组件了。这不是完美契合nginx的uri_hash概念嘛,这样既解决了单点问题,又解决了数据混乱的问题。

说干就干,在nginx.conf上补充得到完整的配置。如下:

worker_processes 1;
events{worker_connections 1024;
}http {# 开启gzip压缩gzip on;# 下面这两项需要加,不加flink推送的指标数据过大,nginx转发会报错413# 配置请求体缓存区大小client_max_body_size 10m;# 配置客户端请求体最大值client_body_buffer_size 10m;# 配置上游服务器资源池upstream pushgateway_servers {# 配置按照uri进行hash,需要引入对应模块hash $request_uri# 配置pgw的实例ipserver xxx.xxx.xxx.xxx:9091;server xxx.xxx.xxx.xxx:9091;}# 定义日志打印格式log_format main '$remote_addr - "$request" ''$status $upstream_addr';server {listen 9099;server_name localhost;location / {proxy_pass http://pushgateway_servers; #使用上面的资源池proxy_set_header Host $host;proxy_set_header X-Real-IP $remote_addr;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;proxy_set_header X-Forwarded-Proto $scheme;# 使用上面的日志定义access_log logs/access.log main;}}
}

配置完成后重启,发现一切“美好”~ 我们来分析一下现在的流程。

每个组件调用nginx时uri里已经表明了身份,通过url_hash,只会去往固定的pgw。也就是全量的组件数据会均匀(假设hash的均匀)分布在所有的pgw上,每个pgw拥有的数据都是独一无二且最新的,prometheusc从所有pgw获取数据拼凑成完整数据。

如果有pgw节点掉线(假设pgw1),nginx对于同一个uri的请求后续也会发往某个具体的pgw新节点(假设pgw2),不会发送给多个节点。同时因为pgw节点掉线,prometheus也无法从对应的pgw获取(老)数据,则不会产生多个数据值的问题。最多会因为抖动过程中,prometheus获取不到数据,在grafana上展示一个空点,但这是可以接受的。

那现在完美了吗?我又要说但是了,但是当pgw1节点恢复后,nginx由于自带的探活机制,又会重新把JM1的指标上送给pgw1,现在又出现pgw1和pgw2同时拥有JM1的数据,且pgw2是老数据。

现在的问题又到了,pgw1恢复时pgw2关于JM2的数据如何删除呢。dynamic-sharding里提到的,当pgw节点宕机需要从consul上剔除该节点,即使后面恢复也不使用该节点,这一点在consul里可以做到,但在nginx里无法控制啊。如果想要节点恢复使用,需要重启所有pgw,以清除所有pgw内存中保留的数据。等下,重启pgw就可以清除旧数据,那我在pgw1节点恢复时,触发机制去重启pgw2不就相当于删除了JM2的旧数据。事实上,这样并不好做。nginx根本没有提供相应的配置能让你在节点切走和切回时插入你想要的操作,可以通过lua脚本切入整个转发流程,但学习和使用成本较高。

其实,在pgw的前端页面上,右上角有按钮“Delete All Groups”是可以删除当前pgw的所有数据的,通过这种方式可以实现不重启进程也能完成数据的删除,即通过API请求发到对应的pgw就可以实现清除其内存中的数据,而且不需要身份认证。

PUT /api/v1/admin/wipe

但由于无法得知pgw1挂了后,对应组件的指标数据被nginx重新分配到哪个节点中。目前采用的折衷处理方式是,在pgw的服务启动脚步里增加一个广播API请求到所有其他pgw节点上,以删除它们内存中的数据。这虽然可能会使最终prometheus中有些指标会缺失一两个数据点,但总比错误数据效果好一些吧。如果你有其他更好的方案,也欢迎来讨论。

至此,也就基本完成了pgw的HA。总结一下,通过部署多个pgw节点,前置nginx做负载均衡,通过uri_hash确保数据独立且均匀分布在所有pgw节点上。然后通过prometheus连接所有的pgw节点读取数据聚合形成flink集群完整数据。在pgw节点宕机时,数据会漂移至其他pgw节点继续上报。但当pgw节点恢复时,通过启动脚步广播API的方式清除其他所有pgw节点的内存数据来保障数据的独一无二性。

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

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

相关文章

01-Chromedriver下载与配置(mac)

下载地址: 这里我用的最后一个,根据自己chrome浏览器选择相应的版本号即可 ChromeDriver官网下载地址:https://sites.google.com/chromium.org/driver/downloads ChromeDriver官网最新版下载地址:https://googlechromelabs.git…

使用docker-compose安装Milvus向量数据库及Attu可视化连接工具

首先确保系统已经安装上了docker 然后去https://github.com/docker/compose/releases/下载安装docker-compose 跟随自己下系统和服务器情况下载 上传到服务器 mv docker-compose-linux-aarch64 docker-compose chmod x docker-compose2.dockr-compose命令 docker-compose …

Conda + JuiceFS :增强 AI 开发环境共享能力

Conda 是当前 AI 应用开发领域中非常流行的环境和包管理系统,因其能够简单便捷地创建与系统资源相隔离的虚拟环境广受欢迎。 Conda 支持在不同的操作系统上重建相同的工作环境,但在环境共享复用方面仍存在一些挑战。比如,在不同机器上复用相…

【SpringBoot】31 Session + Redis 实战

Gitee https://gitee.com/Lin_DH/system 介绍 【SpringBoot】30 Cookie、Session、Token https://blog.csdn.net/weixin_44088274/article/details/144241595 背景 Spring Session 是 Spring 的一个子项目,它提供了一种管理用户会话信息的方法,无论…

关于网站的权重和百度蜘蛛爬虫的关系

网站的权重和百度蜘蛛爬虫的关系是密切关联的。 网站权重是一个衡量网站在搜索引擎中重要性的概念,它反映了网站在搜索引擎算法中的相对重要程度。而百度蜘蛛爬虫则是百度搜索引擎用来抓取网页内容的工具,通过分析网页的URL、内容、链接等因素来评估网站…

游戏引擎学习第35天

开场介绍 今天的任务是继续改进一个虚拟的瓦片地图系统,使其适合处理更大的世界。我们希望这个系统能管理大范围的游戏世界,其中包含按需存储的小区域。昨天,我们介绍了“内存区域”的概念,用于管理持久性存储。我们计划今天继续…

Leetcode经典题5--轮转数组

题目描述 给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。 输入输出示例 : 输入: nums [1,2,3,4,5,6,7], k 3 输出: [5,6,7,1,2,3,4] 解释: 向右轮转 1 步: [7,1,2,3,4,5,6] 向右轮转 2 步: [6,7,1,2,3,4,5] 向右…

【JS】简单CSS简单JS写的上传进度条

纯JS写的&#xff0c;简单的上传进度条&#xff0c;当上传的文件较大&#xff0c;加一个动态画面&#xff0c;就不会让人觉得出错了或网络卡了 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"v…

2023 年“泰迪杯”数据分析技能赛B 题企业财务数据分析与造假识别

2023 年“泰迪杯”数据分析技能赛B 题企业财务数据分析与造假识别 一、背景 财务数据是指企业经营活动和财务结果的数据记录&#xff0c;反映了企业的财务状况与经营成果。对行业、企业的财务数据进行分析&#xff0c;就是要评价其过去的经营业绩、衡量现在的财务状况、预测未…

perl Window安装教程

perl Window安装教程 下载地址 https://platform.activestate.com/tangxing806/ActivePerl-5.28/distributions 运行state-remote-installer.exe 按下图截图步骤 检查perl版本 参考文献&#xff1a; perl安装教程

知识图谱9:知识图谱的展示

1、知识图谱的展示有很多工具 Neo4j Browser - - - - 浏览器版本 Neo4j Desktop - - - - 桌面版本 graphX - - - - 可以集成到Neo4j Desktop Neo4j 提供的 Neo4j Bloom 是用户友好的可视化工具&#xff0c;适合非技术用户直观地浏览图数据。Cypher 是其核心查询语言&#x…

【数据分享】1901-2023年我国省市县三级逐年最低气温数据(Shp/Excel格式)

之前我们分享过1901-2023年1km分辨率逐月最低气温栅格数据和Excel和Shp格式的省市县三级逐月最低气温数据&#xff0c;原始的逐月最低气温栅格数据来源于彭守璋学者在国家青藏高原科学数据中心平台上分享的数据&#xff01;基于逐月栅格数据我们采用求年平均值的方法得到逐年最…

HBU深度学习实验15-循环神经网络(2)

LSTM的记忆能力实验 飞桨AI Studio星河社区-人工智能学习与实训社区 (baidu.com) 长短期记忆网络&#xff08;Long Short-Term Memory Network&#xff0c;LSTM&#xff09;是一种可以有效缓解长程依赖问题的循环神经网络&#xff0e;LSTM 的特点是引入了一个新的内部状态&am…

使用windows的包管理工具chocolatey

开发人员&#xff0c;在windows环境下&#xff0c;最头疼的是安装和配置各种环境变量&#xff0c;现在chocolatey 可以一键安装&#xff0c;不需要再去配置环境变量了。比如你安装一个java的环境&#xff0c;仅仅需要你敲几个命令&#xff0c;都能帮你搞定。 我自己已经使用这…

VTK知识学习(21)- 数据的读写

1、前言 对于应用程序而言&#xff0c;都需要处理特定的数据&#xff0c;VTK应用程序也不例外。 VTK应用程序所需的数据可以通过两种途径获取: 第一种是生成模型&#xff0c;然后处理这些模型数据(如由类 vtkCylinderSource 生成的多边形数据); 第二种是从外部存储介质里导…

QT 中 QString 转换为 Unicode 和 ASCII 的方法

目录 ​编辑 前言 一、QString转换成 Unicode编码 二、QString转换成ASCII编码 三、Unicode编码转换成QString汉字 四、ASCII编码转成QString 五、注意事项 六、总结 前言 在 Qt 开发中&#xff0c;经常会遇到需要将QString中的字符转换为特定编码格式的需求。本文将介…

基于51单片机64位病床呼叫系统设计( proteus仿真+程序+设计报告+原理图+讲解视频)

基于51单片机病床呼叫系统设计( proteus仿真程序设计报告原理图讲解视频&#xff09; 仿真图proteus7.8及以上 程序编译器&#xff1a;keil 4/keil 5 编程语言&#xff1a;C语言 设计编号&#xff1a;S0095 1. 主要功能&#xff1a; 基于51单片机的病床呼叫系统proteus仿…

【OpenDRIVE_Python】使用python脚本更新OpenDRIVE数据中路口Junction名称

示例代码说明&#xff1a; 遍历OpenDRIVE数据中每个路口JunctionID,读取需要变更的路口ID和路口名称的TXT文件,若JunctionID与TXT文件中的ID一致&#xff0c;则将TXT对应的点位名称更新到OpenDRIVE数据中Junction name字段。补充&#xff1a;需要保持TXT和OpenDRIVE数据文件编…

java+ssm+mysql商品管理系统

项目介绍&#xff1a; 使用javassmmysql开发的商品库存管理系统&#xff0c;系统包含管理员&#xff0c;员工角色&#xff0c;功能如下&#xff1a; 管理员&#xff1a;员工管理&#xff1b;供应商管理&#xff1b;客户管理&#xff1b;商品管理&#xff1b;商品进货&#xf…

android studio创建虚拟机注意事项

emulator 启动模拟器的时候&#xff0c;可以用 AVD 界面&#xff0c;也可以用命令行启动&#xff0c;但命令行启 动的时候要注意&#xff0c;系统有两个 emulator.exe &#xff0c;建议使用 emulator 目录下的那个&#xff01;&#xff01; 创建类型为google APIs的虚拟机可从…