Prometheus批量部署监控主机

针对节点的监控,方式应该有一下两种:

  1. 在各个节点部署node_export然后通过静态文件发现的方式让Prometheus实现批量监控
  2. 同样是是用node_export不过还可以通过使用consul来做服务发现

对于第一种方式来说,静态文件的维护其实也是挺繁琐的,而且不够灵活。对于应用的发现需要重复操作。
第二种方式需要额外维护一套consul集群,好处是,应用层面的服务发现也可以通过它来实现
虽然第二种方式有优点,不过这里还是使用第一种(主要是不太会第二种

准备主机列表

主机列表信息主要有两个用途:

  1. 生成ansible hosts配置文件
  2. 生成Prometheus自动发现所需要的文件

准备ansible

ansible hosts配置文件主要用作主机管理以及远程登陆时账号密码的配置。

  1. ansible 命令格式为ansible [HOST] -m [MODULE_NAME] -a [ARGS],ansible管理的主机需要配置在/etc/ansible/hosts文件中,[HOST]可以是文件中的单个IP地址也可以是分组名。配置文件中有参考示例,不在展示

  2. ansible 批量管理的原理是登录到远程主机执行自动生成的Python脚本,对于没有事先做免密认证的主机来说,在配置文件中配置用户名密码也是可以的。看一下示例:

    192.168.4.66 ansible_ssh_user=root ansible_ssh_pass=password
    192.168.4.247 ansible_ssh_user=root ansible_ssh_pass=password
    192.168.5.176 ansible_ssh_user=root ansible_ssh_pass=password
    192.168.5.175 ansible_ssh_user=root ansible_ssh_pass=password
    192.168.3.21 ansible_ssh_user=root ansible_ssh_pass=password
    192.168.4.172 ansible_ssh_user=root ansible_ssh_pass=password
    192.168.3.133 ansible_ssh_user=root ansible_ssh_pass=password
  3. 除此之外,首次登录一般会有一个指纹认证,当然我们还是要关掉,执行这条命令添加一个全局环境变量,最好配置到启动脚本中去: export ANSIBLE_HOST_KEY_CHECKING=False

准备Prometheus

基于文件的自动发现其实就是配置一个IP列表,然后Prometheus会根据列表中的地址拉去数据。在主配置文件中指定自定义的自动发现文件路径,然后创建对应文件即可。

# Prometheus基于文件的自动发现配置,配置在主配置文件(部分)中
scrape_configs:
...
  - job_name: "ENV_TEST_HOST"
    file_sd_configs:
      - files:
        - "/opt/prometheus/target/*.json"
        refresh_interval: 5m
...

然后在指定的路径创建对应的json文件,格式如下:

[
    {
        "targets": [ 
            "192.168.4.22:9119",
            "192.168.4.12:9119",
            "192.168.4.15:9119",
            "192.168.4.14:9119",
            "192.168.5.239:9119"
            ···
            ],
        "labels": {
        "job": "ENV_TEST_HOST:9119",
        "service": "test_service"
        }
    }
]

分割线中的内容为展示示例以备查询

target中就是主机列表,labels会在发现后添加到Prometheus的lebel列表中

如果需要对每一台主机进行更加个性化的定制,可以创建多个target,根据用途分组,添加label

除了对主机进行发现,服务发现也可以基于文件进行,下面是一个更加详细的示例:

[
   {
      "targets": [
         "172.16.0.96:9119"
      ],
      "labels": {
         "project_name": "项目A",
         "env_name": "开发环境",
         "template_type": "主机模板",
         "host_name": "host.domain"
      }
   },
 {
      "targets": [
         "172.16.0.96:30013",
         "172.16.0.96:30015"
      ],
      "labels": {
         "project_name": "项目A",
         "env_name": "开发环境",
         "soft_name": "测试应用A",
         "template_type": "APP模板",
         "host_name": "host.domain"
      }
   },
{
      "metrics_path": "/_prometheus/metrics",
      "targets": [
         "172.16.0.96:9200"
      ],
      "labels": {
         "project_name": "项目A",
         "env_name": "开发环境",
         "soft_name": "测试应用B",
         "template_type": "APP 模板",
         "host_name": "host.domain"
      }
   }
]

分割线中的内容为展示示例以备查询

部署node_export并启动

准备配置文件及安装程序

对于没有集成Prometheus的应用,通常会使用各种exporter,Prometheus的一大优势在于其可以将Prometheus集成到项目应用中去,Prometheus直接通过应用暴露出来的地址就可以完成采集数据的目标。对于没有集成的,有很多官方及第三方的exporter,基本涵盖了常见应用。

https://prometheus.io/download/ 根据系统下载对应版本的node_exporter,解压之后只有一个二进制程序,放着备用。然后准备服务配置文件:

[root@prom ~]# vim /etc/systemd/system/node_exporter.service

[Unit]
Description=This is prometheus node exporter
After=network.service
[Service]
Type=simple
EnvironmentFile=/opt/exporter/node_exporter.cnf
ExecStart=/opt/exporter/node_exporter $ARGS
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
Restart=on-failure
[Install]
WantedBy=multi-user.target

这里使用了一个EnvironmentFile来指定启动参数,原因是我把参数直接加进来死活起不来

ExecStart:这个是启动文件路径,根据自己的组织放置

EnvironmentFile:环境文件,同样自定义路径

[root@prom ~]# vim /opt/exporter/node_exporter.cnf

ARGS='--web.listen-address=":9119"'

完成之后,使用systemctl daemon-reload && systemctl start node_export && systemctl start node_export查看服务能否正常启动,然后使用ss -tunl | grep 9119查看端口是否正常。如果没有正常启动,根据报错信息排错。

说明: 这里的node_exporter是用来9119端口,启动参数就是用来指定这个的,原因在于我的主机列表中有些主机上node_exporter默认的9100是被占用的,在正式开始之前务必检查,否则出错之后再改就很麻烦

使用ansible all -m shell -a 'ss -tunl | grep 9100' 来批量查看端口占用情况

部署并启动

一共三个文件,使用ansible copy模块传过去然后启动就可以了。如果不嫌烦,可以写个ansible的role来执行,几条命令的事,我懒。

执行下面的命令来部署启动node_exporter,可能还需要先创建目录

ansible all -m shell -a "mkdir -pv /opt/exporter"
ansible all -m copy -a "src=/root/node_exporter dest=/opt/exporter/node_exporter mode=755"
ansible all -m copy -a "src=/root/node_exporter.cfg dest=/opt/exporter/node_exporter.cfg"
ansible all -m copy -a "src=/etc/systemd/system/node_exporter.service dest=/etc/systemd/system/node_exporter.service"
ansible all -m shell -a "systemctl daemon-reload && systemctl start node_exporter"
ansible all -m shell -a "ss -tunl | grep 9119"
ansible all -m shell -a "systemctl enable node_exporter"

命令很简单,只用了copy和shell模块,复制了三个文件,然后启动服务。shell模块是万能的 :)

copy模块文档地址: https://docs.ansible.com/ansible/2.8/modules/copy_module.html#copy-module

部署配置Prometheus及Grafana

安装Grafana

Grafana 下载地址:https://grafana.com/grafana/download?pg=get&plcmt=selfmanaged-box1-cta1

根据自己的系统下载需要的版本,然后:

wget https://dl.grafana.com/oss/release/grafana-7.4.5-1.x86_64.rpm
sudo yum install grafana-7.4.5-1.x86_64.rpm
systemctl start grafana-server && systemctl enable grafana-server

建议开启下面的配置:

[root@prom ~]# vim /etc/grafana/grafana.ini

...
[server]
# Protocol (http, https, h2, socket)
protocol = http

# The ip address to bind to, empty will bind to all interfaces
http_addr = 192.168.3.53

# The http port  to use
;http_port = 3000

# The public facing domain name used to access grafana from a browser
domain = 192.168.3.53
...

主要是http_addr和domain,这俩在后续报警的时候会有用,启动之后就可以通过 http://IP:3000 访问Grafana了

安装Prometheus

在下载node_exporter的地址下载Prometheus,同样的,解压到自己规划的目录中去。然后准备访问启动文件以及将刚才准备的配置文件放好。

[root@prom ~]# vim /etc/systemd/system/prometheus.service

[Unit]
Description=prometheus
After=network.target
[Service]
Type=simple
User=root
ExecStart=/opt/prometheus/prometheus --config.file=/opt/prometheus/prometheus.yml --storage.tsdb.path=/var/lib/prometheus --storage.tsdb.retention=15d --log.level=info
Restart=on-failure
[Install]
WantedBy=multi-user.target

然后,在主配置文件中添加新的Job,参考:

...
scrape_configs:
...
  - job_name: "ENV_TEST_HOST"
    file_sd_configs:
      - files:
        - "/opt/prometheus/target/*.json"
        refresh_interval: 5m
...

然后将开始准备好的json文件放到Job中指定的路径,之后就可以启动了

systemctl start prometheus && systemctl enable prometheus

通过 http://IP:9090 就可以查看Prometheus的界面了

配置报警alertmanager + 钉钉

使用钉钉接收报警信息,和zabbix使用agent往服务端推送监控指标不同,Prometheus服务端主动取拉去信息。可能也是因为这样,Prometheus在报警之后似乎不能对故障做额外操作,zabbix可以通过配置触发动作实现一定程度的自愈。

配置钉钉机器人

  1. 创建一个钉钉群,可能需要管理员才能创建
  2. 点击群设置→智能群助手→添加机器人
  3. 在弹出的页面中选择添加机器人,然后选择自定义,然后点击添加
  4. 给机器人取个名字
  5. 安全设置这里至少选择一个,这里使用自定义关键字,然后点击完成
  6. 复制Webhook地址备用

自定义关键字指的是,通过Webhook发送的信息中必须包含自定义的字符或字符串才能发送

使用加签的话,系统会生成一段SEC开头的字符串,发送的时候,需要通过算法根据这个字符串算出一个签名附加到Webhook地址上。(签名是算出来的,每次计算结果都不一样

IP地址段就是IP白名单,很容易理解

安装配置prometheus-webhook-dingding

可以根据文档自己写这个东西,第三发的实现也有多个,钉钉官方帮助文档在: https://developers.dingtalk.com/document/app/develop-enterprise-internal-robots

这是第三发开发的一个插件,作用是格式化数据以及Webhook地址的拼接等工作,其在GitHub的地址是: https://github.com/timonwong/prometheus-webhook-dingtalk

这个东西是使用Go语言开发的,项目提供了编译过的二进制程序,直接在对应的release页面下载编译好的程序就好,也可以使用编译安装,项目页面有编译安装方法。

下载解压,放到规划的目录,然后创建service文件,内容:

[root@prom ~]# vim /etc/systemd/system/prometheus-webhook-dingtalk.service

[Unit]
Description=prometheus-webhook-dingtalk
After=network.target
[Service]
Restart=on-failure
ExecStart=/opt/web-hook-dingding/prometheus-webhook-dingtalk --ding.profile=ops_dingding=https://oapi.dingtalk.com/robot/send?access_token=xxx
[Install]
WantedBy=multi-user.target

和之前的配置类似,注意替换Webhook的地址。

具体的配置以及模版自定义,需要自行百度。

然后启动服务

安装配置alertmanager

在下载Prometheus的地址下载alertmanager,按照之前的方法配置service文件,文件内容:

[root@prom ~]# vim /etc/systemd/system/alertmanager.service

[Unit]
Description=alertmanager
Documentation=https://github.com/prometheus/alertmanager
After=network.target
[Service]
Type=simple
User=root
ExecStart=/opt/alertmanager/alertmanager --config.file=/opt/alertmanager/alertmanager.yml
Restart=on-failure
[Install]
WantedBy=multi-user.target

然后定义alertmanager的route和receivers

global:
  resolve_timeout: 5m

route:
  group_by: ['alertname']
  group_wait: 10s
  group_interval: 10s
  repeat_interval: 1h
  receiver: 'web.hook'
receivers:
- name: 'web.hook'
  webhook_configs:
  - url: 'http://127.0.0.1:8060/dingtalk/ops_dingding/send'
    send_resolved: true

url是prometheus-webhook-dingding的地址及端口,uri不用改。

完成之后启动服务

配置Prometheus

看示例把:

# Alertmanager configuration
alerting:
  alertmanagers:
  - static_configs:
    - targets: ['localhost:9093']
       #- "localhost:9093"

# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
rule_files:
   - "rules/node.yml"
   #- "second_rules.yml"

在alertmanager configuration指定alertmanager的地址和端口,然后在rulefile里指定报警规则文件,可以指定多个规则文件,node.yml的内容如下:

expr是Prometheus的查询语句

groups:
- name: Node_alert
  rules:

  - alert: "实例丢失"
    expr: up == 0
    for: 60s
    labels:
      severity: page
    annotations:
      summary: ">> Prometheus中断了与 {{ $labels.instance }} 的连接"
      description: ">> {{ $labels.instance }} 已经失联至少 1 分钟了"
 
  - alert: "磁盘可用容量不足15%"
    expr: 100-((node_filesystem_avail_bytes{job="ENV_TEST_HOST",mountpoint!="/boot",fstype=~"ext4|xfs|ext2|ext3"} * 100)/(node_filesystem_size_bytes {job="ENV_TEST_HOST",mountpoint!="/boot",fstype=~"ext4|xfs|ext2|ext3"})) > 85
    for: 30s
    annotations:
      summary: ">> 节点 {{ $labels.instance }} 有分区空间剩余不足"
      description: ">> {{ $labels.instance }}有磁盘已使用 {{ $value }}"
 
  - alert: "可用内存不足10%"
    expr: ((node_memory_MemTotal_bytes - node_memory_MemFree_bytes - node_memory_Buffers_bytes - node_memory_Cached_bytes) / (node_memory_MemTotal_bytes )) * 100 > 90
    for: 300s
    labels:
      severity: warning
    annotations:
      summary: ">> 节点 {{ $labels.instance }} 可用内存不足"
      description: ">> {{ $labels.instance }} 内存资源已不足10%已持续5分钟\n   当前值: {{ $value }}"
 
  - alert: "CPU15分钟平均负载大于8个"
    expr: node_load15 > 8
    for: 30s
    annotations:
      sumary: ">> 节点 {{ $labels.instance }} CPU 负载略高"
      description: ">> {{ $labels.instance }} CPU15分钟平均负载超过8\n   当前值: {{ $value }}"
 
  - alert: "磁盘读 I/O 超过 30MB/s"
    expr: irate(node_disk_read_bytes_total{device="sda"}[1m]) > 31457280
    for: 30s
    annotations:
      sumary: ">> 节点 {{ $labels.instance }} I/O 读超过30MB/s"
      description: ">> {{ $labels.instance }}I/O 每分钟读已超过 30MB/s,当前值: {{ $value }}"
 
  - alert: "磁盘写 I/O 超过 30MB/s"
    expr: irate(node_disk_written_bytes_total{device="sda"}[1m]) > 31457280
    for: 30s
    annotations:
      sumary: ">> 节点 {{ $labels.instance }} I/O 写超过30MB/s"
      description: ">> {{ $labels.instance }}I/O 每分钟写已超过 30MB/s,当前值: {{ $value }}"
 
# - alert: "网卡流出速率大于 10MB/s"
#   expr: (irate(node_network_transmit_bytes_total{device!~"lo"}[1m]) / 1000) > 10485760
#   for: 30s
#   annotations:
#     sumary: "服务器实例 {{ $labels.instance }} 网卡流量负载 告警通知"
#     description: "{{ $labels.instance }}网卡 {{ $labels.device }} 流量已经超过 10MB/s, 当前值: {{ $value }}"
 
  - alert: "CPU 使用率大于 90%"
    expr: 100 - ((avg by (instance,job,env)(irate(node_cpu_seconds_total{mode="idle"}[30s]))) *100) > 90
    for: 300s
    annotations:
      sumary: ">> 节点 {{ $labels.instance }} CPU 使用率过高"
      description: ">> {{ $labels.instance }}CPU 使用率已超过90%已持续5分钟, 当前值: {{ $value }}"

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!