前言

随着 Prometheus 监控的组件、数量、指标越来越多,Prometheus 对计算性能的要求会越来越高,资源占用也会越来越高。

在这种情况下,要优化 Prometheus 性能, 优化存储占用. 第一时间想到的可能是各种 Prometheus 的兼容存储方案, 如 Thanos 或 VM、Mimir 等。但是实际上虽然集中存储、长期存储、存储降采样及存储压缩可以一定程度解决相关问题,但是治标不治本。

真正的本,还是在于指标量(series)过于庞大。

  • 治本之法,应该是减少指标量。有 2 种办法:
    • 解决高基数问题
    • 根据实际使用情况,只保留(keep)展示(Grafana Dashboards)和告警(prometheus rules)会用到的指标。

高基数问题

什么是基数(Cardinality)?

基数的 基本定义 是指一个给定集合中的元素的数量。

在Prometheus中指代series 的基数 (High Cardinality)

Prometheus 和可观察性的世界里,标签基数 是非常重要的,因为它影响到你的监控系统的性能和资源使用。

下面这张图, 可以清晰地反应基数的重要性:

基数激增: Prometheus 中的基数的基本图示。

简单地说。基数 是指一个标签的总体数值的计数。在上面的例子中,标签 status_code 的基数是 5,(即:1xx 2xx 3xx 4xx 5xx),environment的基数是 2(即 prod dev),而指标server_responses 的总体基数是 10。

多少算高基数?

一般来说:

  • 较低的基数 1:5 的标签值比率,
  • 标准基数 1:80 的标签值比率
  • 高基数 1:10000 的标签值比率。

还是上面的例子, 如果 status_code 是详细的 code, 如 200 404…, 那它的基数就可能高达数百个, environment 的基数再多一些, 指标 server_responses 的总体基数就会迅速膨胀.

高基数的典型案例

这还不够形象, 再举 2 个特别典型的例子:

  1. 有一个指标叫做:http_request_duration_seconds_bucket
    1. 它有 instance label, 对应 100 个实例;
    2. le label, 对应的是不同的 buckets, 有 10 个 buckets, 如(0.002 0.004 0.008=+inf)
    3. 它还有url这个 label, 对应的是不通的 url:
      1. 即使规模很小, url 可能也会有 400 个 url
      2. 这里还有个特别恐怖的隐患, 就是对于大规模系统来说, 这个 url 可能是近乎于 无穷!!!
    4. 它还有 http_method 这个 label, 对应有 5 个 http method
    5. 在这种情况下, 该指标的 label
      1. 小规模也会有: 100*10*400*5=2 000 000 200 万个 series 💀💀💀
      2. 如果大规模, url 近乎无穷的话, 那么这个基数根本无法计算出来💥💥💥
  2. 再有一种情况, 将 user_id 甚至是 session_id 经纬度 这种本来基数就很大, 甚至可能是无穷的参数设为 label, 那么对于 Prometheus 来说就是灾难了.💥💥💥

高基数的负面影响

当 Prometheus 有高基数的时候,就会出现各种问题:

  • 监控系统不稳定甚至崩溃
    • 仪表板加载很慢甚至加载失败
    • 监控查询很慢甚至失败
  • 计算存储资源开销巨大
  • 监控充斥着大量噪音干扰
    • SRE 团队不得不疲于应对海量的告警数据, 反而耽误 root cause 的分析定位

📝Notes:

基数 与指标系列(metrics series) 的数量相对应。所以在这篇博文中,会把 series 的数量与基数交替提及。

如何分析高基数问题?

分析高基数问题有以下方法:

  1. 使用 Prometheus UI 分析
  2. 使用 Prometheus PromQL 分析
  3. 使用 Prometheus API 分析
  4. 使用 Grafana Mimirtool 分析未使用的指标

使用 Prometheus UI 分析

从 Prometheus v2.14.0 以后, 在 UI 上直接有 Head Cardinality Stats 这个菜单. 极大方便了我们进行高基数问题的分析! 👍️👍️👍️

位于: Prometheus UI -> Status -> TSDB Status -> Head Cardinality Stats, 截图如下:

📝Notes:

以下截图的系统规模说明: 这就是个我用来做实验的环境, 只有 4 个 1c2g 的 node

从上图可以直观看到:

  1. 值最多的 Label 是 url
  2. 最多的 series 的指标有:
    1. apiserver_request_duration_seconds_bucket 45524
    2. rest_client_rate_limiter_duration_seconds_bucket 36971
    3. rest_client_request_duration_seconds_bucket 10032
  3. 内存使用量最多的 Label: url
  4. 根据 Label 键值对匹配, series 最多的键值对有: (这一项目前对我来说用处不大)
    1. endpoint=metrics 105406
    2. service=pushprox-k3s-server-client 101548
    3. job=k3s-server 101543
    4. namespace=cattle-monitoring-system 101120
    5. metrics_path=/metrics 91761

使用 Prometheus PromQL 分析

如果 Prometheus 版本低于 v2.14.0, 那就需要通过:

  • Prometheus PromQL
  • Prometheus API

来进行分析.

以下提供一些实用的 PromQL:

1
topk(10, count by (__name__)({__name__=~".+"}))

对应的查询结果就是上文的 series 指标最多的 Top10

知道了 Top10, 接下来可以进一步查询细节, 由于基数巨大, 如果查询 range 可能会一直失败, 所以推荐使用 instant 的方式查询细节.

如果要查询标签的维度, 可以执行如下 PromQL:

1
count(count by (label_name) (metric_name))

如:

1
count(count by (url) (apiserver_request_duration_seconds_bucket))

另外还有一些其他的 PromQL, 罗列如下:

  • sum(scrape_series_added) by (job) 通过 job Label 分析 series 增长
  • sum(scrape_samples_scraped) by (job) 通过 job Label 分析 series 总量
  • prometheus_tsdb_symbol_table_size_bytes

注意

由于这些查询涉及所有时间序列,因此它们相对会消耗大量资源

使用 Prometheus API 分析

因为高基数问题的特点, 所以通过 Prometheus PromQL 查询可能经常会超时或失败. 那么可以通过 Prometheus API 进行分析:

分析各个指标的 series 数量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 找到 Prometheus 的 SVC ClusterIP
kubectl get svc -n cattle-monitoring-system
export url=http://10.43.85.24:9090
export now=$(date +%s)
curl -s $url/api/v1/label/__name__/values \
| jq -r ".data[]" \
| while read metric; do
count=$(curl -s \
--data-urlencode 'query=count({__name__="'$metric'"})' \
--data-urlencode "time=$now" \
$url/api/v1/query \
| jq -r ".data.result[0].value[1]")
echo "$count $metric"
done

null 可能是当前没有数据, 但历史数据量可能会很大

活动 series 数量指标名称
nullapiserver_admission_webhook_rejection_count
nullapiserver_registered_watchers
nullapiserver_request_aborts_total
nullapiserver_request_duration_seconds_bucket
nullcluster_quantile:scheduler_e2e_scheduling_duration_seconds:histogram_quantile
nullcluster_quantile:scheduler_scheduling_algorithm_duration_seconds:histogram_quantile
nullkube_pod_container_status_waiting_reason
nullprometheus_target_scrape_pool_target_limit
nullrest_client_rate_limiter_duration_seconds_bucket
5786rest_client_request_duration_seconds_bucket
3660etcd_request_duration_seconds_bucket
2938rest_client_rate_limiter_duration_seconds_count
2938rest_client_rate_limiter_duration_seconds_sum
2840apiserver_response_sizes_bucket
1809apiserver_watch_events_sizes_bucket

获取指定指标的活动 series

这里以 rest_client_request_duration_seconds_bucket 为例:

1
2
3
4
5
6
export metric=rest_client_request_duration_seconds_bucket
curl -s \
--data-urlencode "query=$metric" \
--data-urlencode "time=$now" \
$url/api/v1/query \
| jq -c ".data.result[].metric"

结果如下: (主要原因就是 url 的 value 太多)

获取指定指标的活动 series - url 值太多

获取指定指标的活动 series - url 值太多

获取所有指标的列表

1
curl -s $url/api/v1/label/__name__/values | jq -r ".data[]" | sort

获取标签及其基数的列表

1
2
3
4
5
6
7
8
curl -s $url/api/v1/labels \
| jq -r ".data[]" \
| while read label; do
count=$(curl -s $url/api/v1/label/$label/values \
| jq -r ".data|length")
echo "$count $label"
done \
| sort -n

结果如下: (还是因为 label url 的 value 过多! )

基数标签
2199url
1706__name__
854name
729id
729path
657filename
652container_id
420resource
407le
351secret
302type
182kind

使用 Grafana Mimirtool 分析未使用的指标

📚️Reference:

Grafana Mimirtool | Grafana Mimir documentation

Grafana Mimir 的介绍具体见这里: Intro to Grafana Mimir: The open source time series database that scales to 1 billion metrics & beyond | Grafana Labs

Mimir 有个实用工具叫 mimirtool, 可以通过对比 Prometheus 的指标, 和 AlertManager 以及 Grafana 用到的指标, 来分析哪些指标没有用到. 可以通过如下输入进行分析:

  • Grafana 实例的 Grafana Dashboards
  • Prometheus 实例的 recording rules 和 alerting rules
  • Grafana Dashboard json 文件
  • Prometheus recording 和 alerting rules YAML 文件

完整介绍见这里: Analyzing and reducing metrics usage with Grafana Mimirtool | Grafana Cloud documentation

解决高基数问题

对于高基数问题, 有几种情况:

  1. 某些 label 不合理, 值很多甚至无穷;
  2. 某些 指标 不合理, 值很多;
  3. Prometheus 整体的全部 series 量太大

对于第三个问题, 以下 2 个办法可以解决:

对于高可用 Prometheus 的高基数问题

有一种高基数的情况, 是 Prometheus 以 HA 模式部署, 并且通过 remote_write 方式将数据发送到 VM、Mimir 或 Thanos. 导致数据冗余。

针对这种情况,可以根据 VM、Mimir 或 Thanos 官方文档的指导,添加 external_labels 供这些软件自动处理高基数问题.

示例配置如下:

增加external_labels

  1. cluster
  2. __replicas__

增大采集间隔

增加 Prometheus 的 global scrape_interval(调整全局的该参数, 对于某些确实需要更小采集间隔的, 可以在 job 内详细配置)

一般可能默认是 scrape_interval: 15s
建议将其增大值调整为 scrape_interval: 1m 甚至更大.

在Service/Pod Monitor中可以修改interval的值

过滤和保留 kubernetes-mixin 指标

对于 kubernetes-mixin、Prometheus Operator、kube-prometheus 等项目,都会提供一些开箱即用的:

  • scrape metrics
  • recording rules
  • alerting rules
  • Grafana Dashboards

对于这种情况, 根据对于 Grafana Dashboards 和 alerting rules,可以通过 relabel 保留用到的指标。

📚️Reference:

「译文」通过 Relabel 减少 Prometheus 指标的使用量 - 东风微鸣技术博客 (ewhisper.cn)

示例如下:

// __name__表示要检测的label名称

1
2
3
4
5
6
7
8
9
10
11
12
13
14
remoteWrite:
- url: "<Your Metrics instance remote_write endpoint>"
basicAuth:
username:
name: your_grafanacloud_secret
key: your_grafanacloud_secret_username_key
password:
name: your_grafanacloud_secret
key: your_grafanacloud_secret_password_key
writeRelabelConfigs:
- sourceLabels:
- "__name__"
regex: "apiserver_request_total|kubelet_node_config_error|kubelet_runtime_operations_errors_total|kubeproxy_network_programming_duration_seconds_bucket|container_cpu_usage_seconds_total|kube_statefulset_status_replicas|kube_statefulset_status_replicas_ready|node_namespace_pod_container:container_memory_swap|kubelet_runtime_operations_total|kube_statefulset_metadata_generation|node_cpu_seconds_total|kube_pod_container_resource_limits_cpu_cores|node_namespace_pod_container:container_memory_cache|kubelet_pleg_relist_duration_seconds_bucket|scheduler_binding_duration_seconds_bucket|container_network_transmit_bytes_total|kube_pod_container_resource_requests_memory_bytes|namespace_workload_pod:kube_pod_owner:relabel|kube_statefulset_status_observed_generation|process_resident_memory_bytes|container_network_receive_packets_dropped_total|kubelet_running_containers|kubelet_pod_worker_duration_seconds_bucket|scheduler_binding_duration_seconds_count|scheduler_volume_scheduling_duration_seconds_bucket|workqueue_queue_duration_seconds_bucket|container_network_transmit_packets_total|rest_client_request_duration_seconds_bucket|node_namespace_pod_container:container_memory_rss|container_cpu_cfs_throttled_periods_total|kubelet_volume_stats_capacity_bytes|kubelet_volume_stats_inodes_used|cluster_quantile:apiserver_request_duration_seconds:histogram_quantile|kube_node_status_allocatable_memory_bytes|container_memory_cache|go_goroutines|kubelet_runtime_operations_duration_seconds_bucket|kube_statefulset_replicas|kube_pod_owner|rest_client_requests_total|container_memory_swap|node_namespace_pod_container:container_memory_working_set_bytes|storage_operation_errors_total|scheduler_e2e_scheduling_duration_seconds_bucket|container_network_transmit_packets_dropped_total|kube_pod_container_resource_limits_memory_bytes|node_namespace_pod_container:container_cpu_usage_seconds_total:sum_rate|storage_operation_duration_seconds_count|node_netstat_TcpExt_TCPSynRetrans|node_netstat_Tcp_OutSegs|container_cpu_cfs_periods_total|kubelet_pod_start_duration_seconds_count|kubeproxy_network_programming_duration_seconds_count|container_network_receive_bytes_total|node_netstat_Tcp_RetransSegs|up|storage_operation_duration_seconds_bucket|kubelet_cgroup_manager_duration_seconds_count|kubelet_volume_stats_available_bytes|scheduler_scheduling_algorithm_duration_seconds_bucket|kube_statefulset_status_replicas_current|code_resource:apiserver_request_total:rate5m|kube_statefulset_status_replicas_updated|process_cpu_seconds_total|kube_pod_container_resource_requests_cpu_cores|kubelet_pod_worker_duration_seconds_count|kubelet_cgroup_manager_duration_seconds_bucket|kubelet_pleg_relist_duration_seconds_count|kubeproxy_sync_proxy_rules_duration_seconds_bucket|container_memory_usage_bytes|workqueue_adds_total|container_network_receive_packets_total|container_memory_working_set_bytes|kube_resourcequota|kubelet_running_pods|kubelet_volume_stats_inodes|kubeproxy_sync_proxy_rules_duration_seconds_count|scheduler_scheduling_algorithm_duration_seconds_count|apiserver_request:availability30d|container_memory_rss|kubelet_pleg_relist_interval_seconds_bucket|scheduler_e2e_scheduling_duration_seconds_count|scheduler_volume_scheduling_duration_seconds_count|workqueue_depth|:node_memory_MemAvailable_bytes:sum|volume_manager_total_volumes|kube_node_status_allocatable_cpu_cores"
action: "keep"

🐾Warning:

以上配置可能根据不同的版本, 会有不同的变化, 请酌情参考使用.
或者根据上文提到的 mimirtool 自行分析生成适合自己的配置.

通过 Relabel 减少 Prometheus 指标的使用量

举一个简单例子如下:

1
2
3
4
write_relabel_configs:
- source_labels: [__name__]
regex: "apiserver_request_duration_seconds_bucket"
action: drop

通过 recording rules 聚合指标并和 relabel drop 结合使用

比如对于 apiserver_request_duration_seconds_bucket, 我需要的是一些高纬度的指标 - 如 API Server 的可用率, 那么这些指标可以通过 recording rules 进行记录和存储, 示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
groups:
- interval: 3m
name: kube-apiserver-availability.rules
rules:
- expr: >-
avg_over_time(code_verb:apiserver_request_total:increase1h[30d]) *
24 * 30
record: code_verb:apiserver_request_total:increase30d
- expr: >-
sum by (cluster, code, verb)
(increase(apiserver_request_total{job="apiserver",verb=~"LIST|GET|POST|PUT|PATCH|DELETE",code=~"2.."}[1h]))
record: code_verb:apiserver_request_total:increase1h
- expr: >-
sum by (cluster, code, verb)
(increase(apiserver_request_total{job="apiserver",verb=~"LIST|GET|POST|PUT|PATCH|DELETE",code=~"5.."}[1h]))
record: code_verb:apiserver_request_total:increase1h

之后可以再在 remote_wirte 等阶段删掉原始指标:

1
2
3
4
write_relabel_configs:
- source_labels: [__name__]
regex: "apiserver_request_duration_seconds_bucket"
action: drop

利用Mimirtool根据实际使用情况,只keep Grafana Dashboards和prometheus rules会用到的指标

Grafana Mimirtool

Grafana Mimir 是一款以对象存储为存储方式的 Prometheus 长期存储解决方案, 从 Cortex 演化而来. 官方号称支持亿级别的 series 写入存储和查询.

Grafana Mimirtool 是 Mimir 发布的一个实用工具, 可单独使用.

Grafana Mimirtool 支持从以下方面提取指标:

  • Grafana 实例中的 Grafana Dashboards(通过 Grafana API)
  • Mimir 实例中的 Prometheus alerting 和 recording rules
  • Grafana Dashboards JSON 文件
  • Prometheus 记 alerting 和 recording rules 的 YAML 文件

然后,Grafana Mimirtool 可以将这些提取的指标与 Prometheus 或 Cloud Prometheus 实例中的活动 series 进行比较,并输出一个 used 指标和 unused 指标的列表。

Prometheus 精简指标实战

假定

  • 通过 kube-prometheus-stack 安装 Prometheus
  • 已安装 Grafana 且作为展示端
  • 已配置相应的 告警规则
  • 除此之外, 无其他需要额外保留的指标

前提

  1. Grafana Mimirtool 从 releases 中找到 mimirtool 对应平台的版本下载即可使用

    curl -L https://github.com/grafana/mimir/releases/download/mimir-2.6.0/mimirtool-linux-amd64 -o /usr/local/bin/mimirtool && chmod +x /usr/local/bin/mimirtool

  2. 创建 Grafana API token

  3. Prometheus已安装和配置.

  4. 如果是k8s环境,为Prometheus和Grafana配置NodePort访问,这里假设各端口grafana是32651,Prometheus是30090

第一步: 分析 Grafana Dashboards 用到的指标

通过 Grafana API

具体如下:

1
2
3
# 通过 Grafana API 分析 Grafana 用到的指标
# 前提是现在 Grafana 上创建 API Keys
mimirtool analyze grafana --address http://172.16.0.20:32651 --key=eyJrIjoiYjBWMGVoTHZTY3BnM3V5UzNVem9iWDBDSG5sdFRxRVoiLCJuIjoibWltaXJ0b29sIiwiaWQiOjF9

📝说明:

  • http://172.16.0.20:32651 是 Grafana 地址
  • --key=eyJr 是 Grafana API Token. 通过如下界面获得: 【老板可以如下创建 Grafana API Token,10.x版本之后可以在ServiceAccount中查看】

获取到的是一个 metrics-in-grafana.json, 内容概述如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
{
"metricsUsed": [
":node_memory_MemAvailable_bytes:sum",
"alertmanager_alerts",
"alertmanager_alerts_invalid_total",
"alertmanager_alerts_received_total",
"alertmanager_notification_latency_seconds_bucket",
"alertmanager_notification_latency_seconds_count",
"alertmanager_notification_latency_seconds_sum",
"alertmanager_notifications_failed_total",
"alertmanager_notifications_total",
"cluster",
"cluster:namespace:pod_cpu:active:kube_pod_container_resource_limits",
"cluster:namespace:pod_cpu:active:kube_pod_container_resource_requests",
"cluster:namespace:pod_memory:active:kube_pod_container_resource_limits",
"cluster:namespace:pod_memory:active:kube_pod_container_resource_requests",
"cluster:node_cpu:ratio_rate5m",
"container_cpu_cfs_periods_total",
"container_cpu_cfs_throttled_periods_total",
"..."
],
"dashboards": [
{
"slug": "",
"uid": "alertmanager-overview",
"title": "Alertmanager / Overview",
"metrics": [
"alertmanager_alerts",
"alertmanager_alerts_invalid_total",
"alertmanager_alerts_received_total",
"alertmanager_notification_latency_seconds_bucket",
"alertmanager_notification_latency_seconds_count",
"alertmanager_notification_latency_seconds_sum",
"alertmanager_notifications_failed_total",
"alertmanager_notifications_total"
],
"parse_errors": null
},
{
"slug": "",
"uid": "c2f4e12cdf69feb95caa41a5a1b423d9",
"title": "etcd",
"metrics": [
"etcd_disk_backend_commit_duration_seconds_bucket",
"etcd_disk_wal_fsync_duration_seconds_bucket",
"etcd_mvcc_db_total_size_in_bytes",
"etcd_network_client_grpc_received_bytes_total",
"etcd_network_client_grpc_sent_bytes_total",
"etcd_network_peer_received_bytes_total",
"etcd_network_peer_sent_bytes_total",
"etcd_server_has_leader",
"etcd_server_leader_changes_seen_total",
"etcd_server_proposals_applied_total",
"etcd_server_proposals_committed_total",
"etcd_server_proposals_failed_total",
"etcd_server_proposals_pending",
"grpc_server_handled_total",
"grpc_server_started_total",
"process_resident_memory_bytes"
],
"parse_errors": null
},
{...}
]
}

(可选)通过 Grafana Dashboards json 文件

如果无法创建 Grafana API Token, 只要有 Grafana Dashboards json 文件, 也可以用来分析, 示例如下:

1
2
3
# 通过 Grafana Dashboard json 分析 Grafana 用到的指标
mimirtool analyze dashboard grafana_dashboards/blackboxexporter-probe.json
mimirtool analyze dashboard grafana_dashboards/es.json

第二步: 分析 Prometheus Alerting 和 Recording Rules 用到的指标

具体操作如下:

1
2
3
4
5
6
7
# (可选)通过 kubectl cp 将用到的 rule files 拷贝到本地
# 注:cp需要保证从相对路径开始,所以需要提前将容器内的文件cp -r到相对路径下如:cp -r /etc/prometheus/rules/prometheus-rancher-monitoring-prometheus-rulefiles-0 /prometheus/prometheus-rancher-monitoring-prometheus-rulefiles-0
kubectl cp cattle-monitoring-system/prometheus-rancher-monitoring-prometheus-0:prometheus-rancher-monitoring-prometheus-rulefiles-0 -c prometheus ./kube-prometheus-stack/rulefiles/

# 通过 Prometheus rule files 分析 Prometheus Rule 用到的指标(涉及 recording rule 和 alert rules)
# 注:./kube-prometheus-stack/rulefiles/下还有一一个文件夹,名称是日期
mimirtool analyze rule-file ./kube-prometheus-stack/rulefiles/{替换下}/*

结果如下 metrics-in-ruler.json:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
{
"metricsUsed": [
"ALERTS",
"aggregator_unavailable_apiservice",
"aggregator_unavailable_apiservice_total",
"apiserver_client_certificate_expiration_seconds_bucket",
"apiserver_client_certificate_expiration_seconds_count",
"apiserver_request_terminations_total",
"apiserver_request_total",
"blackbox_exporter_config_last_reload_successful",
"..."
],
"ruleGroups": [
{
"namspace": "default-monitor-kube-prometheus-st-kubernetes-apps-ae2b16e5-41d8-4069-9297-075c28c6969e",
"name": "kubernetes-apps",
"metrics": [
"kube_daemonset_status_current_number_scheduled",
"kube_daemonset_status_desired_number_scheduled",
"kube_daemonset_status_number_available",
"kube_daemonset_status_number_misscheduled",
"kube_daemonset_status_updated_number_scheduled",
"..."
]
"parse_errors": null
},
{
"namspace": "default-monitor-kube-prometheus-st-kubernetes-resources-ccb4a7bc-f2a0-4fe4-87f7-0b000468f18f",
"name": "kubernetes-resources",
"metrics": [
"container_cpu_cfs_periods_total",
"container_cpu_cfs_throttled_periods_total",
"kube_node_status_allocatable",
"kube_resourcequota",
"namespace_cpu:kube_pod_container_resource_requests:sum",
"namespace_memory:kube_pod_container_resource_requests:sum"
],
"parse_errors": null
},
{...}
]
}

第三步: 分析没用到的指标

具体如下:

1
2
# 综合分析 Prometheus 采集到的 VS. (展示(Grafana Dashboards) + 记录及告警(Rule files))
mimirtool analyze prometheus --address=http://172.16.0.20:30090/ --grafana-metrics-file="metrics-in-grafana.json" --ruler-metrics-file="metrics-in-ruler.json"

📝说明:

  • --address=http://172.16.0.20:30090/ 为 prometheus 地址
  • --grafana-metrics-file="metrics-in-grafana.json" 为第一步得到的 json 文件
  • --ruler-metrics-file="kube-prometheus-stack-metrics-in-ruler.json" 为第二步得到的 json 文件

输出结果prometheus-metrics.json 如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
{
"total_active_series": 270336,
"in_use_active_series": 61055,
"additional_active_series": 209281,
"in_use_metric_counts": [
{
"metric": "rest_client_request_duration_seconds_bucket",
"count": 8855,
"job_counts": [
{
"job": "kubelet",
"count": 4840
},
{
"job": "kube-controller-manager",
"count": 1958
},
{...}
]
},
{
"metric": "grpc_server_handled_total",
"count": 4394,
"job_counts": [
{
"job": "kube-etcd",
"count": 4386
},
{
"job": "default/kubernetes-ebao-ebaoops-pods",
"count": 8
}
]
},
{...}
],
"additional_metric_counts": [
{
"metric": "rest_client_rate_limiter_duration_seconds_bucket",
"count": 81917,
"job_counts": [
{
"job": "kubelet",
"count": 53966
},
{
"job": "kube-proxy",
"count": 23595
},
{
"job": "kube-scheduler",
"count": 2398
},
{
"job": "kube-controller-manager",
"count": 1958
}
]
},
{
"metric": "rest_client_rate_limiter_duration_seconds_count",
"count": 7447,
"job_counts": [
{
"job": "kubelet",
"count": 4906
},
{
"job": "kube-proxy",
"count": 2145
},
{
"job": "kube-scheduler",
"count": 218
},
{
"job": "kube-controller-manager",
"count": 178
}
]
},
{...}
]
}

备注:提取指标内容【可选】

  • 提取正在被使用的指标:
1
jq -r ".in_use_metric_counts[].metric" prometheus-metrics.json | sort > used_metrics.txt
  • 提取没有被使用的指标:
1
jq -r ".additional_metric_counts[].metric" prometheus-metrics.json | sort > unused_metrics.txt

第四步: 仅 keep 用到的指标

write_relabel_configs 环节配置

如果你有使用 remote_write, 那么直接在 write_relabel_configs 环节配置 keep relabel 规则, 简单粗暴.

可以先用 jp 命令得到所有需要 keep 的 metric name:

1
2
3
4
5
jq '.metricsUsed' metrics-in-grafana.json \
| tr -d '", ' \
| sed '1d;$d' \
| grep -v 'grafanacloud*' \
| paste -s -d '|' -

输出结果类似如下:

1
instance:node_cpu_utilisation:rate1m|instance:node_load1_per_cpu:ratio|instance:node_memory_utilisation:ratio|instance:node_network_receive_bytes_excluding_lo:rate1m|instance:node_network_receive_drop_excluding_lo:rate1m|instance:node_network_transmit_bytes_excluding_lo:rate1m|instance:node_network_transmit_drop_excluding_lo:rate1m|instance:node_vmstat_pgmajfault:rate1m|instance_device:node_disk_io_time_seconds:rate1m|instance_device:node_disk_io_time_weighted_seconds:rate1m|node_cpu_seconds_total|node_disk_io_time_seconds_total|node_disk_read_bytes_total|node_disk_written_bytes_total|node_filesystem_avail_bytes|node_filesystem_size_bytes|node_load1|node_load15|node_load5|node_memory_Buffers_bytes|node_memory_Cached_bytes|node_memory_MemAvailable_bytes|node_memory_MemFree_bytes|node_memory_MemTotal_bytes|node_network_receive_bytes_total|node_network_transmit_bytes_total|node_uname_info|up

然后直接在 write_relabel_configs 环节配置 keep relabel 规则:

1
2
3
4
5
6
7
8
9
remote_write:
- url: <remote_write endpoint>
basic_auth:
username: < 按需 >
password: < 按需 >
write_relabel_configs:
- source_labels: [__name__]
regex: instance:node_cpu_utilisation:rate1m|instance:node_load1_per_cpu:ratio|instance:node_memory_utilisation:ratio|instance:node_network_receive_bytes_excluding_lo:rate1m|instance:node_network_receive_drop_excluding_lo:rate1m|instance:node_network_transmit_bytes_excluding_lo:rate1m|instance:node_network_transmit_drop_excluding_lo:rate1m|instance:node_vmstat_pgmajfault:rate1m|instance_device:node_disk_io_time_seconds:rate1m|instance_device:node_disk_io_time_weighted_seconds:rate1m|node_cpu_seconds_total|node_disk_io_time_seconds_total|node_disk_read_bytes_total|node_disk_written_bytes_total|node_filesystem_avail_bytes|node_filesystem_size_bytes|node_load1|node_load15|node_load5|node_memory_Buffers_bytes|node_memory_Cached_bytes|node_memory_MemAvailable_bytes|node_memory_MemFree_bytes|node_memory_MemTotal_bytes|node_network_receive_bytes_total|node_network_transmit_bytes_total|node_uname_info|up
action: keep

metric_relabel_configs 环节配置

如果没有使用 remote_write, 那么只能在 metric_relabel_configs 环节配置了.

以 etcd job 为例: (以 prometheus 配置为例, Prometheus Operator 请自行按需调整)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
- job_name: serviceMonitor/default/monitor-kube-prometheus-st-kube-etcd/0
honor_labels: false
kubernetes_sd_configs:
- role: endpoints
namespaces:
names:
- kube-system
scheme: https
tls_config:
insecure_skip_verify: true
ca_file: /etc/prometheus/secrets/etcd-certs/ca.crt
cert_file: /etc/prometheus/secrets/etcd-certs/healthcheck-client.crt
key_file: /etc/prometheus/secrets/etcd-certs/healthcheck-client.key
relabel_configs:
- source_labels:
- job
target_label: __tmp_prometheus_job_name
- ...
metric_relabel_configs:
- source_labels: [__name__]
regex: etcd_disk_backend_commit_duration_seconds_bucket|etcd_disk_wal_fsync_duration_seconds_bucket|etcd_mvcc_db_total_size_in_bytes|etcd_network_client_grpc_received_bytes_total|etcd_network_client_grpc_sent_bytes_total|etcd_network_peer_received_bytes_total|etcd_network_peer_sent_bytes_total|etcd_server_has_leader|etcd_server_leader_changes_seen_total|etcd_server_proposals_applied_total|etcd_server_proposals_committed_total|etcd_server_proposals_failed_total|etcd_server_proposals_pending|grpc_server_handled_total|grpc_server_started_total|process_resident_memory_bytes|etcd_http_failed_total|etcd_http_received_total|etcd_http_successful_duration_seconds_bucket|etcd_network_peer_round_trip_time_seconds_bucket|grpc_server_handling_seconds_bucket|up
action: keep





metricRelabelings:
- action: keep
regex: >-
apiserver_request_slo_duration_seconds_bucket|kubelet_runtime_operations_duration_seconds_bucket|nginx_http_request_duration_seconds_bucket|apiserver_request_total|storage_operation_duration_seconds_bucket|kube_pod_status_phase|workqueue_queue_duration_seconds_bucket|node_cpu_seconds_total|container_memory_working_set_bytes|container_cpu_usage_seconds_total|container_memory_rss|container_fs_writes_total|container_fs_reads_total|kube_replicaset_owner|kube_pod_status_scheduled|rest_client_request_duration_seconds_bucket|kubeproxy_network_programming_duration_seconds_bucket|kube_pod_info|container_memory_swap|container_memory_cache|nodejs_heap_space_size_available_bytes|nodejs_heap_space_size_used_bytes|nodejs_heap_space_size_total_bytes|apiserver_request_slo_duration_seconds_count|container_cpu_system_seconds_total|container_cpu_user_seconds_total|node_network_receive_drop_total|node_network_transmit_drop_total|node_network_transmit_bytes_total|node_network_receive_bytes_total|container_fs_writes_bytes_total|container_fs_reads_bytes_total|nginx_http_requests_total|kube_pod_owner|namespace_workload_pod:kube_pod_owner:relabel|kubelet_cgroup_manager_duration_seconds_bucket|kubelet_pod_worker_duration_seconds_bucket|container_network_receive_errors_total|container_network_transmit_packets_dropped_total|container_network_receive_packets_total|container_network_transmit_bytes_total|container_network_transmit_packets_total|container_network_transmit_errors_total|container_network_receive_packets_dropped_total|container_network_receive_bytes_total|node_filesystem_free_bytes|node_filesystem_size_bytes|node_filesystem_avail_bytes|node_network_transmit_packets_total|node_network_transmit_errs_total|node_network_receive_packets_total|node_network_receive_errs_total|up|kubelet_pleg_relist_duration_seconds_bucket|kube_pod_container_resource_requests|kubelet_runtime_operations_total|workqueue_depth|workqueue_adds_total|kubeproxy_sync_proxy_rules_duration_seconds_bucket|storage_operation_duration_seconds_count|kube_pod_container_resource_limits|rest_client_requests_total|process_start_time_seconds|kubelet_pod_start_duration_seconds_bucket|kubelet_pleg_relist_interval_seconds_bucket|process_cpu_seconds_total|process_resident_memory_bytes|nodejs_active_handles_total|nodejs_version_info|nodejs_external_memory_bytes|nodejs_heap_size_total_bytes|nodejs_eventloop_lag_seconds|nodejs_heap_size_used_bytes|nodejs_active_requests_total|kube_node_status_allocatable|volume_manager_total_volumes|scheduler_scheduling_algorithm_duration_seconds_bucket|node_disk_io_time_seconds_total|kubelet_cgroup_manager_duration_seconds_count|kubelet_pod_worker_duration_seconds_count|kubelet_running_containers|kube_namespace_status_phase|node_memory_Cached_bytes|node_disk_written_bytes_total|node_memory_MemAvailable_bytes|container_cpu_cfs_periods_total|kubelet_runtime_operations_errors_total|node_memory_MemTotal_bytes|node_disk_read_bytes_total|node_memory_MemFree_bytes|node_load1|container_cpu_cfs_throttled_seconds_total|node_disk_io_time_weighted_seconds_total|go_goroutines|container_cpu_cfs_throttled_periods_total|node_memory_Buffers_bytes|kubelet_node_name|node_vmstat_pgmajfault|node_uname_info|node_exporter_build_info|node_load15|node_memory_Slab_bytes|node_time_seconds|node_load5|nginx_http_connections|kubelet_pod_start_duration_seconds_count|kubelet_pleg_relist_duration_seconds_count|kubeproxy_sync_proxy_rules_duration_seconds_count|kubeproxy_network_programming_duration_seconds_count|kubelet_running_pods|namespace_memory:kube_pod_container_resource_requests:sum|namespace_memory:kube_pod_container_resource_limits:sum|scheduler_scheduling_algorithm_duration_seconds_count|namespace_cpu:kube_pod_container_resource_requests:sum|namespace_cpu:kube_pod_container_resource_limits:sum
sourceLabels:
- __name__