因此,欢迎使用与Consul一起使用Consul进行服务侦听的系列文章的第二部分。 在第二篇文章中,我们将研究如何使用许多与Consul相关的工具,从而使服务发现以及其他一些相关功能变得更加容易。
对于本系列的其他文章,您可以在这里查看:
回顾架构以及我们将要做什么
在本文中,我们将扩展在上一篇文章中创建的设置。 在那篇文章中,我们使用Docker和Consul创建了以下微服务架构:
因此,如果您还没有这样做,请遍历该文章中的步骤,并确保您具有可以使用Docker Swarm,Docker Compose并具有正确的Docker网络设置的设置。 要检查所有设置是否正确,请检查以下命令的输出:
# See the previous article for the definitions of these commands
#
# First, check Consul. If consul isn't up, the swarm won't come up or work.
$ . dm-env nb-consul
$ docker ps --format '{{ .ID }}\t{{ .Image }}\t{{ .Command }}\t{{ .Names}}'
b5d55e6df248 progrium/consul "/bin/start -server -" clever_panini
# if consul isn't up, make sure to start it, before trying any of the swarm
# commands.
$ . dm-env nb1 --swarm
$ docker ps -a --format '{{ .ID }}\t{{ .Image }}\t{{ .Command }}\t{{ .Names}}'
bf2000882dcc progrium/consul "/bin/start -ui-dir /" nb1/consul_agent_1
a1bc26eef516 progrium/consul "/bin/start -ui-dir /" nb2/consul_agent_2
eb0d1c0cc075 progrium/consul "/bin/start -ui-dir /" nb3/consul_agent_3
d27050901dc1 swarm:latest "/swarm join --advert" nb3/swarm-agent
f66738e086b8 swarm:latest "/swarm join --advert" nb2/swarm-agent
0ac59ef54207 swarm:latest "/swarm join --advert" nb1/swarm-agent
17fc5563d018 swarm:latest "/swarm manage --tlsv" nb1/swarm-agent-master
# this should at least list the swarm master, swarm agents, and the consul agents.
# if not, see previous article on how to setup your environment.
# the last thing we need to check is our network. When the swarm master is selected
# run the following command:
$ docker network ls | grep -i my-net
8ecec72e7b68 my-net overlay
# if it shows an overlay network with my-net we're all set.
如果您看到一些前端或后端服务,最好停止并删除它们。 这样,您可以按照本文其余部分的命令进行操作。
那么我们将在本文中做什么。 好吧,我们将向您展示如何使用Consul Universe中的以下两个工具:
- Consultemplate :通过Consultemplate,您可以侦听Consul的事件(例如,添加服务时),并基于这些更新,重写并重新加载配置文件。 我们将使用它来使用一组最新的健康服务自动更新基于HAProxy的反向代理的配置。
- EnvConsul :使用Envconsul,您可以轻松地直接从Consul读取环境变量,而不必在启动Docker容器时手动将其传入。 我们将展示如何与本文中使用的服务一起使用。
Consultemplate,docker和HAProxy
在先前的设置中,我们创建了一个架构,如上图所示。 通过使用Consul的DNS功能进行服务发现和一些基本的故障转移,这已经非常有效,但是我们必须依靠DNS和套接字超时来确定服务何时变得不正常。 尽管此方法有效,但它并不是那么可靠,仅提供了一些简单的故障转移和负载平衡功能。 在这种情况下,我们要做的是创建以下内容:
因此,我们将遇到以下情况:
- 用户将通过HAProxy访问我们的前端服务。
- HAProxy会将请求转发给健康服务之一。
- 前端服务要访问后端服务。 它还通过HAProxy组件执行此操作。
Consul将确保每当服务向Consul注册自身时,它都会更新HAProxy的配置。
它是如何工作的
如果您已经为这些文章( https://github.com/josdirksen/next-build-consul )提取了存储库,则还可以找到一个名为extra / consul-template的目录。 在该目录中是我们将用于此示例的HAProxy。 我还将它添加到了dockerhub( https://hub.docker.com/r/josdirksen/demo-haproxy/)中 ,这使得使用起来更加容易。 在介绍如何定义模板之前,让我们先看一下该Docker映像的功能。 最简单的方法是查看启动脚本:
#!/bin/bash
HAPROXY="/etc/haproxy"
PIDFILE="/var/run/haproxy.pid"
CONFIG_FILE=${HAPROXY}/haproxy.cfg
cd "$HAPROXY"
haproxy -f "$CONFIG_FILE" -p "$PIDFILE" -D -st $(cat $PIDFILE)
/usr/local/bin/consul-template -consul=${CONSUL_ADDRESS} -config=/consul.hcl
没什么特别的,当我们启动该容器时, consul-template将与指定的配置文件一起运行会发生什么情况。 请注意,我们需要提供CONSUL_ADDRESS环境变量,以将consul-template指向我们的代理之一或consul服务器。 有趣的东西在consul.hcl文件中:
max_stale = "10m"
retry = "10s"
wait = "5s:20s"
template {
source = "/etc/haproxy/haproxy.template"
destination = "/etc/haproxy/haproxy.cfg"
command = "/hap.sh"
perms = 0600
}
此文件仅供参考。 基本上发生的事情是,只要Consul中发生任何更改,模板haproxy.template就会针对Consul中的信息运行,结果将替换haproxy.cfg文件。 之后,运行hap.sh命令以重新加载配置。 为了完整起见, hap.sh文件如下所示:
#!/bin/bash
haproxy -f /etc/haproxy/haproxy.cfg -p /var/run/haproxy.pid -D -st $(cat /var/run/haproxy.pid)
这样做是为了以干净的方式重新加载配置文件。 那么,模板中包含什么? 让我们看一下haproxy.template :
global
log 127.0.0.1 local0
log 127.0.0.1 local1 notice
chroot /var/lib/haproxy
user haproxy
group haproxy
defaults
log global
mode http
option httplog
option dontlognull
balance roundrobin
timeout connect 5000
timeout client 50000
timeout server 50000
errorfile 400 /etc/haproxy/errors/400.http
errorfile 403 /etc/haproxy/errors/403.http
errorfile 408 /etc/haproxy/errors/408.http
errorfile 500 /etc/haproxy/errors/500.http
errorfile 502 /etc/haproxy/errors/502.http
errorfile 503 /etc/haproxy/errors/503.http
errorfile 504 /etc/haproxy/errors/504.http
listen stats
bind *:8001
stats enable
stats uri /
stats auth admin:123123q
stats realm HAProxy\ Statistics
frontend nb-front
bind *:1080
mode http
default_backend nb-frontend
frontend nb-back
bind *:1081
mode http
default_backend nb-backend
backend nb-frontend
balance roundrobin{{range service "frontend-service"}}
server {{.Node}} {{.Address}}:{{.Port}} check{{end}}
backend nb-backend
balance roundrobin{{range service "backend-service"}}
server {{.Node}} {{.Address}}:{{.Port}} check{{end}}
前几行没有意思。 在定义前端和后端元素时变得很有趣。 我们在这里说的是,我们指定haproxy将在端口1080侦听对前端的请求,并将这些请求转发到后端nb-frontend中定义的服务。 在此元素中,我们配置一个模板。 在此示例中,我们使用Consul命名为frontend-service的所有服务,并为每个服务写下一个条目。 因此,当我们在端口1080上调用haproxy时,它将把请求转发到在Consul中注册的名称为frontend-service的任何服务。 我们对backend-service进行完全相同的操作。
运行!
因此,既然我们知道它是如何工作的,该是运行该代理的时候了。 我们为此定义了一个docker-compose文件,它将在节点nb1中运行HAProxy:
version: '2'
services:
nb-proxy:
image: josdirksen/demo-haproxy
container_name: nb-haproxy
ports:
- 1080:1080
- 1081:1081
environment:
- CONSUL_ADDRESS=192.168.99.106:8500
- "constraint:node==nb1"
networks:
default:
external:
name: my-net
执行以下命令以执行此操作:
# make sure we're at the swarm master
$ . dm-env nb1 --swarm
# in the root of the nextbuild-consul project
$ docker-compose -f ./docker-compose-haproxy.yml up -d
Creating nb-haproxy
$ docker ps -a --format '{{ .ID }}\t{{ .Image }}\t{{ .Command }}\t{{ .Names}}'
dc28caa4c420 josdirksen/demo-haproxy "/startup.sh" nb1/nb-haproxy
bf2000882dcc progrium/consul "/bin/start -ui-dir /" nb1/consul_agent_1
a1bc26eef516 progrium/consul "/bin/start -ui-dir /" nb2/consul_agent_2
eb0d1c0cc075 progrium/consul "/bin/start -ui-dir /" nb3/consul_agent_3
d27050901dc1 swarm:latest "/swarm join --advert" nb3/swarm-agent
f66738e086b8 swarm:latest "/swarm join --advert" nb2/swarm-agent
0ac59ef54207 swarm:latest "/swarm join --advert" nb1/swarm-agent
17fc5563d018 swarm:latest "/swarm manage --tlsv" nb1/swarm-agent-master
在我的设置中,我可以看到HAProxy已启动。 但是,由于我们没有任何后端或前端服务在运行,因此haproxy的配置应反映出以下几点:
$ docker exec -ti nb1/nb-haproxy cat /etc/haproxy/haproxy.cfg | tail -n 15
frontend nb-front
bind *:1080
mode http
default_backend nb-frontend
frontend nb-back
bind *:1081
mode http
default_backend nb-backend
backend nb-frontend
balance roundrobin
backend nb-backend
balance roundrobin
而且,如果我们打开端口1080(用于前端)或1081(用于后端API),则会看到HAProxy发出的错误。
这是可以预料的,因为我们没有任何前端或后端服务正在运行。 因此,让HAProxy可以使用许多后端服务:
$ docker-compose -f ./docker-compose-backend.yml up -d
Creating Backend2
Creating Backend3
Creating Backend1
现在,我们应该在HAProxy后面运行许多后端。 首先让我们检查Consul是否更新了我们的HAProxy实例:
$ docker exec -ti nb1/nb-haproxy cat /etc/haproxy/haproxy.cfg | tail -n 8
backend nb-frontend
balance roundrobin
backend nb-backend
balance roundrobin
server a1bc26eef516 10.0.9.7:8081 check
server bf2000882dcc 10.0.9.9:8081 check
server eb0d1c0cc075 10.0.9.8:8081 check
酷,对! Haproxy现在定义了三个服务。 这应该允许我们调用http://nb1.local:1081,并且它应该返回一种后端服务的API:
如果刷新几次,您应该看到它在各种服务中循环。
如果我们杀死了一个,我们应该会看到它自动跳过被杀死的一个:
$ docker stop nb1/Backend1
nb1/Backend1
$ curl nb1.local:1081
{"result" : {
"servername" : "Server2",
"querycount" : 80
}
}
$ curl nb1.local:1081
{"result" : {
"servername" : "Server3",
"querycount" : 86
}
}
现在让我们看看我们的前端服务是否可以相同的方式使用它。 为此,我们像这样启动前端组件:
$ docker-compose up -f ./docker-compose-frontend-proxy
# remember we killed one of the backend services, so we only see two backend ones
$ docker exec -ti nb1/nb-haproxy cat /etc/haproxy/haproxy.cfg | tail -n 10
backend nb-frontend
balance roundrobin
server a1bc26eef516 10.0.9.11:8090 check
server bf2000882dcc 10.0.9.9:8090 check
server eb0d1c0cc075 10.0.9.10:8090 check
backend nb-backend
balance roundrobin
server a1bc26eef516 10.0.9.7:8081 check
server eb0d1c0cc075 10.0.9.8:8081 check
看起来HAProxy已使用新服务正确更新。 现在,我们应该能够在haproxy上调用端口1080,以获取前端服务之一,并使用该按钮来调用可用的后端服务之一(再次通过HAproxy)。
它按预期工作! 如果刷新此页面,您会看到它在前端服务中循环,当您多次单击按钮时,它将仅调用可用的服务。 而且,正如您期望的那样,一旦我们再次启动后端服务,点击按钮时它将显示在列表中:
$ docker start nb1/Backend1
nb1/Backend1
结果是:
HAProxy和领事模板结论
这是将Docker与Consultemplate和HAProxy一起使用的非常快速的介绍。 如您所见,使所有这些工作变得相当容易。 有了docker和consul配置之后,绑定额外的组件就变得容易得多。 在实际情况下,我们还将让HAProxy在Consul中进行注册,以便其他服务可以轻松找到HAProxy实例。
作为本文的最后一部分,我们将快速了解EnvConsul所提供的某些功能。
EnvConsul,在启动应用程序时轻松提供环境属性。
将配置数据传递到应用程序的通常方法(尤其是在docker / microservices架构中)是使用环境变量。 这是配置服务的一种简便,与语言无关的方式。 它甚至是12因子应用程序的主题之一。
“十二因素应用程序将配置存储在环境变量中(通常简称为env vars或env)。 Env var易于在部署之间进行更改,而无需更改任何代码。 与配置文件不同,它们很少有可能被意外检入代码存储库; 与自定义配置文件或其他配置机制(例如Java系统属性)不同,它们是与语言和操作系统无关的标准。”
但是,当您处理大型应用程序或大型配置时,尤其是当您必须处理转义引号/换行符等时,它变得非常麻烦。
幸运的是我们可以通过领事来解决这个问题。 您可能现在已经知道,Consul还是分布式键值存储:
使用EnvConsul,我们可以在启动应用程序之前将Consul内KV存储中的信息用作环境参数。 那么这将如何工作? 由于EnvConsul只是可以运行的golang可执行文件,因此我们可以非常轻松地对其进行测试。 我已经在存储库中(在extras目录中)添加了Mac版本,但是您可以从以下位置下载适用于您的OS的版本: https : //releases.hashicorp.com/envconsul/0.6.1/
我们将运行它,看看会发生什么:
./envconsul -consul=nb-consul.local:8500 -prefix docker -once env
...
network/v1.0/endpoint/815dd44b77f391bd9a63f4e107aa1a7d3f371c91427abcf4be34493aa7ec25cd/=
nodes/192.168.99.112:2376=192.168.99.112:2376
swarm/nodes/192.168.99.110:2376=192.168.99.110:2376
...
我将大部分结果留在了这里,但是基本上我们在这里要做的是使用env-consul来获取存储在docker树中的所有密钥,并将它们添加为环境变量。 设置完这些后,我们运行env命令,该命令仅输出我们拥有的所有环境变量。 如果您自己运行此命令,则会看到大量与docker-network和docker-swarm相关的信息设置为环境变量。
我们当然也可以自己设置一些KV对:
当我们检索这些值时,我们可以看到这些值作为环境变量直接传递到我们的命令中:
$ ./envconsul -consul=nb-consul.local:8500 -prefix smartjava -once env | tail -n 2
key2=The value of key 2
key1=The Value of Key 1
但是,这不是您可以使用envconsul做的所有事情。 它还提供了一种对键值对的变化做出反应的简便方法。 假设您有一个通过env属性配置的服务正在运行。 如果该env属性发生更改,则实际上您应该重新启动服务。 这是EnvConsul可以为您做的事情:
$ cat env.sh
#!/bin/bash
env
tail -f /dev/null
$ ./envconsul -consul=nb-consul.local:8500 -pristine -prefix=smartjava ./env.sh
PWD=/Users/jos/dev/git/nextbuild-consul/extra/envconsul
SHLVL=1
key2=The value of key 2
key1=The Value of Key 1
_=/usr/bin/env
此时,该进程将继续运行,并且envconsul将等待对Consul密钥库的更新。 因此,当我们在那里更新内容时,结果如下:
如该图所示,每当我们更改KV存储中的值时,我们的脚本都会以新配置作为环境变量传递来重新启动。
结论
在本文中,我们证明了使用HAProxy实际上非常容易。 您可以使用上一篇文章中看到的相同概念和思想将其合并到体系结构中。 将其与ConsulTemplate结合使用,即可轻松实现高度可配置的软件负载均衡器设置。
另一个很酷的技术是EnvConsul,它与Consul提供的分布式KV商店很好地集成在一起。 它甚至提供了在配置更改时重新启动应用程序的功能,这是一项非常强大的功能。
并在本系列的第二篇文章中进行了总结。 在本系列的下一部分中,我们将研究如何使用Consul简化服务注册。 因此,请使用更高级的解决方案替换上一篇文章中显示的自定义脚本。
翻译自: https://www.javacodegeeks.com/2016/05/service-discovery-docker-consul-part-2.html