【实践】Prometheus日渐增长的内存如何做hash分离

Prometheus唯一的缺点就是监控项越来越多的情况下,内存消耗是很大的,如果根据指标项进行搜集分割?

落盘设计:

Prometheus在数据落盘的流程上进行了一定的考虑。
完整的数据流程应该是:pull->prometheus->memory->wal->tsdb->filesystem.
正常的情况下,Prometheus会把最新搜集到的数据放在内存中,针对搜集量较大,metrics内包含较大、较多label的和监控项 time series较多的监控指标来说存在内存里是不太友好的,会占用较高的内存使用,同时内存里面也会存储相对较老还没有落盘的数据。以t0-t1为最新时间,t1-t2为较老时间,t3,t4... 内存中: (t0-t1的series+t1-t2的series)

那么带来的问题就来了:
1.如果Prometheus重启怎么办?
如果Prometheus重启的话,Prometheus本地会有缓存文件WAL存储还未落盘的数据,一般来说不会造成大量的数据丢失,但是在一定的数据量下,Prometheus重启会进行WAL文件加载到内存,加载过程会造成1-2个拉取间隔的数据丢失,出现断层。同时根据数据量大小不同,加载时间也会不一样
2.如果查询更加久远的数据怎么办?
Prometheus对于更加久远的数据查询方式是从filesystem->tsdb->Memory的方式,如果对于一些time series较多的指标来说,压力是比较大的,此时内存中(查询的time series+新拉取的time series),所有会发现根据查询数据量的不同内存会有不同程度的上升,很大的可能撑爆内存进行重启。
3.如果落盘+内存不足怎么办
如果对于新拉取数据来不及落盘就会进行重启、OOM等,那这个时候有必要扩大内存或者进行分离了

针对内存问题进行分离

架构策略

逻辑

1.将所有要拉起的target等信息打好标签注册到consul
2.Prometheus通过hashmod方式分块拉取consul配置
3.每个单独Prometheus都会拉取不同于其他的配置
4.最好对每个单独的Prometheus做数据汇总或者查询汇总

步骤(以K8S部署为例)

1.针对官方的镜像新增hashmod模块分配值
Dockerfile:

FROM  prometheus/prometheus:2.20.0
MAINTAINER name gecailong

COPY ./entrypoint.sh /bin

ENTRYPOINT ["/bin/entrypoint.sh"]
entrypoint.sh:

#!/bin/sh

ID=${POD_NAME##*-}

cp /etc/prometheus/prometheus.yml /prometheus/prometheus-hash.yml

sed -i "s/ID_NUM/$ID/g" /prometheus/prometheus-hash.yml

/bin/prometheus --config.file=/prometheus/prometheus-hash.yml --query.max-concurrency=20 --storage.tsdb.path=/prometheus --storage.tsdb.max-block-duration=2h --storage.tsdb.min-block-duration=2h  --storage.tsdb.retention=2h --web.listen-address=:9090 --web.enable-lifecycle --web.enable-admin-api

IDNUM: 为我们后面配置做准备
2.Prometheus部署
Prometheus配置文件:

  prometheus.yml: |
    global:
      scrape_interval:     15s
      evaluation_interval: 15s

      external_labels:
         monitor: 'k8s-sh-prod'
         service: 'k8s-all'
         ID: 'ID_NUM'
         ...

这个ID是为了我们在查询的时候可以区分同时也可以作为等下hashmod模块的对应值

以拉取Node信息为例:

    - job_name: 'kubernetes-nodes'
      kubernetes_sd_configs:
      - role: node
      scheme: https
      tls_config:
        insecure_skip_verify: true
      bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
      relabel_configs:
      - action: labelmap
        regex: __meta_kubernetes_node_label_(.+)
      - source_labels: [__meta_kubernetes_node_address_InternalIP]
        regex: (.+)
        target_label: __address__
        replacement: ${1}:10250 
      - source_labels: [__meta_kubernetes_node_name]
        regex: (.+)
        target_label: __metrics_path__
        replacement: /metrics
        #以下为hashmod配置部分
      - source_labels: [__meta_kubernetes_node_name]
        modulus:       10   #要分成的模块总数
        target_label:  __tmp_hash
        action:        hashmod
      - source_labels: [__tmp_hash]
        regex:         ID_NUM  #当前所属模块数
        action:        keep

部署文件:

apiVersion: apps/v1
kind: StatefulSet
metadata:
  labels:
    app: prometheus
  name: prometheus-sts
  namespace: monitoring
spec:
  serviceName: "prometheus"
  replicas: 10   #hashmod总模块数
  selector:
    matchLabels:
      app: prometheus
  template:
    metadata:
      labels:
        app: prometheus
    spec:
      containers:
      - image: prometheus:2.20.0-hash
        name: prometheus
        securityContext:
           runAsUser: 0
        command:
        - "/bin/entrypoint.sh"  #hashmod脚本执行
        env:
        - name: POD_NAME   #根据statefulset的特性传入POD名称用于模块取值
          valueFrom:
            fieldRef:
              apiVersion: v1
              fieldPath: metadata.name
        ports:
        - name: http
          containerPort: 9090
          protocol: TCP

针对数据存储和查询的问题可以通过Thanos进行查询聚合,也可以通过victoriametrics进行数据存储的聚合在grafana界面进行统一的查询

针对内存问题进行分离