# 部署

# 单节点

  • 下载二进制版:

    wget https://github.com/prometheus/prometheus/releases/download/v2.42.0/prometheus-2.42.0.linux-amd64.tar.gz
    

    解压后启动:

    ./prometheus
                --config.file /etc/prometheus/prometheus.yml  # 使用指定的配置文件
                # --web.config.file=web.yml
                # --web.listen-address 0.0.0.0:9090           # 监听的地址
                # --web.external-url http://10.0.0.1:9090/    # 供外部访问的 URL
                # --web.enable-admin-api                      # 启用管理员的 HTTP API ,比如删除数据
                # --web.enable-lifecycle                      # 启用 reload、quit 等 HTTP API
                # --web.enable-remote-write-receiver
    
                # --storage.tsdb.retention.time=15d           # TSDB 中数据的最大保存时长
                # --storage.tsdb.retention.size=500GB         # TSDB 整个 data 目录占用的最大磁盘空间
                # --query.timeout=2m                          # 每次查询的超时时间
                # --query.max-samples=50000000                # 每次查询时最多将多少个指标载入内存,如果超过该数量,则查询失败
                # --log.level=info
                --log.format=json
    
    • 配置文件 prometheus.yml 主要用于控制 Prometheus 的监控任务,而 Prometheus 自身的运行状态只能通过命令行参数控制。
    • 配置文件 web.yml 用于启用身份认证,如下:
      basic_auth_users:
        <username>: <password>   # 这里需要填密码的哈希值,可用命令 htpasswd -Bbn <username> <password> 生成
      
  • 或者用 docker-compose 部署:

    version: "3"
    
    services:
      prometheus:
        container_name: prometheus
        image: prom/prometheus:v2.42.0
        restart: unless-stopped
        command:
          - --web.external-url=http://10.0.0.1:9090
        ports:
          - 9090:9090
        volumes:
          - .:/prometheus
    

    需要调整挂载目录的权限:

    mkdir data
    chown -R 65534 .
    

# 集群

  • 以普通方式部署的 Prometheus 单节点就有很高性能,足够监控几千台服务器。但某些情况下需要部署 Prometheus 集群:
    • 如果存在多个机房,用一个 Prometheus 通过公网采集这些服务器的 metrics 。则可能存在一些问题:网络带宽低、网络延迟偶尔超过 scrape_timeout 、偶尔断网。
      • 此时建议采用 federate、agent 等集群方案。大致原理是在每个机房部署一个 Prometheus 子节点,通过内网采集 metrics 。然后将所有 Prometheus 子节点的数据,通过公网汇总到一个 Prometheus 父节点。
    • 如果存在海量监控指标,导致 Prometheus 采集慢、查询慢,则可考虑 Thanos、VictoriaMetrics 等集群方案。

Prometheus 的多种集群部署方案:

  • federate

    • 部署流程:
      1. 按普通方式部署一些 Prometheus ,担任子节点。
      2. 部署一个 Prometheus ,担任父节点,添加 scrape_configs 配置:通过 federate 接口,抓取各个 Prometheus 子节点已采集的 metrics 。配置示例:
        scrape_configs:
        - job_name: federate
          honor_labels: true        # 设置 true ,以保存原指标中的 job 、instance 标签
          metrics_path: /federate   # 抓取的路由
          params:
            match[]:                # 指定筛选表达式。至少需要指定一个,如果指定多个则取并集
              - "{job!=''}"
              - go_goroutines
          static_configs:
            - targets:              # 目标 Prometheus 的地址
              - 10.0.0.2:9090
              - 10.0.0.3:9090
        
    • 缺点:
      • 只能抓取目标 Prometheus 最新采集的 metrics 。如果目标 Prometheus 掉线一段时间,则重新连接之后,并不会抓取掉线期间的 metrics 。
  • remote write

    • 部署流程:
      1. 部署一个 Prometheus ,担任父节点,添加命令行选项 --web.enable-remote-write-receiver 。
      2. 部署一些 Prometheus ,担任子节点,添加 remote_write 配置:将本机采集到的 metrics ,发送到 Prometheus 父节点的 /api/v1/write 路由。配置示例:
        scrape_configs: ...
        remote_write:
          - url: https://prometheus.test.com/api/v1/write
            # enable_http2: true
            # remote_timeout: 30s     # 每次发送 HTTP 请求给 remote 的超时时间
            # basic_auth:
            #   username: ***
            #   password: ***
            write_relabel_configs:    # 在发送 metrics 之前,修改 label
              - <relabel_config>
            # queue_config:           # 配置推送队列
              # capacity: 2500        # 从 WAL 读取 metrics 到 queue 时,每个 shard 最多缓冲多少条 metrics ,如果缓冲区满了则暂停读取 WAL
              # max_shards: 200       # 当前 queue 最多划分多少个 shard
              # min_shards: 1         # 当前 queue 启动时初始有多少个 shard 。如果 Prometheus 认为推送速度慢,则会自动增加 shard 数量
              # max_samples_per_send: 500   # 每个 shard 每次最多推送多少条 metrics 。建议将 capacity 设置为 max_samples_per_send 的几倍
              # batch_send_deadline: 5s     # 每个 shard 等待缓冲了 max_samples_per_send 条 metrics 才推送一次,如果等待超时,即使数量不足也推送一次
              # min_backoff: 30ms           # 连续推送失败时,重试间隔从 min_backoff 开始增加,每次倍增,最大为 max_backoff
              # max_backoff: 5s
        
        • 整个 queue 占用的内存大概为 number_of_shards * (capacity + max_samples_per_send) 。1K 条 metrics 大概占用 50KB 内存。
    • remote write 与 federate 相似,都是将 metrics 汇总存储到一个 Prometheus ,但 remote write 更可靠:
      • federate 方式可能重复采集某个时刻的 metrics ,也可能遗漏采集某个时刻的 metrics 。
      • remote write 方式会为每个 remote 创建一个 metrics 推送队列(queue)。
        • 如果队列中的 metrics 推送失败,则自动重试。除非持续失败 2 小时,WAL 预写日志文件被压缩。
        • 每个 queue 分成多个分片(shard),可以并发推送。
        • 普通部署方案,会在 scrape 瞬间对 Prometheus 造成很大负载。而通过队列推送,负载很平稳。
  • remote read

    • 部署流程:
      1. 按普通方式部署一些 Prometheus ,担任子节点。
      2. 部署一个 Prometheus ,担任父节点,添加 remote_read 配置:当本机需要查询 metrics 时,允许发送查询请求到各个 Prometheus 子节点的 /api/v1/read 路由,然后汇总它们查询到的 metrics 。
    • 缺点:
      • 向 Prometheus 子节点发送的查询请求,不是 PromQL 格式,而是 protobuf 格式,只能根据时间范围、标签查询 metrics ,因此查询的效率不高。
      • 即使给 Prometheus 添加了 remote_read 配置,执行 recording rules 和 alerting rules 时,只会使用本地 TSDB 的 metrics ,不会读取其它 Prometheus 的 metrics 。
  • agent

    • Prometheus v2.32.0 增加了 agent 工作模式,起源于 Grafana agent 。
    • agent 模式是 remote write 模式的基础上,减少 Prometheus 子节点的开销:
      • agent 禁用了本地存储 TSDB ,因此占用磁盘更少。采集到的 metrics 会先缓存在 data-agent/wal/ 目录,只要转发成功,就立即删除。
      • agent 禁用了查询、警报功能,因此占用内存更少。
    • 部署流程:
      1. 按 remote_write 方案部署 Prometheus 集群。
      2. 给 Prometheus 子节点添加命令行选项 --enable-feature=agent 。
  • Thanos (opens new window)

    • :一套第三方软件,基于 Prometheus 搭建分布式监控系统。包含多个组件:
      • Sidecar :为每个 Prometheus 部署一个 Sidecar ,将该 Prometheus 采集的 metrics 发送到 S3 云存储。
      • Receiver :接收 Prometheus 通过 remote write 发送的 metrics ,然后保存到云存储。
      • Compactor :压缩云存储中的 metrics 数据。
      • Ruler :对云存储中的 metrics 执行 recording rules 和 alerting rules 。
      • Query :实现 Prometheus 的查询 API ,被 Query Frontend 调用。
      • Query Frontend :供用户访问,执行 PromQL 查询表达式。
    • federate 等集群方案主要用于横向扩容 Prometheus 集群,依然存在单点故障的风险。而 thanos 可给每个组件部署多实例,实现高可用。
  • VictoriaMetrics (opens new window)

    • :一个监控工具。可替代 Prometheus 软件。也可用作 Prometheus 的存储层,以 remote write 方式写入数据,兼容 Prometheus 的查询 API 。
    • 与 Prometheus 相比,优点如下:
      • 采集、查询涉及的 seriesId 基数很大时,性能比 Prometheus 更好。
      • 除了单节点部署,也支持集群部署,可通过部署多实例来实现高可用。

# 配置

# 示例

下例是让 Prometheus 监控自身的步骤:

  1. 在配置文件 prometheus.yml 中加入监控任务:

    global:
      scrape_interval: 30s          # 每隔多久采集一次指标,默认为 1m 。这是全局值,可以被局部值覆盖
      scrape_timeout: 10s           # 每次采集的超时时间。默认为 10s ,该值不能超过 scrape_interval
      evaluation_interval: 30s      # 每隔多久执行一次 rules ,默认为 1m
      # external_labels:            # 与 Alertmanager 等外部组件通信时,会加上这些标签
      #   monitor: codelab-monitor
    
    # rule_files:                   # 导入 rules 文件
    # - rules_1.yml
    
    scrape_configs:                 # 配置需要监控的对象
    - job_name: prometheus          # 声明一个监控任务,常用于监控同一种 targets
      static_configs:
      - targets:                    # 可声明多个 targets 的地址
        - 10.0.0.1:9090
    
  2. 重启 Prometheus 以重新加载配置文件,然后访问其 Web 页面。

    • 在 Status -> Targets 页面,可以看到所有监控对象及其状态。
    • 在 Graph 页面,执行一个查询表达式即可获得监控数据,比如 go_goroutines

# scrape_configs

  • scrape_configs 的详细配置:
    scrape_configs:
    - job_name: prometheus
      # honor_labels: false
      # metrics_path: /metrics
      # follow_redirects: true          # 是否跟随状态码为 3xx 的重定向
      # scheme: http                    # 通信协议
      # scrape_interval: 30s
      # scrape_timeout: 10s
      # basic_auth:
      #   username: <string>
      #   password: <string>
      # proxy_url: <string>
      # tls_config:
      #   insecure_skip_verify: false   # 是否跳过认证 HTTPS 证书
      static_configs:
      - targets:                        # 一组监控对象的 IP:Port
        - 10.0.0.1:9090
        - 10.0.0.1:9091
        # labels:                       # 从 targets 采集 metrics 时,添加额外的标签
        #   nodename: CentOS-1
      - targets: ['10.0.0.2:9090']      # 第二组监控对象
      # relabel_configs:
      #  - <relabel_config>
      # metric_relabel_configs:
      #  - <relabel_config>
    
    - job_name: node_exporter
      file_sd_configs:                  # 从文件读取配置,这样修改配置时不必重启 Prometheus
      - files:
        - targets/node_exporter*.json   # 文件路径的最后一个字段可使用通配符 *
        # refresh_interval: 5m          # 每隔多久重新读取一次
    
  • 通过 file_sd_configs 方式读取的文件可以是 YAML 或 JSON 格式,如下:
    - targets:
      - 10.0.0.1:9090
      labels:
        nodename: CentOS-1
    - targets:
      - 10.0.0.2:9090
      labels:
        nodename: CentOS-2
    
    [{
        "targets": [
            "10.0.0.1:9100"
        ],
        "labels": {
            "nodename": "CentOS-1"
        }
    }, {
        "targets": [
            "10.0.0.2:9100"
        ],
        "labels": {
            "nodename": "CentOS-2"
        }
    }]
    
  • Prometheus 会将采集到的 metrics 加工之后再保存。假设 targets 的原 metrics 如下:
    http_requests_total{code="200"} 162
    http_requests_total{code="500"} 0
    
    则 Prometheus 最终保存的 metrics 如下:
    # Prometheus 默认会为每个 metrics 添加 job: "$job_name"、instance: "$target" 两个标签
    http_requests_total{code="200", job="prometheus", instance="10.0.0.1:9090"} 162
    http_requests_total{code="500", job="prometheus", instance="10.0.0.1:9090"} 0
    
    # Prometheus 会为每个 targets 添加如下三个 metrics
    up{job="prometheus", instance="10.0.0.1:9090"} 1                          # 该监控对象是否在线。取值 1、0 分别代表在线、离线
    scrape_samples_scraped{job="prometheus", instance="10.0.0.1:9090"} 2      # 本次抓取的指标数
    scrape_duration_seconds{job="prometheus", instance="10.0.0.1:9090"} 0.01  # 本次抓取的耗时
    
    • 原 metrics 中的 label 如果以 __ 开头,则采集之后不会保存。
    • 用户可通过 static_configs[].labels 添加额外的标签。比如 instance 中的 IP 地址不方便记忆,可添加 nodename 标签。
    • 给 metrics 添加 label 时,如果原 metrics 中已存在同名的 label ,则根据 honor_labels 进行处理:
      • honor_labels: false :默认值,将原 label 改名为 exported_<label_name> ,再添加新 label 。
      • honor_labels: true :保留原 label 不变,不添加新 label 。
    • 用户可通过 relabel_configs 在采集之前修改 label ,通过 metric_relabel_configs 在采集之后修改 label 。例:
      metric_relabel_configs:
      - replacement: test
        target_label: project   # 添加一个 label ,名称为 project ,取值为 test
      - action: replace         # action 默认为 replace ,是将 source_labels 多个标签的值用 separator 拼接成一个字符串,如果与 regex 正则匹配,则生成字符串 replacement ,赋值给 target_label 。如果不正则匹配,则不操作
        source_labels: [<label>, ...]
        separator: ;
        regex: (.+)
        replacement: $1
        target_label: <label>
      - action: replace         # 这条规则是当 project 取值为空时,设置默认值
        source_labels: project
        regex: ^$
        replacement: test
        target_label: project
      - action: keep            # keep 动作:如果 source_labels 的值与 regex 完全正则匹配,则保留该 label ,否则不保留
        source_labels: [nodename]
        regex: .*test.*
      

# rules

  • Prometheus 可配置两种规则:

    • Recording Rules :用于将某个查询表达式的结果保存为新指标。这样可以避免在用户查询时才计算,减少开销。
    • Alerting Rules :用于在满足某个条件时进行告警。(它只是产生警报,需要由 Alertmanager 加工之后转发给用户)
  • 用户可在 prometheus.yml 中导入自定义的 rules.yml 文件,格式如下:

    groups:
    - name: recording_rules               # 规则组的名称
      # interval: 15s                     # 每隔多久执行一次该 rules
      rules:
      - record: go_goroutines:sum_by_job  # 定义一个新指标
        expr: sum(go_goroutines) by (job) # 查询表达式
    
    - name: alerting_rules                # 规则组的名称
      rules:
      - alert: 测试告警-1                  # 定义一个告警规则
        expr: go_goroutines > 100         # 设置告警条件。只要表达式的执行结果是向量,就视作满足条件
        for: 5m                           # 如果持续满足告警条件 5 分钟,则触发告警
        # keep_firing_for: 1m             # Prometheus v2.42.0 增加的配置参数,表示触发告警之后,如果不再满足告警条件,则告警至少持续多久才能关闭。这样可以避免告警轰炸
        # labels:
        #   severity: error
        annotations:
          summary: "节点地址:{{$labels.instance}}, 协程数:{{$value}}"
    
    • 可以重复定义同样内容的 rules ,但最终输出时,多个重复的数据会合并为一个。
    • Prometheus 会在每次抓取指标时自动检查一次 Alerting Rules ,因此不需要设置 interval 。
    • 默认会将 expr 计算结果中的所有 label 添加到告警信息中。
      • 可以通过 labels 子句添加一些标签到告警信息中,但是如果与已有的 label 重名则不会生效。
      • 可以通过 annotations 子句添加一些标签作为注释。
      • 给这些标签赋值时允许引用变量(基于 Golang 的模板语法)。
    • 上例中,最终生成的警报包含以下信息:
      {
          "status": "firing",
          "labels": {
              "alertname": "进程数归零",
              "instance":"10.0.0.1:9090",
              "job":"prometheus",
          },
          "annotations": {
              "summary":"节点地址:10.0.0.1:9090, 协程数:90",
          },
          "startsAt": "2020-07-09T01:23:22.627587301Z",
          "endsAt": "0001-01-01T00:00:00Z"
      }
      
  • 当异常开始时,Prometheus 会产生 "status": "firing" 的警报。当异常结束时,还会产生 "status": "resolved" 的警报。

    • startsAt 参数表示警报的开始时间。根据 alerting_rules ,可能等 metrics 持续异常一段时间之后才产生警报。
    • resolved 类型的警报中,会增加一个 endsAt 参数。
  • 在 Web 页面上可以看到 Alerting Rules 的状态:

    • 不满足告警条件时,属于 Inactive 状态。
    • 满足告警条件时,属于 Active 状态。
      • 如果不超过阙值时间,则属于 Pending 状态。
      • 如果超过阙值时间,则属于 Firing 状态。
  • 可参考的告警规则:awesome-prometheus-alerts (opens new window)

# HTTP API

  • 用于管理 Prometheus 的 HTTP API :

    GET   /-/healthy  # 用于健康检查,总是返回 HTTP 200
    GET   /-/ready    # 返回 HTTP 200 则代表可以处理 HTTP 请求
    POST  /-/reload   # 重新加载配置文件
    POST  /-/quit     # 终止进程
    
  • 关于数据的 API :

    GET   /api/v1/query?query=go_goroutines{instance='10.0.0.1:9090'}&time=1589241600
      # 查询与表达式匹配,且位于某个时刻的 point ,然后返回。如果不指定 time ,则采用当前时刻
    
    GET   /api/v1/query_range?query=go_goroutines{instance='10.0.0.1:9090'}&start=1589241600&end=1589266000&step=1m
      # 查询与表达式匹配,且位于 start 至 end 时间范围内的所有 points ,每隔 step 时长取一个 point 返回
      # 相当于从 start 时刻开始,每隔 step 时长执行一次 /api/v1/query 请求
      # query、start、end、step 几个参数必须都指定,不能为空
    
    POST  /api/v1/admin/tsdb/delete_series?match[]=go_goroutines&start=1589241600&end=1589266000
      # 删除与表达式匹配的 points 。如果不指定时间范围 start、end ,则删除全部时间的 points
    
    POST  /api/v1/admin/tsdb/clean_tombstones
      # 让 TSDB 立即释放已删除 points 占用的磁盘空间