Apache APISIX 集成 Consul KV 服务发现:consul_kv 模块配置、数据流与调试实战

发布时间:2026/9/14 19:11:24
Apache APISIX 集成 Consul KV 服务发现:consul_kv 模块配置、数据流与调试实战
Apache APISIX 集成 Consul KV 服务发现consul_kv 模块配置、数据流与调试实战【免费下载链接】apisixThe Cloud-Native API Gateway项目地址: https://gitcode.com/GitHub_Trending/ap/apisix本篇技术指南围绕 Apache APISIX 内置的consul_kv服务发现模块展开讲解如何让 APISIX 直接从 Consul 的 KV 存储而非 Consul 的健康检查注册中心中拉取上游节点实现 HTTPL7与 TCP/UDPL4流量的动态负载均衡。读完本文你将掌握conf/config.yaml中discovery.consul_kv的完整配置、Consul KV 键值模板的注册方式、reload 场景下的 dump 数据兜底机制以及通过控制面 API 进行内存与文件级调试的方法。一、背景与适用场景consul_kv模块主要面向从 nginx-upsync-module如微博移动端团队迁移到 APISIX、且以 Consul KV 作为服务发现数据源的用户。其核心思路是将服务的节点列表以 KV 形式写入 ConsulAPISIX 的consul_kv模块定期或长轮询拉取这些 KV 数据动态刷新内存中的上游节点表从而在节点增删、权重调整时无需重启或 reload APISIX 即可生效。从源码结构看该模块位于 apisix/discovery/consul_kv/由两个文件组成init.lua核心实现负责连接 Consul、解析 KV、更新应用表、dump 读写与控制 APIschema.lua配置项校验与默认值定义。模块版本号为0.3见 init.lua。模块本身不在文档中自带数据流示意图但结合 init.lua 的实现可以还原其 worker 数据流worker 0 通过ngx.timer.at启动对每个 Consul server 的连接任务并拉取数据更新本地applications表后通过 apisix/events.lua 的 pubsub 事件广播给其他 worker其他 worker 注册事件回调后同步更新自身的内存节点表见 init.lua 的init_worker与 init.lua 的discovery_consul_callback。二、discovery 客户端配置2.1 启用发现模块APISIX 会在启动时读取conf/config.yaml中的discovery段并动态加载对应的发现模块apisix/discovery/init.lua 遍历local_conf.discovery中的每个 key执行require(apisix.discovery. .. discovery_name)随后在init_worker阶段依次调用各模块的init_worker()。因此只要在配置中写入了consul_kv段模块就会被自动启用。2.2 完整配置示例在conf/config.yaml中加入以下配置默认值注释来自 schema.luadiscovery: consul_kv: servers: - http://127.0.0.1:8500 - http://127.0.0.1:8600 token: ... # 若 Consul 集群开启了 ACL 访问控制需要指定 token prefix: upstreams skip_keys: # 如果需要跳过特殊 key - upstreams/unused_api/ timeout: connect: 1000 # 默认 2000 ms read: 1000 # 默认 2000 ms wait: 60 # 默认 60 sec weight: 1 # 默认 1 fetch_interval: 5 # 默认 3 sec仅在 keepalive: false 时生效 keepalive: true # 默认 true使用长轮询方式查询 consul servers default_server: # 可以定义未命中时的默认 server host: 127.0.0.1 port: 20999 metadata: fail_timeout: 1 # 默认 1 ms weight: 1 # 默认 1 max_fails: 1 # 默认 1 dump: # 需要时注册节点更新后可以 dump 到文件 path: logs/consul_kv.dump expire: 2592000 # 单位秒这里是 30 天也可以只写最少配置其余全部走默认值discovery: consul_kv: servers: - http://127.0.0.1:85002.3 配置项详解与源码校验结合 schema.lua 与 init.lua 的format_consul_params各配置项说明如下配置项类型/默认值说明serversarray必填minItems 1Consul server 地址列表格式必须是http://address:port。源码中对每个地址执行http.parse_uri解析若 scheme 不是http或路径不是根路径如带/后缀会直接报错only support consul http schema address与invalid consul server address见 init.luatokenstring默认Consul ACL token会以token参数附加到每次 KV 请求中见 init.luaprefixstring默认upstreamsKV 的根前缀实际请求的 consul key 为/kv/.. prefix见 init.luaskip_keysarray需要跳过的 key 列表命中后该 key 对应的节点不会被加入节点表见 init.lua 与parse_instance中的判断timeout.connectinteger默认 2000ms连接 Consul 的超时timeout.readinteger默认 2000ms读取响应的超时timeout.waitinteger默认 60s长轮询阻塞等待时间keepalive: true时作为wait参数传给 Consul见 init.luaweightinteger默认 1最小值 1节点默认权重当 KV 值中未显式给出weight时使用见 init.lua 与 init.luafetch_intervalinteger默认 3s最小值 1仅在keepalive: false的短轮询模式下生效作为ngx.timer_every的周期参数见 init.luakeepaliveboolean默认 true见下文 2.4default_server/default_serviceobject未命中任何节点时的兜底节点。注意文档示例中写作default_server但 schema 与源码实现中的字段名为default_service见 schema.lua 与 init.lua实际配置请使用default_service。其metadata子项fail_timeout、weight、max_fails默认值均为 1dumpobjectdump 文件相关配置见第三节需要特别说明default_service的兜底逻辑当请求某个服务名但内存中没有对应节点时_M.nodes()会返回default_service作为唯一节点见 init.lua并且其weight会被强制设置为全局weight值见 init.lua。测试用例 t/discovery/consul_kv.t 中在 20999 端口起了一个返回missing consul_kv services的兜底 server 来验证该行为。2.4 keepalive 两种拉取模式keepalive有两个可选值这也是官方文档明确推荐的取舍点true默认且推荐使用**长轮询long pull**方式查询 Consul server。源码中会设置args.wait timeout.wait与args.index 0并利用 Consul 响应头X-Consul-Index做增量判断——仅当 index 发生变化时才重新解析 body 并更新节点表同时把最新的 index 带回下一次请求形成阻塞式长轮询见 init.luafalse不推荐使用**短轮询short pull**方式每次拉取后通过ngx.timer_every(fetch_interval, ...)周期性地重新连接 Consul见 init.lua此时可通过fetch_interval控制拉取间隔。无论哪种模式每次拉取失败后都会以指数退避retry_delay从 1 秒起每次乘 4重试连接见 init.lua避免对 Consul 造成瞬时风暴。三、Dump 数据机制解决 reload 竞态问题3.1 为什么需要 dump在线 reload APISIX 时consul_kv模块从 Consul 加载数据的速度通常慢于 APISIX 从 etcd 加载路由的速度因此在 Consul 数据加载成功之前的窗口期请求可能命中如下错误日志http_access_phase(): failed to set upstream: no valid upstream node为此模块引入了dump功能reload 时会先从 dump 文件加载节点数据兜底当 Consul 中注册的节点发生更新时又自动把最新上游节点写入 dump 文件。3.2 dump 配置项dump: path: logs/consul_kv.dump load_on_init: true expire: 2592000三个可选项的语义pathdump 文件保存路径。支持相对路径如logs/consul_kv.dump支持绝对路径如/tmp/consul_kv.bin请确保 dump 文件所在父目录已存在请确保 APISIX 对 dump 文件具备读写权限例如chown www:root conf/upstream.d/。load_on_init默认true。为true时启动阶段会先尝试从 dump 文件加载数据不关心文件是否存在再向 Consul 拉取为false时忽略 dump 文件无论true还是false都不需要为 APISIX 预先准备 dump 文件。expire单位秒用于避免加载过期 dump 数据。默认0表示永不过期官方推荐2592000即 30 天等于 3600 × 24 × 30。3.3 源码实现启动加载init_worker中若配置了dump且load_on_init为真会调用read_dump_srvs()见 init.lua。该函数读取文件、校验 JSON 结构必须包含services与last_update字段并用entity.last_update expire与当前时间比较判断是否过期过期则忽略见 init.lua自动写入每次 Consul 数据更新成功且配置了dump时通过ngx_timer_at(0, write_dump_srvs)异步写文件内容为{services applications, last_update ngx.time(), expire ...}见 init.lua 与 init.lua。对应的完整行为验证可参考测试用例 t/discovery/consul_kv_dump.t。四、向 Consul KV 注册 HTTP 服务4.1 Key / Value 模板服务注册的键值模板如下Key: {Prefix}/{Service_Name}/{IP}:{Port} Value: {weight: Num, max_fails: Num, fail_timeout: Num}Key 默认以upstreams作为前缀对应prefix配置项Service_Name既可以是简单的服务名如webpages也可以带多级路径如webpages/oneteam/hello多级路径会被解析为不同的服务名节点实例的 IP 与端口拼接成新的 key 段IP:Port。源码中的解析正则与之一一对应( .. prefix .. /.*/)([a-zA-Z0-9.]):([0-9])即从 key 中提取出服务名、IP与端口三部分见 init.lua 与 init.lua。4.2 注册节点示例以服务名webpages为例向 Consul 注册两个节点curl \ -X PUT \ -d {weight: 1, max_fails: 2, fail_timeout: 1} \ http://127.0.0.1:8500/v1/kv/upstreams/webpages/172.19.5.12:8000 curl \ -X PUT \ -d {weight: 1, max_fails: 2, fail_timeout: 1} \ http://127.0.0.1:8500/v1/kv/upstreams/webpages/172.19.5.13:80004.3 值解析细节Consul KV 的 Value 在 HTTP API 中以 Base64 编码返回模块在parse_instance中会先ngx.decode_base64解码再core.json.decode解析出 JSON例如Base64 形态IHsid2VpZ2h0IjogMTIwLCAibWF4X2ZhaWxzIjogMiwgImZhaWxfdGltZW91dCI6IDJ9原始内容{weight: 120, max_fails: 2, fail_timeout: 2}解析时还会检查metadata.check_status若值为false或字符串false该节点会被视为不健康而跳过见 init.lua这一字段可用于把故障节点在 Consul 侧直接摘除。节点的weight优先取 KV 值中的weight缺省时回退到全局配置的weight见 init.lua。4.4 多 Consul server 场景当同一个 key 存在于多个 Consul server 时为避免混淆实践中建议把完整的 Consul key URL 路径直接作为服务名即service_name写成http://host:port/v1/kv/prefix/service/的完整形式。这样不同 server 上的同名服务在 APISIX 侧会被区分为不同的服务名互不干扰。五、Upstream 配置与使用5.1 L7HTTP场景下面示例将 URI 为/*的请求路由到名为http://127.0.0.1:8500/v1/kv/upstreams/webpages/的服务并通过consul_kv发现客户端解析节点。:::note 可以先从config.yaml中取出admin_key并保存到环境变量方便后续命令使用admin_key$(yq .deployment.admin.admin_key[0].key conf/config.yaml | sed s///g):::$ curl http://127.0.0.1:9180/apisix/admin/routes/1 -H X-API-KEY: $admin_key -X PUT -i -d { uri: /*, upstream: { service_name: http://127.0.0.1:8500/v1/kv/upstreams/webpages/, type: roundrobin, discovery_type: consul_kv } }返回格式如下{ node: { value: { priority: 0, update_time: 1612755230, upstream: { discovery_type: consul_kv, service_name: http://127.0.0.1:8500/v1/kv/upstreams/webpages/, hash_on: vars, type: roundrobin, pass_host: pass }, id: 1, uri: /*, create_time: 1612755230, status: 1 }, key: /apisix/routes/1 } }从返回可以看出discovery_type: consul_kv与service_name是触发动态节点解析的关键字段——APISIX 在请求处理阶段会调用_M.nodes(service_name)获取该服务的实时节点列表见 init.lua节点权重等属性随之参与负载均衡。更多用法参见 t/discovery/consul_kv.t。5.2 L4四层场景consul_kv同样支持在 L4stream中使用配置方式与 L7 类似只需通过 stream_routes 管理接口创建四层路由$ curl http://127.0.0.1:9180/apisix/admin/stream_routes/1 -H X-API-KEY: $admin_key -X PUT -i -d { remote_addr: 127.0.0.1, upstream: { scheme: tcp, service_name: http://127.0.0.1:8500/v1/kv/upstreams/webpages/, type: roundrobin, discovery_type: consul_kv } }注意 L4 场景下额外增加了scheme: tcp字段以指明四层协议。对应的测试见 t/discovery/stream/consul_kv.t。六、调试 API控制面consul_kv模块通过dump_data()与control_api()两个方法向 APISIX 控制面暴露调试接口见 init.lua。控制面路由 apisix/control/router.lua 会为所有 discovery 模块自动拼接/v1/discovery/{模块名}前缀并注册路由因此实际访问路径为/v1/discovery/consul_kv/...。6.1 内存 Dump APIGET /v1/discovery/consul_kv/dump该接口返回模块当前的完整运行状态config为配置快照services为内存中的全部服务与节点列表对应dump_data()的实现见 init.lua。示例# curl http://127.0.0.1:9090/v1/discovery/consul_kv/dump | jq { config: { fetch_interval: 3, timeout: { wait: 60, connect: 6000, read: 6000 }, prefix: upstreams, weight: 1, servers: [ http://172.19.5.30:8500, http://172.19.5.31:8500 ], keepalive: true, default_service: { host: 172.19.5.11, port: 8899, metadata: { fail_timeout: 1, weight: 1, max_fails: 1 } }, skip_keys: [ upstreams/myapi/gateway/apisix/ ] }, services: { http://172.19.5.31:8500/v1/kv/upstreams/webpages/: [ { host: 127.0.0.1, port: 30513, weight: 1 }, { host: 127.0.0.1, port: 30514, weight: 1 } ], http://172.19.5.30:8500/v1/kv/upstreams/1614480/grpc/: [ { host: 172.19.5.51, port: 50051, weight: 1 } ], http://172.19.5.30:8500/v1/kv/upstreams/webpages/: [ { host: 127.0.0.1, port: 30511, weight: 1 }, { host: 127.0.0.1, port: 30512, weight: 1 } ] } }注意该 API 默认监听在控制面端口9090具体端口以conf/config.yaml中deployment.control配置为准与 Admin API 的9180端口不同。返回内容同时也印证了 4.4 节的做法——以完整 URL 作为服务名时同一服务在不同 Consul server 上会呈现为两个独立的服务条目。6.2 Dump 文件查看 API模块还提供了查看 dump 文件的控制 API便于确认磁盘上的兜底数据是否最新、是否过期GET /v1/discovery/consul_kv/show_dump_file该接口由control_api()注册见 init.lua内部直接读取 dump 文件内容返回见 init.lua。示例curl http://127.0.0.1:9090/v1/discovery/consul_kv/show_dump_file | jq { services: { http://172.19.5.31:8500/v1/kv/upstreams/1614480/webpages/: [ { host: 172.19.5.12, port: 8000, weight: 120 }, { host: 172.19.5.13, port: 8000, weight: 120 } ] }, expire: 0, last_update: 1615877468 }其中last_update是最近一次写入 dump 的时间戳expire为配置的过期时长0 表示永不过期可据此判断兜底数据是否仍在有效期内。官方文档说明未来可能会在此基础上增加更多调试 API。七、测试用例与验证路径仓库为consul_kv提供了三个层面的测试可作为上手与排障的参考t/discovery/consul_kv.tL7 场景的核心测试覆盖了完整配置多 server、prefix、skip_keys、timeout、weight、keepalive、default_service以及带 ACL token 的配置变体并起多个本地 mock server3051130514 端口验证节点动态更新与兜底服务20999 端口行为t/discovery/consul_kv_dump.tdump 文件的读写、过期与 reload 兜底行为测试t/discovery/stream/consul_kv.tL4 场景下 stream route 使用consul_kv发现节点的测试。八、总结consul_kv是 APISIX 面向 Consul KV 存储的服务发现方案适合从 nginx-upsync-module 迁移或习惯以 KV 方式管理节点的团队。其要点可归纳为配置在conf/config.yaml的discovery.consul_kv段配置servers必填、prefix、skip_keys、timeout、weight、fetch_interval等推荐开启keepalive: true长轮询以降低 Consul 压力注册按{prefix}/{service_name}/{ip}:{port}的 Key 模板与{weight,max_fails,fail_timeout}的 Value 模板写入 KV多 Consul server 场景建议用完整 URL 作为服务名兜底配置dump与default_service分别解决 reload 竞态导致的no valid upstream node错误和节点未命中时的降级问题调试通过控制面9090端口的/v1/discovery/consul_kv/dump与/v1/discovery/consul_kv/show_dump_file两个 API 快速检查内存节点与 dump 文件状态。更深入的实现细节长轮询 index 机制、Base64 值解析、事件广播、指数退避重试等可在 apisix/discovery/consul_kv/init.lua 与 apisix/discovery/consul_kv/schema.lua 中直接阅读源码。【免费下载链接】apisixThe Cloud-Native API Gateway项目地址: https://gitcode.com/GitHub_Trending/ap/apisix创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考