监控神器Prometheus(4)

监控神器Prometheus(4)

标签: prometheus  

Prometheus 自动发现

本文主旨是为了让大家明白Prometheus监控系统在多云下如实现何自动发现,至于使用哪种方式更为便捷,取决于你自己的环境。

Let’s Go!

为什么要用自动发现?

在基于云(IaaS或者CaaS)的基础设施环境中用户可以像使用水、电一样按需使用各种资源(计算、网络、存储)。按需使用就意味着资源的动态性,这些资源可以随着需求规模的变化而变化。例如在AWS中就提供了专门的AutoScall服务,可以根据用户定义的规则动态地创建或者销毁EC2实例,从而使用户部署在AWS上的应用可以自动的适应访问规模的变化。

这种按需的资源使用方式对于监控系统而言就意味着没有了一个固定的监控目标,所有的监控对象(基础设施、应用、服务)都在动态的变化。对于Nagias这类基于Push模式传统监控软件就意味着必须在每一个节点上安装相应的Agent程序,并且通过配置指向中心的Nagias服务,受监控的资源与中心监控服务器之间是一个强耦合的关系,要么直接将Agent构建到基础设施镜像当中,要么使用一些自动化配置管理工具(如Ansible、Chef)动态的配置这些节点。当然实际场景下除了基础设施的监控需求以外,我们还需要监控在云上部署的应用,中间件等等各种各样的服务。要搭建起这样一套中心化的监控系统实施成本和难度是显而易见的。

而对于Prometheus这一类基于Pull模式的监控系统,显然也无法继续使用的static_configs的方式静态的定义监控目标。而对于Prometheus而言其解决方案就是引入一个中间的代理人(服务注册中心),这个代理人掌握着当前所有监控目标的访问信息,Prometheus只需要向这个代理人询问有哪些监控目标控即可, 这种模式被称为服务发现。

在不同的场景下,会有不同的东西扮演者代理人(服务发现与注册中心)这一角色。比如在AWS公有云平台或者OpenStack的私有云平台中,由于这些平台自身掌握着所有资源的信息,此时这些云平台自身就扮演了代理人的角色。Prometheus通过使用平台提供的API就可以找到所有需要监控的云主机。在Kubernetes这类容器管理平台中,Kubernetes掌握并管理着所有的容器以及服务信息,那此时Prometheus只需要与Kubernetes打交道就可以找到所有需要监控的容器以及服务对象。Prometheus还可以直接与一些开源的服务发现工具进行集成,例如在微服务架构的应用程序中,经常会使用到例如Consul这样的服务发现注册软件,Promethues也可以与其集成从而动态的发现需要监控的应用服务实例。除了与这些平台级的公有云、私有云、容器云以及专门的服务发现注册中心集成以外,Prometheus还支持基于DNS以及文件的方式动态发现监控目标,从而大大的减少了在云原生,微服务以及云模式下监控实施难度。

如上所示,展示了Push系统和Pull系统的核心差异。相较于Push模式,Pull模式的优点可以简单总结为以下几点:

  • 只要Exporter在运行,你可以在任何地方(比如在本地),搭建你的监控系统;

  • 你可以更容易的查看监控目标实例的健康状态,并且可以快速定位故障;

  • 更利于构建DevOps文化的团队;

  • 松耦合的架构模式更适合于云原生的部署环境。

基于文件的服务发现

在Prometheus支持的众多服务发现的实现方式中,基于文件的服务发现是最通用的方式。这种方式不需要依赖于任何的平台或者第三方服务。对于Prometheus而言也不可能支持所有的平台或者环境。通过基于文件的服务发现方式下,Prometheus会定时从文件中读取最新的Target信息,因此,你可以通过任意的方式将监控Target的信息写入即可。 用户可以通过JSON或者YAML格式的文件,定义所有的监控目标。例如,在下面的JSON文件中分别定义了3个采集任务,以及每个任务对应的Target列表:

[
  {
    "targets": [ "localhost:8080"],
    "labels": {
      "env": "localhost",
      "job": "cadvisor"
    }
  },
  {
    "targets": [ "localhost:9104" ],
    "labels": {
      "env": "prod",
      "job": "mysqld"
    }
  },
  {
    "targets": [ "localhost:9100"],
    "labels": {
      "env": "prod",
      "job": "node"
    }
  }
]

同时还可以通过为这些实例添加一些额外的标签信息,例如使用env标签标示当前节点所在的环境,这样从这些实例中采集到的样本信息将包含这些标签信息,从而可以通过该标签按照环境对数据进行统计。

创建Prometheus配置文件/data/prometheus/conf/prometheus-file-sd.yml,并添加以下内容:

global:
  scrape_interval: 15s
  scrape_timeout: 10s
  evaluation_interval: 15s
scrape_configs:
- job_name: 'file_ds'
  file_sd_configs:
  - files:
    - targets.json

这里定义了一个基于file_sd_configs的监控采集任务,其中模式的任务名称为file_ds。在JSON文件中可以使用job标签覆盖默认的job名称,此时启动Prometheus服务:

prometheus --config.file=/etc/prometheus/prometheus-file-sd.yml --storage.tsdb.path=/data/prometheus

在Prometheus UI的Targets下就可以看到当前从targets.json文件中动态获取到的Target实例信息以及监控任务的采集状态,同时在Labels列下会包含用户添加的自定义标签:

Prometheus默认每5m重新读取一次文件内容,当需要修改时,可以通过refresh_interval进行设置,例如:

- job_name: 'file_ds'
  file_sd_configs:
  - refresh_interval: 1m
    files:
    - targets.json

通过这种方式,Prometheus会自动的周期性读取文件中的内容。当文件中定义的内容发生变化时,不需要对Prometheus进行任何的重启操作。

这种通用的方式可以衍生了很多不同的玩法,比如与自动化配置管理工具(Ansible)结合、与Cron Job结合等等。 对于一些Prometheus还不支持的云环境,比如国内的阿里云、腾讯云等也可以使用这种方式通过一些自定义程序与平台进行交互自动生成监控Target文件,从而实现对这些云环境中基础设施的自动化监控支持。

基于Consul发现

服务发现与Relabel

通过服务发现的方式,管理员可以在不重启Prometheus服务的情况下动态的发现需要监控的Target实例信息。

如上图所示,对于线上环境我们可能会划分为:dev, stage, prod不同的集群。每一个集群运行多个主机节点,每个服务器节点上运行一个Node Exporter实例。Node Exporter实例会自动注册到Consul中,而Prometheus则根据Consul返回的Node Exporter实例信息动态的维护Target列表,从而向这些Target轮询监控数据。

然而,如果我们可能还需要:

  • 按照不同的环境dev, stage, prod聚合监控数据?
  • 对于研发团队而言,我可能只关心dev环境的监控数据,如何处理?
  • 如果为每一个团队单独搭建一个Prometheus Server。那么如何让不同团队的Prometheus Server采集不同的环境监控数据?

面对以上这些场景下的需求时,我们实际上是希望Prometheus Server能够按照某些规则(比如标签)从服务发现注册中心返回的Target实例中有选择性的采集某些Exporter实例的监控数据。

接下来,我们实验如何通过Prometheus强大的Relabel机制来实现以上这些具体的目标。

Prometheus的Relabeling机制

在Prometheus所有的Target实例中,都包含一些默认的Metadata标签信息。可以通过Prometheus UI的Targets页面中查看这些实例的Metadata标签的内容:

默认情况下,当Prometheus加载Target实例完成后,这些Target时候都会包含一些默认的标签:

  • __address__:当前Target实例的访问地址<host>:<port>
  • __scheme__:采集目标服务访问地址的HTTP Scheme,HTTP或者HTTPS
  • __metrics_path__:采集目标服务访问地址的访问路径
  • __param_<name>:采集任务目标服务的中包含的请求参数

上面这些标签将会告诉Prometheus如何从该Target实例中获取监控数据。除了这些默认的标签以外,我们还可以为Target添加自定义的标签,例如,在“基于文件的服务发现”小节中的示例中,我们通过JSON配置文件,为Target实例添加了自定义标签env,如下所示该标签最终也会保存到从该实例采集的样本数据中:

node_cpu{cpu="cpu0",env="prod",instance="localhost:9100",job="node",mode="idle"}

一般来说,Target以作为前置的标签是在系统内部使用的,因此这些标签不会被写入到样本数据中。不过这里有一些例外,例如,我们会发现所有通过Prometheus采集的样本数据中都会包含一个名为instance的标签,该标签的内容对应到Target实例的address__。 这里实际上是发生了一次标签的重写处理。

这种发生在采集样本数据之前,对Target实例的标签进行重写的机制在Prometheus被称为Relabeling。

Prometheus允许用户在采集任务设置中通过relabel_configs来添加自定义的Relabeling过程。

使用replace/labelmap重写标签

Relabeling最基本的应用场景就是基于Target实例中包含的metadata标签,动态的添加或者覆盖标签。例如,通过Consul动态发现的服务实例还会包含以下Metadata标签信息:

  • __meta_consul_address:consul地址
  • __meta_consul_dc:consul服务所在的数据中心
  • __meta_consulmetadata:服务的metadata
  • __meta_consul_node:consul服务node节点的信息
  • __meta_consul_service_address:服务访问地址
  • __meta_consul_service_id:服务ID
  • __meta_consul_service_port:服务端口
  • __meta_consul_service:服务名称
  • __meta_consul_tags:服务包含的标签信息

在默认情况下,从Node Exporter实例采集上来的样本数据如下所示:

node_cpu{cpu="cpu0",instance="localhost:9100",job="node",mode="idle"} 93970.8203125

我们希望能有一个额外的标签dc可以表示该样本所属的数据中心:

node_cpu{cpu="cpu0",instance="localhost:9100",job="node",mode="idle", dc="dc1"} 93970.8203125

在每一个采集任务的配置中可以添加多个relabel_config配置,一个最简单的relabel配置如下:

scrape_configs:
  - job_name: node_exporter
    consul_sd_configs:
      - server: localhost:8500
        services:
          - node_exporter
    relabel_configs:
    - source_labels:  ["__meta_consul_dc"]
      target_label: "dc"

该采集任务通过Consul动态发现Node Exporter实例信息作为监控采集目标。在上一小节中,我们知道通过Consul动态发现的监控Target都会包含一些额外的Metadata标签,比如标签__meta_consul_dc表明了当前实例所在的Consul数据中心,因此我们希望从这些实例中采集到的监控样本中也可以包含这样一个标签,例如:

node_cpu{cpu="cpu0",dc="dc1",instance="172.21.0.6:9100",job="consul_sd",mode="guest"}

这样可以方便的根据dc标签的值,根据不同的数据中心聚合分析各自的数据。

在这个例子中,通过从Target实例中获取__meta_consul_dc的值,并且重写所有从该实例获取的样本中。

完整的relabel_config配置如下所示:

# The source labels select values from existing labels. Their content is concatenated
# using the configured separator and matched against the configured regular expression
# for the replace, keep, and drop actions.
[ source_labels: '[' <labelname> [, ...] ']' ]

# Separator placed between concatenated source label values.
[ separator: <string> | default = ; ]

# Label to which the resulting value is written in a replace action.
# It is mandatory for replace actions. Regex capture groups are available.
[ target_label: <labelname> ]

# Regular expression against which the extracted value is matched.
[ regex: <regex> | default = (.*) ]

# Modulus to take of the hash of the source label values.
[ modulus: <uint64> ]

# Replacement value against which a regex replace is performed if the
# regular expression matches. Regex capture groups are available.
[ replacement: <string> | default = $1 ]

# Action to perform based on regex matching.
[ action: <relabel_action> | default = replace ]

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

repalce操作允许用户根据Target的Metadata标签重写或者写入新的标签键值对,在多环境的场景下,可以帮助用户添加与环境相关的特征维度,从而可以更好的对数据进行聚合。

除了使用replace以外,还可以定义action的配置为labelmap。与replace不同的是,labelmap会根据regex的定义去匹配Target实例所有标签的名称,并且以匹配到的内容为新的标签名称,其值作为新标签的值。

例如,在监控Kubernetes下所有的主机节点时,为将这些节点上定义的标签写入到样本中时,可以使用如下relabel_config配置:

- job_name: 'kubernetes-nodes'
  kubernetes_sd_configs:
  - role: node
  relabel_configs:
  - action: labelmap
    regex: __meta_kubernetes_node_label_(.+)

而使用labelkeep或者labeldrop则可以对Target标签进行过滤,仅保留符合过滤条件的标签,例如:

relabel_configs:
  - regex: label_should_drop_(.+)
    action: labeldrop

该配置会使用regex匹配当前Target实例的所有标签,并将符合regex规则的标签从Target实例中移除。labelkeep正好相反,会移除那些不匹配regex定义的所有标签。

使用keep/drop过滤Target实例

在上一部分中我们介绍了Prometheus的Relabeling机制,并且使用了replace/labelmap/labelkeep/labeldrop对标签进行管理。而本节开头还提到过第二个问题,使用中心化的服务发现注册中心时,所有环境的Exporter实例都会注册到该服务发现注册中心中。而不同职能(开发、测试、运维)的人员可能只关心其中一部分的监控数据,他们可能各自部署的自己的Prometheus Server用于监控自己关心的指标数据,如果让这些Prometheus Server采集所有环境中的所有Exporter数据显然会存在大量的资源浪费。如何让这些不同的Prometheus Server采集各自关心的内容?答案还是Relabeling,relabel_config的action除了默认的replace以外,还支持keep/drop行为。例如,如果我们只希望采集数据中心dc1中的Node Exporter实例的样本数据,那么可以使用如下配置:

scrape_configs:
  - job_name: node_exporter
    consul_sd_configs:
      - server: localhost:8500
        services:
          - node_exporter
    relabel_configs:
    - source_labels:  ["__meta_consul_dc"]
      regex: "dc1"
      action: keep

当action设置为keep时,Prometheus会丢弃source_labels的值中没有匹配到regex正则表达式内容的Target实例,而当action设置为drop时,则会丢弃那些source_labels的值匹配到regex正则表达式内容的Target实例。可以简单理解为keep用于选择,而drop用于排除。

使用hashmod计算source_labels的Hash值

当relabel_config设置为hashmod时,Prometheus会根据modulus的值作为系数,计算source_labels值的hash值。例如:

scrape_configs
- job_name: 'file_ds'
  relabel_configs:
    - source_labels: [__address__]
      modulus:       4
      target_label:  tmp_hash
      action:        hashmod
  file_sd_configs:
  - files:
    - targets.json

根据当前Target实例address的值以4作为系数,这样每个Target实例都会包含一个新的标签tmp_hash,并且该值的范围在1~4之间,查看Target实例的标签信息,可以看到如下的结果,每一个Target实例都包含了一个新的tmp_hash值:

在第6章的“Prometheus高可用”小节中,正是利用了Hashmod的能力在Target实例级别实现对采集任务的功能分区的:

scrape_configs:
  - job_name: some_job
    relabel_configs:
    - source_labels: [__address__]
      modulus:       4
      target_label:  __tmp_hash
      action:        hashmod
    - source_labels: [__tmp_hash]
      regex:         ^1$
      action:        keep

这里需要注意的是,如果relabel的操作只是为了产生一个临时变量,以作为下一个relabel操作的输入,那么我们可以使用__tmp作为标签名的前缀,通过该前缀定义的标签就不会写入到Target或者采集到的样本的标签中。

Consul架构

上图是官网提供的一个事例系统图,图中的Server是consul服务端高可用集群,Client是consul客户端。consul客户端不保存数据,客户端将接收到的请求转发给响应的Server端。Server之间通过局域网或广域网通信实现数据一致性。每个Server或Client都是一个consul agent。

Consul集群间使用了GOSSIP协议通信和raft一致性算法。上面这张图涉及到了很多术语:

  • Agent——agent是一直运行在Consul集群中每个成员上的守护进程。通过运行consul agent来启动。agent可以运行在client或者server模式。指定节点作为client或者server是非常简单的,除非有其他agent实例。所有的agent都能运行DNS或者HTTP接口,并负责运行时检查和保持服务同步。

  • Client——一个Client是一个转发所有RPC到server的代理。这个client是相对无状态的。client唯一执行的后台活动是加入LAN gossip池。这有一个最低的资源开销并且仅消耗少量的网络带宽。

  • Server——一个server是一个有一组扩展功能的代理,这些功能包括参与Raft选举,维护集群状态,响应RPC查询,与其他数据中心交互WAN gossip和转发查询给leader或者远程数据中心。

  • DataCenter——虽然数据中心的定义是显而易见的,但是有一些细微的细节必须考虑。例如,在EC2中,多个可用区域被认为组成一个数据中心。我们定义数据中心为一个私有的,低延迟和高带宽的一个网络环境。这不包括访问公共网络,但是对于我们而言,同一个EC2中的多个可用区域可以被认为是一个数据中心的一部分。

  • Consensus——一致性,使用Consensus来表明就leader选举和事务的顺序达成一致。为了以容错方式达成一致,一般有超过半数一致则可以认为整体一致。Consul使用Raft实现一致性,进行leader选举,在consul中的使用bootstrap时,可以进行自选,其他server加入进来后bootstrap就可以取消。

  • Gossip——Consul建立在Serf的基础之上,它提供了一个用于多播目的的完整的gossip协议。Serf提供成员关系,故障检测和事件广播。Serf是去中心化的服务发现和编制的解决方案,节点失败侦测与发现,具有容错、轻量、高可用的特点。

  • LAN Gossip——它包含所有位于同一个局域网或者数据中心的所有节点。

  • WAN Gossip——它只包含Server。这些server主要分布在不同的数据中心并且通常通过因特网或者广域网通信。

  • RPC——远程过程调用。这是一个允许client请求server的请求/响应机制。

在每个数据中心,client和server是混合的。一般建议有3-5台server。这是基于有故障情况下的可用性和性能之间的权衡结果,因为越多的机器加入达成共识越慢。然而,并不限制client的数量,它们可以很容易的扩展到数千或者数万台。

同一个数据中心的所有节点都必须加入gossip协议。这意味着gossip协议包含一个给定数据中心的所有节点。这服务于几个目的:第一,不需要在client上配置server地址。发现都是自动完成的。第二,检测节点故障的工作不是放在server上,而是分布式的。这使的故障检测相比心跳机制有更高的可扩展性。第三:它用来作为一个消息层来通知事件,比如leader选举发生时。

每个数据中心的server都是Raft节点集合的一部分。这意味着它们一起工作并选出一个leader,一个有额外工作的server。leader负责处理所有的查询和事务。作为一致性协议的一部分,事务也必须被复制到所有其他的节点。因为这一要求,当一个非leader得server收到一个RPC请求时,它将请求转发给集群leader。

server节点也作为WAN gossip Pool的一部分。这个Pool不同于LAN Pool,因为它是为了优化互联网更高的延迟,并且它只包含其他Consul server节点。这个Pool的目的是为了允许数据中心能够以low-touch的方式发现彼此。这使得一个新的数据中心可以很容易的加入现存的WAN gossip。因为server都运行在这个pool中,它也支持跨数据中心请求。当一个server收到来自另一个数据中心的请求时,它随即转发给正确数据中想一个server。该server再转发给本地leader。

这使得数据中心之间只有一个很低的耦合,但是由于故障检测,连接缓存和复用,跨数据中心的请求都是相对快速和可靠的。

Consul集群安装环境

此处启动的是单实例多端口,如果你是多实例,请自行更改相关配置

ServerName IP Addr & Port Consul Roles
server1 172.26.42.229:8500 Server1
server2 172.26.42.229:28500 Server2
server3 172.26.42.229:38500 Server3
node-exporter 172.26.42.229:9100 Node-Exporter

二进制安装Consul1.6.0

配置为systemd启动, Docker方式部署链接(https://juejin.im/post/5d4289e1e51d45620b21c34a)

# 下载consul
wget https://releases.hashicorp.com/consul/1.6.0/consul_1.6.0_linux_amd64.zip

# 解压安装
unzip consul_${CONSUL_VERSION}_linux_amd64.zip
chown root:root consul
mv consul /usr/local/bin/
consul --version

# 启用自动补全
consul -autocomplete-install
complete -C /usr/local/bin/consul consul

# 创建用户和目录
useradd -M -s /sbin/nologin consul
mkdir /data/consul/server{1..3}/{data,config}
chown -R consul.consul /data/consul/

# 配置Systemd

# consul-server1
cat > /lib/systemd/system/consul-server1.service << EOF
[Unit]
Description="consul server1"
Requires=network-online.target
After=network-online.target

[Service]
User=consul
Group=consul
ExecStart=/usr/local/bin/consul agent -config-dir=/data/consul/server1/config
ExecReload=/usr/local/bin/consul reload
KillMode=process
Restart=on-failure
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
EOF

# consul-server2
cat > /lib/systemd/system/consul-server2.service << EOF
[Unit]
Description="consul server2"
Requires=network-online.target
After=network-online.target

[Service]
User=consul
Group=consul
ExecStart=/usr/local/bin/consul agent -config-dir=/data/consul/server2/config
ExecReload=/usr/local/bin/consul reload
KillMode=process
Restart=on-failure
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
EOF

# consul-server3
cat > /lib/systemd/system/consul-server3.service << EOF
[Unit]
Description="consul server3"
Requires=network-online.target
After=network-online.target

[Service]
User=consul
Group=consul
ExecStart=/usr/local/bin/consul agent -config-dir=/data/consul/server3/config
ExecReload=/usr/local/bin/consul reload
KillMode=process
Restart=on-failure
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
EOF

创建Server{1-3}配置文件

注意,consul acl bootstrap只能执行一次.

# 生成密钥
CONSUL_KEY=`consul keygen`

# 生成http_acl_token,写入config.json,
consul acl bootstrap
export CONSUL_HTTP_TOKEN='your_token'

# node_id 一定不可以重复

# 创建server1配置文件
cat > /data/consul/server1/config/config.json << EOF
{
  "datacenter": "prometheus",
  "bind_addr":"172.26.42.229",
  "log_level": "INFO",
  "node_id":"09d82408-bc4f-49e0-5555-61ef1d4842f7",
  "node_name": "server1",
  "data_dir":"/data/consul/server1/data",
  "server": true,
  "bootstrap_expect": 3,
  "encrypt": "${CONSUL_KEY}",
  "ui":true,
  "client_addr":"0.0.0.0",
  "retry_join":["172.26.42.229:8301","172.26.42.229:28301","172.26.42.229:38301"],
  "ports": {
     "http": 8500,
     "dns": 8600,
     "serf_lan":8301,
     "serf_wan":8302,
     "server":8300,
     "grpc":8400
  },
  "acl": {
    "enabled": true,
    "default_policy": "deny",
    "down_policy": "extend-cache",
    "tokens":{
        "master":"${CONSUL_HTTP_TOKEN}",
        "agent":"${CONSUL_HTTP_TOKEN}"
    }
  }
}
EOF

# 创建server2配置文件
cat > /data/consul/server2/config/config.json << EOF
{
  "datacenter": "prometheus",
  "bind_addr":"172.26.42.229",
  "log_level": "INFO",
  "node_id":"613ccd6e-68d1-3bbd-6666-3cbc450f019d",
  "node_name": "server2",
  "data_dir":"/data/consul/server2/data",
  "server": true,
  "bootstrap_expect": 3,
  "encrypt": "${CONSUL_KEY}",
  "ui":true,
  "client_addr":"0.0.0.0",
  "retry_join":["172.26.42.229:8301","172.26.42.229:28301","172.26.42.229:38301"],
  "ports": {
     "http": 28500,
     "dns": 28600,
     "serf_lan":28301,
     "serf_wan":28302,
     "server":28300,
     "grpc":28400
  },
  "acl": {
    "enabled": true,
    "default_policy": "deny",
    "down_policy": "extend-cache",
    "tokens":{
        "master":"${CONSUL_HTTP_TOKEN}",
        "agent":"${CONSUL_HTTP_TOKEN}"
    }
  }
}
EOF

# 创建server3配置文件
cat > /data/consul/server3/config/config.json << EOF
{
  "datacenter": "prometheus",
  "bind_addr":"172.26.42.229",
  "log_level": "INFO",
  "node_id":"d8a09ffd-7ccb-84bd-7777-8d8b7a01951e",
  "node_name": "server3",
  "data_dir":"/data/consul/server3/data",
  "server": true,
  "bootstrap_expect": 3,
  "encrypt": "${CONSUL_KEY}",
  "ui":true,
  "client_addr":"0.0.0.0",
  "retry_join":["172.26.42.229:8301","172.26.42.229:28301","172.26.42.229:38301"],
  "ports": {
     "http": 38500,
     "dns": 38600,
     "serf_lan":38301,
     "serf_wan":38302,
     "server":38300,
     "grpc":38400
  },
  "acl": {
    "enabled": true,
    "default_policy": "deny",
    "down_policy": "extend-cache",
    "tokens":{
        "master":"${CONSUL_HTTP_TOKEN}",
        "agent":"${CONSUL_HTTP_TOKEN}"
    }
  }
}
EOF

启动服务

systemctl enable consul-server1 consul-server2 consul-server3
systemctl stop consul-server1 consul-server2 consul-server3
systemctl restart consul-server1 consul-server2 consul-server3
systemctl status consul-server1 consul-server2 consul-server3

查看集群

返回空节点是正常的,因为开启了ACL,所以访问的时候需要加入token

# 环境变量
cat >> /etc/profile << EOF
export CONSUL_HTTP_TOKEN='your_token'
EOF

# consul members  --token='your_token'
Node     Address              Status  Type    Build  Protocol  DC          Segment
server1  172.26.42.229:8301   alive   server  1.6.0  2         prometheus  <all>
server2  172.26.42.229:28301  alive   server  1.6.0  2         prometheus  <all>
server3  172.26.42.229:38301  alive   server  1.6.0  2         prometheus  <all>

# 验证集群UI
在页面http://127.0.0.1:8500/ui/prometheus/acls/tokens 输入配置中的 master token,再刷新界面可以在services和nodes中查看到信息

# 验证API,通过在header中增加x-consul-token则可返回节点列表
curl http://127.0.0.1:8500/v1/catalog/nodes -H 'x-consul-token: ${CONSUL_HTTP_TOKEN}'

将Consul日志加入Syslog

此处为可选项,如果你需要单独将日志输出到ELK,那么此项配置非常有必要,因为默认的日志都打到syslog中了。

# 创建目录&赋权
mkdir -p /var/log/consul/
chown -R syslog.syslog /var/log/consul/

# 创建日志配置文件
cat >/etc/rsyslog.d/consul.conf <<EOF
local0.* /var/log/consul/consul.log
EOF

# 修改默认配置文件中的以下内容
vim /etc/rsyslog.d/50-default.conf

# 变更前
*.*;auth,authpriv.none          -/var/log/syslog

# 变更后
*.*;auth,authpriv.none,local0.none          -/var/log/syslog

# 重启rsyslog让配置生效。
$ systemctl restart rsyslog

# 创建日志轮循规则
$ cat >/etc/logrotate.d/consul <<EOF
/var/log/consul/*log {
missingok
compress
notifempty
daily
rotate 5
create 0600 root root
}
EOF

# 在Systemd启动脚本中加入`-syslog`参数
sed -i 's@ExecStart=/usr/local/bin/consul agent@ExecStart=/usr/local/bin/consul agent -syslog@g' /lib/systemd/system/consul-server{1..3}.service

# 重启服务
systemctl restart consul-server1 consul-server2 consul-server3

# 查看输出日志,对于加入ELK的配置就不过多描述了,如果想了解,加入我们的qq群与微信群咨询相关解决方案。
tail -f /var/log/consul/consul.log

FAQ : 如果集群加入失败 或 提示:Error retrieving members: Unexpected response code: 403 (ACL not found),toekn格式不对,需要重新检查集群节点tokens配置,格式为:'55eca91c-b5f7-e82d-7777-dba7637e8888',然后删除/data/consul/server{1..3}/data/目录下的数据,重启服务即可。

Prometheus集成Consul

# 基于AWS EC2 REDIS 发现规则

cat >> /data/prometheus/conf/prometheus.yml <<EOF
  - job_name: 'ec2_exporter'
      consul_sd_configs:
          - server: 172.26.42.229:8500
          token: '${CONSUL_HTTP_TOKEN}'
          services: ['node_exporter']
      relabel_configs:
          - source_labels: [__address__]
          regex: 172.26.42.229:8300
          action: drop
          - source_labels: [__meta_consul_tags]
          regex: ".*,prod,.*"
          replacement: prod
          action: replace
          target_label: env

  - job_name: 'redis_exporter'
      consul_sd_configs:
          - server: 172.26.42.229:28500
          token: '${CONSUL_HTTP_TOKEN}'
          services: ['redis_exporter']
      relabel_configs:
          - source_labels: [__address__]
          regex: 172.26.42.229:28300
          action: drop
          - source_labels: [__meta_consul_tags]
          regex: ".*,prod,.*"
          replacement: prod
          action: replace
          target_label: env
  - job_name: 'mysql_exporter'
      consul_sd_configs:
          - server: 172.26.42.229:38500
          token: '${CONSUL_HTTP_TOKEN}'
          services: ['mysql_exporter']
      relabel_configs:
          - source_labels: [__address__]
          regex: 172.26.42.229:38300
          action: drop
          - source_labels: [__meta_consul_tags]
          regex: ".*,prod,.*"
          replacement: prod
          action: replace
          target_label: env
EOF


# registered nginx01 service to consul1
curl -H "x-consul-token: ${CONSUL_HTTP_TOKEN}" -X PUT -d '{"ID": "node_exporter01", "Name": "node_exporter", "Address": "172.26.42.229", "Port": 9100, "Tags": ["prod"], "EnableTagOverride": false}' \
http://172.26.42.229:28500/v1/agent/service/register

# registered redis01 to consul2
curl -H "x-consul-token: ${CONSUL_HTTP_TOKEN}" -X PUT -d '{"ID": "redis_exporter01", "Name": "redis_exporter", "Address": "172.26.42.229", "Port": 9121, "Tags": ["prod"], "EnableTagOverride": false}' \
http://172.26.42.229:28500/v1/agent/service/register

# registered mysql01 to consul2
curl -H "x-consul-token: ${CONSUL_HTTP_TOKEN}" -X PUT -d '{"ID": "mysql_exporter01", "Name": "mysql_exporter", "Address": "172.26.42.229", "Port": 9105, "Tags": ["prod"], "EnableTagOverride": false}' \
http://172.26.42.229:38500/v1/agent/service/register 

# delete nginx01 service 
curl -X PUT -H "x-consul-token: ${CONSUL_HTTP_TOKEN}" http://172.26.42.229:8500/v1/agent/service/deregister/node_exporter01

# delete redis01 service
curl -X PUT -H "x-consul-token: ${CONSUL_HTTP_TOKEN}" http://172.26.42.229:28500/v1/agent/service/deregister/redis_exporter01

# delete mysql_exporter01 service
curl -X PUT -H "x-consul-token: ${CONSUL_HTTP_TOKEN}" http://172.26.42.229:28500/v1/agent/service/deregister/mysql_exporter01

使用Consul-template动态配置服务

# 安装Consul-template 下载地址:https://releases.hashicorp.com/consul-template/
wget https://releases.hashicorp.com/consul-template/0.22.0/consul-template_0.22.0_linux_amd64.zip
unzip consul-template_0.22.0_linux_amd64.zip
mv consul-template /usr/local/bin/
# 查看版本
consul-template -v
consul-template v0.22.0 (6cae10fe)

#常用参数的作用:
-consul-auth=<username[:password]>     # 设置基本的认证用户名和密码。
-consul-addr=<address>                 # 设置Consul实例的地址。
-max-stale=<duration>                  # 查询过期的最大频率,默认是1s。
-dedup                                 # 启用重复数据删除,当许多consul template实例渲染一个模板的时候可以降低consul的负载。
-consul-ssl                            # 使用https连接Consul。
-consul-ssl-verify                     # 通过SSL连接的时候检查证书。
-consul-ssl-cert                       # SSL客户端证书发送给服务器。
-consul-ssl-key                        # 客户端认证时使用的SSL/TLS私钥。
-consul-ssl-ca-cert                    # 验证服务器的CA证书列表。
-consul-token=<token>                  # 设置Consul API的token。
-syslog                                # 把标准输出和标准错误重定向到syslog,syslog的默认级别是local0。
-syslog-facility=<facility>            # 设置syslog级别,默认是local0,必须和-syslog配合使用。
-template=<template>                   # 增加一个需要监控的模板,格式是:'templatePath:outputPath(:command)',多个模板则可以设置多次。
-wait=<duration>                       # 当呈现一个新的模板到系统和触发一个命令的时候,等待的最大最小时间。如果最大值被忽略,默认是最小值的4倍。
-retry=<duration>                      # 当在和consul api交互的返回值是error的时候,等待的时间,默认是5s。
-config=<path>                         # 配置文件或者配置目录的路径。
-pid-file=<path>                       # PID文件的路径。
-log-level=<level>                     # 设置日志级别,可以是"debug","info", "warn" (default), and "err"。
-dry                                   # Dump生成的模板到标准输出,不会生成到磁盘。
-once                                  # 运行consul-template一次后退出,不以守护进程运行

# 在conf目录下创建1个nginx.json的配置文件
cat >> /data/consul/server1/config/nginx.json <<EOF
{
    "service":{
        "name":"nginx",
        "tags":[
            "web"
        ],
        "port":80,
        "check":{
            "http":"http://127.0.0.1:80",
            "interval":"10s"
        },
        "token":"233b604b-b92e-48c8-a253-5f11514e4b50"
    }
}
EOF

# 热加载配置文件
consul reload
# 验证服务是否注册成功
curl -H "x-consul-token: ${CONSUL_HTTP_TOKEN}" http://172.26.42.229:8500/v1/catalog/service/nginx | python -m json.tool
[
    {
        "Address": "172.26.42.229",
        "CreateIndex": 21233,
        "Datacenter": "prometheus",
        "ID": "09d82408-bc4f-49e0-4208-61ef1d4842f7",
        "ModifyIndex": 21233,
        "Node": "server1",
        "NodeMeta": {
            "consul-network-segment": ""
        },
        "ServiceAddress": "",
        "ServiceConnect": {},
        "ServiceEnableTagOverride": false,
        "ServiceID": "nginx",
        "ServiceKind": "",
        "ServiceMeta": {},
        "ServiceName": "nginx",
        "ServicePort": 80,
        "ServiceProxy": {
            "MeshGateway": {}
        },
        "ServiceTags": [
            "web"
        ],
        "ServiceWeights": {
            "Passing": 1,
            "Warning": 1
        },
        "TaggedAddresses": {
            "lan": "172.26.42.229",
            "wan": "172.26.42.229"
        }
    }
]

# 创建模板
cat > tmpltest.ctmpl << EOF
{{range services}}
{{.Name}}
{{range .Tags}}
{{.}}{{end}}
{{end}}
EOF

# 调用模板渲染
consul-template -consul-addr 172.26.42.229:8500 -template "/data/consul/consul-template/conf/tmpltest.ctmpl:result" -once

# 查看模板渲染的输出结果,返回的结果:consul是系统自带的服务,nginx是刚才注册的服务,Tags是web
cat result

consul

nginx

web

# 创建nginx模板文件
cat >> /data/consul/consul-template/conf/nginx.conf.ctmpl << EOF
{{range services}} {{$name := .Name}} {{$service := service .Name}}
upstream {{$name}} {
  zone upstream-{{$name}} 64k;
  {{range $service}}server {{.Address}}:{{.Port}} max_fails=3 fail_timeout=60 weight=1;
  {{else}}server 127.0.0.1:65535; # force a 502{{end}}
} {{end}}

server {
  listen 80 default_server;

  location / {
    root /usr/share/nginx/html/;
    index index.html;
  }

  location /stub_status {
    stub_status;
  }

{{range services}} {{$name := .Name}}
  location /{{$name}} {
    proxy_pass http://{{$name}};
  }
{{end}}
}
EOF

# 调用模板文件生成Nginx配置文件
consul-template -consul-addr 172.26.42.229:8500 -template "/data/consul/consul-template/conf/nginx.conf.ctmpl:nginx.conf" -once

# 为了更加安全,token从环境变量里读取,使用CONSUL_TOKEN和VAULT_TOKEN。强烈建议你不要把token放到未加密的文本配置文件中。
# 创建一个nginx.hcl文件
cat >> nginx.hcl << EOF
consul {
address = "172.26.42.229:8500"
}

template {
source = "/data/consul/consul-template/conf/nginx.conf.ctmpl"
destination = "/etc/nginx/conf/conf.d/default.conf"
command = "service nginx reload"
}
EOF

# 执行渲染命令
consul-template -config "nginx.hcl:test.out"

# 同时渲染多个template并放出后台启动
cat > consul_temp.sh << EOF
#!/bin/bash
#prom 
cd /data/consul/consul-template/ && nohup /usr/local/bin/consul-template -config=/data/consul/consul-template/hcl_conf/ & 2>&1
EOF

结语:

相比于直接使用静态配置和基于文件发现,是不利于云环境以及k8s环境的,因为我们大多时候更多监控对象都是动态的。因此,通过服务发现,使得Prometheus相比于其他传统监控解决方案更适用于云以及k8s环境下的监控需求。 对于Consul的使用方法还有很多,大同小异,主要还是根据你自己的环境与架构来设计,如果你有什么不懂的地方,可以扫描下面的二维码,加入我们来一起探讨云原生生态相关技术。 下一篇幅,讲讲Prometheus的数据持久化该如何做。

微信公众号

扫描下面的二维码关注我的微信公众号,在微信公众帐号中回复'群'即可加入到我的"kubernetes技术栈"讨论群里面共同学习。

wechat-account-qrcode

「真诚赞赏,手有余香」

青牛踏雪

请我喝杯咖啡?

使用微信扫描二维码完成支付

相关文章