当前位置:   article > 正文

数据库|什么?通过 Prometheus 编写巡检脚本?

数据库|什么?通过 Prometheus 编写巡检脚本?

目录

一、背景

二、认识PromQL

三、修改PromQL

四、巡检脚本

五、总结


一、背景

笔者近日在驻场期间,观察到该处的TiDB集群数量颇为可观,竟有近150套之多。这些集群的规模亦是不小,最小的集群亦由6个节点组成,而最大的集群则拥有超过200个节点。面对如此庞大的集群体量,日常的巡检工作变得异常繁琐。

那么有没有什么办法能够代替手动巡检,并且能够快速准确的获取到集群相关信息的方法呢?

答案是,有但不完全有。

其实可以利用 tidb 的 Prometheus 来获取集群相关的各项数据,比如告警就是一个很好的例子。可惜了,告警只是获取了当前数据进行告警判断,而巡检需要使用一段时间的数据来作为判断的依据。而且,告警是已经达到临界值了,巡检却是要排查集群的隐患,提前开始规划,避免出现异常。

那直接用 Prometheus 获取一段时间的数据,并且把告警值改低不就行了?

二、认识PromQL

要使用 Prometheus ,那必须要先了解什么是 PromQL 。

PromQL查询语言和日常使用的数据库SQL查询语言(SELECT * FROM ...)是不同的,PromQL 是一种嵌套的函数式语言,就是我们要把需要查找的数据描述成一组嵌套的表达式,每个表达式都会评估为一个中间值,每个中间值都会被用作它上层表达式中的参数,而查询的最外层表达式表示你可以在表格、图形中看到的最终返回值。

比如下面的查询语句:

  1. histogram_quantile( # 查询的根,最终结果表示一个近似分位数。
  2. 0.9, # histogram_quantile() 的第一个参数,分位数的目标值
  3. # histogram_quantile() 的第二个参数,聚合的直方图
  4. sum by(le, method, path) (
  5. # sum() 的参数,直方图过去5分钟每秒增量。
  6. rate(
  7. # rate() 的参数,过去5分钟的原始直方图序列
  8. demo_api_request_duration_seconds_bucket{job="demo"}[5m]
  9. )
  10. )
  11. )

然后还需要认识一下告警的 PromQL 中,经常出现的一些函数:

  • rate

用于计算变化率的最常见函数是 rate(),rate()函数用于计算在指定时间范围内计数器平均每秒的增加量。因为是计算一个时间范围内的平均值,所以我们需要在序列选择器之后添加一个范围选择器。

  • irate

由于使用 rate 或者 increase 函数去计算样本的平均增长速率,容易陷入长尾问题当中,其无法反应在时间窗口内样本数据的突发变化。

例如,对于主机而言在 2 分钟的时间窗口内,可能在某一个由于访问量或者其它问题导致 CPU 占用 100%的情况,但是通过计算在时间窗口内的平均增长率却无法反应出该问题。

为了解决该问题,PromQL 提供了另外一个灵敏度更高的函数irate(v range-vector)。irate 同样用于计算区间向量的计算率,但是其反应出的是瞬时增长率。

  • histogram_quantile

获取数据的分位数。histogram_quantile(φ scalar, b instant-vector) 函数用于计算历史数据指标一段时间内的分位数。该函数将目标分位数 (0 ≤ φ ≤ 1) 和直方图指标作为输入,就是大家平时讲的 pxx,p50 就是中位数,参数 b 一定是包含 le 这个标签的瞬时向量,不包含就无从计算分位数了,但是计算的分位数是一个预估值,并不完全准确,因为这个函数是假定每个区间内的样本分布是线性分布来计算结果值的,预估的准确度取决于 bucket 区间划分的粒度,粒度越大,准确度越低。

该部分引用:Prometheus基础相关--PromQL 基础(2)

Prometheus基础相关--PromQL 基础(2) - 知乎) 想学习的同学可以去看看原文

三、修改PromQL

要让巡检使用 PromQL ,就必须要修改告警中的 PromQL。这里需要介绍一个函数:max_over_time(range-vector),它是获取区间向量内每个指标的最大值。其实还有其他这类时间聚合函数,比如avg_over_time、min_over_time、sum_over_time等等,但是我们只需要获取到最大值,来提醒 dba 就行了。

Prometheus 是支持子查询的,它允许我们首先以指定的步长在一段时间内执行内部查询,然后根据子查询的结果计算外部查询。子查询的表示方式类似于区间向量的持续时间,但需要冒号后添加了一个额外的步长参数:[<duration>:<resolution>]。

举个例子:

  1. # 原版
  2. sum(rate(tikv_thread_cpu_seconds_total{name=~"(raftstore|rs)_.*"}[1m])) by(instance)
  3. # 修改
  4. max_over_time(avg(rate(tikv_thread_cpu_seconds_total{name=~"(raftstore|rs)_.*"}[1m])) by(instance)[24h:1m])

这是获取 TiKV raftstore 线程池 CPU 使用率的告警项。原版是直接将1分钟内所有线程的变化率相加,而笔者的修改版是将1分钟内所有线程的使用率取平均值,并且从此刻向后倒24小时内,每一分钟执行一次获取平均线程使用率的查询,再取最大值。

也就是说,从24小时前,到现在,每分钟执行一次(步长为1分钟):avg(rate(tikv_thread_cpu_seconds_total{name=~"(raftstore|rs)_.*"}[1m])) by (instance),并获取其中最大的一次值。这样就满足了我们需要使用一段时间的数据来判断集群是否有风险的依据了。

然后我们可以选取合适的 PromQL 来加上时间聚合函数和查询时间及步长信息:

  1. # TiKV 1
  2. 'TiDB.tikv.TiKV_server_is_down': {
  3. 'pql': 'probe_success{group="tikv",instance=~".*"} == 0',
  4. 'pql_max': '',
  5. 'note': 'TiKV 服务不可用'
  6. },
  7. 'TiDB.tikv.TiKV_node_restart': {
  8. 'pql': 'changes(process_start_time_seconds{job="tikv",instance=~".*"}[24h])> 0',
  9. 'pql_max': 'max(changes(process_start_time_seconds{job="tikv",instance=~".*"}[24h]))',
  10. 'note': 'TiKV 服务5分钟内出现重启'
  11. },
  12. 'TiDB.tikv.TiKV_GC_can_not_work': {
  13. 'pql_max': '',
  14. 'pql': 'sum(increase(tikv_gcworker_gc_tasks_vec{task="gc", instance=~".*"}[2d])) by (instance) < 1and (sum(increase('
  15. 'tikv_gc_compaction_filter_perform{instance=~".*"}[2d])) by (instance) < 1and sum(increase('
  16. 'tikv_engine_event_total{cf="write",db="kv",type="compaction",instance=~".*"}[2d])) by (instance) >= 1)',
  17. 'note': 'TiKV 服务GC无法工作'
  18. },
  19. # TiKV 2
  20. 'TiDB.tikv.TiKV_raftstore_thread_cpu_seconds_total': {
  21. 'pql_max': 'max_over_time(avg(rate(tikv_thread_cpu_seconds_total{name=~"(raftstore|rs)_.*"}[1m])) by (instance)[24h:1m])',
  22. 'pql': 'max_over_time(avg(rate(tikv_thread_cpu_seconds_total{name=~"(raftstore|rs)_.*"}[1m])) by (instance)[24h:1m]) > 0.8',
  23. 'note': 'TiKV raftstore 线程池 CPU 使用率过高'
  24. },
  25. 'TiDB.tikv.TiKV_approximate_region_size': {
  26. 'pql_max': 'max_over_time(histogram_quantile(0.99, sum(rate(tikv_raftstore_region_size_bucket{instance=~".*"}[1m])) '
  27. 'by (le,instance))[24h:1m])',
  28. 'pql': 'max_over_time(histogram_quantile(0.99, sum(rate(tikv_raftstore_region_size_bucket{instance=~".*"}[1m])) '
  29. 'by (le,instance))[24h:1m]) > 1073741824',
  30. 'note': 'TiKV split checker 扫描到的最大的 Region approximate size 大于 1GB'
  31. },
  32. 'TiDB.tikv.TiKV_async_request_write_duration_seconds': {
  33. 'pql_max': 'max_over_time(histogram_quantile(0.99, sum(rate(tikv_storage_engine_async_request_duration_seconds_bucket'
  34. '{type="write", instance=~".*"}[1m])) by (le, instance, type))[24h:1m])',
  35. 'pql': 'max_over_time(histogram_quantile(0.99, sum(rate(tikv_storage_engine_async_request_duration_seconds_bucket'
  36. '{type="write", instance=~".*"}[1m])) by (le, instance, type))[24h:1m]) > 1',
  37. 'note': 'TiKV 中Raft写入响应时间过长'
  38. },
  39. 'TiDB.tikv.TiKV_scheduler_command_duration_seconds': {
  40. 'pql_max': 'max_over_time(histogram_quantile(0.99, sum(rate(tikv_scheduler_command_duration_seconds_bucket[20m])) by (le, instance, type) / 1000)[24h:20m]) ',
  41. 'pql': 'max_over_time(histogram_quantile(0.99, sum(rate(tikv_scheduler_command_duration_seconds_bucket[20m])) by (le, instance, type) / 1000)[24h:20m]) > 20',
  42. 'note': 'TiKV 调度器请求响应时间过长'
  43. },
  44. 'TiDB.tikv.TiKV_scheduler_latch_wait_duration_seconds': {
  45. 'pql_max': 'max_over_time(histogram_quantile(0.99, sum(rate(tikv_scheduler_latch_wait_duration_seconds_bucket[20m])) by (le, instance, type))[24h:20m]) ',
  46. 'pql': 'max_over_time(histogram_quantile(0.99, sum(rate(tikv_scheduler_latch_wait_duration_seconds_bucket[20m])) by (le, instance, type))[24h:20m]) > 20',
  47. 'note': 'TiKV 调度器锁等待响应时间过长'
  48. },
  49. 'TiDB.tikv.TiKV_write_stall': {
  50. 'pql_max': 'max_over_time(delta(tikv_engine_write_stall{instance=~".*"}[10m])[24h:10m])',
  51. 'pql': 'max_over_time(delta('
  52. 'tikv_engine_write_stall{instance=~".*"}[10m])[24h:10m]) > 10',
  53. 'note': 'TiKV 中存在写入积压'
  54. },
  55. # TiKV 3
  56. 'TiDB.tikv.TiKV_server_report_failure_msg_total': {
  57. 'pql_max': 'max_over_time(sum(rate(tikv_server_report_failure_msg_total{type="unreachable"}[10m])) BY (instance)[24h:10m])',
  58. 'pql': 'max_over_time(sum(rate(tikv_server_report_failure_msg_total{type="unreachable"}[10m])) BY (instance)[24h:10m]) > 10',
  59. 'note': 'TiKV 节点报告失败次数过多'
  60. },
  61. 'TiDB.tikv.TiKV_channel_full_total': {
  62. 'pql_max': 'max_over_time(sum(rate(tikv_channel_full_total{instance=~".*"}[10m])) BY (type, instance)[24h:10m])',
  63. 'pql': 'max_over_time(sum(rate(tikv_channel_full_total{instance=~".*"}[10m])) BY (type, instance)[24h:10m]) > 0',
  64. 'note': 'TIKV 通道已占满 tikv 过忙'
  65. },
  66. 'TiDB.tikv.TiKV_raft_log_lag': {
  67. 'pql_max': 'max_over_time(histogram_quantile(0.99, sum(rate(tikv_raftstore_log_lag_bucket{instance=~".*"}[1m])) by (le,instance))[24h:10m])',
  68. 'pql': 'max_over_time(histogram_quantile(0.99, sum(rate(tikv_raftstore_log_lag_bucket{instance=~".*"}[1m])) by (le, '
  69. 'instance))[24h:10m]) > 5000',
  70. 'note': 'TiKV 中 raft 日志同步相差过大'
  71. },
  72. 'TiDB.tikv.TiKV_thread_unified_readpool_cpu_seconds': {
  73. 'pql_max': 'max_over_time(avg(rate(tikv_thread_cpu_seconds_total{name=~"unified_read_po*", instance=~".*"}[1m])) by (instance)[24h:1m])',
  74. 'pql': 'max_over_time(avg(rate(tikv_thread_cpu_seconds_total{name=~"unified_read_po*", instance=~".*"}[1m])) '
  75. 'by (instance)[24h:1m]) > 0.7',
  76. 'note': 'unifiled read 线程池使用率大于70%'
  77. },
  78. 'TiDB.tikv.TiKV_low_space': {
  79. 'pql_max': 'sum(tikv_store_size_bytes{type="available"}) by (instance) / sum(tikv_store_size_bytes{type="capacity"}) by (instance)',
  80. 'pql': 'sum(tikv_store_size_bytes{type="available"}) by (instance) / sum(tikv_store_size_bytes{type="capacity"}) by (instance) < 0.3',
  81. 'note': 'TiKV 当前存储可用空间小于阈值'
  82. },

由于有的告警项是获取了5分钟或者10分钟的数据,在写步长的时候也要同步修改为5分钟或者10分钟,保持一致可以保证,检查能覆盖选定的全部时间段,并且不会重复计算造成资源浪费。

顺带一提,如果不加max_over_time 可以获取到带有时间戳的全部数据,而不是只获取到最大的一个数据。这个带时间戳的全部数据可以方便画图,像grafana那样展示数据趋势。

四、巡检脚本

了解了以上所有知识,我们就可以开始编写巡检脚本了。

这是笔者和同事共同编写的一部分巡检脚本,最重要的是 tasks 中的 PromQL ,在脚本执行之前要写好 PromQL,其他部分可以随意更改。如果一次性巡检天数太多,比如一次巡检一个月的时间,Prometheus 可能会因检查数据太多而报错的,所以使用的时候要注意报错信息,避免漏掉一些巡检项。

  1. # -*- coding: utf-8 -*-
  2. importsubprocess
  3. importre
  4. importdatetime
  5. importrequests
  6. importsys
  7. importpandas aspd
  8. days = None
  9. defget_cluster_name():
  10. try:
  11. command = "tiup cluster list"
  12. result = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  13. output, error = result.communicate()
  14. cluster_name_match = re.search(r'([a-zA-Z0-9_-]+)\s+tidb\s+v', output.decode('utf-8'))
  15. ifcluster_name_match:
  16. returncluster_name_match.group(1)
  17. else:
  18. returnNone
  19. exceptException ase:
  20. print("An error occurred:", e)
  21. returnNone
  22. defdisplay_cluster_info(cluster_name):
  23. ifnotcluster_name:
  24. print("Cluster name not found.")
  25. return
  26. try:
  27. command = "tiup cluster display {0}".format(cluster_name)
  28. result = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  29. output, error = result.communicate()
  30. returnoutput.decode('utf-8')
  31. exceptException ase:
  32. print("An error occurred:", e)
  33. defextract_id_role(output):
  34. id_role_dict = {}
  35. lines = output.strip().split("\n")
  36. forline inlines:
  37. print(line)
  38. parts = line.split()
  39. ifis_valid_ip_port(parts[0]):
  40. node_id, role = parts[0], parts[1]
  41. id_role_dict[node_id] = role
  42. returnid_role_dict
  43. defis_valid_ip_port(input_str):
  44. pattern = re.compile(r'^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d{1,5}$')
  45. returnbool(pattern.match(input_str))
  46. defget_prometheus_ip(data_dict):
  47. prometheus_ip = None
  48. forkey, value indata_dict.items():
  49. ifvalue == 'prometheus':
  50. prometheus_ip = key
  51. break
  52. returnprometheus_ip
  53. defget_tasks():
  54. globaldays
  55. tasks = {
  56. # TiKV 1
  57. 'TiDB.tikv.TiKV_server_is_down': {
  58. 'pql': 'probe_success{group="tikv",instance=~".*"} == 0',
  59. 'pql_max': '',
  60. 'note': 'TiKV 服务不可用'
  61. },
  62. 'TiDB.tikv.TiKV_node_restart': {
  63. 'pql': 'changes(process_start_time_seconds{job="tikv",instance=~".*"}[24h])> 0',
  64. 'pql_max': 'max(changes(process_start_time_seconds{job="tikv",instance=~".*"}[24h]))',
  65. 'note': 'TiKV 服务5分钟内出现重启'
  66. },
  67. 'TiDB.tikv.TiKV_GC_can_not_work': {
  68. 'pql_max': '',
  69. 'pql': 'sum(increase(tikv_gcworker_gc_tasks_vec{task="gc", instance=~".*"}[2d])) by (instance) < 1 and (sum(increase('
  70. 'tikv_gc_compaction_filter_perform{instance=~".*"}[2d])) by (instance) < 1 and sum(increase('
  71. 'tikv_engine_event_total{cf="write",db="kv",type="compaction",instance=~".*"}[2d])) by (instance) >= 1)',
  72. 'note': 'TiKV 服务GC无法工作'
  73. },
  74. # TiKV 2
  75. 'TiDB.tikv.TiKV_raftstore_thread_cpu_seconds_total': {
  76. 'pql_max': 'max_over_time(avg(rate(tikv_thread_cpu_seconds_total{name=~"(raftstore|rs)_.*"}[1m])) by (instance)[24h:1m])',
  77. 'pql': 'max_over_time(avg(rate(tikv_thread_cpu_seconds_total{name=~"(raftstore|rs)_.*"}[1m])) by (instance)[24h:1m]) > 0.8',
  78. 'note': 'TiKV raftstore 线程池 CPU 使用率过高'
  79. },
  80. 'TiDB.tikv.TiKV_approximate_region_size': {
  81. 'pql_max': 'max_over_time(histogram_quantile(0.99, sum(rate(tikv_raftstore_region_size_bucket{instance=~".*"}[1m])) '
  82. 'by (le,instance))[24h:1m])',
  83. 'pql': 'max_over_time(histogram_quantile(0.99, sum(rate(tikv_raftstore_region_size_bucket{instance=~".*"}[1m])) '
  84. 'by (le,instance))[24h:1m]) > 1073741824',
  85. 'note': 'TiKV split checker 扫描到的最大的 Region approximate size 大于 1 GB'
  86. },
  87. 'TiDB.tikv.TiKV_async_request_write_duration_seconds': {
  88. 'pql_max': 'max_over_time(histogram_quantile(0.99, sum(rate(tikv_storage_engine_async_request_duration_seconds_bucket'
  89. '{type="write", instance=~".*"}[1m])) by (le, instance, type))[24h:1m])',
  90. 'pql': 'max_over_time(histogram_quantile(0.99, sum(rate(tikv_storage_engine_async_request_duration_seconds_bucket'
  91. '{type="write", instance=~".*"}[1m])) by (le, instance, type))[24h:1m]) > 1',
  92. 'note': 'TiKV 中Raft写入响应时间过长'
  93. },
  94. 'TiDB.tikv.TiKV_write_stall': {
  95. 'pql_max': 'max_over_time(delta(tikv_engine_write_stall{instance=~".*"}[10m])[24h:10m])',
  96. 'pql': 'max_over_time(delta('
  97. 'tikv_engine_write_stall{instance=~".*"}[10m])[24h:10m]) > 10',
  98. 'note': 'TiKV 中存在写入积压'
  99. },
  100. # TiKV 3
  101. 'TiDB.tikv.TiKV_server_report_failure_msg_total': {
  102. 'pql_max': 'max_over_time(sum(rate(tikv_server_report_failure_msg_total{type="unreachable"}[10m])) BY (instance)[24h:10m])',
  103. 'pql': 'max_over_time(sum(rate(tikv_server_report_failure_msg_total{type="unreachable"}[10m])) BY (instance)[24h:10m]) > 10',
  104. 'note': 'TiKV 节点报告失败次数过多'
  105. },
  106. 'TiDB.tikv.TiKV_channel_full_total': {
  107. 'pql_max': 'max_over_time(sum(rate(tikv_channel_full_total{instance=~".*"}[10m])) BY (type, instance)[24h:10m])',
  108. 'pql': 'max_over_time(sum(rate(tikv_channel_full_total{instance=~".*"}[10m])) BY (type, instance)[24h:10m]) > 0',
  109. 'note': 'TIKV 通道已占满 tikv 过忙'
  110. },
  111. 'TiDB.tikv.TiKV_raft_log_lag': {
  112. 'pql_max': 'max_over_time(histogram_quantile(0.99, sum(rate(tikv_raftstore_log_lag_bucket{instance=~".*"}[1m])) by (le,instance))[24h:10m])',
  113. 'pql': 'max_over_time(histogram_quantile(0.99, sum(rate(tikv_raftstore_log_lag_bucket{instance=~".*"}[1m])) by (le, '
  114. 'instance))[24h:10m]) > 5000',
  115. 'note': 'TiKV 中 raft 日志同步相差过大'
  116. },
  117. 'TiDB.tikv.TiKV_thread_unified_readpool_cpu_seconds': {
  118. 'pql_max': 'max_over_time(avg(rate(tikv_thread_cpu_seconds_total{name=~"unified_read_po*", instance=~".*"}[1m])) by (instance)[24h:1m])',
  119. 'pql': 'max_over_time(avg(rate(tikv_thread_cpu_seconds_total{name=~"unified_read_po*", instance=~".*"}[1m])) '
  120. 'by (instance)[24h:1m]) > 0.7',
  121. 'note': 'unifiled read 线程池使用率大于70%'
  122. },
  123. 'TiDB.tikv.TiKV_low_space': {
  124. 'pql_max': 'sum(tikv_store_size_bytes{type="available"}) by (instance) / sum(tikv_store_size_bytes{type="capacity"}) by (instance)',
  125. 'pql': 'sum(tikv_store_size_bytes{type="available"}) by (instance) / sum(tikv_store_size_bytes{type="capacity"}) by (instance) < 0.3',
  126. 'note': 'TiKV 当前存储可用空间小于阈值'
  127. },
  128. }
  129. forkey, value intasks.items():
  130. forinner_key, inner_value invalue.items():
  131. ifisinstance(inner_value, str) and'pql'ininner_key:
  132. value[inner_key] = inner_value.replace("24h:", f"{24* days}h:").replace("[24h]", f"[{24* days}h]")
  133. returntasks
  134. defrequest_prome(prometheus_address, query):
  135. try:
  136. response = requests.get('http://%s/api/v1/query'% prometheus_address, params={'query': query})
  137. returnresponse
  138. except:
  139. returnNone
  140. defhas_response(prometheus_address, query):
  141. response = request_prome(prometheus_address, query)
  142. ifnotresponse:
  143. returnFalse
  144. try:
  145. ifresponse.json()["data"]['result']:
  146. returnTrue
  147. else:
  148. returnFalse
  149. except:
  150. returnFalse
  151. defcheck_prome_alive(prometheus_address):
  152. # dummy query is used to judge if prometheus is alive
  153. dummy_query = 'probe_success{}'
  154. returnhas_response(prometheus_address, dummy_query)
  155. deffind_alive_prome(prometheus_addresses):
  156. ifcheck_prome_alive(prometheus_addresses):
  157. returnprometheus_addresses
  158. returnNone
  159. # ip:port -> ip_port
  160. defdecode_instance(instance):
  161. returninstance.replace(':', '_')
  162. defcheck_metric(alert_name, prometheus_address, pql, is_value, pql_max):
  163. record = []
  164. try:
  165. is_warning = "异常"
  166. response = request_prome(prometheus_address, pql)
  167. alert_name = alert_name.split('.')
  168. result = response.json()['data']['result']
  169. # 判断是否出现异常
  170. iflen(result) == 0:
  171. is_warning = "正常"
  172. ifpql_max == '':
  173. result = [{'metric': {}, 'value': [0, '0']}]
  174. else:
  175. response = request_prome(prometheus_address, pql_max)
  176. result = response.json()['data']['result']
  177. fori inresult:
  178. # 判断是否按节点显示
  179. if'instance'ini['metric']:
  180. instance = i['metric']['instance']
  181. node = decode_instance(instance)
  182. else:
  183. node = '集群'
  184. # 判断是否有type
  185. if'type'ini['metric']:
  186. type = i['metric']['type']
  187. else:
  188. type = '无类型'
  189. value = i['value'][1]
  190. ifvalue == 'NaN':
  191. value = 0
  192. else:
  193. value = round(float(value), 3)
  194. message = "%s,%s,%s,%s,%s,%s,%s,%s"% (
  195. datetime.datetime.now(), node, alert_name[1], alert_name[2], type, is_warning, is_value, value)
  196. print(message)
  197. record.append(message)
  198. exceptException ase:
  199. print(alert_name[2] + "----An error occurred check_metric:", e)
  200. return
  201. returnrecord
  202. defcsv_report(record):
  203. data = pd.DataFrame([line.split(',') forline inrecord],
  204. columns=['timestamp', 'ip_address', 'service', 'event_type', 'type', 'status', 'description',
  205. 'value'])
  206. grouped = data.groupby("service")
  207. writer = pd.ExcelWriter("inspection_report.xlsx", engine="xlsxwriter")
  208. forname, group ingrouped:
  209. group.to_excel(writer, sheet_name=name, index=False)
  210. worksheet = writer.sheets[name]
  211. fori, col inenumerate(group.columns):
  212. column_len = max(group[col].astype(str).str.len().max(), len(col)) + 2
  213. worksheet.set_column(i, i, column_len)
  214. writer.save()
  215. defrun_tasks(role_metrics, prometheus_address):
  216. record = []
  217. foralert inrole_metrics:
  218. pql = role_metrics[alert]['pql']
  219. is_value = role_metrics[alert]['note']
  220. pql_max = role_metrics[alert]['pql_max']
  221. message = check_metric(alert, prometheus_address, pql, is_value, pql_max)
  222. fordata inmessage:
  223. record.append(data)
  224. csv_report(record)
  225. defrun_script(prometheus_addresses):
  226. active_prometheus_address = find_alive_prome(prometheus_addresses)
  227. # check if all prometheus are down
  228. ifnotactive_prometheus_address:
  229. sys.exit()
  230. tasks = get_tasks()
  231. run_tasks(tasks, active_prometheus_address)
  232. defget_user_input():
  233. globaldays
  234. try:
  235. user_input = int(input("请输入需要巡检的天数: "))
  236. days = user_input
  237. exceptValueError:
  238. print("输入无效,请输入一个有效的数字。")
  239. if__name__ == "__main__":
  240. # 输入巡检天数
  241. get_user_input()
  242. prometheus_ip = '10.3.65.136:9091'
  243. # prometheus_ip = None
  244. ifprometheus_ip isNone:
  245. cluster_name = get_cluster_name()
  246. cluster_info = display_cluster_info(cluster_name)
  247. id_role_dict = extract_id_role(cluster_info)
  248. print(id_role_dict)
  249. prometheus_ip = get_prometheus_ip(id_role_dict)
  250. print(prometheus_ip)
  251. run_script(prometheus_ip)

五、总结

一个完善的巡检脚本的编写是一个长期的工作。因为时间有限,笔者只编写了基于 Prometheus 的一部分巡检项,有兴趣的同学可以继续编写更多巡检项。

目前巡检脚本都是基于 Prometheus 的数据来作判断,但是在真实的巡检当中,dba还会查看一些 Prometheus 没有的数据,比如表的健康度、一段时间内的慢SQL、热力图、日志信息等等,这些信息在后面一些时间,可能会慢慢入到巡检脚本中。

最后,笔者能力有限,如有错误的地方欢迎指出。

作者:蔡一凡| 后端开发工程师

版权声明:本文由神州数码云基地团队整理撰写,若转载请注明出处。

公众号搜索神州数码云基地,后台回复数据库,加入数据库技术交流群。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小丑西瓜9/article/detail/287409
推荐阅读
相关标签
  

闽ICP备14008679号