HashiCorp Consul是一个服务网格,用于服务发现、运行时配置和微服务应用程序和基础设施的服务分割。Consul允许注册和发现“内部”服务到您的基础设施,以及“外部”服务,例如第三方SaaS提供的服务,以及其他不可能直接运行Consul代理的环境。
这篇博文解释了如何与Consul的外部服务合作,以及如何使用Consul ESM(外部服务监视器)来对这些服务进行健康检查。我们将介绍:
- 这个主题的关键术语的术语表
- 内部服务注册和健康检查
- 外部服务注册和健康检查
- 拉/推健康检查
- 使用Consul ESM监控外部服务的健康状况
本文中的所有示例都使用Consul agent(下载)1.2.1版本在本地以Consul ui启用的-dev模式运行,并使用-enable-script-check标志来支持简单的基于ping的健康检查。我们还规定了节点名为web,而不是主机名的默认名称,以使示例更清晰。
$ consul agent -dev -enable-script-checks -node=web -ui
随着Consul dev agent的运行,本文中的示例使用curl与Consul的HTTP API以及Consul的Web UI进行交互,在http://127.0.0.1:8500上可访问。
这个主题的关键术语的术语表
- 代理(Agent): Consul集群的每个成员上的长时间运行的守护进程。代理可以在client或server模式下运行。
- 节点(Node): 节点表示运行代理的“物理”机器。这可能是一个裸露的金属设备、VM或容器。
- 服务(Service): 服务是应用程序或进程,注册在Consul目录中以供发现。服务可以是内部的(在您的数据中心内)或外部的(如RDS集群),并且可以选择与之相关联的健康检查。
- 检查(Check): 检查是一个本地运行的命令或操作,它返回附加对象的状态。检查可以附加到节点和服务上。
- 目录(Catalog): 目录是所有已注册节点、服务和检查的注册中心。
内部服务注册和健康检查
首先,我们来看看如何注册内部服务。在Consul的上下文中,内部服务是由Consul代理可以直接运行的节点(机器)提供的。
内部服务通过服务定义注册。服务定义可以由Consul代理启动时加载的配置文件提供,也可以通过/agent/service/register的本地HTTP API端点提供。
我们将用以下配置注册一个内部web示例,web.json:
{
"id": "web1",
"name": "web",
"port": 80,
"check": {
"name": "ping check",
"args": ["ping","-c1","www.google.com"],
"interval": "30s",
"status": "passing"
}
}
这个示例web服务将具有惟一的id web1、逻辑名称web、运行在端口80上并进行一次健康检查。
通过使用PUT请求调用HTTP API来注册示例web服务:
$ curl --request PUT --data @web.json localhost:8500/v1/agent/service/register
要验证示例web服务已注册上去,请查询/catalog/service/:service 端点:
$ curl localhost:8500/v1/catalog/service/web
[
{
"ID": "a2ebf70e-f912-54b5-2354-c65e2a2808de",
"Node": "web",
"Address": "127.0.0.1",
"Datacenter": "dc1",
"TaggedAddresses": {
"lan": "127.0.0.1",
"wan": "127.0.0.1"
},
"NodeMeta": {
"consul-network-segment": ""
},
"ServiceID": "web1",
"ServiceName": "web",
"ServiceAddress": "",
"ServiceMeta": {},
"ServicePort": 80,
"ServiceEnableTagOverride": false,
"CreateIndex": 7,
"ModifyIndex": 7
}
]
向 agent/service/register 端点注册服务定义将本地节点注册为服务提供者。节点上的本地Consul代理负责为注册的服务进行健康检查,并相应地更新目录。
在我们的示例web服务中,我们使用以下配置定义了一个健康检查:
"check": {
"name": "ping check",
"args": ["ping","-c1","www.google.com"],
"interval": "30s",
"status": "passing"
}
这个健康检查验证我们的web服务可以通过每30秒ping一次 google.com连接到公共互联网。对于实际的web服务,应该配置更有用的健康检查。Consul提供几种健康检查方式,包括:脚本,HTTP, TCP,存活时间(TTL), Docker,和gPRC。
要检验为注册到给定本地节点的服务配置的所有健康检查,请使用/agent/checks/ 端点:
$ curl localhost:8500/v1/agent/checks
{
"service:web1": {
"Node": "web",
"CheckID": "service:web1",
"Name": "ping check",
"Status": "passing",
"Notes": "",
"Output": "PING www.google.com (172.217.3.164): 56 data bytes\n64 bytes from 172.217.3.164: icmp_seq=0 ttl=52 time=21.902 ms\n\n--- www.google.com ping statistics ---\n1 packets transmitted, 1 packets received, 0.0% packet loss\nround-trip min/avg/max/stddev = 21.902/21.902/21.902/0.000 ms\n",
"ServiceID": "web1",
"ServiceName": "web",
"ServiceTags": [
"rails"
],
"Definition": {},
"CreateIndex": 0,
"ModifyIndex": 0
}
}
可以使用/health/service/:service 查询单个服务的健康状况,使用/health/node/:node 查询单个节点的健康状态。
健康检查也可以在Consul UI上查看:
外部服务注册和健康检查
在Consul的上下文中,外部服务是由不能运行Consul代理的节点提供的服务。这些节点可能位于基础设施内部(例如大型机、虚拟设备或不支持的平台)或基础设施外部(例如SaaS平台)。
因为根据定义,外部服务在没有运行Consul代理的节点上运行,因此无法向本地代理注册。相反,它们必须使用/catalog/register端点 直接与目录一起注册。这个端点的对象上下文是节点,而不是像/agent/service/register 端点那样的服务。当使用/catalog/register端点时,将注册整个节点。而使用/agent/service/register 端点(这是我们在上面的第一个例子中使用的端点),本地节点上下文中的单个服务被注册。
直接通过目录注册的外部服务的配置与通过代理注册的内部服务的配置略有不同:
- 节点(Node)和地址(Address)都是必需的,因为它们不能从本地节点Consul代理自动确定。
- 服务(Service)和健康检查(Checks)是分开定义的。
- 如果提供的ServiceID与该节点上的服务ID匹配,则该检查将被视为服务级健康检查,而不是节点级健康检查。
- 可以为Definition 字段提供TCP或HTTP健康检查的详细信息。有关更多信息,请参阅健康检查。
要演示外部服务注册是如何工作的,请考虑谷歌提供的外部搜索服务。我们用以下配置来注册这个服务,extern.json:
{
"Node": "google",
"Address": "www.google.com",
"NodeMeta": {
"external-node": "true",
"external-probe": "true"
},
"Service": {
"ID": "search1",
"Service": "search",
"Port": 80
},
"Checks": [{
"Name": "http-check",
"status": "passing",
"Definition": {
"http": "https://www.google.com",
"interval": "30s"
}
}]
}
该配置定义了一个名为google 的节点,可以在地址www.google.com上访问,该节点提供一个search服务,ID为search1,运行在端口80上。它还定义了每30秒运行一次的http类型健康检查,并设置了该检查的初始状态 passing。
通常,外部服务是通过专门用于此目的的节点注册的,因此我们将继续使用Consul dev代理(localhost)进行示例,就好像它是在和内部web服务(例如“Web”)不同的另一个节点(例如“External Services”)上运行一样。
下面这张图显示了向Consul注册内部和外部服务之间的区别:
使用PUT请求注册外部服务:
$ curl --request PUT --data @external.json localhost:8500/v1/catalog/register
true
与通过代理端点注册的内部服务一样,我们可以通过查询/catalog/service/:service 端点来验证外部服务的注册:
$ curl localhost:8500/v1/catalog/service/search
[
{
"ID": "",
"Node": "google",
"Address": "www.google.com",
"Datacenter": "dc1",
"TaggedAddresses": null,
"NodeMeta": {
"external-node": "true",
"external-probe": "true"
},
"ServiceID": "search1",
"ServiceName": "search",
"ServiceTags": [],
"ServiceAddress": "",
"ServiceMeta": {},
"ServicePort": 80,
"ServiceEnableTagOverride": false,
"CreateIndex": 246,
"ModifyIndex": 246
}
]
在我们的内部web服务示例中,我们通过查询本地代理端点 /agent/checks 来验证健康检查是活跃的。如果在添加外部服务之后再次查询此内容,我们将不会看到列出的健康检查,因为它是在服务目录中注册的,而不是本地代理。相反,我们需要查询目录级端点,例如/health/service/:service、/health/node/:node、/health/state/:state。
我们还可以看到Consul UI中列出的服务和健康检查:
从上面可以看到,目录中有一个健康检查条目。但是,因为这个节点和服务是直接在目录中注册的,所以没有设置实际的健康检查,也不会监视节点的健康状况。
为了演示直接通过目录注册的健康检查与通过本地代理注册的健康检查之间的区别,可以在模拟停机前后查询节点健康状况。
要模拟停机,请断开与internet的连接,稍等片刻,并检查Consul UI:
在上面的输出中,我们看到内部web服务的不再通过健康检查,但是在不应该看到外部search服务通过健康检查时,却看到健康检查通过了(passing)。在断开internet连接时,这两个服务的健康检查都应该失败,但只有对内部web服务的检查显示为失败。
拉和推健康检查
传统健康检查的一个问题是拉动式模型的使用。定期,监视服务器询问所有节点是否健康,节点以状态响应。这就造成了瓶颈,因为随着节点数量的增加,监视服务的流量也在增加。这会阻塞本地网络流量,并给集群带来不必要的负载。
Consul使用一个基于推送的模型,其中代理仅在状态更改时发送事件。因此,即使是大型集群也只有很少的请求。边缘触发监测的问题是没有活性心跳。也就是说,在没有任何更新的情况下,Consul不知道检查是否处于稳定状态,或者服务器是否死亡。Consul通过使用基于gossip协议的故障检测器来解决这个问题。所有集群成员都参与一个后台gossip,不管集群大小如何,这个gossip都有一个恒定的负载。gossip和edge触发的更新的组合允许Consul扩展到非常大的集群大小,而不需要重载。
因为Consul monitoring 要求Consul代理在被监视的服务上运行,所以不会对外部服务执行健康检查。要启用外部服务的健康监视,请使用Consul External Service Monitor(ESM)。
使用Consul ESM监控外部服务
Consul ESM是一个与Consul一起运行的守护进程,用于运行外部节点的健康检查并更新目录中那些健康检查的状态。ESM的多个实例可以运行以获得可用性,并且ESM将通过持有一个Consul中的锁来执行领导人选举。然后,领导者将继续监视Consul对目录的更新,并执行在它发现的任何外部节点上定义的健康检查。这允许外部注册的服务和检查访问与Consul代理在本地注册相同的功能。
与先前的图表更新显示Consul ESM如何与Consul合作监测外部服务的健康状况:
Consul ESM是作为一个单一的二进制文件提供的。要安装,请下载适合您系统的版本,并使其在您的路径中可用。本文中的示例使用Consul ESM v0.2.0。
打开一个新的终端,执行 consul-esm启用Consul ESM。
$ consul-esm
2018/06/14 12:50:44 [INFO] Connecting to Consul on 127.0.0.1:8500...
Consul ESM running!
Datacenter: (default)
Service: "consul-esm"
Leader Key: "consul-esm/lock"
Node Reconnect Timeout: "72h0m0s"
Log data will now stream in as it occurs:
2018/06/14 12:50:44 [INFO] Waiting to obtain leadership...
2018/06/14 12:50:44 [INFO] Obtained leadership
如果您的internet提供者不允许UDP ping,为了使本文中的示例正常工作,您可能需要在配置文件中设置ping_type = "socket",并使用该配置文件启动consult -esm。如果你正在使用macOS,你将需要执行sudo:
$ sudo consul-esm -config-file=./consul-esm.hcl
示例外部服务定义中包含以下节点元数据(NodeMeta),这些元数据使Consul ESM能够进行健康监视:
- “external-node”标识该节点是Consul ESM应该监视的外部节点。
- “external-probe”:“true”告诉Consul ESM定期对节点执行ping,并维护节点的外部节点健康检查(类似于Consul代理使用的serfHealth检查)。
一旦Consul ESM运行,再次模拟从互联网连接中断。现在,在Consul UI中,可以看到我们的外部服务的健康检查更新为critical 状态:
Consul ESM支持HTTP和TCP健康检查。Ping健康检查自动添加了"external-probe": "true".
总结
Consul内部服务是由直接运行Consul代理的节点提供的服务。外部服务是由不能运行Consul代理的节点提供的服务。
内部服务通过本地Consul代理在服务定义中注册。节点上的本地Consul代理负责运行为服务注册的任何健康检查,并相应地更新目录。外部服务必须直接与目录注册,因为根据定义,它们在没有Consul代理的节点上运行。
内部和外部服务都可以进行健康检查。Consul的健康检查使用基于推送的模型,其中代理仅在状态更改时发送事件。Consul提供几种健康检查,包括:脚本,HTTP, TCP,TTL, Docker,和gPRC。因为Consul监视要求Consul代理在被监视的服务上运行,所以不会对外部服务执行健康检查。要启用外部服务的健康监视,请使用Consul External Service Monitor (ESM)。