记一次Prometheus的离奇经历

前情提要 书接上回,为了获取客户的 Prometheus 监控数据,我写了个脚本用来通过 API 获取监控数据,然后转换成 OpenMetrics 格式以方便传输和导入,代码如下。 import datetime import subprocess import requests import sys """ http://localhost:9090 """ prometheus_url = input("请输入Prometheus链接: ") username = input("请输入用户名: 如无认证,请回车跳过") password = input("请输入密码: 如无认证,请回车跳过") print("下面的两个变量只需填写一个或者不填使用默认值") step = input("请输入每两个数据点间隔(单位秒,建议为5的倍数): ") hours = input("请输入往前查询的小时数(单位小时,建议不填): ") auth = None metric_param = "e" # 将查询出所有带有elasticsearch的指标 if username != "" and password != "": auth = (username, password) # 检查用户输入是否为数字 def is_number(value): try: int(value) return True except ValueError: return False # 如果用户同时设置了hours和step,按用户的输入值查询 if hours != "" and step != "": hours = int(hours) step = int(step) print("将查询过去{}小时的数据,步长为{}秒".format(hours, step)) else: # 如果用户没有输入hours和step,使用默认值 if hours == "" and step == "": print("将使用默认值查询") hours = 30 step = 10 elif hours != "": hours = int(hours) # 根据用户输入的hours计算step step = int(60 * 60 / (11000 / hours)) + 1 print("将查询过去{}小时的数据,步长为{}秒".format(hours, step)) elif step != "": step = int(step) # 根据用户输入的step计算hours hours = int(11000 / (60 / step) / 60) print("将查询过去{}小时的数据,步长为{}秒".format(hours, step)) else: print("输入的小时数和步长必须为有效的数字。") sys.exit(1) end_time = datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%SZ") query_time = datetime.datetime.now() - datetime.timedelta(hours=hours) start_time = query_time.strftime("%Y-%m-%dT%H:%M:%SZ") series = requests.get('{}/api/v1/label/__name__/values'.format(prometheus_url), auth=auth) if series.status_code != 200: print("查询失败,请检查{}/api/v1/label/__name__/values的连通性".format(prometheus_url)) sys.exit(1) else: metric_list = [queryQL for queryQL in series.json()['data'] if '{}'.format(metric_param) in queryQL] # 将查询出所有带有elasticsearch的指标 print("本次一共查询了{}个指标".format(len(metric_list))) with open('openmetrics.txt', 'w') as f: line_count = 0 # 用于统计行数的计数器 for metric_name in metric_list: metric_response = requests.get( '{}/api/v1/query_range?query={}&start={}&end={}&step={}s'.format( prometheus_url, metric_name, start_time, end_time, step)) print(metric_response.url) if metric_response.status_code != 200: print("查询失败,状态码为{}".format(metric_response.status_code)) sys.exit(1) else: prometheus_data = metric_response.json() for result in prometheus_data['data']['result']: metric_name = result['metric']['__name__'] labels = [] for key, value in result['metric'].items(): if key != '__name__': labels.append(f'{key}="{value}"') labels = ','.join(labels) openmetrics = [] for value in result['values']: openmetrics.append(f'{metric_name}{{{labels}}} {value[1]} {value[0]}\n') openmetrics = ''.join(openmetrics) f.write(openmetrics) line_count += openmetrics.count('\n') # 统计写入的行数 f.write('# EOF\n') # 文档末尾标志必须添加,promtool才能正常识别 with open('./note.txt', 'a') as f: # 使用'a'模式来追加内容 f.write('\n') f.write("本次查询的时间范围数据为{},查询的步长为{}秒,共查询了{}种指标,共写入{}行数据".format( end_time, step, len(metric_list), line_count)) # print("写入完成,共写入{}行数据".format(line_count)) # subprocess.run(['gzip', 'openmetrics.txt']) # 脚本运行完毕后自动压缩文本文件 已知我本地测试的 Prometheus 采集频率是每秒一次,假设我们把一秒当作最小的单位,那么可以说采集到的监控数据是一条连续的线。根据代码中的公式 hours = int(11000 / (60 / step) / 60),我们说当步长越大时,跨步就越大,那么数据点的数量应该越小,就好比你走同一段路,你迈的步子越大,是不是需要的步数就越少。 为了记录每一次的步长,所以在代码的最后面将每次查询的参数记录到另一个文本中,如下所示: # 没有停止各种exporter,数据会不停地增加 本次查询的时间范围数据为2023-09-25T19:28:22Z,查询的步长为5秒,共查询了593种指标,共写入9265448行数据 本次查询的时间范围数据为2023-09-25T19:29:41Z,查询的步长为10秒,共查询了593种指标,共写入7027362行数据 本次查询的时间范围数据为2023-09-25T19:31:04Z,查询的步长为6秒,共查询了593种指标,共写入10554827行数据 本次查询的时间范围数据为2023-09-25T19:31:40Z,查询的步长为12秒,共查询了593种指标,共写入5942033行数据 本次查询的时间范围数据为2023-09-25T19:32:14Z,查询的步长为60秒,共查询了593种指标,共写入6093494行数据 本次查询的时间范围数据为2023-09-25T19:33:08Z,查询的步长为30秒,共查询了593种指标,共写入4305816行数据 本次查询的时间范围数据为2023-09-25T19:33:39Z,查询的步长为15秒,共查询了593种指标,共写入4897653行数据 本次查询的时间范围数据为2023-09-25T19:34:03Z,查询的步长为45秒,共查询了593种指标,共写入5551486行数据 本次查询的时间范围数据为2023-09-25T19:37:27Z,查询的步长为55秒,共查询了593种指标,共写入5826733行数据 本次查询的时间范围数据为2023-09-25T19:38:07Z,查询的步长为6秒,共查询了593种指标,共写入10711991行数据 # 停止了exporter但是并不是完全没有变动 本次查询的时间范围数据为2023-09-25T19:46:03Z,查询的步长为5秒,共查询了593种指标,共写入9281524行数据 本次查询的时间范围数据为2023-09-25T19:46:32Z,查询的步长为6秒,共查询了593种指标,共写入10808191行数据 本次查询的时间范围数据为2023-09-25T19:46:57Z,查询的步长为7秒,共查询了593种指标,共写入9272944行数据 本次查询的时间范围数据为2023-09-25T19:47:22Z,查询的步长为8秒,共查询了593种指标,共写入8113585行数据 本次查询的时间范围数据为2023-09-25T19:47:44Z,查询的步长为9秒,共查询了593种指标,共写入7386695行数据 本次查询的时间范围数据为2023-09-25T19:48:14Z,查询的步长为10秒,共查询了593种指标,共写入7019614行数据 本次查询的时间范围数据为2023-09-25T19:48:44Z,查询的步长为5秒,共查询了593种指标,共写入9267445行数据 本次查询的时间范围数据为2023-09-25T19:49:37Z,查询的步长为55秒,共查询了593种指标,共写入5840872行数据 本次查询的时间范围数据为2023-09-25T19:50:40Z,查询的步长为15秒,共查询了593种指标,共写入4990036行数据 本次查询的时间范围数据为2023-09-25T19:54:53Z,查询的步长为15秒,共查询了593种指标,共写入5005466行数据 求助智障 以下是和人工智障的对话内容: ...

2023-09-25 · 11 min

获取客户 Prometheus 监控数据

业务背景 在排查问题时,想通过 Grafana 看板查看用户的监控,只能靠拍照,效率低,质量一般。设计一个方案能够方便地将问题出现前 24 小时的监控数据拿到,在本地导入,就能够在本地 Grafana 方便地查看。Prometheus 本身只提供了 API 查询的功能,并没有导出数据功能;自带的 promtool 也只提供验证规则文件和配置文件、调试等功能。 参考文章 Analyzing Prometheus data with external tools Prometheus backfilling 方案一:使用 API 导出转换成 CSV 使用 API 查询,将查询到的数据转换成 CSV。刚好 Grafana 有插件能够将 CSV 作为数据源。经过实验后并不是特别顺利,能够读取到 CSV,但没有成功绘制出图像。 总结 经过实验后并不是特别顺利,能够读取到 CSV 但没有成功绘制出图像。看板中部分查询语句中包含看板变量,CSV 数据源无法实现看板变量。 方案二:拷贝 Prometheus 数据文件 Prometheus 按照两个小时为一个时间窗口,将两小时内产生的数据存储在一个块(Block)中。每个块都是一个单独的目录,里面包含该时间窗口内的所有样本数据(chunks)、元数据文件(meta.json)以及索引文件(index)。其中索引文件会将指标名称和标签索引到样本数据的时间序列中。此期间如果通过 API 删除时间序列,删除记录会保存在单独的逻辑文件 tombstone 当中。 Prometheus 为了防止丢失暂存在内存中的还未被写入磁盘的监控数据,引入了 WAL 机制。WAL 被分割成默认大小为 128M 的文件段(segment),之前版本默认大小是 256M,文件段以数字命名,长度为 8 位的整型。WAL 的写入单位是页(page),每页的大小为 32KB,所以每个段大小必须是页的大小的整数倍。如果 WAL 一次性写入的页数超过一个段的空闲页数,就会创建一个新的文件段来保存这些页,从而确保一次性写入的页不会跨段存储。这些数据暂时没有持久化,TSDB 通过 WAL 将数据保存到磁盘上(保存的数据没有压缩,占用内存较大),当出现宕机时,启动多协程读取 WAL,恢复数据。 [mingming.chen@m162p65 data]$ tree . ├── 01E2MA5GDWMP69GVBVY1W5AF1X │ ├── chunks # 保存压缩后的时序数据,每个 chunks 大小为 512M,超过会生成新的 chunks │ │ └── 000001 │ ├── index # chunks 中的偏移位置 │ ├── meta.json # 记录 block 块元信息,比如样本的起始时间、chunks 数量和数据量大小等 │ └── tombstones # 通过 API 方式对数据进行软删除,将删除记录存储在此处(API 的删除方式,并不是立即将数据从 chunks 文件中移除) ├── 01E2MH175FV0JFB7EGCRZCX8NF │ ├── chunks │ │ └── 000001 │ ├── index │ ├── meta.json │ └── tombstones ├── 01E2MQWYDFQAXXPB3M1HK6T20A │ ├── chunks │ │ └── 000001 │ ├── index │ ├── meta.json │ └── tombstones ├── lock ├── queries.active └── wal # 防止数据丢失(数据收集上来暂时是存放在内存中,wal 记录了这些信息) ├── 00000366 # 每个数据段最大为 128M,存储默认存储两个小时的数据量 ├── 00000367 ├── 00000368 ├── 00000369 └── checkpoint.000365 └── 00000000 无论是 block 数据还是 wal 数据,都是可以直接打包,转移到本地的 Prometheus。需要注意的是版本问题,且本地 Prometheus 不能有数据。如果本地监控数据目录不为空,那么导入时会出现问题(因为时间问题)。只需要近期数据,太远的数据没有价值,可以通过 block 文件里面的 meta.json 查看时间戳。 ...

2023-09-08 · 17 min

折腾博客的意义

WordPress 没有像大城市里的 Z 时代青少年一样在初中高中就开始折腾各种网络安全、编程技术、折腾博客,我在大二时才有建一个网站的想法,买了阿里云的 ECS,买了域名,装了宝塔,然后 LNMP 一键启动,WordPress 无脑梭哈,还折腾了备案。但是除了写了几个测试的文章,后面连续费云主机的动力都没有了,于是索性连域名也卖了。记得好像是十几块钱买的,还卖了 9 块钱。我的博客从网络世界消失了。对于我来说,我知道了原来网站是这么运行的,原来还有宝塔和 WordPress 这么牛逼的工具,原来备案 TM 的这么麻烦! Hexo 再后来技术上走向正规,经常查一些技术文章,看到别人写的博客,知道了 Hexo,知道了 GitHub Pages,开始折腾静态博客,使用 Hexo 和使用人数最多的主题 Next,再结合网络上的各种方案魔改。当时我想的就是“整挺好,以后就这样不换了”。Hexo 是截止我写这篇文时用的最久的框架,相处久了,了解的多了,遇到麻烦的次数也就多了。 VuePress 期间还折腾了文档站点,看到了别人的文档站点挺好的,我的一些文档也可以放进文档站点作为文档库啊。然后又开始了 VuePress、Docusaurus,因为抱着一旦启用就不换的决心,每个方案都折腾了不少时间。最终我的 GitHub 仓库还残存着一个 Docs 的仓库。然后我发现文档这个东西就是个伪需求,就此作罢。 Hexo in Docker 一开始 Hexo 运行在 Mac 上,我不敢随便重置系统。后来对 Docker 的运用加深,开始折腾在 Docker 里运行 Hexo,也还算顺利。Dockerfile 存在 Dropbox 里,存在 iCloud 里,非常的安稳和踏实。但是后面的事告诉我事情没这么简单:Dockerfile 中每次都拉取 latest 的 node 基础镜像,git clone 也是每次都是最新的,导致版本不兼容时就会有报错,或者是警告。这个问题不是不能解决,指定版本就好了呀,是的。但我还是开始嫌弃 Hexo 了,甚至我都没有继续写下去的动力了(借口)。在群里吐槽 Hexo 的问题的时候,馆长说那你可以用 Hugo 啊,就一个可执行文件。此时 Hexo 的种种不好,都浮现脑海:速度慢,环境麻烦,node_modules…… Hugo 终于下定决心,试试 Hugo。 brew install hugo hugo new site blog cd blog git clone [你的想要的主题] themes hugo new post/first.md hugo server -D 不行我要开始无脑吹了:把之前的 blog 的 Markdown 文件复制到 Hugo 目录,生成速度嗖嗖的。我选的非常精简的主题,摆脱了 npm,摆脱了 package.json,整个主题非常的简单、简洁。以前 Hexo 需要安装插件实现搜索、字数统计,Hugo 全都聚合在了一个可执行文件里。有好处也有不好:好处是,不用折腾了,啥都有;坏处可能就好像花钱买了不需要的东西,目前这点坏处我是无所谓的。 ...

2023-08-28 · 4 min

Flask+Vue 前后端分离记录

如题,记录告警平台从原来的 layui 升级到 Vue,并实现 Flask+Vue 前后端分离,记录前后端三种解决跨域的方式(有点像茴香豆的四种写法?没事,技多不压身) ...

2023-07-12 · 3 min

分享一份值得分享的 SRE 面试题

如题,分享记录一份还不错的面试题。看面试题的目的不是为了记住题目然后去应试(也不可能人家题都不带换的),如果是这样的话,就算过了,面试真正在工作中遇到挑战时一脸懵逼,怕是离大礼包不远了。看面试题是为了知道自己的水平,了解哪些是常见问题,如果有不足的地方就要提升弥补。 ...

2023-07-11 · 5 min

Kuboard-spray 图形化安装高可用的 Kubernetes

不管你有没有听说过 Kuboard,它都是一个非常有名的 K8s 管理工具,官方描述的是 Kuboard - Kubernetes 多集群管理界面,我这次想介绍的是 Kuboard-spray ...

2023-07-07 · 2 min

记录在 K8s 中部署 Nginx 并修改主页

如题,记录在 K8s 中部署 Nginx 并修改主页 ...

2023-06-20 · 3 min

K8s 中 Pod 的调试技巧

今天在逛大佬的博客时又学到好东西了,特此记录一下,关于 K8s 中 Pod 调试的奇技淫巧,虽然不一定能用到,但是值得记录 ...

2023-06-13 · 2 min

Kubernetes 和 Flask 的组合

众所周知,Minikube 有自带的 dashboard,输入命令 minikube dashboard 打开链接就能看到。某日突发奇想,如果公司内部需要一个自定义的 Kubernetes 监控平台,以满足一些个性化的需求呢?比如,我想看到最近新建的 100 个 Pod,或者我想查看最近的 K8s 集群 Events。使用 Flask 来开发一个平台满足这些自定义需求,是一个不错的选择。 ...

2023-05-14 · 2 min

我国开源软件开发者数量突破 800 万

根据 4 月 17 日央视新闻联播的报道,引用工业和信息化部的消息,中国开源软件开发者的数量已经超过了 800 万,位居全球第二。开源软件是一种通过分布式手段开发的软件,具有公开、可使用、可修改、可分发等特点。央视新闻联播发布了一条简短的视频消息,宣布了这一消息。 ...

2023-04-18 · 1 min