一、Consul 介绍

Consul 是基于 GO 语言开发的开源工具,主要面向分布式,服务化的系统提供服务注册、服务发现和配置管理的功能。Consul 提供服务注册/发现、健康检查、Key/Value存储、多数据中心和分布式一致性保证等功能。

在没有使用 consul 服务自动发现的时候,我们需要频繁对 Prometheus 配置文件进行修改,无疑给运维人员带来很大的负担。引入consul之后,只需要在consul中维护监控组件配置,prometheus就能够动态发现配置了。

Prometheus 支持的多种服务发现机制

#Prometheus数据源的配置主要分为静态配置和动态发现, 常用的为以下几类:
1)static_configs: #静态服务发现
2)file_sd_configs: #文件服务发现
3)dns_sd_configs: DNS #服务发现
4)kubernetes_sd_configs: #Kubernetes 服务发现
5)consul_sd_configs: Consul #服务发现
...

#在监控kubernetes的应用场景中,频繁更新的pod,svc,等等资源配置应该是最能体现Prometheus监控目标自动发现服务的好处


二、环境、软件准备

本次演示环境,我是在虚拟机上安装 Linux 系统来执行操作,以下是安装的软件及版本:

  • Oracle VirtualBox: 5.1.20 r114628 (Qt5.6.2)
  • System: CentOS Linux release 7.3.1611 (Core)
  • Docker: 18.06.1-ce
  • Prometheus: v2.11.1
  • Consul: 1.6.1

注意:这里为了方便启动 Prometheus、Consul服务,我使用 Docker 方式启动,所以本机需要安装好 Docker 环境,这里忽略 Docker 的安装过程。其中 Prometheus 安装配置,可以参照之前文章 Prometheus 监控报警系统 AlertManager 之邮件告警,这里着重介绍一下如何启动并配置 Consul 并配置 Prometheus 基于 Consul 实现自动服务发现。


三、Consul 安装配置

Consul 安装很方便,​​官网​​ 提供各个系统版本二进制安装包,解压安装即可,同时也可以通过 Docker 来快速安装。

3.1、安装

yum安装

sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo
sudo yum -y install consul

源码安装

以 Linux 系统为例,源码安装并以开发模式启动一个单节点,下载最新版二进制安装包,解压启动即可。

$ wget https://releases.hashicorp.com/consul/1.6.1/consul_1.6.1_linux_amd64.zip
$ unzip consul_1.5.3_linux_amd64.zip

#数据持久化
mkdir -p /app/consul/data

#启动命令
$ nohup consul agent -server -data-dir=/app/consul/data -node=agent-one -bind=172.21.1.11 -bootstrap-expect=1 -client=0.0.0.0 -ui > /app/consul/consul.log 2>&1 &

  • 启动脚本

cat << EOF > /etc/systemd/system/consul.service
[Unit]
Description="HashiCorp Consul - A service mesh solution"
Documentation=https://www.consul.io/
Requires=network-online.target
After=network-online.target
ConditionFileNotEmpty=/etc/consul.d/consul.hcl

[Service]
EnvironmentFile=-/etc/consul.d/consul.env
User=consul
Group=consul
ExecStart=/usr/bin/consul agent -config-dir=/etc/consul.d/
ExecReload=/bin/kill --signal HUP $MAINPID
KillMode=process
KillSignal=SIGTERM
Restart=on-failure
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
EOF

Docker 安装

使用 Docker 启动 Consul 单节点服务,直接获取最新版官方镜像 consul:latest 命令如下:

$ docker run -d -p 8500:8500 --restart=always --name=consul consul:latest agent -server -bootstrap -ui -node=1 -client='0.0.0.0'

  • agent: 表示启动 Agent 进程。
  • server: 表示启动 Consul Server 模式
  • client: 表示启动 Consul Cilent 模式。
  • bootstrap: 表示这个节点是 Server-Leader ,每个数据中心只能运行一台服务器。技术角度上讲 Leader 是通过 Raft 算法选举的,但是集群第一次启动时需要一个引导 Leader,在引导群集后,建议不要使用此标志。
  • ui: 表示启动 Web UI 管理器,默认开放端口 8500,所以上面使用 Docker 命令把 8500 端口对外开放。
  • node: 节点的名称,集群中必须是唯一的,默认是该节点的主机名。
  • client: consul服务侦听地址,这个地址提供HTTP、DNS、RPC等服务,默认是127.0.0.1所以不对外提供服务,如果你要对外提供服务改成0.0.0.0
  • join: 表示加入到某一个集群中去。 如:-json=192.168.0.11。


3.2、配置

我们将 keygen 保存到一个单独的文件中,以便在丢失 consul 配置文件时进行备份

consul keygen > /etc/consul.d/consul.keygen

要配置 consul,我们将编辑 /etc/consul.d/consul.hcl 并添加以下配置:

datacenter = "marcsg"
bootstrap_expect = 1
server = true
data_dir = "/opt/consul"
log_level = "INFO"
client_addr = "0.0.0.0"
bind_addr = "172.21.1.11" #多网卡需指定
node_name = "consul"
leave_on_terminate = true
rejoin_after_leave = true
connect{
enabled = true
}
ui_config{
enabled = true
}
encrypt = "3EY9MGe2a1ElUNrsl5E6rHAtJEL7lqe+1CDZAyY8yTU="
  • datacenter: 将确定我们的数据中心名称,必须与我们的客户相同。
  • bootstrap_expect: 预期的领事服务器数量。
  • server: 将其设置为 true 将使我们的 consul 服务作为服务器运行,而不是作为客户端或代理运行。
  • data_dir: consul 的默认数据目录,它存储一些持久服务器值。
  • log_level: 在运行 consul 的命令时我们将如何处理日志。
  • client_addr: 我们的客户地址,如果我们想要更多地保护它,我们可以使用 - 子网划分或只允许来自确定的 ip 的连接。
  • bind_addr: 我们的服务器ip地址,如果只使用一个网卡则不需要
  • node_name: 我们的领事节点名称,如果未定义,它将使用机器 - 主机名。
  • leave_on_terminate: 终止实例时离开集群
  • rejoin_after_leave: 离开前加入集群
  • connect: 允许网状连接。
  • ui_config: 基于Web的用户界面。
  • encrypt: 我们之前生成的consul 密钥。

3.3、启动

# Change consul directories permissions
chown -R consul:consul /opt/consul
chown -R consul:consul /etc/consul.d

# Enable consul service
systemctl enable consul.service
systemctl start consul.service
systemctl status consul.service

现在,如果我们去​​http://172.21.1.11:8500​​​我们可以访问 consul 的 web ui 并查看我们的上/下服务,目前它只是 consul 服务器本身

Prometheus 通过 consul 实现自动服务发现_consul


四、API 注册服务到 Consul

接下来,我们要注册服务到 Consul 中,可以通过其提供的 API 标准接口来添加。consul主要是添加和删除命令,都是使用接口调用

#删除
$ curl -X PUT http://172.21.1.11:8500/v1/agent/service/deregister/node-exporter-172.21.1.22


#添加(以node:172.21.1.21 为例)
$ curl -X PUT -d '{"id": "node-exporter","name": "node-exporter-172.21.1.21","address": "172.21.1.21","port":"9100","tags": ["test"],"checks": [{"http": "http://172.21.1.21:9100/","interval": "5s"}]}' http://172.21.1.11:8500/v1/agent/service/register

执行完毕后,刷新一下 Consul Web 控制台页面,可以看到成功注册到 Consul 中。

批量添加可以使用下面脚本:

##hosts文件格式

node01 172.21.1.21
node02 172.21.1.22
$ cat registry.sh    # 脚本内容如下
#!/bin/bash
while read host_name host_addr
do
curl -X PUT -d '{"id": "'${host_name}'","name": "node-exporter","address": "'${host_addr}'","port":9100,"tags": ["dam"],"checks": [{"http": "http://'${host_addr}':9100/","interval": "15s"}]}' http://172.21.1.11:8500/v1/agent/service/register
done < hosts



五、配置 Prometheus 实现自动服务发现

现在 Consul 服务已经启动完毕,并成功注册了一个服务,接下来,我们需要配置 Prometheus 来使用 Consul 自动服务发现,目的就是能够将上边添加的服务自动发现到 Prometheus 的 Targets 中,增加 prometheus.yml 配置如下:

$ cat  /usr/local/prometheus/prometheus.yml
#类似下面格式 这个server为consul的地址,根据标签匹配组
- job_name: 'consul-prometheus'
consul_sd_configs:
- server: '172.21.1.11:8500'
services: [node-exporter]



#复杂一点,需要正则表达式
- job_name: dam-exporter
honor_labels: true
metrics_path: /metrics
scheme: http
consul_sd_configs:
- server: 172.21.1.11:8500
services: [node-exporter]
relabel_configs:
- source_labels: ['__meta_consul_tags']
target_label: 'product'
- source_labels: ['__meta_consul_dc']
target_label: 'idc'
- source_labels: ['product']
regex: ",node-exporter,"
action: keep

说明一下:这里需要使用 consul_sd_configs 来配置使用 Consul 服务发现类型,server 为 Consul 的服务地址,这里跟上边要对应上。 配置完毕后,重启 Prometheus 服务,此时可以通过 Prometheus UI 页面的 Targets 下查看是否配置成功。

可以看到,在 Targets 中能够成功的自动发现 Consul 中的 Services 信息,后期需要添加新的 Targets 时,只需要通过 API 往 Consul 中注册服务即可,Prometheus 就能自动发现该服务,是不是很方便。

不过,我们会发现有如下几个问题:

会发现 Prometheus 同时加载出来了默认服务 consul,这个是不需要的。

默认只显示 job 及 instance 两个标签,其他标签都默认属于 before relabeling 下,有些必要的服务信息,也想要在标签中展示,该如何操作呢?

如果需要自定义一些标签,例如 team、group、project 等关键分组信息,方便后边 alertmanager 进行告警规则匹配,该如何处理呢?

所有 Consul 中注册的 Service 都会默认加载到 Prometheus 下配置的 consul_prometheus 组,如果有多种类型的 exporter,如何在 Prometheus 中配置分配给指定类型的组,方便直观的区别它们?

以上问题,我们可以通过 Prometheus 配置中的 relabel_configs 参数来解决。


六、配置 relabel_configs 实现自定义标签及分类

我们先来普及一下 relabel_configs 的功能, Prometheus 允许用户在采集任务设置中,通过 relabel_configs 来添加自定义的 Relabeling 的额过程,来对标签进行指定规则的重写。 Prometheus 加载 Targets 后,这些 Targets 会自动包含一些默认的标签,Target 以 __ 作为前置的标签是在系统内部使用的,这些标签不会被写入到样本数据中。眼尖的会发现,每次增加 Target 时会自动增加一个 instance 标签,而 instance 标签的内容刚好对应 Target 实例的 __address__ 值,这是因为实际上 Prometheus 内部做了一次标签重写处理,默认 __address__ 标签设置为 <host>:<port> 地址,经过标签重写后,默认会自动将该值设置为 instance 标签,所以我们能够在页面看到该标签。

详细 relabel_configs 配置及说明可以参考 relabel_config 官网说明,这里我简单列举一下里面每个 relabel_action 的作用,方便下边演示。

replace: 根据 regex 的配置匹配 source_labels 标签的值(注意:多个 source_label 的值会按照 separator 进行拼接),并且将匹配到的值写入到 target_label 当中,如果有多个匹配组,则可以使用 ${1}, ${2} 确定写入的内容。如果没匹配到任何内容则不对 target_label 进行重新, 默认为 replace。

keep: 丢弃 source_labels 的值中没有匹配到 regex 正则表达式内容的 Target 实例

drop: 丢弃 source_labels 的值中匹配到 regex 正则表达式内容的 Target 实例

hashmod: 将 target_label 设置为关联的 source_label 的哈希模块

labelmap: 根据 regex 去匹配 Target 实例所有标签的名称(注意是名称),并且将捕获到的内容作为为新的标签名称,regex 匹配到标签的的值作为新标签的值

labeldrop: 对 Target 标签进行过滤,会移除匹配过滤条件的所有标签

labelkeep: 对 Target 标签进行过滤,会移除不匹配过滤条件的所有标签

接下来,我们来挨个处理上述问题。

问题一,我们可以配置 relabel_configs 来实现标签过滤,只加载符合规则的服务。以上边为例,可以通过过滤 __meta_consul_tags 标签为 test 的服务,relabel_config 向 Consul 注册服务的时候,只加载匹配 regex 表达式的标签的服务到自己的配置文件。修改 prometheus.yml 配置如下:

...
- job_name: 'consul-prometheus'
consul_sd_configs:
- server: '172.30.12.167:8500'
services: []
relabel_configs:
- source_labels: [__meta_consul_tags]
regex: .*test.*
action: keep


解释下,这里的 relabel_configs 配置作用为丢弃源标签中 __meta_consul_tags 不包含 test 标签的服务,__meta_consul_tags 对应到 Consul 服务中的值为 "tags": ["test"],默认 consul 服务是不带该标签的,从而实现过滤。重启 Prometheus 可以看到现在只获取了 node-exporter-172.30.12.167 这个服务了。

问题二和问题三可以归为一类,就是将系统默认标签或者用户自定义标签转换成可视化标签,方便查看及后续 Alertmanager 进行告警规则匹配分组。不过要实现给服务添加自定义标签,我们还得做一下修改,就是在注册服务时,将自定义标签信息添加到 Meta Data 数据中,具体可以参考 [这里](Consul Service - Agent HTTP API) 官网说明,下边来演示一下如何操作。

新建 consul-0.json 如下:

$ vim consul-0.json
{
"ID": "node-exporter",
"Name": "node-exporter-172.30.12.167",
"Tags": [
"test"
],
"Address": "172.30.12.167",
"Port": 9100,
"Meta": {
"app": "spring-boot",
"team": "appgroup",
"project": "bigdata"
},
"EnableTagOverride": false,
"Check": {
"HTTP": "http://172.30.12.167:9100/metrics",
"Interval": "10s"
},
"Weights": {
"Passing": 10,
"Warning": 1
}
}

说明一下:该 Json 文件为要注册的服务信息,同时往 Meta 信息中添加了 app=spring-boot,team=appgroup,project=bigdata 三组标签,目的就是为了方便告警分组使用。执行如下命令进行注册:

$ curl --request PUT --data @consul-0.json http://172.30.12.167:8500/v1/agent/service/register?replace-existing-checks=1

参数:

  • replace-existing-checks:对请求进行检查,删除没有健康检查的部分。以URL方式提供。
  • Name:指定服务名称,必选。
  • ID:指定服务ID,如果未指定,默认为服务名称。
  • Tags:指定标记,用于筛选。
  • Address:指定服务的地址。
  • Port:指定服务的端口。
  • Meta:指定 KV 元数据。
  • Weights:指定服务的权重。
  • Check:指定健康检测。具体设置请参考健康检测部分。

注册完毕,通过 Consul Web 管理页面可以查看到已注册成功,并且包含了 Meta 信息。


然后修改 prometheus.yml 配置如下:

...
- job_name: 'consul-prometheus'
consul_sd_configs:
- server: '172.30.12.167:8500'
services: []
relabel_configs:
- source_labels: [__meta_consul_tags]
regex: .*test.*
action: keep
- regex: __meta_consul_service_metadata_(.+)
action: labelmap


解释一下,增加的配置作用为匹配 __meta_consul_service_metadata_ 开头的标签,将捕获到的内容作为新的标签名称,匹配到标签的的值作为新标签的值,而我们刚添加的三个自定义标签,系统会自动添加 __meta_consul_service_metadata_app=spring-boot、__meta_consul_service_metadata_team=appgroup、__meta_consul_service_metadata_project=bigdata 三个标签,经过 relabel 后,Prometheus 将会新增 app=spring-boot、team=appgroup、project=bigdata 三个标签。重启 Prometheus 服务,可以看到新增了对应了三个自定义标签。

问题四,将自动发现的服务进行分类,本质上跟上边的处理方式一致,可以添加自定义的标签方式,通过标签来区分,二可以通过服务 Tag 来进行匹配来创建不同的类型 exporter 分组。这里我以第二种为例,通过给每个服务标记不同的 Tag,然后通过 relabel_configs 来进行匹配区分。我们来更新一下原 node-exporter-172.30.12.167 服务标签,同时注册一个其他类型 exporter 的服务如下:

$ vim consul-1.json
{
"ID": "node-exporter",
"Name": "node-exporter-172.30.12.167",
"Tags": [
"node-exporter"
],
"Address": "172.30.12.167",
"Port": 9100,
"Meta": {
"app": "spring-boot",
"team": "appgroup",
"project": "bigdata"
},
"EnableTagOverride": false,
"Check": {
"HTTP": "http://172.30.12.167:9100/metrics",
"Interval": "10s"
},
"Weights": {
"Passing": 10,
"Warning": 1
}
}

# 更新注册服务
$ curl --request PUT --data @consul-1.json http://172.30.12.167:8500/v1/agent/service/register?replace-existing-checks=1

$ vim consul-2.json
{
"ID": "cadvisor-exporter",
"Name": "cadvisor-exporter-172.30.12.167",
"Tags": [
"cadvisor-exporter"
],
"Address": "172.30.12.167",
"Port": 8080,
"Meta": {
"app": "docker",
"team": "cloudgroup",
"project": "docker-service"
},
"EnableTagOverride": false,
"Check": {
"HTTP": "http://172.30.12.167:8080/metrics",
"Interval": "10s"
},
"Weights": {
"Passing": 10,
"Warning": 1
}
}

# 注册服务

$ curl --request PUT --data @consul-2.json http://172.30.12.167:8500/v1/agent/service/register?replace-existing-checks=1


说明一下,我们更新了原 node-exporter-172.30.12.167 服务的标签为 node-exporter,同时注册一个新类型 cadvisor-exporter-172.30.12.167 服务,并设置标签为 cadvisor-exporter,以示区别。注册完毕,通过 Consul Web 控制台可以看到成功注册了这两个服务。

最后,我们修改 prometheus.yml 配置如下:

...
- job_name: 'consul-node-exporter'
consul_sd_configs:
- server: '172.30.12.167:8500'
services: []
relabel_configs:
- source_labels: [__meta_consul_tags]
regex: .*node-exporter.*
action: keep
- regex: __meta_consul_service_metadata_(.+)
action: labelmap

- job_name: 'consul-cadvisor-exproter'
consul_sd_configs:
- server: '172.30.12.167:8500'
services: []
relabel_configs:
- source_labels: [__meta_consul_tags]
regex: .*cadvisor-exporter.*
action: keep
- regex: __meta_consul_service_metadata_(.+)
action: labelmap


这里需要根据每种类型的 exporter 新增一个关联 job,同时 relabel_configs 中配置以 Tag 来做匹配区分。重启 Prometheus 服务,可以看到服务已经按照类型分类了,方便查看。


备份还原导入导出

备份与还原

需要备份的主要有两类数据:consul相关的配置文件、consul的​​服务器​​状态,采用下面的脚本备份就可以了:

ts=$(date +%Y%m%d%H%M%S)

# 备份配置文件
tar -czpf consul_config_$ts.tar.gz /etc/consul/config.json /etc/consul/consul.d

# 备份consul的服务器状态,注意由于该consul开启了ACL,执行consul snapshot save时必须带Management Token
# 关于consul ACL token的说明见上一篇"consul安全加固"
consul snapshot save --http-addr=http://172.21.1.11:8500 -token=b3a9bca3-6e8e-9678-ea35-ccb8fb272d42 consul_state_$ts.snap

# 查看一下生成的consul服务器状态文件
consul snapshot inspect consul_state_$ts.snap

最后将生成的​​consul_config_xxx.tar.gz​​​、​​consul_state_xxx.snap​​拷贝到其它服务器妥善存储。

还原也比较简单,采用下面的脚本就可以了:

# 还原配置文件
tar -xzpf consul_config_20180521145032.tar.gz -C /

# 还原consul服务器状态
consul snapshot restore --http-addr=http://172.21.1.11:8500 -token=b3a9bca3-6e8e-9678-ea35-ccb8fb272d42 consul_state_20180521145032.snap


KV存储的导入导出

consul直接提供命令对KV里存储的数据进行导入导出,如下:

$ ts=$(date +%Y%m%d%H%M%S)

# 导出所有kv键值对,注意最后一个参数是导出键值对的前缀,为空字符串说明要导出所有
$ consul kv export --http-addr=http://172.21.1.11:8500 -token=b3a9bca3-6e8e-9678-ea35-ccb8fb272d42 '' > consul_kv_$ts.json

# 查看下导出的json文件格式
$ cat consul_kv_$ts.json
[
{
"key": "xxxxxx",
"flags": 0,
"value": "yyyyyy"
},
{
"key": "xxxxxx2",
"flags": 0,
"value": "eyJ2ZXJzaW9uX3RpbWVzdGFtcCI6IC0xfQ=="
},
]

发现是每个键值对都是json数值中一项,其中key为键值对Key的名称,value为键值对Value的base64编码,使用​​base64 -d​​命令编码就可以看到原始的value值,如:

$ echo 'eyJ2ZXJzaW9uX3RpbWVzdGFtcCI6IC0xfQ==' | base64 -d
{"version_timestamp": -1}

导入就更简单了:

consul kv import --http-addr=http://172.21.1.11:8500 -token=b3a9bca3-6e8e-9678-ea35-ccb8fb272d42 @consul_kv_20180521150322.json

参考链接:https://blog.csdn.net/aixiaoyang168/article/details/103022342