Consul 服务发现实战:注册、健康检查与 KV 配置入门

在微服务或混合部署环境里,服务地址经常变:扩缩容、发版、故障迁移都会让 IP 与端口漂移,靠配置文件写死迟早出事。Consul 解决的就是这件事:服务启动时注册上去,Consul 持续做健康检查,消费方通过 DNS 或 HTTP 接口永远能拿到当前健康的实例列表,附带还能用 KV 存动态配置。本文部署三 Server 加 Client 的标准结构,走通注册、检查、发现、KV 四个动作。环境口径:Rocky Linux 9、Consul 1.18,Server 节点 consul-01(192.0.2.41)、consul-02(192.0.2.42)、consul-03(192.0.2.43),业务机器各跑一个 Client 代理。

前置条件

  • 三台 Server 主机名或 IP 互通、时间同步正常——Server 间用 Raft 选主,时钟偏差大同样会引发选主抖动。
  • 防火墙放行:8300(RPC)、8301(Serf LAN,TCP 与 UDP 都要放行)、8302(Serf WAN,多数据中心才需要)、8500(HTTP API 与 Web 界面)、8600(DNS,TCP 与 UDP)。
  • 业务机器只访问本机 Client 的 8500 与 8600,别让应用直连 Server。
  • 数据目录要放在持久化路径上,不能用临时文件系统——重启丢了数据目录等于丢集群状态。

先定位:Consul 的三件套

Consul 常被拿来和 Nacos、Eureka 相比。简单划分:Eureka 偏注册与心跳,配置能力弱且已进入维护状态;Nacos 在注册之外自带配置中心,与 Spring Cloud 结合紧密,国内用得多;Consul 的特点是三件套齐全——服务发现、健康检查、KV 配置都有,而且健康检查是代理主动探测目标,而非只靠心跳上报,检查手段(HTTP、TCP、脚本、TTL)也最灵活。代价是部署稍重,Server 要按 Raft 规矩用奇数节点组集群。

还有一个细节:服务目录在节点间是最终一致的,不同节点短时查到的结果可能略有差异。对地址发现没影响,但别拿它当强一致数据库;KV 的强一致读要显式带参数并接受更高延迟。

第一步:部署三台 Server

三台 Server 的配置只差 node_name 与 bind_addr 两处。bootstrap_expect=3 表示集齐 3 台才完成首次选主;生产环境别用单节点 -bootstrap 模式先起一台再慢慢扩,那会给组网埋坑:

# /etc/consul.d/server.hcl(以 consul-01 为例)
server = true
bootstrap_expect = 3
node_name = "consul-01"
datacenter = "dc1"
data_dir = "/opt/consul/data"
bind_addr = "192.0.2.41"
client_addr = "0.0.0.0"
retry_join = ["192.0.2.41", "192.0.2.42", "192.0.2.43"]
ui_config {
  enabled = true
}
sudo systemctl enable --now consul
consul members
consul operator raft list-peers

Client 配置去掉 server 与 bootstrap_expect 两项,retry_join 同样指向三台 Server。业务机器只跑 Client:本机应用经它注册与查询,它宕机只影响本机服务的可见性,不动集群数据。

第二步:注册服务(两种方式)

方式一是配置文件注册:在 Client 配置目录放一份服务定义,reload 即生效,适合地址固定的服务,定义还能进版本管理。方式二是 HTTP API 动态注册,适合容器平台或程序自注册。先看配置文件方式,定义放在 /etc/consul.d/ 下:

# /etc/consul.d/web.json
{
  "service": {
    "name": "web",
    "tags": ["nginx"],
    "port": 8080,
    "check": {
      "http": "http://127.0.0.1:8080/health",
      "interval": "10s",
      "timeout": "2s"
    }
  }
}
sudo consul reload
consul catalog services
consul catalog nodes -service=web

API 方式一条命令即可,注意请求打的是本机 Client 的 8500,而不是 Server:

curl -X PUT http://127.0.0.1:8500/v1/agent/service/register -d '{
  "Name": "api",
  "Port": 9000,
  "Check": { "HTTP": "http://127.0.0.1:9000/health", "Interval": "10s", "Timeout": "2s" }
}'
# 注销对应接口
curl -X PUT http://127.0.0.1:8500/v1/agent/service/deregister/api

选择口径:能写进配置文件的用配置文件,进程自知上下线的用 API。容器平台里由编排层在调度时调 API 注册注销,务必保证注销真的执行,否则目录里会堆积死实例,只能靠健康检查标 critical 兜底。

第三步:健康检查的粒度怎么定

检查类型按目标选。HTTP 检查最常用,前提是业务提供真正能反映可用性的健康端点——能连上数据库、能处理请求才返回 200,而不是进程活着就返回 200,后者查了等于没查。TCP 检查只验端口连通,适合没有 HTTP 接口的中间件。脚本检查最灵活,但需显式打开 enable_local_script_checks,本质是让代理执行任意命令,安全风险自担,非必要不用。TTL 检查反过来,要求应用主动报心跳,适合批处理这类无常驻端口的任务。

参数纪律:interval 不低于 5 秒,timeout 必须小于 interval。检查太密会拖垮代理和被查服务;检查反复抖动绝大多数是参数太紧或端点本身慢,不是 Consul 的问题。另外检查失败只是把实例标为不健康并剔除,Consul 不负责重启服务,拉起交给 systemd 或编排系统。

第四步:消费服务(DNS 与 HTTP 两种姿势)

DNS 方式对老应用最友好:把 8600 配成上游解析或让应用直查,返回的只有健康实例,还能用 SRV 记录连端口一起拿到:

dig @127.0.0.1 -p 8600 web.service.consul
dig @127.0.0.1 -p 8600 web.service.dc1.consul SRV

HTTP 方式信息更全,能拿到节点、标签与检查明细,程序里通常封装一层本地缓存并订阅变更,而非每次请求实时查:

# 只取检查全部通过的实例
curl "http://127.0.0.1:8500/v1/health/service/web?passing=true"
# 目录视角:不管健康状态,全量列出
curl "http://127.0.0.1:8500/v1/catalog/service/web"

一句话:DNS 胜在零改造,但受 TTL 缓存影响且拿不到细节;HTTP 胜在可控与信息完整,代价是要写客户端逻辑。存量系统先用 DNS 顶上,新系统优先 HTTP。

第五步:KV 配置与 watch

KV 适合存功能开关、限流值这类小配置,不适合大文件,更不能当业务数据库。watch 是最实用的小功能:盯住某个键,值一变就自动执行命令,配置变更不用重启服务即生效:

consul kv put config/web/rate_limit 200
consul kv get config/web/rate_limit
consul kv get -recurse config/
# 值变化时触发本地脚本(该进程需常驻,交给 systemd 管理)
consul watch -type=key -key=config/web/rate_limit /usr/local/bin/reload-web.sh

两个纪律:处理脚本必须幂等并自带超时,watch 触发时脚本失败 Consul 不会重试;KV 改值与删除没有审批流,生产环境靠 ACL 与审计兜底。

验证

  • consul members 显示 3 台 Server 均为 alive,operator raft list-peers 能看到 Leader 与两个 Follower。
  • 在某台 Client 上注册 web 服务后,从任意节点查询 catalog 都能看到该服务与实例。
  • 停掉 web 的后端进程,十秒左右健康检查应转为 critical,DNS 查询与 health 接口都不再返回该实例;恢复进程后自动回正。
  • 修改 KV 里 rate_limit 的值,watch 绑定的脚本被触发执行,应用侧读到新值。

常见问题

1. 新节点加不进集群,Serf 通信一直失败

十有八九是 8301 只放行了 TCP、漏了 UDP——Serf 的成员发现与 gossip 走 UDP,只开 TCP 时节点互相看不见。其次查 retry_join 地址是否可达;启用了 gossip 加密的,各节点加密 key 必须一致,对不上时日志有解密失败提示。用 consul members 与日志对照即可定界。

2. 健康检查在 passing 与 critical 之间反复跳

先量被查端点的响应时间分布,再看 interval 与 timeout 是否太紧:端点正常耗时 1.5 秒、timeout 却设 1 秒,抖动必然。timeout 设为 interval 的一半以下,并用本机直连探活对照——多数所谓抖动其实是端点真的慢,问题在被查服务,不在 Consul。

3. DNS 查不到刚注册的服务

先分清是“没注册”还是“没通过检查”:catalog 有而 DNS 查不到,多半是检查未通过(DNS 默认只返回健康实例)或本地 DNS 缓存未过期。用 health 接口(带 passing 参数)与 catalog 对照即可定界,别盲目重启代理。

4. Server 选不出 Leader

先查三台之间 8300、8301 连通性与时钟偏差。还有一种隐蔽情况:某台曾用单节点 -bootstrap 起过,数据目录残留旧选主状态,再按三节点组网就会互相干扰。处理:停掉该节点、清掉数据目录、按新成员重新加入,别在脏数据目录上反复折腾。

小结

落地顺序:先铺 Server 集群与 Client 代理,再迁移服务注册,最后用 KV 与 watch 做动态配置。最后强调一句:注册中心的可信度全靠检查结果是否真实,把健康端点做扎实,比把集群做大重要得多。

发表回复

后才能评论