bigywater.tech
微服务 · 52

Consul:agent、server 与 gossip 的分工,健康检查类型与状态流转

Consul 里 server、client agent、gossip 各管什么?一个节点没了多久能发现?HTTP、TTL、TCP 检查的状态怎么流转,failures_before_critical 有什么用?server 失联时服务注册会怎样?这篇用 3 个 server 加 2 个 client agent 逐项量出来。

5.10 ~ 5.37 s
SIGKILL 一个 client agent 后,gossip 判它 failed(服务变 critical)的用时,三次
2.33 ~ 2.96 s
failures_before_critical=3 的 HTTP 检查,从后端返回 500 到变 critical 的用时(默认设置只要 0.44 ~ 1.04 s)
3.39 s
server 恢复后 catalog 里出现 agent 本地早已登记的 api,以及 dep 变 critical 的用时

角色

实测 · 本机执行输出
  gossip 成员 | role 标签 | 状态   
  ----------+---------+------
  v52a0     | node    | alive
  v52a1     | node    | alive
  v52cn0    | consul  | alive
  v52cn1    | consul  | alive
  v52cn2    | consul  | alive
  Raft 投票成员数(configuration.Servers 中 Voter 为真):3;其中节点名:['v52cn0', 'v52cn1', 'v52cn2']
  在 client agent v52a0 上登记 web;通过另一个 client agent(v52a1)查 catalog:[('v52a0', 'web-on-a0', '127.0.0.1')]
  /v1/status/peers 返回 3 个 server 地址

server 参与 Raft,client agent 只加入 gossip;服务挂在登记它的 agent 节点上。

gossip 与节点故障

实测 · 本机执行输出
  怎么没的         | 成员状态不再是 alive(s) | serfHealth 变 critical(s) | 服务不再出现在 ?passing(s)  | 结束时                  
  -------------+------------------+--------------------------+----------------------+----------------------
  SIGKILL      | 5.10             | 5.10                     | 5.10                 | catalog 1 / passing 0
  SIGKILL      | 5.35             | 5.35                     | 5.35                 | catalog 1 / passing 0
  SIGKILL      | 5.37             | 5.37                     | 5.37                 | catalog 1 / passing 0
  consul leave | 0.06             | -(节点已被移除,没有 serfHealth)  | 0.25(节点从 catalog 消失) | catalog 0 / passing 0
  SIGKILL 的节点被 gossip 判为 failed 要几秒(探测、怀疑、确认);在那之前服务照样 passing。节点被标成 failed 之后,它上面的服务因为 serfHealth 变成 critical 而不再出现在 ?passing 里,但 catalog 里还在。优雅离开则是节点自己通知集群,节点直接从 catalog 里消失

被杀的节点几秒后才被判为 failed,在那之前服务照样 passing;优雅离开则立刻从 catalog 消失。

HTTP 检查的状态流转

实测 · 本机执行输出
  刚登记时(后端一直是 200):default critical,slow critical;变成 passing 用了 default 0.29 s,slow 0.17 s(从登记调用返回算起)
  后端状态码改变 | default:到目标状态用的秒数(6 轮范围) | slow(3 次失败 / 2 次成功):同上
  --------+--------------------------+-----------------------
  200→429 | 0.06~0.99                | 0.42~0.95             
  429→500 | 0.44~1.04                | 2.33~2.96             
  500→200 | 0.18~0.87                | 1.08~1.80             
  各情形下 Output 字段的开头(原样):
    500            'HTTP GET http://127.0.0.1:9182/: 500 Internal Server Error Output: x'
    429            'HTTP GET http://127.0.0.1:9182/: 429 Too Many Requests Output: x'
    后端响应超过 timeout 'Get "http://127.0.0.1:9182/": context deadline exceeded (Client.Timeout exceeded while awaiting headers)'
    后端已停(连接被拒)     'Get "http://127.0.0.1:9182/": dial tcp 127.0.0.1:9182: connect: connection refused'

HTTP 检查按 interval 轮询,状态码 429 是 warning;failures_before_critical 和 success_before_passing 决定翻转的速度。

TTL、TCP 与脚本检查

实测 · 本机执行输出
  操作      | 状态       | 备注                                              
  --------+----------+-------------------------------------------------
  刚登记     | critical |                                                 
  汇报 pass | passing  |                                                 
  之后不再汇报  | critical | 3.00 s 后变 critical(TTL 3 s);Output:'TTL expired'
  汇报 warn | warning  |                                                 
  汇报 fail | critical |                                                 
  TCP 检查:端口监听时变 passing 用了 0.79 s;关掉端口后变 critical 用了 0.53 s;Output:'dial tcp 127.0.0.1:9183: connect: connection refused'
  脚本检查(Args)在默认配置下登记:HTTP 500,响应 "Scripts are disabled on this agent from remote calls; to enable, configure 'enable_script_checks' to true"

TTL 检查靠应用自己汇报,过期就是 critical;TCP 检查只看端口;脚本检查默认配置下登记被拒。

agent 本地状态与同步

实测 · 本机执行输出
  拆 server 之前,经 agent 查 catalog 里 dep 的检查状态:[('serfHealth', 'passing'), ('depc', 'passing')]
  观察(只剩 1 个 server,没有 leader)                  | 结果                                                                                                                        
  ---------------------------------------------+---------------------------------------------------------------------------------------------------------------------------
  agent 本地:depc 检查(把后端改成 500 后)                | critical(改完后 1.00 s 变化)                                                                                                   
  登记 api(PUT /v1/agent/service/register)       | HTTP 200                                                                                                                  
  agent 本地服务列表                                 | api-1,chk-1,dep-1,tcp-1,ttl-1,web-on-a0                                                                                   
  经 agent 查 catalog 的 api(默认)8 次               | HTTP 500 rpc error getting client: failed to get conn: di ×5; HTTP 500 rpc error making call: rpc error getting client: ×3
  经 agent 查 catalog 的 api(?stale)8 次           | HTTP 500 rpc error getting client: failed to get conn: di ×2; HTTP 200 (0 条) ×6                                           
  经 agent 查 catalog 里 dep 的 depc 状态(?stale)8 次 | HTTP 200 depc=passing ×8                                                                                                  
  (agent 本地 dep 的检查状态)                         | critical                                                                                                                  
  把两个 server 拉起来之后(从启动命令算起):可以写入(选出 leader)2.54 s;catalog 里出现 api 3.39 s;catalog 里 dep 的检查变成 critical 3.39 s

server 失联时 agent 本地照常登记和检查,catalog 返回的是旧数据,server 恢复后再同步。

各个入口怎么对待 warning

实测 · 本机执行输出
  接口                               | 实例数 | 内容                                        
  ---------------------------------+-----+-------------------------------------------
  catalog(/v1/catalog/service/tri) | 3 个 | 不看健康状态                                    
  /v1/health/service/tri           | 3 个 | tri-1:passing tri-2:warning tri-3:critical
  /v1/health/service/tri?passing   | 1 个 | tri-1                                     
  DNS SRV tri.service.consul       | 2 个 | tri-1 tri-2                               
  四个入口对 warning 实例的处理不一样:catalog 全部列出,health 列出并带状态,?passing 去掉 warning,DNS 的 SRV 应答里有 warning 的实例、没有 critical 的实例

catalog 不看健康状态,?passing 去掉 warning,DNS 的应答里有 warning、没有 critical。

自动注销

实测 · 本机执行输出
  刚登记时检查状态:critical(登记后马上读)
  服务   | DeregisterCriticalServiceAfter | 登记后多久变 critical(s) | 变 critical 后多久从 agent 本地消失(s)
  -----+--------------------------------+--------------------+------------------------------
  d10a | 10s                            | 0.1                | 76.3                         
  d10b | 10s                            | 0.0                | 76.3                         
  d30  | 30s                            | 0.0                | 76.3                         
  d90  | 90s                            | 0.0                | 106.3                        
  结束时 catalog 里 dereg 的实例数:0
ERRORS {}

DeregisterCriticalServiceAfter 设得小于一分钟时实际生效时间比设置值长得多,要按实测的时间来规划。

分步演示

Consul 的分工与健康检查
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / Python 3.14.8 / Go / nginx / Envoy / etcd / Consul / Nacos / OpenTelemetry / Prometheus(按各篇注明用到哪些及版本);所有服务都是本机自己起的进程,端口 9150 以上,只监听 127.0.0.1,不连任何外部服务,不碰本机已有的实例;故障用 kill -9 进程、让连接断开或在代码里注入延迟与错误来制造;计时用 time.perf_counter,数字每次运行会有小幅抖动。Consul 是 Homebrew 的 2.0.4,3 个 server(-bootstrap-expect=3)加 2 个 client 模式的 agent,全部在 127.0.0.1;后端是本进程里的小 HTTP 服务;DNS 查询用 dnspython;gossip 时间各测了 3 次(SIGKILL),HTTP 检查的状态流转在随机错开的位置上各测了 6 次
    • 没有验证:gossip 失败检测的时间随集群规模和 gossip 参数变化(这里只有 5 个节点、默认参数);WAN gossip 与多数据中心;ACL、Connect、gRPC / Docker / alias 类检查;检查数量很大时 agent 的负载;DNS 的 only_passing 等配置;DeregisterCriticalServiceAfter 为什么在 76 s 而不是 10 s 生效(我印象中官方文档写的最小值是 1 分钟,没有查文档或源码,这里只有测到的数字);?consistent 一致性模式(见第 48 篇)
    京ICP备2026067502号-1京公网安备11010502063487号