Consul 是一个用于实现分布式系统的服务发现与配置的开源工具。与其他分布式服务注册与发现的方案相比,Consul 的方案更“一站式”,它内置了服务注册与发现框架、分布一致性协议实现、健康检查、Key/Value 存储、多数据中心方案,不再需要依赖其他工具。

Consul 架构

在描述体系结构之前,先了解下 Consul 术语表,以帮助澄清所讨论的内容:

  • Agent - 是 Consul 集群中每个成员上的长时间运行的守护进程,可以通过 consul agent 命令启动它。agent 可以在 clientserver 模式下运行。所有 agent 都可以运行 DNS 或 HTTP 接口,并负责运行检查和保持服务同步。
  • Client - 是将所有 RPCs 转发到 server 的 Agent,client 是相对无状态的。client 执行的唯一后台活动是参与 LAN gossip pool,这将减少资源开销,并且只消耗少量的网络带宽。
  • Server - 是一组具有扩展职责的 Agent,包括参与一致性判断(Raft quorum)、维护集群状态、响应 RPC 查询、与其他数据中心交换 WAN gossip 以及将查询转发给领导者(leaders)或远程数据中心。
  • Datacenter - 私有、低延迟和高带宽的网络环境。
  • Consensus(一致性) - Consul 使用 consensus protocol) 来提供 CAP(一致性,高可用,分区容错性)
  • Gossip - 一种协议,用来保证最终一致性,即:无法保证在某个时刻,所有节点状态一致,但可以保证“最终”一致
  • RPC - 远程过程调用,它是一种请求/响应机制,允许 client 向 server 发出请求。

Consul 的架构是这样的:

Consul 架构

让我们分解这个图像,描述每一块。首先,我们可以看到有两个数据中心,标记为“1”和“2”。Consul 支持多数据中心,在每个数据中心中,我们有 client 和 server 的混合,推荐有 3 到 5 台 server,这就在故障情况下的可用性和性能之间取得了平衡,因为随着更多的机器被添加,将会变得越来越慢。然而,client 的数量是没有限制的,他们可以很容易地扩展到成千上万。

每个数据中心中的 server 都会一起来选出一个 leader(领导者);leader 是一个被选中的有额外职责的 server,负责处理所有查询和事务,当非 leader server 接收到 RPC 请求时,它将其转发给集群 leader。

安装

Consul 支持 Linux、Windows 和 Mac OS X 系统。它只是一个可执行文件,方便部署,与 Docker 等轻量级容器可无缝配合 。

要安装 Consul,先找到适合你的系统的软件包并下载,下载后,解压压缩包即可。最后将 Consul 二进制文件所在的路径添加到环境变量 PATH 上。关于如何在 Linux 和 Mac 上设置 PATH ,请参阅本页

安装 Consul 后,通过打开一个新的终端会话并检查 consul 是否可用,通过执行 consul ,你应该会看到类似这样的帮助输出:

$ consul
usage: consul [--version] [--help] <command> [<args>]

Available commands are:
    agent          Runs a Consul agent
    event          Fire a new event

# ...

启动 Consul

在 Consul 安装后,必须运行 agent,agent 可以在 server 模式或 client 模式下运行。每个数据中心必须至少有一台 Server,不过建议使用一个由 3 或 5台 server 组成的集群。不推荐只部署单个 server,因为在故障场景中数据丢失是不可避免的。

所有其他 agent 都在 client 模式下运行,client 是一个非常轻量级的进程,它注册服务、运行健康检查并将查询转发给 server。agent 必须在集群的每个节点上运行。

启动 Agent

为了简单起见,我们现在开始在开发模式中使用 Consul agent。它不能在生产环境中使用,因为它不持久化任何状态。

-$ consul agent -dev
==> Starting Consul agent...
==> Starting Consul agent RPC...
==> Consul agent running!
           Version: 'v0.7.0'
         Node name: 'Armons-MacBook-Air'
        Datacenter: 'dc1'
            Server: true (bootstrap: false)
       Client Addr: 127.0.0.1 (HTTP: 8500, HTTPS: -1, DNS: 8600, RPC: 8400)
      Cluster Addr: 127.0.0.1 (LAN: 8301, WAN: 8302)
    Gossip encrypt: false, RPC-TLS: false, TLS-Incoming: false

==> Log data will now stream in as it occurs:

    2016/09/15 10:21:10 [INFO] raft: Initial configuration (index=1): [{Suffrage:Voter ID:127.0.0.1:8300 Address:127.0.0.1:8300}]
    2016/09/15 10:21:10 [INFO] raft: Node at 127.0.0.1:8300 [Follower] entering Follower state (Leader: "")
    2016/09/15 10:21:10 [INFO] serf: EventMemberJoin: Armons-MacBook-Air 127.0.0.1
    2016/09/15 10:21:10 [INFO] serf: EventMemberJoin: Armons-MacBook-Air.dc1 127.0.0.1
    2016/09/15 10:21:10 [INFO] consul: Adding LAN server Armons-MacBook-Air (Addr: tcp/127.0.0.1:8300) (DC: dc1)
    2016/09/15 10:21:10 [INFO] consul: Adding WAN server Armons-MacBook-Air.dc1 (Addr: tcp/127.0.0.1:8300) (DC: dc1)
    2016/09/15 10:21:13 [DEBUG] http: Request GET /v1/agent/services (180.708µs) from=127.0.0.1:52369
    2016/09/15 10:21:13 [DEBUG] http: Request GET /v1/agent/services (15.548µs) from=127.0.0.1:52369
    2016/09/15 10:21:17 [WARN] raft: Heartbeat timeout from "" reached, starting election
    2016/09/15 10:21:17 [INFO] raft: Node at 127.0.0.1:8300 [Candidate] entering Candidate state in term 2
    2016/09/15 10:21:17 [DEBUG] raft: Votes needed: 1
    2016/09/15 10:21:17 [DEBUG] raft: Vote granted from 127.0.0.1:8300 in term 2. Tally: 1
    2016/09/15 10:21:17 [INFO] raft: Election won. Tally: 1
    2016/09/15 10:21:17 [INFO] raft: Node at 127.0.0.1:8300 [Leader] entering Leader state
    2016/09/15 10:21:17 [INFO] consul: cluster leadership acquired
    2016/09/15 10:21:17 [DEBUG] consul: reset tombstone GC to index 3
    2016/09/15 10:21:17 [INFO] consul: New leader elected: Armons-MacBook-Air
    2016/09/15 10:21:17 [INFO] consul: member 'Armons-MacBook-Air' joined, marking health alive
    2016/09/15 10:21:17 [INFO] agent: Synced service 'consul'

如上所见,Consul agent 已经启动并输出了一些日志数据。从日志数据中,可以看到 agent 在 server 模式下运行,并且已经声明了集群的领导权(leader)。此外,本地成员被标记为集群的健康成员。

查看集群成员

如果在另一个终端 consul members 命令,你可以看到 Consul 集群的成员:

$ consul members
Node                Address            Status  Type    Build     Protocol  DC
HZ-CJX  192.168.1.144:8301  alive   server  1.3.0  2         dc1

members 命令的输出是基于 gossip 协议的,并且是最终一致的。也就是说,在任何时候,您的本地 agent 所看到的 Consul 节点的状态信息可能与 server 上的状态不完全匹配。想要获取完全匹配的状态可以使用 HTTP API 将请求转发给 Consul servers:

$ curl localhost:8500/v1/catalog/nodes
[
    {
        "ID": "9cf2a455-e4b3-ee34-7909-ee65f2ff2e41",
        "Node": "HZ-CJX",
        "Address": "192.168.1.144",
        "Datacenter": "dc1",
        "TaggedAddresses": {
            "lan": "192.168.1.144",
            "wan": "192.168.1.144"
        },
        "Meta": {
            "consul-network-segment": ""
        },
        "CreateIndex": 9,
        "ModifyIndex": 10
    },
    {
        "ID": "9dd3623f-6371-b0a3-cfc5-654ae411613a",
        "Node": "PC-CJX",
        "Address": "192.168.1.146",
        "Datacenter": "dc1",
        "TaggedAddresses": {
            "lan": "192.168.1.146",
            "wan": "192.168.1.146"
        },
        "Meta": {
            "consul-network-segment": ""
        },
        "CreateIndex": 24,
        "ModifyIndex": 51
    }
]

当 agent 启动后,默认会在 8500 端口启动管理界面。可以在浏览器中输入 http://localhost:8500/ui ,可以查看 Consul 管理界面:

Consul 管理界面

停止 Agent

可以使用 Ctrl-C(中断信号)优雅地停止 agent。在中断 agent 之后,你会看到它离开集群并关闭。

Consul 常用命令

命令解释示例
agent运行一个 Consul agentconsul agent -dev
join将 Consul agent 加入到集群consul join IP
members列出 Consul 集群中的成员consul members
leave将节点优雅地移除 Consul 集群并关闭consul leave

Consul agent 命令

输入 consul agent -help ,可以看到:

Usage: consul agent [options]

  Starts the Consul agent and runs until an interrupt is received. The
  agent represents a single node in a cluster.

HTTP API Options

  -datacenter=<value>
     Datacenter of the agent.

Command Options

  -advertise=<value>
     Sets the advertise address to use.

  -advertise-wan=<value>
     Sets address to advertise on WAN instead of -advertise address.

  -bind=<value>
     Sets the bind address for cluster communication.

  -bootstrap
     Sets server to bootstrap mode.

  -bootstrap-expect=<value>
     Sets server to expect bootstrap mode.

  -client=<value>
     Sets the address to bind for client access. This includes RPC, DNS,
     HTTP, HTTPS and gRPC (if configured).

  -config-dir=<value>
     Path to a directory to read configuration files from. This
     will read every file ending in '.json' as configuration in this
     directory in alphabetical order. Can be specified multiple times.

  -config-file=<value>
     Path to a JSON file to read configuration from. Can be specified
     multiple times.

  -config-format=<value>
     Config files are in this format irrespective of their extension.
     Must be 'hcl' or 'json'

  -data-dir=<value>
     Path to a data directory to store agent state.

  -dev
     Starts the agent in development mode.

  -disable-host-node-id
     Setting this to true will prevent Consul from using information
     from the host to generate a node ID, and will cause Consul to
     generate a random node ID instead.

  -disable-keyring-file
     Disables the backing up of the keyring to a file.

  -dns-port=<value>
     DNS port to use.

  -domain=<value>
     Domain to use for DNS interface.

  -enable-local-script-checks
     Enables health check scripts from configuration file.

  -enable-script-checks
     Enables health check scripts.

  -encrypt=<value>
     Provides the gossip encryption key.

  -grpc-port=<value>
     Sets the gRPC API port to listen on (currently needed for Envoy xDS
     only).

  -hcl=<value>
     hcl config fragment. Can be specified multiple times.

  -http-port=<value>
     Sets the HTTP API port to listen on.

  -join=<value>
     Address of an agent to join at start time. Can be specified
     multiple times.

  -join-wan=<value>
     Address of an agent to join -wan at start time. Can be specified
     multiple times.

  -log-file=<value>
     Path to the file the logs get written to

  -log-level=<value>
     Log level of the agent.

  -log-rotate-bytes=<value>
     Maximum number of bytes that should be written to a log file

  -log-rotate-duration=<value>
     Time after which log rotation needs to be performed

  -node=<value>
     Name of this node. Must be unique in the cluster.

  -node-id=<value>
     A unique ID for this node across space and time. Defaults to a
     randomly-generated ID that persists in the data-dir.

  -node-meta=<key:value>
     An arbitrary metadata key/value pair for this node, of the format
     `key:value`. Can be specified multiple times.

  -non-voting-server
     (Enterprise-only) This flag is used to make the server not
     participate in the Raft quorum, and have it only receive the data
     replication stream. This can be used to add read scalability to
     a cluster in cases where a high volume of reads to servers are
     needed.

  -pid-file=<value>
     Path to file to store agent PID.

  -protocol=<value>
     Sets the protocol version. Defaults to latest.

  -raft-protocol=<value>
     Sets the Raft protocol version. Defaults to latest.

  -recursor=<value>
     Address of an upstream DNS server. Can be specified multiple times.

  -rejoin
     Ignores a previous leave and attempts to rejoin the cluster.

  -retry-interval=<value>
     Time to wait between join attempts.

  -retry-interval-wan=<value>
     Time to wait between join -wan attempts.

  -retry-join=<value>
     Address of an agent to join at start time with retries enabled. Can
     be specified multiple times.

  -retry-join-wan=<value>
     Address of an agent to join -wan at start time with retries
     enabled. Can be specified multiple times.

  -retry-max=<value>
     Maximum number of join attempts. Defaults to 0, which will retry
     indefinitely.

  -retry-max-wan=<value>
     Maximum number of join -wan attempts. Defaults to 0, which will
     retry indefinitely.

  -segment=<value>
     (Enterprise-only) Sets the network segment to join.

  -serf-lan-bind=<value>
     Address to bind Serf LAN listeners to.

  -serf-lan-port=<value>
     Sets the Serf LAN port to listen on.

  -serf-wan-bind=<value>
     Address to bind Serf WAN listeners to.

  -serf-wan-port=<value>
     Sets the Serf WAN port to listen on.

  -server
     Switches agent to server mode.

  -server-port=<value>
     Sets the server port to listen on.

  -syslog
     Enables logging to syslog.

  -ui
     Enables the built-in static web UI server.

  -ui-dir=<value>
     Path to directory containing the web UI resources.

这里介绍几个常用的参数:

  • -datacenter=<value> - 指定 agent 加入到哪一个数据中心中。
  • -bind=<value> - 指定用于集群通信的 IP 地址;如果有多个网卡,并且没有指定 IP 会报 Failed to get advertise address: Multiple private IPs found. Please configure one. 错误。
  • -bootstrap - 设置 server 为 bootstrap 模式,在一个 datacenter 中只能有一个 server 处于 bootstrap 模式,当一个 server 处于 bootstrap 模式时,可以自己选举为 raft leader。
  • -bootstrap-expect=<value> - 该命令用了通知 Consul server 我们期望加入到 datacenter 中的 server 节点个数;当该值提供的时候,Consul 会一直等到达到指定 sever 数目的时候才会引导整个集群,该标记不能和bootstrap 共用。
  • -client=<value> - 为 client 设置绑定的访问地址。这个地址提供 RPC、DNS、HTTP、HTTPS 和 gRPC 等服务,默认是地址是 127.0.0.1。
  • -config-dir=<value> - 指定一个要加载的配置文件所在的目录。Consul 会依次(按字母顺序)读取目录中以 .json 结尾的文件。该选项可以配置多次,进而配置多个配置文件(后边的会合并前边的,相同的值覆盖)。
  • -config-file=<value> - 指定一个要加载的配置文件;该选项可以配置多次,进而配置多个配置文件(后边的会合并前边的,相同的值覆盖)
  • -data-dir=<value> - 指定 agent 存储代理状态的目录
  • -dev - 以开发模式启动 agent。 该参数配置下,不会有任何持久化操作,即不会有任何数据写入到磁盘,这种模式不能用于生产环境。
  • -encrypt=<value> - 指定 gossip 加密密钥(key),使 Consul 在通讯时进行加密,key 可以通过 consul keygen 生成,同一个集群中的节点必须使用相同的 key。
  • -join=<value> - 指定要加入的一个已经启动的 agent 的 IP 地址;可以多次指定多个 agent 的地址,如果Consul 不能加入任何指定的地址中,则 agent 会启动失败,默认 agent 启动时不会加入任何节点。
  • -log-file=<value> - 指定日志文件被写入到的文件的目录(PATH)
  • -log-level=<value> - Consul agent 启动后显示的日志信息级别。默认是info,可选:trace、debug、info、warn、err。
  • -node=<value> - 指定节点在集群中的名称,该名称在集群中必须是唯一的,默认是该节点的主机名(host)。
  • -server - 指定 agent 以 server 模式运行,每个集群至少有一个 server(推荐为 3 到 5 台)。
  • -ui - 指定启用内置的静态 web UI 服务器。

注册服务

前面讲述了如何运行一个 agent,查看集群成员并查询该节点。在本节中,我们将注册一个服务并查询该服务。

定义服务

可以通过提供 service definition 或通过调用 HTTP API 来注册服务,service definition是最常见的注册服务的方式 。

首先,为创建一个 Consul configuration 目录,Consul 启动时加载配置目录中的所有配置文件。在 Unix 系统上一个常见约定是将目录命名为 /etc/consul.d.d 后缀表示“此目录包含一组配置文件” )。

$ sudo mkdir /etc/consul.d

接下来,我们将编写一个服务定义配置文件。我们假设有一个名为“web”的服务在端口80上运行。另外,我们将给它一个标签,我们可以使用它作为额外的查询服务的方式:

$ echo '{"service": {"name": "web", "tags": ["primary"], "port": 80}}' \
    | sudo tee /etc/consul.d/web.json

上面的配置文件内容只是为了方便快速阅读,配置文件的完整的格式请参阅 service definition

现在,重新启动 agent,并指定配置目录:

$ consul agent -dev -config-dir=/etc/consul.d
==> Starting Consul agent...
...
    [INFO] agent: Synced service 'web'
...

你会注意到在输出中包含了 "synced" 字样,这意味着 agent 从配置文件加载了服务定义,并成功地在服务目录中注册了它。如果希望注册多个服务,可以在 Consul 配置目录中创建多个服务定义文件。

也可以使用 HTTP API 注册服务,使用 curl 或者 Postman 的 PUT 方法调用 API:

curl http://127.0.0.1:8500/v1/agent/service/register -X PUT -i -H "Content-Type:application/json" -d '{
  "ID": "web",            // 服务id
  "Name": "web",            // 服务名    
  "Tags": [                 // 服务的tag,自定义,可以根据这个tag来区分同一个服务名的服务
    "primary",
    "v1"
  ],
  "Address": "127.0.0.1", // 服务注册到 Consul 的 IP,服务发现,发现的就是这个IP
  "Port": 8000,
  "EnableTagOverride": false,
  "Check": {             // 健康检查部分
    "DeregisterCriticalServiceAfter": "90m",
    "HTTP": "http://127.0.0.1/you_check_uri", // 指定健康检查的 URL,调用后只要返回 20X,Consul 都认为是健康的
    "Interval": "10s"      // 健康检查间隔时间,每隔10s,调用一次上面的 URL
  }
}'

查询服务

可以在浏览器中输入 http://localhost:8500/ui 在 Consul 管理界面查询服务,也可以使用 DNS 或 HTTP API 查询服务 。

DNS 方式由于可读性差这里就不介绍了,感兴趣的可以去 这里 查看。下面使用 HTTP API 查询服务:

$ curl http://localhost:8500/v1/catalog/service/web
[{"Node":"PC-CJX","Address":"192.168.1.146","ServiceID":"web", \
    "ServiceName":"web","ServiceTags":["primary"],"ServicePort":80}]  

当然你也可以只查询健康的示例:

$ curl http://localhost:8500/v1/catalog/service/web?passing
[{"Node":"PC-CJX","Address":"192.168.1.146","ServiceID":"web", \
    "ServiceName":"web","ServiceTags":["primary"],"ServicePort":80}]  

更新服务

使用配置文件定义服务后,可以通过发送 SIGHUP 信号给 agent 来进行更新,这样你可以让你在不关闭服务或者保持服务可用的情况下进行更新。

$ consul reload
Configuration reload triggered

但是使用 HTTP API 动态地添加、删除和修改服务后不需要使用信号更新。

健康检查

在本节中,我们将向节点和服务添加健康检查,健康检查是为了及时发现和防止不健康的服务。

定义检查

和服务类似,一个检查可以通过 check definition 或调用适当的 HTTP API 注册。

我们将使用检查定义方法,因为与服务一样,定义是设置检查的最常见方式。在 Consul 0.9.0 及以后的版本中,必须将代理配置为 enable_script_checks ,以便启用脚本检查。

在 Consul 配置目录中创建两个配置文件:

$ echo '{"check": {"name": "ping",
  "args": ["ping", "-c1", "google.com"], "interval": "30s"}}' \
  >/etc/consul.d/ping.json

$ echo '{"service": {"name": "web", "tags": ["rails"], "port": 80,
  "check": {"args": ["curl", "localhost"], "interval": "10s"}}}' \
  >/etc/consul.d/web.json

第一个定义添加了一个名为 “ping” 的主机级检查,此检查以30秒的间隔运行,调用 ping -c1 google.com。在基于脚本的健康检查中,检查与启动 Consul 的过程相同,如果命令以退出码 >= 2 退出,则检查将被标记为失败,服务将被认为不健康。这是任何基于脚本的健康检查的共同的约定。

第二个命令修改名为 “web” 的服务,添加一个检查,每 10 秒钟通过 curl 发送一个请求,以验证 web 服务器是否可访问。与主机级健康检查一样,如果脚本退出时退出代码为 >= 2,检查将被标记为失败,服务将被认为不健康。

现在,使用 consul reload 命令重新启动 agent,或者发送一个 SIGHUP 信号。您应该会看到以下日志行:

==> Starting Consul agent...
...
    [INFO] agent: Synced service 'web'
    [INFO] agent: Synced check 'service:web'
    [INFO] agent: Synced check 'ping'
    [WARN] Check 'service:web' is now critical

前几行表示代理已经同步了新的定义,最后一行表示我们为 web 服务添加的检查是错误的,这是因为我们实际上并没有运行 web 服务器,所以 curl 测试失败了!

检查健康状态

现在我们已经添加了一些简单的检查,我们可以使用 HTTP API 来检查它们。首先,我们可以使用这个命令查找任何失败的检查(注意,这可以在任何节点上运行):

$ curl http://localhost:8500/v1/health/state/critical
[{"Node":"agent-two","CheckID":"service:web","Name":"Service 'web' check","Status":"critical","Notes":"","ServiceID":"web","ServiceName":"web","ServiceTags":["rails"]}]

我们可以看到只有一个 web 服务检查处于 critical  状态。

HTTP API

Consul 的主要接口都是 RESTful HTTP API,这些 API 可以用来 CRUD(增删查改)nodes、services、checks、configguration 等。

如果想查看所有和最新的 API,可以在 Consul API 查看。

标签: 微服务, 服务注册, 服务发现, Consul

添加新评论