摘抄:https://cloud.tencent.com/developer/article/1444664

一、什么是服务发现

微服务的框架体系中,服务发现是不能不提的一个模块。我相信了解或者熟悉微服务的童鞋应该都知道它的重要性。这里我只是简单的提一下,毕竟这不是我们的重点。我们看下面的一幅图片:

图中,客户端的一个接口,需要调用服务A, B, …, N。客户端必须要知道所有服务的网络位置的,以往的做法是配置是配置文件中,或者有些配置在数据库中。这里就带出几个问题:

  • 需要配置N个服务的网络位置,加大配置的复杂性
  • 服务的网络位置变化,都需要改变每个调用者的配置
  • 集群的情况下,难以做负载(反向代理的方式除外)

总结起来一句话:服务多了,配置很麻烦,问题多多

既然有这些问题,那么服务发现就是解决这些问题的。话说,怎么解决呢?我们再看一张图

与之前一张不同的是,加了个服务发现模块。图比较简单,这边文字描述下。服务A, B, …, N把当前自己的网络位置注册到服务发现模块(这里注册的意思就是告诉),服务发现就以K-V的方式记录下,K一般是服务名,V就是IP:PORT。服务发现模块定时的轮询查看这些服务能不能访问的了(这就是健康检查)。客户端在调用服务A, B, …, N的时候,就跑去服务发现模块问下它们的网络位置,然后再调用它们的服务。这样的方式是不是就可以解决上面的问题了呢?客户端完全不需要记录这些服务网络位置,客户端和服务端完全解耦!

图中的服务发现模块基本上就是中服务发现的作用了。





二、consul 简介

做服务发现的框架常用的有

  • zookeeper
  • eureka
  • etcd
  • consul

这里就不比较哪个好哪个差了,需要的童鞋自己谷歌百度。

那么consul是啥?consul就是提供服务发现的工具。然后下面是简单的介绍:

consul是分布式的、高可用、横向扩展的。

consul提供的一些关键特性:

  • service discovery:consul通过DNS或者HTTP接口使服务注册和服务发现变的很容易,一些外部服务,例如saas提供的也可以一样注册。
  • health checking:健康检测使consul可以快速的告警在集群中的操作。和服务发现的集成,可以防止服务转发到故障的服务上面。
  • key/value storage:一个用来存储动态配置的系统。提供简单的HTTP接口,可以在任何地方操作。
  • multi-datacenter:无需复杂的配置,即可支持任意数量的区域。 我们这里会介绍服务发现,健康检查,还有一些基本KV存储。多数据中心有机会另一篇文章再说。

总结:只要知道它是解决我上一部分提出的问题就行,其它的东西慢慢理解





三、consul的几个概念

上图来自于consul官方文档

我们只看数据中心1,可以看出consul的集群是由N个SERVER,加上M个CLIENT组成的。而不管是SERVER还是CLIENT,都是consul的一个节点,所有的服务都可以注册到这些节点上,正是通过这些节点实现服务注册信息的共享。除了这两个,还有一些小细节,一一简单介绍。

  • CLIENT: CLIENT表示consul的client模式,就是客户端模式。是consul节点的一种模式,这种模式下,所有注册到当前节点的服务会被转发到SERVER,本身是不持久化这些信息。
  • SERVER: SERVER表示consul的server模式,表明这个consul是个server,这种模式下,功能和CLIENT都一样,唯一不同的是,它会把所有的信息持久化的本地,这样遇到故障,信息是可以被保留的。
  • SERVER-LEADER: 中间那个SERVER下面有LEADER的字眼,表明这个SERVER是它们的老大,它和其它SERVER不一样的一点是,它需要负责同步注册的信息给其它的SERVER,同时也要负责各个节点的健康监测。

其它信息其它信息包括它们之间的通信方式,还有一些协议信息,算法。它们是用于保证节点之间的数据同步,实时性要求等等一系列集群问题的解决。这些有兴趣的自己看看官方文档

AI解读

一、数据中心内部(以 Datacenter 1 为例)

  1. 节点角色与通信
    • Server 节点分为LEADER(领导者)和FOLLOWER(跟随者)。
      • Leader Forwarding:非 Leader 的 Server(如 Follower)收到客户端请求后,通过 LEADER FORWARDING 将请求转发给 Leader 处理,确保操作的集中协调。
      • Replication:Leader 处理完数据变更(如写操作)后,通过 REPLICATION(复制)将数据同步给同数据中心内的 Follower,保证数据一致性,使用 TCP/8300 端口。
    • Client 节点:通过 RPC(TCP/8300) 与 Server 通信,发送服务发现、配置查询等请求。
  2. LAN Gossip 机制
    • 同一数据中心内的节点(Client 和 Server)通过 LAN GOSSIP(TCP/UDP/8301) 通信,实现成员发现、状态同步(如节点存活检测),确保集群感知内部节点变化。

二、跨数据中心(Datacenter 1 与 Datacenter 2)

  1. WAN Gossip 通信
    • 不同数据中心的 Server 借助 WAN GOSSIP(TCP/UDP/8302) 跨网络(通过 Internet)通信,用于跨数据中心的成员发现和状态同步,支持多数据中心架构。
  2. 跨数据中心请求处理
    • Remote DC Forwarding:若本地数据中心无法处理请求(如跨数据中心的服务发现),通过 REMOTE DC FORWARDING(TCP/8300) 将请求转发到目标数据中心的 Server 处理,实现跨数据中心的协作。

三、整体协作逻辑

  • 请求处理:客户端请求优先由本地数据中心的 Server 处理,非 Leader 节点通过 Leader Forwarding 集中到 Leader;跨数据中心请求通过 WAN Gossip 和 Remote DC Forwarding 协作。
  • 数据一致性:通过 Replication 在数据中心内同步数据,通过多数据中心的通信机制保证全局状态感知,最终实现分布式环境下的服务发现、配置管理等高可用功能。




四、安装 Consul

1
brew install consul

检查 consul 是否可用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
~ ··················································································································································································································································································· 42s 00:04:18
❯ consul
Usage: consul [--version] [--help] <command> [<args>]

Available commands are:
acl Interact with Consul's ACLs
agent Runs a Consul agent
catalog Interact with the catalog
config Interact with Consul's Centralized Configurations
connect Interact with Consul Connect
debug Records a debugging archive for operators
event Fire a new event
exec Executes a command on Consul nodes
force-leave Forces a member of the cluster to enter the "left" state
info Provides debugging information for operators.
intention Interact with Connect service intentions
join Tell Consul agent to join cluster
keygen Generates a new encryption key
keyring Manages gossip layer encryption keys
kv Interact with the key-value store
leave Gracefully leaves the Consul cluster and shuts down
lock Execute a command holding a lock
login Login to Consul using an auth method
logout Destroy a Consul token created with login
maint Controls node or service maintenance mode
members Lists the members of a Consul cluster
monitor Stream logs from a Consul agent
operator Provides cluster-level tools for Consul operators
peering Create and manage peering connections between Consul clusters
reload Triggers the agent to reload configuration files
resource Interact with Consul's resources
rtt Estimates network round trip time between nodes
services Interact with services
snapshot Saves, restores and inspects snapshots of Consul server state
tls Builtin helpers for creating CAs and certificates
troubleshoot CLI tools for troubleshooting Consul service mesh
validate Validate config files/directories
version Prints the Consul version
watch Watch for changes in Consul

如果出现上面这样代表consul是没问题的。





五、运行 Consul Agent

Consul安装之后,Agent必须运行。 Agent可以在服务器或客户端模式下运行。 每个数据中心都必须至少有一台服务器,但推荐使用3台或5台服务器。 一个单一的服务器部署是非常不推荐的,因为在故障情况下数据丢失是不可避免的。

所有其他代理以客户端模式运行。 客户端是一个非常轻量级的进程,它注册服务,运行健康检查,并将查询转发给服务器。 代理程序必须在集群中的每个节点上运行。

为了简单起见,我们现在将以开发模式启动Consul代理。 这种模式对于快速简单地启动单节点Consul环境非常有用。 它并不打算在生产中使用,因为它不会持续任何状态。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
~ ······················································································································································································································································································· 00:19:22
❯ consul agent -dev
==> Starting Consul agent...
Version: '1.20.5'
Build Date: '2025-03-11 10:16:18 +0000 UTC'
Node ID: '270aaffe-66cf-67ce-80a9-d3e1985cd60e'
Node name: 'GJVXWHGXQ1'
Datacenter: 'dc1' (Segment: '<all>')
Server: true (Bootstrap: false)
Client Addr: [127.0.0.1] (HTTP: 8500, HTTPS: -1, gRPC: 8502, gRPC-TLS: 8503, DNS: 8600)
Cluster Addr: 127.0.0.1 (LAN: 8301, WAN: 8302)
Gossip Encryption: false
Auto-Encrypt-TLS: false
ACL Enabled: false
Reporting Enabled: false
ACL Default Policy: allow
HTTPS TLS: Verify Incoming: false, Verify Outgoing: false, Min Version: TLSv1_2
gRPC TLS: Verify Incoming: false, Min Version: TLSv1_2
Internal RPC TLS: Verify Incoming: false, Verify Outgoing: false (Verify Hostname: false), Min Version: TLSv1_2

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

2025-03-19T00:19:25.846+0800 [DEBUG] agent.grpc.balancer: switching server: target=consul://dc1.270aaffe-66cf-67ce-80a9-d3e1985cd60e/server.dc1 from=<none> to=<none>
2025-03-19T00:19:25.857+0800 [INFO] agent.server.raft: initial configuration: index=1 servers="[{Suffrage:Voter ID:270aaffe-66cf-67ce-80a9-d3e1985cd60e Address:127.0.0.1:8300}]"
2025-03-19T00:19:25.857+0800 [INFO] agent.server.raft: entering follower state: follower="Node at 127.0.0.1:8300 [Follower]" leader-address= leader-id=
2025-03-19T00:19:25.859+0800 [INFO] agent.server.serf.wan: serf: EventMemberJoin: GJVXWHGXQ1.dc1 127.0.0.1
2025-03-19T00:19:25.859+0800 [INFO] agent.server.serf.lan: serf: EventMemberJoin: GJVXWHGXQ1 127.0.0.1
2025-03-19T00:19:25.859+0800 [INFO] agent.router: Initializing LAN area manager
2025-03-19T00:19:25.860+0800 [DEBUG] agent.grpc.balancer: switching server: target=consul://dc1.270aaffe-66cf-67ce-80a9-d3e1985cd60e/server.dc1 from=<none> to=dc1-127.0.0.1:8300
2025-03-19T00:19:25.860+0800 [INFO] agent.server: Adding LAN server: server="GJVXWHGXQ1 (Addr: tcp/127.0.0.1:8300) (DC: dc1)"
2025-03-19T00:19:25.860+0800 [INFO] agent.server: Handled event for server in area: event=member-join server=GJVXWHGXQ1.dc1 area=wan
2025-03-19T00:19:25.861+0800 [INFO] agent.server.autopilot: reconciliation now disabled
2025-03-19T00:19:25.863+0800 [DEBUG] agent.server.autopilot: autopilot is now running
...(省略)
默认端口
1
2
3
4
~ ······················································································································································································································································································· 01:10:44
❯ lsof -i :8500
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
consul 84416 guowei.gong 17u IPv4 0x8f4e9332d460a11b 0t0 TCP localhost:fmtp (LISTEN)

Consul 的 agent 启动后,默认端口通常是 8500 ,这是其 HTTP API 和 Web 界面的默认端口。通过浏览器访问http://localhost:8500 ,在正常启动且端口未被占用的情况下,能看到 Consul 的管理界面。

不过,端口也可以在启动时通过配置参数修改,例如在命令中使用 -http-port 参数指定其他端口 ,像consul agent -dev -http-port=8600,这样启动后访问端口就变为 8600 。 此外,Consul 还有用于其他功能的默认端口,如用于 LAN Gossip 协议的 8301 端口、用于 WAN Gossip 协议的 8302 端口等。





六、集群成员

在另一个终端运行consul members,可以看到Consul集群的成员。 应该只看到一个成员(你自己):

1
2
3
4
~ ······················································································································································································································································································· 00:38:34
❯ consul members
Node Address Status Type Build Protocol DC Partition Segment
GJVXWHGXQ1 127.0.0.1:8301 alive server 1.20.5 2 dc1 default <all>

输出显示了我们自己的节点,它正在运行的地址,运行状况,在集群中的角色以及一些版本信息。 额外的元数据可以通过提供-detailed标志来查看。

1
2
3
4
~ ······················································································································································································································································································· 00:44:04
❯ consul members -detailed
Node Address Status Tags
GJVXWHGXQ1 127.0.0.1:8301 alive acls=0,ap=default,build=1.20.5:74efe419,dc=dc1,ft_fs=1,ft_si=1,grpc_port=8502,grpc_tls_port=8503,id=270aaffe-66cf-67ce-80a9-d3e1985cd60e,port=8300,raft_vsn=3,role=consul,segment=<all>,vsn=2,vsn_max=3,vsn_min=2,wan_join_port=8302

members命令的输出基于gossip协议,并最终一致。 也就是说,在任何时候,当地代理所看到的可能与服务器上的状态不完全一致。 要获得完全一致,请使用HTTP API再将HTTP请求转发给Consul服务器:

为什么不一致?

Consul 中 members 命令输出基于 Gossip 协议出现不一致,主要源于 Gossip 协议的异步特性与 Consul 服务器(Server)的强一致性机制差异,具体原因如下:

1. Gossip 协议的异步传播特性

  • 非实时同步:Gossip 协议通过节点间随机、异步的消息传播同步状态。本地代理(Agent)通过 Gossip 收集成员信息时,消息在集群中扩散需要时间。例如,新节点加入或旧节点故障时,状态变更消息可能因网络延迟、传播路径等因素,无法立即同步到所有节点。
  • 最终一致性:Gossip 仅保证 “最终一致性”,而非强一致性。在传播完成前,不同节点(包括本地代理)看到的成员列表可能是旧状态,导致本地 members 命令输出与集群真实状态存在短暂偏差。

2. Consul 服务器的强一致性保障

  • Raft 协议主导:Consul 服务器(尤其是 Leader 节点)通过 Raft 共识算法维护强一致性。所有关键状态(如成员列表、服务注册数据)的写操作都由 Leader 处理,并通过 Raft 日志复制到其他服务器,确保服务器集群状态实时一致。
  • HTTP API 的权威获取:当通过 HTTP API 将请求转发给 Consul 服务器时,本质是直接查询由 Raft 保证强一致性的权威数据源。服务器会返回当前集群的最新、一致状态,跳过 Gossip 协议的异步传播延迟。

总结

Gossip 协议的设计目标是去中心化、高容错的成员发现,牺牲了实时性;而 Consul 服务器借助 Raft 实现强一致性。因此,依赖 Gossip 的 members 命令可能因异步传播延迟看到旧状态,而 HTTP API 直接查询服务器(Raft 强一致存储)能获取最新一致结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
~ ·········································································································································································································································· 12:23:53
❯ curl localhost:8500/v1/catalog/nodes
[
{
"ID": "394c8c97-ef46-5f5f-fcb5-df5f9ca6d04a",
"Node": "GJVXWHGXQ1",
"Address": "127.0.0.1",
"Datacenter": "dc1",
"TaggedAddresses": {
"lan": "127.0.0.1",
"lan_ipv4": "127.0.0.1",
"wan": "127.0.0.1",
"wan_ipv4": "127.0.0.1"
},
"Meta": {
"consul-network-segment": "",
"consul-version": "1.20.5"
},
"CreateIndex": 12,
"ModifyIndex": 16
}
]%

除了HTTP API之外,还可以使用DNS接口查询节点。 请注意,必须确保将DNS查找默认情况下指向在端口8600上运行的Consul代理的DNS服务器。 DNS条目的格式(如GJVXWHGXQ1.node.consul)将在后面详细介绍。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
~ ···································································································································································································································································· 14:08:31
❯ dig @127.0.0.1 -p 8600 GJVXWHGXQ1.node.consul

; <<>> DiG 9.10.6 <<>> @127.0.0.1 -p 8600 GJVXWHGXQ1.node.consul
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 22286
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 3
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;GJVXWHGXQ1.node.consul. IN A

;; ANSWER SECTION:
GJVXWHGXQ1.node.consul. 0 IN A 127.0.0.1

;; ADDITIONAL SECTION:
GJVXWHGXQ1.node.consul. 0 IN TXT "consul-version=1.20.5"
GJVXWHGXQ1.node.consul. 0 IN TXT "consul-network-segment="

;; Query time: 3 msec
;; SERVER: 127.0.0.1#8600(127.0.0.1)
;; WHEN: Wed Mar 19 14:27:35 CST 2025
;; MSG SIZE rcvd: 137
为什么是8600

在使用 dig 命令时指定 -p 8600,也就是将查询请求发送到端口 8600,这往往和 Consul 服务相关。下面为你详细解释:

Consul 服务默认端口

Consul 是 HashiCorp 公司推出的一款用于服务发现、配置和分段的工具。它自带 DNS 接口,能够让用户借助 DNS 查询来发现服务和节点信息。在默认情况下,Consul 代理的 DNS 服务器会监听 8600 端口。

当你使用 dig 命令去查询 Consul 管理的域名时,就需要把请求发送到 Consul 代理的 DNS 服务器所监听的端口,也就是 8600 端口。例如,你想通过 DNS 查询来获取 Consul 中某个服务的节点信息,就可以执行如下命令:

1
dig @127.0.0.1 -p 8600 service_name.service.consul

这里的 @127.0.0.1 表示向本地的 Consul 代理发起请求,-p 8600 则表明请求会被发送到 8600 端口。

配置修改

虽然 Consul 默认的 DNS 端口是 8600,但你也可以通过配置文件或者命令行参数对其进行修改。要是你把 Consul 代理的 DNS 端口修改成了其他值,那么在使用 dig 命令时,就需要将 -p 参数的值修改为你所设置的新端口号。

多服务端口占用情况

在某些情况下,服务器上可能会同时运行多个服务,这些服务可能会使用不同的端口。为了避免端口冲突,你可能需要对 Consul 代理的 DNS 端口进行调整。此时,你要依据实际的配置来指定正确的端口号。





七、停止 Agent

Consul Agent 的两种停止方式及影响

  1. 优雅停止(使用 Ctrl-C)
    • 操作本质:通过发送中断信号(Ctrl-C)告知 Agent 停止运行。
    • 集群交互:Agent 会主动通知集群内其他成员 “自己即将离开”。
    • 后续处理:
      • 该节点的服务注册信息、健康检查记录会从 Consul 目录中直接删除,相当于向集群明确宣告 “该节点不再参与服务”。
      • 对集群其他成员的影响较小,属于 “友好退出”,其他节点能正常更新集群成员状态。
  2. 强行终止(直接杀死进程)
    • 操作本质:未经通知直接结束 Agent 进程(如 kill -9)。
    • 集群交互:集群其他成员无法收到节点离开的通知,只能通过心跳检测等机制发现该节点 “故障”。
    • 后续处理:
      • 该节点的服务健康状态会被标记为 “关键(Critical)”,但服务注册信息不会从目录中删除
      • Consul 会持续尝试重新连接这个 “故障节点”,期待它从网络波动等临时问题中恢复。若节点实际已退出,这种重试会浪费资源,且集群无法及时感知真实状态。

服务器角色的特殊注意事项

当 Agent 以服务器(Server)角色运行时,优雅停止尤为重要:

  • 一致性协议影响:Consul 服务器通过 Raft 等协议维护集群数据一致性。若服务器被强行终止,可能导致 Raft 协议的日志同步、领导者选举等流程中断,引发集群可用性问题(如短暂无法处理写请求)。
  • 安全操作要求:为避免破坏集群一致性,添加或删除服务器节点需遵循官方指南的规范流程(如先优雅退出,再执行删除操作),确保集群在节点变更过程中仍能稳定运行。




八、注册服务

8.1 服务定义

服务可以通过提供服务定义或通过对HTTP API进行适当的调用来注册。

服务定义是注册服务最常用的方式,所以我们将在这一步中使用这种方法。 我们将建立在上一步中介绍的代理配置。

首先,为Consul配置创建一个目录。 Consul将所有配置文件加载到配置目录中,因此Unix系统上的一个通用约定是将目录命名为/etc/consul.d(.d后缀意味着“该目录包含一组配置文件”)。

1
sudo mkdir /etc/consul.d

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

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

现在,重新启动代理程序,提供配置目录:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
~ ························································································································································································································································· 11h 32m 5s 11:51:30
❯ consul agent -dev -config-dir=/etc/consul.d
==> Starting Consul agent...
Version: '1.20.5'
Build Date: '2025-03-11 10:16:18 +0000 UTC'
Node ID: '394c8c97-ef46-5f5f-fcb5-df5f9ca6d04a'
Node name: 'GJVXWHGXQ1'
Datacenter: 'dc1' (Segment: '<all>')
Server: true (Bootstrap: false)
Client Addr: [127.0.0.1] (HTTP: 8500, HTTPS: -1, gRPC: 8502, gRPC-TLS: 8503, DNS: 8600)
Cluster Addr: 127.0.0.1 (LAN: 8301, WAN: 8302)
Gossip Encryption: false
Auto-Encrypt-TLS: false
ACL Enabled: false
Reporting Enabled: false
ACL Default Policy: allow
HTTPS TLS: Verify Incoming: false, Verify Outgoing: false, Min Version: TLSv1_2
gRPC TLS: Verify Incoming: false, Min Version: TLSv1_2
Internal RPC TLS: Verify Incoming: false, Verify Outgoing: false (Verify Hostname: false), Min Version: TLSv1_2

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

2025-03-19T11:51:54.790+0800 [DEBUG] agent.grpc.balancer: switching server: target=consul://dc1.394c8c97-ef46-5f5f-fcb5-df5f9ca6d04a/server.dc1 from=<none> to=<none>
2025-03-19T11:51:54.801+0800 [INFO] agent.server.raft: initial configuration: index=1 servers="[{Suffrage:Voter ID:394c8c97-ef46-5f5f-fcb5-df5f9ca6d04a Address:127.0.0.1:8300}]"
2025-03-19T11:51:54.801+0800 [INFO] agent.server.raft: entering follower state: follower="Node at 127.0.0.1:8300 [Follower]" leader-address= leader-id=
2025-03-19T11:51:54.803+0800 [INFO] agent.server.serf.wan: serf: EventMemberJoin: GJVXWHGXQ1.dc1 127.0.0.1
2025-03-19T11:51:54.804+0800 [INFO] agent.server.serf.lan: serf: EventMemberJoin: GJVXWHGXQ1 127.0.0.1
...(省略)
2025-03-19T11:51:54.867+0800 [DEBUG] agent.server.cert-manager: leaf certificate watch fired - updating auto TLS certificate: uri=spiffe://4c442345-39e8-a7c8-6d16-63d0adbd5ea8.consul/agent/server/dc/dc1
2025-03-19T11:51:54.882+0800 [DEBUG] agent: Skipping remote check since it is managed automatically: check=serfHealth
2025-03-19T11:51:54.882+0800 [INFO] agent: Synced node info
2025-03-19T11:51:54.883+0800 [INFO] agent: Synced service: service=web
2025-03-19T11:51:54.883+0800 [DEBUG] agent: Node info in sync
2025-03-19T11:51:54.883+0800 [DEBUG] agent: Service in sync: service=web
2025-03-19T11:51:55.287+0800 [INFO] agent.server: federation state anti-entropy synced
2025-03-19T11:51:55.808+0800 [DEBUG] agent.server.cert-manager: CA config watch fired - updating auto TLS server name: name=server.dc1.peering.4c442345-39e8-a7c8-6d16-63d0adbd5ea8.consul
2025-03-19T11:51:56.280+0800 [DEBUG] agent: Skipping remote check since it is managed automatically: check=serfHealth
2025-03-19T11:51:56.280+0800 [DEBUG] agent: Node info in sync
2025-03-19T11:51:56.280+0800 [DEBUG] agent: Service in sync: service=web

您会注意到它在输出中“同步”了Web服务。 这意味着代理程序从配置文件加载了服务定义,并已成功将其注册到服务目录中。

如果您想注册多个服务,您可以在Consul配置目录中创建多个服务定义文件。





8.2 查询服务

一旦代理启动并且服务同步,我们可以使用DNS或HTTP API来查询服务。

DNS API:我们首先使用DNS API来查询我们的服务。 对于DNS API,服务的DNS名称是<NAME>.service.consul。 默认情况下,所有DNS名称始终在consul命名空间中,尽管这是可配置的。 服务子域告诉Consul我们正在查询服务,NAME是服务的名称。

对于我们注册的Web服务,这些约定和设置会生成web.service.consul的完全限定的域名:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
~ ······································································································································································································································ 15s 11:49:17
❯ dig @127.0.0.1 -p 8600 web.service.consul

; <<>> DiG 9.10.6 <<>> @127.0.0.1 -p 8600 web.service.consul
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 252
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;web.service.consul. IN A

;; ANSWER SECTION:
web.service.consul. 0 IN A 127.0.0.1

;; Query time: 3 msec
;; SERVER: 127.0.0.1#8600(127.0.0.1)
;; WHEN: Wed Mar 19 11:57:11 CST 2025
;; MSG SIZE rcvd: 63

正如你所看到的,一个A记录返回了服务可用的节点的IP地址。 A记录只能保存IP地址。

您也可以使用DNS API来检索整个地址/端口对作为SRV记录:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
~ ···································································································································································································································································· 12:25:10
❯ dig @127.0.0.1 -p 8600 web.service.consul SRV

; <<>> DiG 9.10.6 <<>> @127.0.0.1 -p 8600 web.service.consul SRV
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 56020
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 4
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;web.service.consul. IN SRV

;; ANSWER SECTION:
web.service.consul. 0 IN SRV 1 1 80 GJVXWHGXQ1.node.dc1.consul.

;; ADDITIONAL SECTION:
GJVXWHGXQ1.node.dc1.consul. 0 IN A 127.0.0.1
GJVXWHGXQ1.node.dc1.consul. 0 IN TXT "consul-version=1.20.5"
GJVXWHGXQ1.node.dc1.consul. 0 IN TXT "consul-network-segment="

;; Query time: 16 msec
;; SERVER: 127.0.0.1#8600(127.0.0.1)
;; WHEN: Wed Mar 19 12:25:17 CST 2025
;; MSG SIZE rcvd: 179

SRV记录表示Web服务正在端口80上运行,并且存在于节点GJVXWHGXQ1.node.dc1.consul.上。DNS使用该记录的A记录返回附加部分。

最后,我们也可以使用DNS API来按标签过滤服务。 基于标记的服务查询的格式是[TAG.]<NAME>.service.consul。 在下面的例子中,我们向Consul询问所有带有“rails”标签的web服务。 自从我们使用该标签注册我们的服务后,我们得到了成功的回应:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
~ ···································································································································································································································································· 12:27:05
❯ dig @127.0.0.1 -p 8600 rails.web.service.consul

; <<>> DiG 9.10.6 <<>> @127.0.0.1 -p 8600 rails.web.service.consul
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 58922
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;rails.web.service.consul. IN A

;; ANSWER SECTION:
rails.web.service.consul. 0 IN A 127.0.0.1

;; Query time: 4 msec
;; SERVER: 127.0.0.1#8600(127.0.0.1)
;; WHEN: Wed Mar 19 14:06:50 CST 2025
;; MSG SIZE rcvd: 69

HTTP API:除了DNS API之外,HTTP API还可以用来查询服务:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
~ ···································································································································································································································································· 14:06:50
❯ curl http://localhost:8500/v1/catalog/service/web
[
{
"ID": "394c8c97-ef46-5f5f-fcb5-df5f9ca6d04a",
"Node": "GJVXWHGXQ1",
"Address": "127.0.0.1",
"Datacenter": "dc1",
"TaggedAddresses": {
"lan": "127.0.0.1",
"lan_ipv4": "127.0.0.1",
"wan": "127.0.0.1",
"wan_ipv4": "127.0.0.1"
},
"NodeMeta": {
"consul-network-segment": "",
"consul-version": "1.20.5"
},
"ServiceKind": "",
"ServiceID": "web",
"ServiceName": "web",
"ServiceTags": [
"rails"
],
"ServiceAddress": "",
"ServiceWeights": {
"Passing": 1,
"Warning": 1
},
"ServiceMeta": {},
"ServicePort": 80,
"ServiceSocketPath": "",
"ServiceEnableTagOverride": false,
"ServiceProxy": {
"Mode": "",
"MeshGateway": {},
"Expose": {}
},
"ServiceConnect": {},
"ServiceLocality": null,
"CreateIndex": 17,
"ModifyIndex": 17
}
]%
使用api查找所有服务
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
~ ···································································································································································································································································· 14:42:44
❯ curl http://localhost:8500/v1/agent/services
{
"web": {
"ID": "web",
"Service": "web",
"Tags": [
"rails"
],
"Meta": {},
"Port": 80,
"Address": "",
"Weights": {
"Passing": 1,
"Warning": 1
},
"EnableTagOverride": false,
"Datacenter": "dc1"
}
}%

目录API提供了托管给定服务的所有节点。 正如我们稍后将看到的健康检查一样,您通常只需要查询检查通过的健康实例。 这是DNS正在做的事情。 这是一个查询只查找健康的实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
~ ···································································································································································································································································· 14:27:35
❯ curl 'http://localhost:8500/v1/health/service/web?passing'
[
{
"Node": {
"ID": "394c8c97-ef46-5f5f-fcb5-df5f9ca6d04a",
"Node": "GJVXWHGXQ1",
"Address": "127.0.0.1",
"Datacenter": "dc1",
"TaggedAddresses": {
"lan": "127.0.0.1",
"lan_ipv4": "127.0.0.1",
"wan": "127.0.0.1",
"wan_ipv4": "127.0.0.1"
},
"Meta": {
"consul-network-segment": "",
"consul-version": "1.20.5"
},
"CreateIndex": 12,
"ModifyIndex": 16
},
"Service": {
"ID": "web",
"Service": "web",
"Tags": [
"rails"
],
"Address": "",
"Meta": null,
"Port": 80,
"Weights": {
"Passing": 1,
"Warning": 1
},
"EnableTagOverride": false,
"Proxy": {
"Mode": "",
"MeshGateway": {},
"Expose": {}
},
"Connect": {},
"PeerName": "",
"CreateIndex": 17,
"ModifyIndex": 17
},
"Checks": [
{
"Node": "GJVXWHGXQ1",
"CheckID": "serfHealth",
"Name": "Serf Health Status",
"Status": "passing",
"Notes": "",
"Output": "Agent alive and reachable",
"ServiceID": "",
"ServiceName": "",
"ServiceTags": [],
"Type": "",
"Interval": "",
"Timeout": "",
"ExposedPort": 0,
"Definition": {},
"CreateIndex": 12,
"ModifyIndex": 12
}
]
}
]%




8.3 更新服务

在 Consul 中,服务更新的两种方式(配置文件 + SIGHUP 或 HTTP API)对应不同的使用场景,核心区别在于 静态配置管理动态自动化。以下是详细解释:


1. 方式一:通过配置文件更新 + SIGHUP 信号

操作流程

  1. 修改配置文件:

    • 编辑 Consul Agent 的服务定义文件(如 /etc/consul.d/web.json)。
    • 调整服务参数(如健康检查频率、标签、元数据等)。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    {
    "service": {
    "name": "web",
    "port": 8080,
    "check": {
    "http": "http://localhost:8080/health",
    "interval": "10s" // 修改检查间隔
    }
    }
    }
  2. 发送 SIGHUP 信号:

    1
    2
    3
    4
    5
    # 找到 Consul Agent 的进程 ID(PID)
    PID=$(pgrep consul)
    # 发送 SIGHUP 信号
    # -HUP:指定发送 SIGHUP 信号(Hang Up,挂断信号)。
    kill -HUP $PID
    • 作用:通知 Consul Agent 重新加载配置文件,无需重启服务。

特点

  • 零停机:Agent 在重新加载配置时,现有服务发现和健康检查继续运行。
  • 适合场景:
    • 静态环境(如基础设施即代码管理)。
    • 需要通过版本控制管理配置。
  • 注意事项:
    • 配置文件语法错误可能导致加载失败,需提前验证。
    • 仅支持修改 Agent 本地注册的服务,无法跨节点操作。

2. 方式二:通过 HTTP API 动态更新

操作示例

  • 添加 / 修改服务:(agent,重启就没了)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    curl -X PUT -d @- http://localhost:8500/v1/agent/service/register <<EOF
    {
    "Name": "web02",
    "Port": 8080,
    "Check": {
    "HTTP": "http://localhost:8080/health",
    "Interval": "5s"
    }
    }
    EOF
  • 删除服务:

    1
    2
    3
    4
    5
    curl -X PUT -d @- http://localhost:8500/v1/agent/service/deregister <<EOF
    {
    "ServiceID": "web02"
    }
    EOF

特点

  • 实时生效:API 调用后立即更新服务注册信息。
  • 灵活控制:
    • 支持动态添加、删除、修改任意服务。
    • 可通过脚本或 CI/CD 集成自动化管理。
  • 适合场景:
    • 动态环境(如 Kubernetes 中服务实例频繁变化)。
    • 需要实时响应业务需求的场景。
  • 注意事项:
    • API 操作需配置 ACL 令牌(Access Control List)保证安全性。
    • 跨数据中心操作需通过 Consul Server 集群。

3. 两种方式对比

维度 配置文件 + SIGHUP HTTP API
操作方式 修改文件后发送信号 直接调用 API
生效时间 秒级(需 Agent 加载配置) 毫秒级(API 直接返回)
适用场景 静态配置、基础设施即代码 动态环境、自动化管理
配置范围 仅本地 Agent 管理的服务 可跨节点操作(需 Server 权限)
安全性 依赖文件系统权限 依赖 ACL 令牌和网络访问控制
典型使用 手动维护配置、版本控制 云原生部署、服务网格动态调整

4. 实际应用建议

  • 选择配置文件 + SIGHUP:
    • 服务配置稳定,变更频率低。
    • 需要审计配置历史(通过版本控制)。
  • 选择 HTTP API:
    • 服务实例动态变化(如容器化部署)。
    • 需要与监控、日志系统集成自动化流程。
  • 混合使用:
    • 基础配置通过文件管理,动态参数(如权重、标签)通过 API 调整。

总结

  • 配置文件 + SIGHUP:通过修改文件并通知 Agent 重载,适合静态管理。
  • HTTP API:实时动态操作,适合自动化和云原生场景。
  • 核心目标:两种方式均支持 零停机更新,但适用的场景和管理粒度不同。




九、Consul 集群

我们已经开始了我们的第一个代理,并注册和查询该代理的服务。 这显示了使用Consul是多么的容易,但并没有表明如何将其扩展到可扩展的生产级服务发现基础设施。 在这一步中,我们将创建我们的第一个真正的集群与多个成员。

当一个Consul代理启动时,它不知道任何其他节点:它是一个孤立的集群。 要了解其他集群成员,代理必须加入现有集群。 要加入现有的集群,只需要知道一个现有的成员。 代理加入后,会与该成员通讯,并迅速发现集群中的其他成员。 Consul代理可以加入任何其他代理,而不仅仅是服务器模式下的代理。





9.1 启动代理

在之前的示例里,我们运用 -dev 参数迅速搭建了一个开发服务器。不过,这种方式在集群环境中并不适用。接下来,我们会去掉 -dev 标志,转而使用适合集群环境的配置。

  1. 节点名称设置

集群中的每个节点都要有独一无二的名称。默认情况下,Consul 会采用机器的主机名作为节点名称,但我们可以通过 -node 命令行选项手动指定节点名称。这样做能确保在集群中每个节点的标识清晰明确,避免名称冲突。

  1. 绑定地址指定

使用 -bind 选项来指定 Consul 监听的地址。这个地址必须能被集群中的其他所有节点访问到。虽然不指定绑定地址 Consul 也能运行,它默认会尝试监听系统上的所有 IPv4 接口,但在生产环境中,服务器通常有多个网络接口,若找到多个私有 IP,Consul 可能会因无法确定使用哪个 IP 而启动失败。所以,指定绑定地址能避免 Consul 绑定到错误的接口。

  1. 服务器节点标识

第一个节点将作为这个集群里唯一的服务器节点,通过 -server 选项来明确标识该节点的服务器角色。

  1. 预期服务器节点数量设置

-bootstrap-expect 选项的作用是告知 Consul 服务器,我们预期加入集群的其他服务器节点的数量。这个选项能让 Consul 延迟复制日志的初始化过程,直到预期数量的服务器节点都成功加入集群,有助于保证集群数据的一致性和稳定性。

  1. 启用脚本检查

我们把 -enable-script-checks 选项设置为 true,目的是开启可以执行外部脚本的健康检查功能。后续的示例中会用到这个功能。不过在生产环境中,为了控制注册任意脚本的权限,你需要配置访问控制列表(ACL)。

  1. 配置文件目录指定

使用 -config-dir 选项来指定存放服务和检查定义的位置。Consul 会从这个目录读取配置文件,以此来了解服务的相关信息和健康检查规则。

综合以上各项设置,最终形成的 Consul 代理启动命令如下:

1
consul agent -server -bootstrap-expect=1 -data-dir=/tmp/consul -node=agent-one -bind=<ip地址_A> -enable-script-checks=true -config-dir=/etc/consul.d

这个命令能启动一个适用于集群环境的 Consul 代理节点,为后续的服务注册、发现和健康检查等功能提供基础支持。


启动第二个节点

在另一个终端中启动第二个 Consul 节点,与第一个节点不同,这个节点作为客户端,而非服务器。具体配置如下:

  • -data-dir:指定 Consul 用于存储数据的目录,这里设置为 /tmp/consul。这个目录用于存放 Consul 运行过程中的各种数据,如状态信息、日志等。
  • -node:为该节点指定一个唯一的名称,这里设置为 agent - two。节点名称用于在集群中标识该节点,方便管理和识别。
  • -bind:指定该节点监听的 IP 地址,这里设置为 192.168.100.102。这个 IP 地址必须能够被集群中的其他节点访问到,以确保节点间的通信正常。
  • -enable-script-checks:设置为 true 以启用可以执行外部脚本的健康检查功能。后续可以利用这个功能对服务进行更灵活的健康检查。
  • -config-dir:指定存放服务和检查定义的配置文件的目录,这里设置为 /etc/consul.d。Consul 会从这个目录读取配置文件,了解服务的相关信息和健康检查规则。

综合以上配置,启动第二个节点的命令如下:

1
consul agent -data-dir=/tmp/consul -node=agent-two -bind=<ip地址_B> -enable-script-checks=true -config-dir=/etc/consul.d

初始状态验证

启动后,当前有两个 Consul 代理在运行,一个是之前启动的服务器节点,另一个是刚刚启动的客户端节点。但此时,两个节点还没有建立连接,它们各自认为自己处于一个单节点集群中。

你可以通过在每个节点上运行 consul members 命令来验证这一点。该命令会列出当前节点所感知到的集群成员信息。由于两个节点还未连接,每个节点运行该命令时只会看到自己这一个成员。例如,在第一个节点上运行 consul members 会只显示 agent - one,在第二个节点上运行则只会显示 agent - two。后续还需要通过一些操作(如使用 consul join 命令)让这两个节点相互发现并加入同一个集群。


默认port

在使用 -bind 选项指定 Consul 监听的 IP 地址时,若未明确指定端口,Consul 会使用默认端口。以下为你详细介绍默认端口的情况:

不同服务的默认端口

Consul 提供多种服务,每种服务都有其对应的默认端口:

  1. Server RPC(RPC 层):端口号为 8300,该端口用于 Consul 服务器节点之间的通信,主要处理服务器间的内部请求。
  2. Serf LAN(局域网内节点通信):端口号为 8301,此端口用于局域网内 Consul 节点间的 Gossip 协议通信,负责节点的发现、状态同步等。
  3. Serf WAN(广域网节点通信):端口号为 8302,若有跨数据中心或者广域网的 Consul 节点通信需求,就会用到这个端口。
  4. HTTP API:端口号为 8500,你可以通过这个端口使用 HTTP 协议与 Consul 进行交互,例如进行服务注册、发现、配置读取等操作。
  5. DNS 接口:端口号为 8600,借助该端口,应用程序能够使用 DNS 查询的方式来发现 Consul 中的服务。
只有一台机器怎么练习?

避免端口冲突

server

1
2
3
4
5
6
7
8
9
10
11
12
13
consul agent \
-server \
-bootstrap-expect=1 \
-data-dir=/tmp/consul-1 \
-node=agent-one \
-bind=127.0.0.1 \
-http-port=8501 \
-dns-port=8601 \
-serf-lan-port=8311 \
-serf-wan-port=8312 \
-server-port=8300 \
-enable-script-checks=true \
-config-dir=/etc/consul.d

client(使用-join可以直接加入,这里我们暂时不加入,使用下面的命令)

1
2
3
4
5
6
7
8
9
10
11
consul agent \
-data-dir=/tmp/consul-2 \
-node=node2 \
-bind=127.0.0.1 \
-http-port=8502 \
-dns-port=8602 \
-serf-lan-port=8321 \
-serf-wan-port=8322 \
-server-port=8310 \
-enable-script-checks=true \
-config-dir=/etc/consul.d
1
2
3
4
5
6
7
8
9
10
11
12
consul agent \
-data-dir=/tmp/consul-2 \
-node=agent-two \
-bind=127.0.0.1 \
-http-port=8502 \
-dns-port=8602 \
-serf-lan-port=8321 \
-serf-wan-port=8322 \
-server-port=8310 \
-enable-script-checks=true \
-config-dir=/etc/consul.d \
-join=127.0.0.1:8311




9.2 加入集群

现在,我们将通过在新终端中运行以下命令来让client加入server:

1
consul join 192.168.100.101
只有一台机器怎么练习?
1
consul join -http-addr=127.0.0.1:8502 127.0.0.1:8311

您应该在每个代理日志中看到一些日志输出。 如果仔细阅读,您会看到他们收到了加入信息。 如果你对每个代理执行consul members,你会看到两个代理人现在彼此了解

(验证集群状态)只有一台机器怎么练习?

验证集群状态

1
2
# 在第一个节点(node1)上执行
consul members -http-addr=127.0.0.1:8501
1
2
3
4
5
~ ···································································································································································································································································· 17:51:02
❯ consul members -http-addr=127.0.0.1:8501
Node Address Status Type Build Protocol DC Partition Segment
agent-one 127.0.0.1:8311 alive server 1.20.5 2 dc1 default <all>
agent-two 127.0.0.1:8321 alive client 1.20.5 2 dc1 default <default>

记住:要加入集群,Consul代理只需要了解一个现有的成员。 加入集群后,代理人互相传播完整的会员信息。





9.3 在启动时自动加入集群

理想情况下,每当新节点出现在您的数据中心时,它就会自动加入Consul集群,无需人工干预。

一、云环境自动发现(推荐)

Consul 支持在云服务(AWS、GCP、Azure)中通过标签匹配实现节点的自动发现与集群加入,无需人工干预:

1
2
3
4
5
6
7
8
9
10
11
12
13
# 示例:AWS EC2 自动发现配置
retry_join_ec2 {
region = "us-west-2"
tag_key = "consul-node"
tag_value = "prod"
}

# 示例:GCP 自动发现配置
retry_join_gce {
project = "my-gcp-project"
zone = "us-central1-a"
instance_tag = "consul-enabled"
}

核心优势

  1. 动态扩展:新实例启动时自动加入集群
  2. 标签过滤:通过标签精确控制加入范围
  3. 跨可用区:支持多可用区部署

二、传统环境配置方式

在非云环境或混合架构中,可通过以下方式实现自动加入:

  1. 静态配置法(硬编码地址)
1
2
3
4
5
# 配置文件方式
start_join = [
"10.0.0.1:8301",
"10.0.0.2:8301"
]
  1. 命令行参数法
1
consul agent -join=10.0.0.1:8301 -join=10.0.0.2:8301

三、配置策略对比

方式 适用场景 优缺点
云自动发现 AWS/GCP/Azure 环境 全自动扩展,需云平台支持
静态配置 传统数据中心 配置简单,地址变更需手动更新
命令行参数 临时测试环境 灵活性高,不适合长期运行

四、最佳实践建议

  1. 生产环境:优先使用云自动发现机制,结合标签进行节点分组管理
  2. 混合环境:同时配置云发现和静态地址,作为双保险
  3. 安全增强:
    • 启用 ACL 令牌验证
    • 限制云标签访问权限
    • 配置防火墙规则仅允许集群端口通信

该机制通过服务发现技术实现节点的动态扩展与管理,确保集群始终处于健康状态。





9.4 查询节点

就像查询服务一样,Consul也有查询节点的API。 您可以通过DNS或HTTP API执行此操作。

对于DNS API,名称的结构是<NAME>.node.consul<NAME>.node.DATACENTER.consul。 如果数据中心被省略,Consul将仅搜索本地数据中心。

例如,从“agent-one”中,我们可以查询节点“agent-two”的地址:

1
dig @127.0.0.1 -p 8600 agent-two.node.consul
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
~ ···································································································································································································································································· 17:52:29
❯ dig @127.0.0.1 -p 8601 agent-two.node.consul

; <<>> DiG 9.10.6 <<>> @127.0.0.1 -p 8601 agent-two.node.consul
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 4860
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 3
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;agent-two.node.consul. IN A

;; ANSWER SECTION:
agent-two.node.consul. 0 IN A 127.0.0.1

;; ADDITIONAL SECTION:
agent-two.node.consul. 0 IN TXT "consul-network-segment="
agent-two.node.consul. 0 IN TXT "consul-version=1.20.5"

;; Query time: 0 msec
;; SERVER: 127.0.0.1#8601(127.0.0.1)
;; WHEN: Wed Mar 19 17:53:16 CST 2025
;; MSG SIZE rcvd: 136

除了服务之外,查找节点的能力对于系统管理任务来说是非常有用的。 例如,知道要通过SSH连接的节点的地址与将节点作为Consul集群的一部分并查询它一样简单。





9.5 离开集群

要离开集群,可以正常退出代理(使用Ctrl-C)或强制终止其中一个代理。 优雅地离开允许节点转换到离开状态; 否则,其他节点将检测到它失败。

Ctrl-C or 杀死进程

1
2
3
4
5
~ ···································································································································································································································································· 17:54:52
❯ consul members -http-addr=127.0.0.1:8501
Node Address Status Type Build Protocol DC Partition Segment
agent-one 127.0.0.1:8311 alive server 1.20.5 2 dc1 default <all>
agent-two 127.0.0.1:8321 left client 1.20.5 2 dc1 default <default>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
❯ consul members -http-addr=127.0.0.1:8501
Node Address Status Type Build Protocol DC Partition Segment
agent-one 127.0.0.1:8311 alive server 1.20.5 2 dc1 default <all>
agent-two 127.0.0.1:8321 alive client 1.20.5 2 dc1 default <default>

~ ···································································································································································································································································· 17:56:36
❯ lsof -i :8321
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
consul 6501 guowei.gong 7u IPv4 0x8f4e9332d4c64afb 0t0 TCP localhost:8321 (LISTEN)
consul 6501 guowei.gong 8u IPv4 0x8f4e932df5e5f773 0t0 UDP localhost:8321

~ ···································································································································································································································································· 17:56:45
❯ kill 6501

~ ···································································································································································································································································· 17:56:51
❯ consul members -http-addr=127.0.0.1:8501
Node Address Status Type Build Protocol DC Partition Segment
agent-one 127.0.0.1:8311 alive server 1.20.5 2 dc1 default <all>
agent-two 127.0.0.1:8321 left client 1.20.5 2 dc1 default <default>




十、健康检查

现在我们已经看到了运行Consul,添加节点和服务以及查询这些节点和服务的简单性。 在本节中,我们将介绍为节点和服务添加健康检查。 健康检查是服务发现的关键组件,可以防止使用不健康的服务。

此步骤建立在之前创建的Consul集群上。 此时,您应该运行一个双节点集群。





10.1 检查定义

与服务类似,可以通过提供检查定义或通过对HTTP API进行适当的调用来注册检查。

我们将使用检查定义方法,因为就像服务一样,定义是设置检查最常用的方法。

在第二个节点的Consul配置目录中创建两个定义文件:

1
echo '{"service": {"name": "ping-service", "port": 0, "checks": [{"id": "ping-check", "name": "Ping Check", "args": ["ping", "-c1", "google.com"], "interval": "30s", "timeout": "1s"}]}}' | sudo tee /etc/consul.d/ping.json
1
echo '{"service": {"name": "web", "tags": ["rails"], "port": 80, "checks": [{"id": "web-curl-check", "name": "Web Service Curl Check", "args": ["curl", "localhost"], "interval": "10s", "timeout": "1s"}]}}' | sudo tee /etc/consul.d/web.json

第一个定义添加了一个名为“ping”的主机级别的检查。 此检查运行间隔30秒,调用ping -c1 google.com。 在基于脚本的运行状况检查上,检查以与启动Consul进程相同的用户身份运行。 如果该命令以非零退出码退出,则该节点将被标记为不健康。 这是任何基于脚本的健康检查的约定。

第二个命令修改名为web的服务,添加一个检查,每隔10秒通过curl发送一个请求,以验证Web服务器是否可访问。 与主机级运行状况检查一样,如果脚本以非零退出代码退出,服务将被标记为不健康。

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

consul服务注册与健康检查





10.2 检查健康状态

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

1
2
3
~ ···································································································································································································································································· 18:40:53
❯ curl http://localhost:8501/v1/health/state/critical
[{"Node":"agent-two","CheckID":"ping-check","Name":"Ping Check","Status":"critical","Notes":"","Output":"Timed out (1s) running check","ServiceID":"ping-service","ServiceName":"ping-service","ServiceTags":[],"Type":"script","Interval":"30s","Timeout":"1s","ExposedPort":0,"Definition":{},"CreateIndex":446,"ModifyIndex":512}]%

这块之后再回来看看





其他操作

> 注销服务

查询服务ID,通过consul UI或者API来查找

  1. 查看指定地址consul中所有的服务名字
1
curl .../v1/catalog/services
  1. 查看指定地址consul中所有指定名字的服务
1
curl .../v1/catalog/service/<service_name>

注销服务

1
curl -X PUT .../v1/agent/service/deregister/<your_service_id>