Consul+Consul-Template+Nginx+Tomcat 半自动化服务发现

关于 Consul 的安装:https://www.consul.io/docs/install/index.html

关于 Consul-Template 的安装:https://github.com/hashicorp/consul-template#installation

关于 Consul 集群搭建:https://book-consul-guide.vnzmi.com/06_setup_cluster.html

Consul 的 C/S 架构,Consul Agent 安装后可以作为 Client,也可以作为 Server,所谓什么 Raft 算法的作用在于多个 Server 集群选取 master,使用的话不需要关注这些细节。

启动 Consul

测试使用两台,一台作为 Server,一台作为 Client。

在 Server 端,运行:

1
consul agent -server -bootstrap-expect 1 -data-dir /tmp/consul -node=s1 -bind=192.168.221.192 -ui-dir ./consul_ui/ -rejoin -config-dir=/etc/consul.d/ -client 0.0.0.0

启动过程中,可以看到如下输出:

1
2
3
4
5
6
7
2018/02/27 10:06:07 [ERR] agent: failed to sync remote state: No cluster leader
2018/02/27 10:06:08 [WARN] raft: Heartbeat timeout from "" reached, starting election
2018/02/27 10:06:08 [INFO] raft: Node at 192.168.221.192:8300 [Candidate] entering Candidate state in term 10
2018/02/27 10:06:08 [INFO] raft: Election won. Tally: 1
2018/02/27 10:06:08 [INFO] raft: Node at 192.168.221.192:8300 [Leader] entering Leader state
2018/02/27 10:06:08 [INFO] consul: cluster leadership acquired
2018/02/27 10:06:08 [INFO] consul: New leader elected: s1

开始时没有 consul leader,随后可以看到通过 raft 算法选举了 leader,在此只有一个 server 节点,所以这台 server 就成为了 leader。

在 Client 端,运行:

1
consul agent -data-dir /tmp/consul -node=c1 -bind=192.168.221.193 -config-dir=/etc/consul.d/ -join 192.168.221.192

在此需要先在 Server 端执行,因为 Client 的 -join 参数制定后找不到 Server 会报错。

执行后再 Server 端看到,已经加入了 Client。

1
2
3
4
[root@localhost ~]# consul members
Node Address Status Type Build Protocol DC Segment
s1 192.168.221.192:8301 alive server 1.0.6 2 dc1 <all>
c1 192.168.221.193:8301 alive client 1.0.6 2 dc1 <default>

注册服务

在此我们注册三个服务,分别为两个 tomcat 和一个 nginx。

启动 consul 时,在启动命令里指定了配置文件目录为 /etc/consul.d,所以在此目录下通过配置文件注册 service.

建立 tomcat.json 注册 tomcat 服务:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
{
"services": [
{
"id": "tomcat_1",
"name": "tomcat",
"tags": [
"primary"
],
"address": "192.168.221.193",
"port": 8110,
"checks": [
{
"http": "http://192.168.221.193:8110",
"interval": "10s",
"timeout": "1s",
"method": "GET"
}
]
},
{
"id": "tomcat_2",
"name": "tomcat",
"tags": [
"secondary"
],
"address": "192.168.221.193",
"port": 8111,
"checks": [
{
"http": "http://192.168.221.193:8111",
"interval": "10s",
"timeout": "1s",
"method": "GET"
}
]
}
]
}

若 name 重名,则 id 必须唯一,tags 标签指定方便管理。

建立 nginx.json 注册 nginx 服务:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
"service":
{
"id": "nginx",
"name": "nginx",
"tags": [
"primary"
],
"address": "192.168.221.193",
"port": 80,
"checks": [
{
"http": "http://192.168.221.193:80",
"interval": "10s",
"timeout": "1s",
"method": "GET"
}
]
}
}

定义好配置文件后来更新服务:

1
consul reload

随后可以看到 consul 的输出显示该 Agent 将载入配置定义并注册到服务目录:

1
2
3
4
5
6
2018/02/27 12:51:53 [INFO] agent: Synced service "nginx"
2018/02/27 12:51:53 [INFO] agent: Synced service "tomcat_1"
2018/02/27 12:51:53 [INFO] agent: Synced service "tomcat_2"
2018/02/27 12:51:57 [INFO] agent: Synced check "service:nginx"
2018/02/27 12:51:59 [INFO] agent: Synced check "service:tomcat_2"
2018/02/27 12:52:02 [INFO] agent: Synced check "service:tomcat_1"

配置更新

来看目的:实时监测 tomcat 的健康状态,并实施更新 Nginx 代理配置文件。

这里需要用到 consul-template。

建立 nginx.conf.ctmpl 文件,写入模板:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

worker_processes 1;

events {
worker_connections 1024;
}

http {
include mime.types;
default_type application/octet-stream;

sendfile on;
#tcp_nopush on;

#keepalive_timeout 0;
keepalive_timeout 65;

#gzip on;

{{service "tomcat" "passing"}} {{$name := .Name}}
upstream {{$name}} {
{{range $service}}
server {{.Address}}:{{.Port}};
{{end}}
session_sticky;
check interval=3000 rise=2 fall=5 timeout=1000 type=http;
check_http_send "HEAD / HTTP/1.0\r\n\r\n";
check_http_expect_alive http_2xx http_3xx;
}

server {
listen 80;
server_name localhost;

location / {
root html;
index index.html index.htm;
proxy_pass http://srm;
client_max_body_size 50m;
}

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

这里只载入了 tomcat 服务,如果要把所有服务载入,可以遍历服务添加代理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

...
{{range services}} {{$name := .Name}} {{$service := service .Name}}
upstream {{$name}} {
{{range $service}}
server {{.Address}}:{{.Port}};
{{end}}
session_sticky;
check interval=3000 rise=2 fall=5 timeout=1000 type=http;
check_http_send "HEAD / HTTP/1.0\r\n\r\n";
check_http_expect_alive http_2xx http_3xx;
} {{end}}
...
{{range services}} {{$name := .Name}}
location /{{$name}} {
proxy_pass http://{{$name}};
}
{{end}}

模板语法是 golang 的模板语法,和其它后端模板语法很像。

{{service "tomcat" "passing"}} 这部分采用的是 tomcat 服务,passing 表示只采用健康的 service。

随后执行:

1
consul-template  -consul-addr 192.168.221.192:8500 -template="nginx.conf.ctmpl:/usr/local/nginx/conf/nginx.conf:/usr/local/nginx/sbin/nginx -s reload" -once

将会生成 nginx.conf 配置文件至 nginx 配置文件目录,并会执行 nginx -s reload 进行重载。

上述命令只能生成一次,官方推荐的做法是写入一个配置文件中。

建立 nginx.hcl 文件,内容如下:

1
2
3
4
5
6
7
8
9
consul {
address = "192.168.221.192:8500"
}

template {
source = "nginx.conf.ctmpl"
destination = "/usr/local/nginx/conf/nginx.conf"
command = "/usr/local/nginx/sbin/nginx -s reload"
}

然后执行 consul-template -config nginx.hcl,就可以运行为一个服务。

当两个 tomcat 正常时,可以看到配置文件如下:

服务正常的配置

手动把一个 tomcat 关闭,可以看到 consul 的输出中报失败:

检查到错误服务

而此时的 nginx 配置文件已经被更新,可以看到内容如下:

配置文件被更新

至此就完成了目的,注册服务 –> 服务健康检查 –> 实时更新服务配置。

后记

这个使用姿势是很奇葩的使用姿势,正确的使用姿势应该配合 DNS 查询 API 和 HTTP API 等去做更进一步的集成,在 DNS 查询层级做负载均衡和高可用的配置处理。服务的注册现在是手动注册,结合 Docker 可以使用 Registrator,不适用 Docker 就要写脚本去检测服务并注册了。

因而题为“半自动化”,这个半应该再加个半,为”半半自动化”为宜。w(゚Д゚)w

参考

-EOF-