服务配置和Consul KV数据集

Consul模板

Consul模板工具可以使用编程的方式从包括Consul KV数据集在内的各种位置渲染配置文件,模板工具基于Go模板,并有许多相同的属性。
Consul模板具有多种用途,我们将重点介绍其中的两个:

  1. 更新配置文件: Consul模板工具可用于更新服务配置文件,一种常见的用法是管理负载均衡配置文件,这些文件需要在许多无法直接连接到Consul群集的计算机上定期动态更新;
  2. 发现有关Consul群集和服务的数据:可以收集有关Consul群集中服务的信息。例如,用户可以收集群集上运行的所有服务的列表,或者可以发现Redis所有服务的地址。注意,这个操作在生产环境有所限制。
    在本章节中,我们将简要讨论Consul模板的工作原理,安装方法以及两个用例,在阅读本章节前,我们假设您对Consul KV和Go模板有所了解。

Consul模板简介

Consul模板是一个简单但功能强大的工具。启动后,它将读取一个到多个模板文件,并向Consul发出查询,加载它们所需的所有数据。通常,consul-template作为守护程序运行,一开始获取初始值,之后持续监控更新,在集群中发生任何相关更改时都会重新加载模板。用户也可以使用-once标志仅加载一次模板,这在测试或者由其他脚本触发时比较有用。最后,更新过程完成后,模板还可以运行后置命令。例如,它可以在进行配置更改后将HUP信号发送到负载均衡器服务。
Consul模板工具非常灵活,可以适应许多不同的环境和工作流程。根据使用情况,用户可能在少数几个主机上只有一个consul-template实例,或者也可能需要在每个主机上运行多个实例。每个Consul模板过程都可以管理多个不相关的文件,如果这些文件共享数据依赖项,则将根据需要对提取的内容进行重复数据删除,这样可以减少Consul服务器上可能共享的负载。

安装Consul模板

在本章节中,我们在开发者模式下使用本地Consul代理,先执行consul agent -dev,Consul代理正常运行后才能进行以下其他步骤。
Consul模板工具本身不包含在Consul二进制文件中,需要单独安装。用户可以直接安装预编译的二进制文件,也可以下载源代码自行编译,我们将安装预编译的二进制文件,首先,先下载consul-template二进制文件:

$ curl -O https://releases.hashicorp.com/consul-template/0.19.5/consul-template<_version_OS>.tgz

接下来,将二进制文件加入$PATH路径中:

$ tar -zxf consul-template<_version_OS>.tgz

用例:Consul KV数据集

在第一个用例中,我们将渲染一个模板,该模板会从Consul KV数据集中提取HashiCorp地址。我们需要创建一个包含HashiCorp地址的简单模板,运行consul-template,为Consul KV数据集添加一个HashiCorp地址的值,最后查看渲染的文件。
首先,我们需要创建一个模板文件find_address.tpl来查询Consul KV数据集:

{{ key "/hashicorp/street_address" }}

运行consul-template命令,同时指定要使用的模板和需要更新的文件:

$ consul-template -template "find_address.tpl:hashicorp_address.txt"

consul-template命令会持续运行,用户使用CTRL+c可以停止其运行,然后我们打开一个新终端,使用命令行指令将数据写入Consul:

$ consul kv put hashicorp/street_address "101 2nd St" 
Success! Data written to: hashicorp/street_address

我们可以通过查看hashicorp_address.txt文件来确保有数据写入文件:

$ cat hashicorp_address.txt 
101 2nd St

如果用户执行命令consul kv put hashicorp/street_address "22b Baker ST"更新hashicorp/street_address值,可以看到该文件立即更新。这个过程虽然很简单,但是意义很强大,比如用户可以用相同的过程来更新HAProxy负载均衡器配置。

用例:发现所有服务

在此示例中,我们将发现Consul群集中运行的所有服务,我们使用上一个示例中的开发环境接着开发。
首先,我们将需要创建一个新模板all-services.tpl来查询所有服务。

{{range services}}# {{.Name}}{{range service .Name}}
{{.Address}}{{end}}

{{end}}

接下来,运行consul-template命令指定我们刚刚创建的模板,并使用-once标志,表示仅运行一次:

$ consul-template -template="all-services.tpl:all-services.txt" -once

如果您在本地代理上完成此操作,则可以通过all-services.txt文件查看consul服务:

# consul
127.0.0.7

在开发或生产集群上,用户会看到所有服务的列表:

# consul
104.131.121.232

# redis
104.131.86.92
104.131.109.224
104.131.59.59

# web
104.131.86.92
104.131.109.224
104.131.59.59

使用会话实现应用程序领导者选举

对于某些应用程序,例如HDFS,有必要将一个实例设置为领导者,这样可以确保应用程序数据是最新且稳定的,本章节介绍了如何使用Consul为服务实例构建面向客户端(client-side)的领导者选举,Consul对会话的支持可帮助用户构建一个能够正常处理故障的系统。
这篇章节的内容与Consul本身的领导者选举无关,如果对该部分内容感兴趣,可以参考consensus协议。

竞争服务实例

想象一下,用户有一组服务实例正在争夺给定服务的领导地位,所有参与的服务实例都应商定一个给定的密钥进行协调,一个好的模式就是:

service//leader

在本章节中,我们的完整密钥可能是service/dbservice/leader,为了简化行文,我们将密钥定义为lead

创建会话

首先使用HTTP API创建一个会话:

$ curl  -X PUT -d '{"Name": "dbservice"}' http://localhost:8500/v1/session/create

这将返回一个包含会话ID的JSON对象:

{
  "ID": "4ca8e74b-6350-7587-addf-a18084928f3c"
}
获取会话

下一步是某个服务实例使用PUT方法获取给定密钥的会话,请求需要拼接?acquire=查询参数。
PUT方法中的应该是代表本地实例的JSON对象。该值对Consul不透明,但应包含客户端与用户应用程序进行通信所需的必要信息(例如,它可以是包含节点名称和应用程序端口的JSON对象)。

$ curl -X PUT -d  http://localhost:8500/v1/kv/lead?acquire=4ca8e74b-6350-7587-addf-a18084928f3c

响应结果为truefalse,如果为true,则表示已经获取到,本地服务实例现在为领导者,如果返回值为false,则说明其他某个节点获得了

监听会话

现在,所有其他实例都处于空闲等待状态。在这种状态下,它们会监听密钥lead的变化,锁可能随时会被释放,实例也可能失效。领导者也必须注意变化,因为运维人员可能手动释放锁,或者系统故障检查误报自动释放锁。
默认情况下,会话仅使用Gossip故障检测器,也就是说,只要默认的Serf健康检查未声明该节点不健康,就认为该节点保留着会话。如果需要,也可以指定其他检查。
通过对使用阻塞查询(blocking query)来监听变化。如果它们察觉到的会话为空,则说明此时没有领导者,然后会重试获取锁。每次尝试获取密钥都需要有一段时间间隔,Consul强制使用了lock-delay

释放会话

领导者可以解除锁来释放资源:

$ curl -X PUT http://localhost:8500/v1/kv/lead?release=4ca8e74b-6350-7587-addf-a18084928f3c

发现领导者

关于领导者选举,另一种常见的场景是,非领导者实例希望在实例集群中辨认出哪个实例是领导者,与选举领导者一样,所有参与的实例都应就用于协调的密钥达成一致,该密钥将被称为公正密钥(just key)

获取密钥

服务实例需要做的非常简单,它们仅需读取密钥就能找到当前的领导者,如果密钥有关联的会话,则说明已经有领导者了。

$ curl -X GET http://localhost:8500/v1/kv/lead
[
  {
    "Session": "4ca8e74b-6350-7587-addf-a18084928f3c",
    "Value": "Ym9keQ==",
    "Flags": 0,
    "Key": "dbservice",
    "LockIndex": 1,
    "ModifyIndex": 29,
    "CreateIndex": 29
  }
]

如果有领导者,则value字段会显示与应用程序相关的信息,信息使用Base64编码。

获取会话信息

用户可以查询/v1/session/info端点获取会话的详细信息:

$ curl -X GET http://localhost:8500/v1/session/info/4ca8e74b-6350-7587-addf-a18084928f3c
[
  {
    "LockDelay": 1.5e+10,
    "Checks": [
      "serfHealth"
    ],
    "Node": "consul-primary-bjsiobmvdij6-node-lhe5ihreel7y",
    "Name": "dbservice",
    "ID": "4ca8e74b-6350-7587-addf-a18084928f3c",
    "CreateIndex": 28
  }
]

使用会话实现分布式信号量

当用户想要调度许多服务,同时某些资源有访问限制时,分布式信号量(distributed semaphore)可能会很有用。在本章节中,我们将重点介绍如何使用Consul对会话的支持以及利用Consul KV数据集来构建分布式信号量。有多种方式可以构建信号量,本章节不会全部覆盖。
在阅读本章节前,用户应该熟悉Consul KV数据集Consul会话

信号量中的竞争节点

想象一下,我们有一组试图获取信号量中插槽(slot)的节点,所有参与竞争的节点都应达成以下三个共识:

  • KV数据集中用于调度的前缀;
  • 单独的键值(Key)作为锁;
  • 持有插槽数量的限制。
会话

第一步是为每个竞争节点创建一个会话,会话使我们能够构建可以正常处理故障的系统。

$ curl -X PUT -d '{"Name": "db-semaphore"}' \ 
    http://localhost:8500/v1/session/create

响应值是包含会话ID的JSON对象。

{
  "ID": "4ca8e74b-6350-7587-addf-a18084928f3c"
}
创建竞争锁

接下来,我们创建一个竞争锁(lock contender)。每个竞争者都会创建一个与会话相关联的KV条目(KV entry)。这样做的目的是,如果竞争者持有某个插槽并发生故障,则其会话会与键值分离 ,然后其他竞争者可以检测到该会话,用户可以发送PUT请求创建竞争锁:

curl -X PUT -d  http://localhost:8500/v1/kv//?acquire=

body的值一般为与该竞争锁相关的有意义的内容 ,比如节点的名称,Consul本身不关注body的内容,这个值对运维人员有用,的值是/v1/session/create请求的返回值。
该调用结果返回true或者false。如果为true,则说明创建竞争锁创建成功。如果为false,则表明创建失败,这通常意味着会话失效。

创建键值(Key)

下一步是创建一个键值(Key),用来协调插槽的持有者。 /.lock是该键值的一个不错选择,我们称这个特殊的键值为

$ curl -X PUT -d  http://localhost:8500/v1/kv/?cas=0

由于正在创建锁,此处cas索引值为0,仅当该值不存在时才放入,body的内容为信号量的插槽限制和当前所有者的会话ID,内容如下所示:

{
  "Limit": 2,
  "Holders": [""]
}

信号量管理

发送GET请求可以读取信号量当前状态:

$ curl http://localhost:8500/v1/kv/?recurse

在返回值中,我们应该重点关注两个值:

[
  {
    "LockIndex": 0,
    "Key": "",
    "Flags": 0,
    "Value": "eyJMaW1pdCI6IDIsIkhvbGRlcnMiOlsiPHNlc3Npb24+Il19",
    "Session": "",
    "CreateIndex": 898,
    "ModifyIndex": 901
  },
  {
    "LockIndex": 1,
    "Key": "/",
    "Flags": 0,
    "Value": null,
    "Session": "",
    "CreateIndex": 897,
    "ModifyIndex": 897
  }
]

返回的响应中,字段使用的是Base64编码。
读取数据并将其解码后,我们可以验证LimitHolders计数是否一致,这用于检测潜在冲突。下一步是确定当前哪些插槽(slot)仍在运行。查询响应结果返回了所有竞争者条目,通过扫描这些条目,我们可以创建了一组会话值,不在该组中的所有Holders都会被修剪。实际上,我们将根据列表结果创建一组实时竞争者,并与Holder进行一定的区别以检测和修剪任何可能失败的Holder。在此示例中,存在于Holders中,并附加在键/上,因此不需要修剪。
如果修剪后的持有者数量少于限制数量,则竞争者会尝试通过将其自己的会话添加到持有者(Holders)列表中,并对进行Check-And-Set操作,这会执行乐观更新。
我们可以通过以下方式获取:

$ curl -X PUT -d  http://localhost:8500/v1/kv/?cas=

lock-modify-index已知的最新ModifyIndex值,在此示例中为901
如果该请求返回true,竞争者将在信号量中保留一个插槽,如果返回false,则可能是与另一个竞争者发生了争夺。
当重新尝试进行数据采集时,我们会注意到的更新。这是因为可能会释放插槽,或者节点发生故障等等。通过对/kv/recurse接口进行阻塞查询可以监控更新。
插槽持有者必须不断关注的更新,因为运维人员可以释放插槽,或者由于故障检测器误报而自动释放插槽。在更改时,必须重新检查锁的持有人(Holders)列表,以确保仍保留该插槽。另外,如果监听器无法连接插槽,则应视为丢失。
此信号量系统纯粹是建议性的。因此,还是应该由客户端来确定在执行某些关键操作之前(和期间)是否已保留插槽。
最后,如果插槽持有者希望自愿释放插槽,则应通过对进行检查并设置(Check-And-Set)操作以从Holders对象中删除其会话。完成后,竞争者密钥/和会话都会被删除。

你可能感兴趣的:(服务配置和Consul KV数据集)