consul logo

Consul是HashiCorp出品的开源服务发现工具。也有人用etcd或者ZooKeeper做类似的事情,它们之间的区别可以看官方文档的对比Consul vs. ZooKeeper, doozerd, etcd

Consul提供了诸如服务发现,健康检查,KV数据库等功能,方便你使用它构建自己的服务集群。

基础概念

  • Agent: agent就是实际运行的consul服务,启动时可选以server或者client模式运行,每个集群至少有1个server,由于使用了Raft算法,所以对于每个集群你应该把它的server数设置成3或5个。
    • Server: 核心的consul服务,存储了所有服务注册的信息,响应查询操作,跨数据中心通信等。
    • Client: 用来在集群中每个机器上运行,进行服务注册/健康检查的进程。
  • Cluster: 集群,由多台共同提供服务的机器组成的集合称为集群,agent在集群的每个成员上都要运行。
  • DataCenter: 数据中心。consul支持跨数据中心组成集群。
  • Node: 安装了agent,接入集群的机器称为node。
  • Service: 你的服务,即服务注册和服务发现之类操作的对象。通过提供config文件或者调用consul的HTTP API来定义一个服务。

使用

因为这玩意是用Go写的,所以你只要下个二进制就能跑了。从 https://www.consul.io/downloads.html 下载对应平台的二进制程序安装即可。

或者更加政治正确一些,可以使用docker容器

docker pull consul:latest

默认端口

consul默认使用下列端口

  • 8300(tcp): Server RPC,server用于接受其他agent的请求
  • 8301(tcp,udp): Serf LAN,数据中心内gossip交换数据用
  • 8302(tcp,udp): Serf WAN,跨数据中心gossip交换数据用
  • 8400(tcp): CLI RPC,接受命令行的RPC调用
  • 8500(tcp): HTTP API及Web UI
  • 8600(tcp udp): DNS服务,可以把它配置到53端口来响应dns请求

部署集群

以容器化部署为例。具体可参考docker hub上的文档
主要以下几点需要注意:

  • 如果你没有丰富的网络知识,那么建议所有agent以容器化部署时,网络模式设置为host模式
  • 容器内的/consul/data路径应挂个volume进去,以持久化agent状态
  • consul配置在/consul/config路径下,也可以通过环境变量CONSUL_LOCAL_CONFIG写一个配置的JSON字符串来修改配置
  • 几台机器的host不能相同,否则无法组成集群

安装server

这个启动命令基本上是从官方文档上抄的,用命令参数进行配置显得并不十分优雅,最好还是弄个config文件。详细的配置参数见文档Configuration

$ docker run -d --net=host -e 'CONSUL_LOCAL_CONFIG={"skip_leave_on_interrupt": true}' consul agent -server -bind=<external ip> -retry-join=<root agent ip> -bootstrap-expect=<number of server agents> -data-dir=/consul/data -node=<node_name> -client=<client ip> -ui

安装client

$ docker run -d --net=host -e 'CONSUL_LOCAL_CONFIG={"skip_leave_on_interrupt": true}' consul agent -bind=<external ip> -retry-join=<root agent ip> -data-dir=/consul/data -node=<node_name> -client=<client interface ip> -client=<client ip> -ui

参数说明

  • -bind: agent绑定到哪个ip(用于集群内部通讯),默认是0.0.0.0,为了安全最好设为内网ip
  • -retry-join: server与集群失联后重新加入集群时请求的ip
  • -bootstrap-expect: 集群server数量
  • -data-dir: 数据目录的位置
  • -client: 作为client提供服务时的ip
  • -ui: 启动web ui

使用

更详细请参考官方API文档 这里只列几个我用到的。可以通过调用client的HTTP API来操作consul,也可以通过封装好的SDK,如python-consul

列出所有节点

curl -X GET \
  http://consul.rocks/v1/catelog/nodes

列出所有服务

curl -X GET \
  http://consul.rocks/v1/catelog/services

列出服务的实例

curl -X GET \
  http://consul.rocks/v1/catelog/service/<service-name>

注册服务

注册一个redis服务,并设置了每10秒钟一次的http健康检查

curl -X PUT \
  http://192.168.5.36:8500/v1/agent/service/register \
  -d '{
  "ID": "my-redis",
  "Name": "redis",
  "Tags": [
    "primary",
    "v1"
  ],
  "Address": "127.0.0.1",
  "Port": 6379,
  "Meta": {
    "redis_version": "4.0"
  },
  "EnableTagOverride": false,
  "Check": {
    "DeregisterCriticalServiceAfter": "90m",
    "HTTP": "http://localhost:5000/health",
    "Interval": "10s"
  }
}'

注销服务

curl -X PUT \
    https://consul.rocks/v1/agent/service/deregister/<service-id>

Web UI

访问任意server的8500端口即可打开Web UI,界面还算精美,但功能比较简陋,可以查看集群内node和service的情况,设置ACL Token,增删改查KV数据库等,但无法对node和service进行进一步修改或删除操作。

Registrator

容器化部署最麻烦的一点是,在容器里的服务对自己所处的环境知之甚少,很难提供有效的信息(如地址端口等)用来进行服务注册,因此需要有上帝视角的外部工具来完成这项任务。Registrator就是这样的工具,它可以用于向etcd, consul, SkyDNS 2中注册服务。具体使用方式参考文档

docker run -d \
    --name=registrator \
    --net=host \
    --volume=/var/run/docker.sock:/tmp/docker.sock \
    gliderlabs/registrator:latest \
      consul://<client ip>:8500

服务注册

它会把当前机器启动的容器自动注册到consul里面去。Registrator注册的服务id是按照如下格式生成的

host:container_name:port

例如一台host为ubuntu的机器上的某个redis服务id可能就是

ubuntu:my_redis:6379

对于同一个容器暴露的不同端口,会分别注册服务。

健康检查

在需要进行健康检查的容器增加如下label或者环境变量,即可启动健康检查,其中80要改成服务的端口

SERVICE_80_CHECK_HTTP=/health/endpoint/path
SERVICE_80_CHECK_INTERVAL=15s
SERVICE_80_CHECK_TIMEOUT=1s     # optional, Consul default used otherwise

所以最终部署出来这一坨的东西基本上长成这个样子

找3台机器分别安装server,组成高可用consul集群,在需要服务注册的节点(node)上安装client和registrator。registrator读取docker的信息发送到当前节点的client进行服务注册。client负责将请求转发至server完成实际的注册工作。健康检查由client进行检查并上报到server。

Screen-Shot-2018-05-17-at-2.10.29-PM