Prometheus 通過 consul 分佈式集羣實現自動服務發現

1、Consul 介紹

Consul 是基於 GO 語言開發的開源工具,主要面向分佈式,服務化的系統提供服務註冊、服務發現和配置管理的功能。Consul 提供服務註冊/發現、健康檢查、Key/Value存儲、多數據中心和分佈式一致性保證等功能。Prometheus 通過 Consul 可以很方便的實現服務自動發現和維護,同時 Consul 支持分佈式集羣部署,將大大提高了穩定性,通過 Prometheus 跟 Consul 集羣二者結合起來,能夠高效的進行數據維護同時保證系統穩定。

2、環境、軟件準備

本次演示環境,我是在虛擬機上安裝 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,我使用 Docker 方式啓動,所以本機需要安裝好 Docker 環境,這裏忽略 Docker 的安裝過程。其中 Prometheus 安裝配置,可以參照之前文章 Prometheus 監控報警系統 AlertManager 之郵件告警,這裏着重介紹一下如何配置 Consul 分佈式集羣,以及使用 nginx 來負載均衡 Consul 集羣,最後分別驗證配置 Prometheus 基於 Consul 集羣來實現自動服務發現。

3、Consul 分佈式集羣搭建

Consul 單機安裝很方便,官網 提供各個系統版本二進制安裝包,解壓安裝即可,可以參照之前文章 Prometheus 通過 consul 實現自動服務發現 文章來安裝。這裏我們要搭建 Consul 分佈式集羣,既然是分佈式集羣,那麼肯定至少得部署到三臺機器上,組成一個集羣,苦於手上沒有那麼多的機器,我們只能在一臺機器上部署三個 Consul 服務來模擬 “分佈式” 集羣,Consul 默認以 client 方式啓動,這裏我們採用 Server 方式來啓動實例,通過不同的端口來區分不同的服務,集羣實例如下:

  • 172.30.12.100:8500 node1 leader
  • 172.30.12.100:8510 node2 follower
  • 172.30.12.100:8520 node2 follower

下載最新版二進制安裝包,解壓後並拷貝到 /usr/local/bin 目錄下

$ cd /root/prometheus/consul
$ wget https://releases.hashicorp.com/consul/1.6.1/consul_1.6.1_linux_amd64.zip
$ unzip consul_1.6.1_linux_amd64.zip  
$ mv consul /usr/local/bin

集羣啓動三個實例,這裏因爲在一臺機器上,直接使用 consul anget -server -bootstrap-expect=3 -data-dir=xxx...命令來啓動,稍顯麻煩且不直觀,這裏我們採用配置文件的方式分別啓動實例。配置 node1 實例,新建配置文件 consul01.json 如下 :

{
  "datacenter": "dc1",
  "data_dir": "/root/prometheus/consul/consul01",
  "log_level": "INFO",
  "server": true,
  "node_name": "node1",
  "ui": true,
  "bind_addr": "172.30.12.100",
  "client_addr": "0.0.0.0",
  "advertise_addr": "172.30.12.100",
  "bootstrap_expect": 3,
  "ports":{
    "http": 8500,
    "dns": 8600,
    "server": 8300,
    "serf_lan": 8301,
    "serf_wan": 8302
    }
}

說明一下參數:

  • datacenter:數據中心名稱
  • data_dir:數據存放本地目錄
  • log_level:輸出的日誌級別
  • server:以 server 身份啓動實例,不指定默認爲 client
  • node_name:節點名稱,集羣中每個 node 名稱不能重複,默認情況使用節點 hostname
  • ui:指定是否可以訪問 UI 界面
  • bind_addr:監聽的 IP 地址,指 Consul 監聽的地址,該地址必須能夠被集羣中的所有節點訪問,默認監聽 0.0.0.0
  • client_addr:客戶端監聽地址,0.0.0.0 表示所有網段都可以訪問
  • advertise_addr:集羣廣播地址
  • bootstrap_expect:集羣要求的最少成員數量
  • ports:該參數詳細配置各個服務端口,如果想指定其他端口,可以修改這裏。

後臺啓動一下 node1 Consul 服務,執行如下命令啓動:

nohup consul agent -config-dir=consul01.json > ./consul01.log 2>&1 &

查看服務啓動日誌會發現如下輸出:

ootstrap_expect > 0: expecting 3 servers
==> Starting Consul agent...
           Version: 'v1.6.1'
           Node ID: '18450af8-64a3-aa0f-2a6c-4c2fc6e35a4b'
         Node name: 'node1'
        Datacenter: 'dc1' (Segment: '<all>')
            Server: true (Bootstrap: false)
       Client Addr: [0.0.0.0] (HTTP: 8500, HTTPS: -1, gRPC: -1, DNS: 8600)
      Cluster Addr: 172.30.12.100 (LAN: 8301, WAN: 8302)
           Encrypt: Gossip: false, TLS-Outgoing: false, TLS-Incoming: false, Auto-Encrypt-TLS: false

==> Log data will now stream in as it occurs:

    2020/03/30 17:03:18 [INFO]  raft: Initial configuration (index=0): []
    2020/03/30 17:03:18 [INFO]  raft: Node at 172.30.12.100:8300 [Follower] entering Follower state (Leader: "")
    2020/03/30 17:03:18 [INFO] serf: EventMemberJoin: node1.dc1 172.30.12.100
    2020/03/30 17:03:18 [INFO] serf: EventMemberJoin: node1 172.30.12.100
    2020/03/30 17:03:18 [INFO] agent: Started DNS server 0.0.0.0:8600 (udp)
    2020/03/30 17:03:18 [INFO] consul: Handled member-join event for server "node1.dc1" in area "wan"
    2020/03/30 17:03:18 [INFO] consul: Adding LAN server node1 (Addr: tcp/172.30.12.100:8300) (DC: dc1)
    2020/03/30 17:03:18 [INFO] agent: Started DNS server 0.0.0.0:8600 (tcp)
    2020/03/30 17:03:18 [INFO] agent: Started HTTP server on [::]:8500 (tcp)
    2020/03/30 17:03:18 [INFO] agent: started state syncer
==> Consul agent running!
==> Newer Consul version available: 1.7.2 (currently running: 1.6.1)
    2020/03/30 17:03:23 [WARN]  raft: no known peers, aborting election
    2020/03/30 17:03:25 [ERR] agent: failed to sync remote state: No cluster leader

我們會看到類似 [ERR] agent: failed to sync remote state: No cluster leader 這樣的報錯,這是因爲目前還沒有任何其他實例加入,無法構成一個集羣。接下來,按照上邊的配置,啓動第二個實例 node2,並加入到 node1 集羣中,新建配置文件 consul02.json 如下 :

{
  "datacenter": "dc1",
  "data_dir": "/root/prometheus/consul/consul02",
  "log_level": "INFO",
  "server": true,
  "node_name": "node2",
  "ui": true, 
  "bind_addr": "172.30.12.100", 
  "client_addr": "0.0.0.0", 
  "advertise_addr": "172.30.12.100", 
  "bootstrap_expect": 3, 
  "ports":{
    "http": 8510,
    "dns": 8610,
    "server": 8310,
    "serf_lan": 8311,
    "serf_wan": 8312
    }
}

注意:由於這裏是一臺服務器上啓動了多個實例,所以必須修改各個服務端口號,否則會報錯,這裏新啓動的 Consul 服務端口爲 8510。啓動 node2 Consul 服務,並加入到 node1 Consul 服務中組成集羣,啓動命令如下:

nohup consul agent -config-dir=consul02.json -join 172.30.12.100:8301 > ./consul02.log 2>&1 &

啓動完畢後,此時會發現 node1 的日誌輸出中會打印如下日誌:

    2020/03/30 17:14:54 [INFO] serf: EventMemberJoin: node2 172.30.12.100
    2020/03/30 17:14:54 [INFO] consul: Adding LAN server node2 (Addr: tcp/172.30.12.100:8310) (DC: dc1)
    2020/03/30 17:14:54 [INFO] serf: EventMemberJoin: node2.dc1 172.30.12.100
    2020/03/30 17:14:54 [INFO] consul: Handled member-join event for server "node2.dc1" in area "wan"

可以看到 node2 成功加入到 node1 集羣,不過接下來它還會一直報錯 agent: failed to sync remote state: No cluster leader,這是爲什麼?日誌明明已經顯示 node2 成功加入了,怎麼還一直報錯呢?這是因爲我們創建時指定了 bootstrap_expect3,那麼就必須至少存在 3 個實例才能組成該 Consul 集羣。那麼,依照以上步驟,繼續啓動 node3 Consul 服務並添加到 node1 集羣中,新建配置文件 consul03.json 如下:

{
  "datacenter": "dc1",
  "data_dir": "/root/prometheus/consul/consul03",
  "log_level": "INFO",
  "server": true,
  "node_name": "node3",
  "ui": true, 
  "bind_addr": "172.30.12.100", 
  "client_addr": "0.0.0.0", 
  "advertise_addr": "172.30.12.100", 
  "bootstrap_expect": 3, 
  "ports":{
    "http": 8520,
    "dns": 8620,
    "server": 8320,
    "serf_lan": 8321,
    "serf_wan": 8322
    }
}

啓動 node3 Consul 實例命令如下:

nohup consul agent -config-dir=consul03.json -join 172.30.12.100:8301 > ./consul03.log 2>&1 &

此時,我們會發現 node1 日誌輸出中會打印如下日誌:

    2020/03/30 17:24:39 [INFO] serf: EventMemberJoin: node3 172.30.12.100
    2020/03/30 17:24:39 [INFO] consul: Adding LAN server node3 (Addr: tcp/172.30.12.100:8320) (DC: dc1)
    2020/03/30 17:24:39 [INFO] consul: Found expected number of peers, attempting bootstrap: 172.30.12.100:8300,172.30.12.100:8310,172.30.12.100:8320
    2020/03/30 17:24:39 [INFO] serf: EventMemberJoin: node3.dc1 172.30.12.100
    2020/03/30 17:24:39 [INFO] consul: Handled member-join event for server "node3.dc1" in area "wan"
    2020/03/30 17:24:40 [WARN]  raft: Heartbeat timeout from "" reached, starting election
    2020/03/30 17:24:40 [INFO]  raft: Node at 172.30.12.100:8300 [Candidate] entering Candidate state in term 2
    2020/03/30 17:24:40 [INFO]  raft: Election won. Tally: 2
    2020/03/30 17:24:40 [INFO]  raft: Node at 172.30.12.100:8300 [Leader] entering Leader state
    2020/03/30 17:24:40 [INFO]  raft: Added peer b7212be5-7d33-a5b6-0ca8-522f05ab9eeb, starting replication
    2020/03/30 17:24:40 [INFO]  raft: Added peer 0afe6fcb-3a07-e443-3802-f822ae9df422, starting replication
    2020/03/30 17:24:40 [INFO] consul: cluster leadership acquired
    2020/03/30 17:24:40 [INFO] consul: New leader elected: node1
    2020/03/30 17:24:40 [WARN]  raft: AppendEntries to {Voter 0afe6fcb-3a07-e443-3802-f822ae9df422 172.30.12.100:8320} rejected, sending older logs (next: 1)
    2020/03/30 17:24:40 [INFO]  raft: pipelining replication to peer {Voter 0afe6fcb-3a07-e443-3802-f822ae9df422 172.30.12.100:8320}
    2020/03/30 17:24:40 [WARN]  raft: AppendEntries to {Voter b7212be5-7d33-a5b6-0ca8-522f05ab9eeb 172.30.12.100:8310} rejected, sending older logs (next: 1)
    2020/03/30 17:24:40 [INFO]  raft: pipelining replication to peer {Voter b7212be5-7d33-a5b6-0ca8-522f05ab9eeb 172.30.12.100:8310}
    2020/03/30 17:24:40 [INFO] consul: member 'node1' joined, marking health alive
    2020/03/30 17:24:40 [INFO] consul: member 'node2' joined, marking health alive
    2020/03/30 17:24:40 [INFO] consul: member 'node3' joined, marking health alive
    2020/03/30 17:24:42 [INFO] agent: Synced node info

node3 成功加入到 node1 集羣,整個集羣已選出 node1 作爲新的 Leader,此時集羣狀態爲 health,那麼整個 “分佈式” 集羣就搭建完畢了。此時,瀏覽器訪問以下任意一個 http://127.0.0.1:8500http://127.0.0.1:8510http://127.0.0.1:8520 地址,均可打開 Consul Web 管理頁面。
consul-1
consul-2
consul-3
我們也可以通過命令行查看集羣狀態、以及集羣成員狀態。

# 查看集羣狀態
$ consul operator raft list-peers
Node   ID                                    Address             State     Voter  RaftProtocol
node1  18450af8-64a3-aa0f-2a6c-4c2fc6e35a4b  172.30.12.100:8300  leader    true   3
node2  b7212be5-7d33-a5b6-0ca8-522f05ab9eeb  172.30.12.100:8310  follower  true   3
node3  0afe6fcb-3a07-e443-3802-f822ae9df422  172.30.12.100:8320  follower  true   3
# 查看集羣成員狀態
$ consul members
Node   Address             Status  Type    Build  Protocol  DC   Segment
node1  172.30.12.100:8301  alive   server  1.6.1  2         dc1  <all>
node2  172.30.12.100:8311  alive   server  1.6.1  2         dc1  <all>
node3  172.30.12.100:8321  alive   server  1.6.1  2         dc1  <all>

4、配置 Prometheus 實現自動服務發現

接下來,我們配置 Prometheus 來使用 Consul 集羣來實現自動服務發現,目的就是能夠將添加的服務自動發現到 Prometheus 的 Targets 中,詳細配置說明可以參考之前文章 Prometheus 通過 consul 實現自動服務發現 中的配置,在修改 Prometheus 配置之前,我們需要往 Consul 集羣中註冊一些數據。首先,我們註冊一個 node-exporter-172.30.12.100 的服務,新建 consul-1.json 如下:

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

# 調用 API 註冊服務
$ curl --request PUT --data @consul-1.json http://172.30.12.100:8500/v1/agent/service/register?replace-existing-checks=1

然後,我們再註冊一個 cadvisor-exporter-172.30.12.100 的服務,新建 consul-2.json 並執行如下命令:

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

# 調用 API 註冊服務
$ curl --request PUT --data @consul-2.json http://172.30.12.100:8500/v1/agent/service/register?replace-existing-checks=1

註冊完畢,通過 Consul Web 管理頁面可以查看到兩服務已註冊成功。注意:這裏需要啓動 node-exporter 及 cadvisor-exporter,否則即使註冊成功了,健康檢測也不通過,在後邊 Prometheus 上服務發現後狀態也是不健康的,這裏我就不在演示了,可以直接拿 Docker 啓動二者即可。

consul-4

接下來,我們修改 Prometheus 配置如下:

...
  - job_name: 'consul-node-exporter'
    consul_sd_configs:
      - server: '172.30.12.100: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.100:8510'
        services: []
      - server: '172.30.12.100:8520'
        services: []
    relabel_configs:
      - source_labels: [__meta_consul_tags]
        regex: .*cadvisor-exporter.*
        action: keep
      - action: labelmap
        regex: __meta_consul_service_metadata_(.+)
...

啓動一下 Prometheus 服務,通過 web 界面看下 Target 中是否能夠發現 Consul 中註冊的服務。
prometheus-consul-5

可以看到,妥妥沒有問題,這裏 consul-node-exporter 我配置指向了 node1 Consul 服務地址,consul-cadvisor-exproter 配置指向了 node2、node3 Consul 服務,二者都能夠正確發現之前註冊的服務,因爲 Consul 集羣數據是保持同步的,無論連接哪一個節點,都能夠獲取到註冊的服務信息,同理,我們也可以指定 consul_sd_configs 分別指向集羣所有節點,這樣即使某個節點掛掉,也不會影響 Prometheus 從 Consul 集羣其他節點獲取註冊的服務,從而實現服務的高可用。

5、配置 nginx 負載均衡 Consul 集羣

雖然我們可以將整個 Consul 集羣 IP 添加到 Prometheus 的配置中,從而實現 Prometheus 從 Consul 集羣獲取註冊的服務,實現服務的高可用,但是這裏有個問題,如果 Consul 集羣節點新增或者減少,那麼 Prometheus 配置也得跟着修改了,這樣不是很友好,我們可以在 Consul 集羣前面使用 nginx 反向代理將請求負載均衡到後端 Consul 集羣各節點服務上,這樣 Prometheus 只需要配置代理地址即可,後期不需要更改了。

這裏爲了方便,nginx 也使用容器方式啓動,新增配置文件 default.conf 如下:

upstream service_consul {
    server 172.30.12.100:8500;
    server 172.30.12.100:8510;
    server 172.30.12.100:8520;
    ip_hash;
}

server {
    listen       80;
    server_name  172.30.12.100;
    index  index.html index.htm;    

    #access_log  /var/log/nginx/host.access.log  main;

    location / {
        add_header Access-Control-Allow-Origin *;
        proxy_next_upstream http_502 http_504 error timeout invalid_header;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_pass http://service_consul;    
    }

    access_log /var/log/consul.access.log;
    error_log /var/log/consul.error.log;    

    error_page  404              /404.html;

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}

Docker 啓動 nginx 服務,啓動命令如下:

$ docker run --name nginx-consul -p 80:80 -v /root/prometheus/nginx/default.conf:/etc/nginx/conf.d/default.conf:ro -d nginx

這裏就沒啥好說的了,啓動完成後,可以瀏覽器訪問一下 http://172.30.12.100/ui ,妥妥也是沒有問題的。

consul-6

最後,我們修改一下 Prometheus 配置文件,將 server 指向 172.30.12.100 即可,修改配置如下:

...
  - job_name: 'consul-node-exporter'
    consul_sd_configs:
      - server: '172.30.12.100'
        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.100'
        services: []
    relabel_configs:
      - source_labels: [__meta_consul_tags]
        regex: .*cadvisor-exporter.*
        action: keep
      - action: labelmap
        regex: __meta_consul_service_metadata_(.+)
...

重啓 prometheus 服務,驗證也是妥妥沒問題的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章