Kong Gateway:从 DB-less 路由实验到 Hybrid 生产治理
一次常见的网关事故是这样的:/api/orders 仍然返回 200,发布平台也显示配置成功,订单团队却发现新版本始终没有流量。入口负载均衡把请求分给两个 DB-less 节点,发布程序只向其中一个节点提交了 /config;成功请求证明某个节点可用,却没有证明每个节点装载了同一份配置。
另一次事故恰好相反。团队把所有 Kong 节点连到 PostgreSQL,以为 Admin API 写入后会在所有节点瞬间可见。变更后的最初几秒里,部分请求仍命中旧 Route。数据库模式共享事实源,但节点缓存失效和轮询使配置传播呈最终一致。选择 Kong 之前,先要回答配置存在哪里、由谁发布、哪个节点执行策略,以及失败时能否拿出配置摘要与真实请求两类证据。
先认清请求经过的对象
客户端不会直接“请求一个插件”。在 Kong 中,一条 HTTP 请求先由监听端口接收,再匹配 Route;Route 关联 Gateway Service,Service 保存上游协议、主机、端口与基础路径;插件可挂在全局、Service、Route 或 Consumer 等作用域,在请求生命周期的相应阶段执行;最后负载均衡器选择 Upstream target 并把响应带回来。
这里的 Gateway Service 是 Kong 配置对象,不等同于 Kubernetes Service,也不一定对应一个微服务。Route 回答“哪些 Host、Path、Method 或 Header 进入这条链”,Service 回答“转发到哪里”,Consumer 表示网关识别出的调用方,Credential 则让插件把一个请求关联到 Consumer。业务服务仍须执行领域授权,不能因为网关完成 API Key 或 JWT 校验,就相信客户端自行发送的 X-User-Id。
三种常见自托管模式的差异在状态路径上:
| 模式 | 配置事实源 | 管理入口 | 数据节点如何得到配置 | 典型取舍 |
|---|---|---|---|---|
| DB-less | 版本化 YAML/JSON,运行时装入节点内存/LMDB | 启动装载或单节点 /config | 每个节点独立装载 | 依赖少、适合不可变发布;全量替换且节点不会互相同步 |
| Traditional | PostgreSQL | Admin API、decK 等 | 节点共享数据库并通过缓存失效/轮询看到变更 | 增量管理方便;要治理数据库、迁移和最终一致窗口 |
| Hybrid | PostgreSQL 位于 Control Plane 一侧,Data Plane 缓存配置 | Control Plane 的管理入口 | Data Plane 经 mTLS 连接接收并缓存配置 | 管理与流量隔离;要治理证书、插件兼容与断连状态 |
Konnect 托管管理侧,但运行侧责任取决于选择自托管 Data Plane、Dedicated Cloud Gateways 还是 Serverless Gateways。它们的网络、插件、容量、版本和费用边界不同,不能统称为“免运维 Kong”。选择时应在 Kong 拓扑与托管选项 和目标合同中逐项确认责任。
用 DB-less 跑通两个上游
隔离实验只需要 Docker Compose。先从 Kong 安装入口 和镜像仓库选择一个完整、受支持的 Gateway tag,记录镜像 digest;再为轻量回显服务选择固定 tag。不要使用 latest,也不要把 Enterprise 镜像、OSS 镜像和需要订阅凭证的分发物混用。
创建 compose.yaml:
services:
blue:
image: ${ECHO_IMAGE:?set ECHO_IMAGE to a fixed tag or digest}
command: ["-listen=:5678", "-text=upstream=blue"]
networks: [kong-lab]
green:
image: ${ECHO_IMAGE:?set ECHO_IMAGE to a fixed tag or digest}
command: ["-listen=:5678", "-text=upstream=green"]
networks: [kong-lab]
kong:
image: ${KONG_IMAGE:?set KONG_IMAGE to a fixed tag or digest}
environment:
KONG_DATABASE: "off"
KONG_DECLARATIVE_CONFIG: /kong/declarative/kong.yml
KONG_PROXY_LISTEN: 0.0.0.0:8000
KONG_ADMIN_LISTEN: 0.0.0.0:8001
KONG_LOG_LEVEL: notice
volumes:
- ./kong.yml:/kong/declarative/kong.yml:ro
ports:
- "127.0.0.1:8000:8000"
- "127.0.0.1:8001:8001"
depends_on: [blue, green]
networks: [kong-lab]
networks:
kong-lab:
name: kong-labKONG_ADMIN_LISTEN=0.0.0.0:8001 只让 Admin API 在容器网络内监听;宿主端口映射仍被限制在 127.0.0.1。若把宿主地址改成 0.0.0.0,同网段主机就可能获得完全管理能力。生产中还要叠加受控管理网络、TLS、身份/RBAC、审计与防火墙,端口号本身不是保护措施。
创建 kong.yml:
_format_version: "3.0"
_transform: true
services:
- name: catalog-blue
url: http://blue:5678
routes:
- name: catalog-blue-route
paths: ["/api/blue"]
strip_path: true
plugins:
- name: key-auth
- name: catalog-green
url: http://green:5678
routes:
- name: catalog-green-route
paths: ["/api/green"]
strip_path: true
consumers:
- username: lab-client
keyauth_credentials:
- key: demo-key-do-not-reuse_format_version 是声明格式版本,不是 Gateway 版本;升级时两者都要检查。strip_path: true 表示匹配到的 /api/blue 或 /api/green 前缀从上游路径中移除。key-auth 只挂在 blue Route,因此 blue 需要凭证,green 不需要,正好能把“路由失败”和“认证失败”分开。
先让目标镜像解析配置,再启动:
export KONG_IMAGE='kong/kong:<verified-tag-or-digest>'
export ECHO_IMAGE='hashicorp/http-echo:<verified-tag-or-digest>'
docker compose -p kong40 config
docker compose -p kong40 run --rm kong kong config parse /kong/declarative/kong.yml
docker compose -p kong40 up -d
for i in $(seq 1 30); do
if curl -fsS http://127.0.0.1:8001/ > kong-status.json; then
break
fi
sleep 2
done
test -s kong-status.json
docker compose -p kong40 ps
docker compose -p kong40 imagesdepends_on 只保证启动顺序,不保证 Kong 已完成声明装载;有限次数的就绪等待失败后应立即查看容器状态和 error log,而不是让流水线无限等待。kong-status.json 应能确认 Gateway 版本以及 database=off,docker compose images 则留下实际镜像 ID。kong config parse 只能证明目标二进制能解析当前声明文件;它不能证明 DNS、上游、外部 Secret 或插件依赖可用。随后执行四组请求:
curl -i http://127.0.0.1:8000/api/green
curl -i -H 'apikey: demo-key-do-not-reuse' http://127.0.0.1:8000/api/blue
curl -i -H 'apikey: wrong-key' http://127.0.0.1:8000/api/blue
curl -i http://127.0.0.1:8000/api/missing前两组应分别在响应体中看到 upstream=green 与 upstream=blue。错误 key 应在网关层得到认证失败的 4xx,未知路径应得到路由未命中的 404;这两类请求不应到达回显容器。保留状态码、X-Kong-* 响应头、Kong 访问日志和两个上游日志,才足以证明请求命中了哪个对象以及在哪一层停止。
全量发布为什么必须带摘要
DB-less 的常规实体写接口基本只读。向 /services 发 POST 应得到 405 Not Allowed,但读取实体、查看状态、校验 schema 和向 /config 装载完整声明仍可使用。/config 不是增量 patch:它用提交内容替换收到请求的那个节点的整份状态。
先保存配置摘要,再进行一次受控变更:
sha256sum kong.yml
curl -sS http://127.0.0.1:8001/services
curl -i -X POST http://127.0.0.1:8001/services \
--data 'name=should-fail' --data 'url=http://blue:5678'
curl -i -X POST 'http://127.0.0.1:8001/config?check_hash=1' \
-F config=@kong.yml发布记录至少保存 artifact digest + kong.yml SHA-256 + 目标节点 + /config 响应 + 两条代理探针。如果有两个 DB-less 节点,就必须逐节点比较配置 hash/实体列表并分别请求;只更新 A 后,B 保持旧状态正是必须验证的反例。团队可以用不可变镜像或启动配置替换整批节点,也可以编排逐节点 /config,但不能假设 Kong 会在 DB-less 节点之间广播。
当前 DB-less 实体存放在 LMDB 中,容量首先受 lmdb_map_size 与实际实体序列化规模约束;mem_cache_size 管的是另外两块共享内存缓存,不能用它替代 LMDB 容量规划。将真实 Service、Route、Consumer、Credential 和 Plugin 按比例复制到隔离环境,逐级放大配置并记录 kong config parse、/config 耗时、LMDB 使用、RSS、代理尾延迟和失败时的旧配置可用性。装载失败必须保持上一份可用声明,而不是自动扩大 map size 后重试到成功;无上限扩容会把错误配置转化成节点磁盘或内存风险。
声明式发布的回滚同样是完整状态发布。把上一份已验证 kong.yml 重新解析并装载,重复 blue、green、错误凭证和未知路径四组探针。不要从故障节点临时拼凑“差不多的旧配置”;期望状态、摘要和发布证据应存放在受访问控制的配置仓库或制品库中,凭据值则通过受支持的 Secret 方案另行管理。
从实验配置接入真实项目
项目接入时,把“网关对象”和“业务契约”分开评审。一个稳定的最小单元通常包含:
Service 指向应用在目标网络中的稳定发现名,而不是开发者机器 IP。Route 明确 Host、Path、Method,并确认 strip_path、preserve_host 与上游基础路径。插件只承担跨服务入口策略,例如消费者认证、限流、request ID、TLS 或日志;订单归属、字段权限等领域授权留在应用中。
应用只信任来自网关网络的转发头,主动清除客户端伪造的身份头,并在日志中关联网关 request ID。健康探针与业务 API 分离;超时、重试和幂等预算由网关与上游共同约定。
以 DB-less 为例,配置仓库可把 Service + Route + Plugin + Consumer 引用 作为同一变更提交。真实 Service 还应显式给出 connect_timeout、write_timeout、read_timeout 与 retries:前三者分别限制建立上游连接、发送请求和等待响应的阶段,单位与默认值以目标 Gateway schema 为准;retries 会增加一次客户端请求可能触发的上游尝试次数。非幂等写请求在无法证明“请求体尚未发送”或上游没有幂等键时,应从 retries: 0 起步。把某个超时调大并不会创造容量,只会让连接、worker 与负载均衡队列占用得更久。
流水线先用目标 Gateway 镜像执行 kong config parse,再在临时网络启动两个回显上游,执行成功、错误凭证、错误路由和上游不可达探针。上游不可达实验要保存 Kong error log、实际尝试次数和总耗时;预期总耗时与配置的连接/读写超时及重试次数同量级,两个正常上游仍不受影响。随后把同一摘要发布到预生产,逐节点验证 Host、Path、Method、strip_path、preserve_host 和上游收到的路径/Host。失败时重发上一份完整声明,并要求配置摘要恢复、四组基础探针恢复、故障发布新增的上游调用停止增长,三项同时成立才算回切完成。数据库模式可用 decK 做 dump、validate、diff 与 sync,但 deck gateway 不能管理 DB-less;decK state 与内置 DB-less 声明格式也并非处处相同,转换后必须再校验和请求验证。
应用回归至少观察三段时间:发布前基线、配置传播窗口、稳定后的新基线。只看最终 200 会漏掉 Traditional 多节点的短暂旧配置,也会漏掉 Hybrid 中“旧 Data Plane 仍能代理,但新配置没有下发”的状态。
Traditional 与 Hybrid 改变了哪些故障面
Traditional 适合需要频繁增量管理、多个管理客户端或数据库持久状态的团队。所有节点连接同一个受支持 PostgreSQL,先按目标发行线执行 kong migrations bootstrap,再启动 Gateway。外部负载均衡器仍负责把代理流量分发到多个节点。请求路径主要使用节点缓存,不是每个请求都查询数据库;Admin API 变更经过数据库和缓存失效传播,因此要记录首次旧值、首次新值与稳定时间,而不是宣称强一致。
首次初始化数据库时,让迁移作业拥有临时 schema 变更权限,代理节点只保留运行所需权限。迁移与运行配置至少固定这些字段,并通过 Secret 文件或 Secret 管理器注入密码,避免把密码直接放在命令参数和 Compose 环境清单中:
database = postgres
pg_host = kong-db.internal
pg_port = 5432
pg_database = kong
pg_user = kong_runtime
pg_ssl = on
pg_ssl_verify = on
lua_ssl_trusted_certificate = /run/secrets/db-ca.crt# 受控启动器读取密码文件,仅向这个迁移子进程注入环境变量
KONG_PG_PASSWORD="$(cat /run/secrets/kong-migration-password)" \
kong migrations bootstrap -c /etc/kong/kong-migration.conf
unset KONG_PG_PASSWORD
kong migrations list -c /etc/kong/kong-runtime.conf
kong check /etc/kong/kong-runtime.conf密码命令替换仍可能被 shell 审计策略记录,生产迁移器应关闭命令回显、限制 /proc 可见性并在专用短生命周期作业中运行。关键边界是把迁移身份与运行身份分离,并证明运行身份无法 CREATE/ALTER/DROP schema。多节点传播反例是在节点 A 创建临时 Route 后,绕过负载均衡轮询节点 B:记录 B 首次 404、首次 2xx 与稳定时间,再删除 Route 重复一次。直接改 PostgreSQL 表会绕过 Kong 的校验和传播机制,不能作为修复手段。
Hybrid 把角色拆开:Control Plane 连接数据库并承载管理;Data Plane 不连接数据库,只代理请求,经 mTLS 获取配置并缓存最后可用状态。Control Plane 或数据库短时不可用时,已有缓存的 Data Plane 可能继续服务旧配置;新 Data Plane 冷启动、缓存损坏、集群证书过期或插件集合不兼容时则可能无法就绪。
一套最小自托管 Hybrid 配置要把端口、角色和证书写明。共享证书模式便于起步,却意味着任一节点私钥泄露会影响整个集群;较大集群应评估基于 CA 的 PKI 模式,为每个 Data Plane 发放独立证书并建立吊销流程。
# Control Plane
role = control_plane
database = postgres
pg_host = kong-db.internal
cluster_listen = 0.0.0.0:8005
cluster_telemetry_listen = 0.0.0.0:8006
cluster_mtls = shared
cluster_cert = /run/secrets/cluster.crt
cluster_cert_key = /run/secrets/cluster.key
admin_listen = 10.20.1.10:8001 ssl# Data Plane
role = data_plane
database = off
cluster_control_plane = kong-cp.internal:8005
cluster_telemetry_endpoint = kong-cp.internal:8006
cluster_mtls = shared
cluster_cert = /run/secrets/cluster.crt
cluster_cert_key = /run/secrets/cluster.key
lua_ssl_trusted_certificate = /run/secrets/cluster.crt
admin_listen = off
status_listen = 0.0.0.0:8100Control Plane 的 8005 是配置通道,8006 是遥测通道,二者都不应暴露给业务客户端;Data Plane 的代理端口才进入入口负载均衡。证书文件应只对 Kong 运行用户可读,证书 SAN/SNI、共享证书或 PKI 字段要按目标发行线完整配置。Control Plane 用 GET /clustering/data-planes 核对节点身份、版本、最后活动时间和配置状态,Data Plane 则通过 Status API、日志及真实请求证明可服务,不能在 Data Plane 打开可写 Admin API 省事。
Hybrid 的状态判断要同时看 Control Plane 的 Data Plane 列表、配置状态/hash、Data Plane 日志和真实代理请求。先发布 route-v1 并确认每个 DP 都成功,再阻断一个 DP 到 8005 的连接,在 CP 新增 route-v2:在线 DP 应出现 v2,断连 DP 应继续服务 v1 且 v2 返回 404。恢复连接后,CP 会发送最新状态而不是逐条重放中间变更;只有该 DP 的配置状态收敛、v2 成功且旧负例仍失败,才能判定恢复。随后在隔离节点删除 dbless.lmdb 并保持 CP 不可达,验证它不能把“空启动”误报为健康;生产故障演练不可直接删除唯一缓存,应使用副本和可回切卷快照。
Data Plane 的 LMDB 缓存默认未加密,里面可能包含 Consumer、Credential、证书和内部拓扑;缓存卷的主机权限、备份、销毁和节点退役都要按高敏数据处理。配置 declarative_config 可作为断连冷启动兜底,但它可能落后于 CP,必须带摘要、生成时间和最大允许陈旧窗口,重连后还要证明 CP 状态覆盖兜底配置。
自定义插件通常要求 Control Plane 与每个 Data Plane 安装兼容代码。Gateway 与插件版本兼容不是“同一大版本就一定安全”:DP 不能比 CP 使用更新的 minor 版本,配置中启用的插件也有独立兼容约束。缺少插件或 schema 不匹配的节点可能继续代理旧配置,却拒绝接收新配置,因此升级门禁必须同时比较 Gateway 版本、启用插件列表、插件 schema 摘要与一次真实配置下发。
较新发行线可在 CP 与 DP 同时设置 incremental_sync=on,只同步变化实体,降低大配置全量重建的峰值;但两侧版本不完全一致时可能静默回落为全量同步。启用后要观察同步协议日志、配置装载耗时、DP RSS 和请求尾延迟,不能仅凭开关值宣称增量生效。回退时设为 off;直接修改数据库会破坏增量同步代际判断,必须禁止。
按证据层排查 4xx、5xx 与超时
先固定同一个 request ID,并保存客户端状态码、Kong 响应头、访问日志、命中 Route/Service、Consumer、插件结果、配置摘要和上游日志。然后按停止位置分层:
| 现象 | 第一证据 | 常见原因 | 修复后验证 |
|---|---|---|---|
| Kong 404,两个上游都无日志 | Route 列表、Host/Path/Method、配置 hash | 路由未装载、节点版本不同、匹配条件错误 | 对每个数据节点重复同一请求并看到预期 Route |
| 认证 4xx,上游无日志 | Consumer、Credential、插件作用域和日志 | key 错误、凭据被撤销、插件挂错层级 | 正确 key 成功,错误/旧 key 仍失败 |
| 502/503 | Kong error log、DNS、Service URL、上游连接 | 名称解析、端口、TLS/SNI、无可用 target | 直连基线与经网关请求都恢复,错误上游仍可区分 |
| 504 或客户端超时 | Kong latency headers、上游耗时、超时/重试配置 | 上游慢、连接池耗尽、重试放大 | 在同一超时预算下比较重试次数与总延迟 |
| 部分节点仍是旧行为 | 节点级配置 hash、版本、缓存传播时间 | DB-less 漏发布、Traditional 最终一致、Hybrid 下发阻断 | 所有节点 hash 和请求行为收敛 |
排障时不要先清空缓存。Traditional 冷缓存可能把请求压力瞬间推向数据库;DB-less 的问题通常也不是数据库缓存。先确认拓扑和节点身份,再决定是重新发布完整声明、等待/修复数据库传播,还是修复 Hybrid 连接与插件兼容。
生产架构与选型要看状态代价
小规模、配置可由 Git 单一驱动、允许整份发布的系统,DB-less 易于复制和回切,但整份配置必须装得进节点缓存,节点数量越多,发布协调与证据收集越重要。配置规模、LMDB/内存余量和 reload 延迟没有通用安全数字,应使用真实对象分布逐级放大,观察装载时长、RSS、错误率和请求尾延迟,保持故障与回滚余量。
需要多个管理客户端、频繁增量变更或数据库备份语义时,Traditional 更自然,但团队要承担 PostgreSQL 高可用、schema migration、最终一致和 Admin API 风险。希望隔离管理故障域与流量故障域时,Hybrid 更合适;代价是 CP/DP mTLS、插件集合、断连缓存、版本兼容和更复杂的发布证据。希望减少自建管理侧时再评估 Konnect,并把网络、区域、数据保留、出口、插件、SLA、费用和退出能力写进决策记录。
无论哪种模式,容量模型都应拆成代理吞吐与连接、插件 CPU/内存、配置对象规模、日志/Trace 出口、数据库或管理连接,以及故障时的 N-1 余量。压测必须包含真实 TLS、认证插件、典型请求体和上游延迟;裸 200 回显的 RPS 不能代表生产容量。
升级不是替换一个镜像标签
升级前固定源版本、目标版本、拓扑、镜像 digest、操作系统或 Kubernetes、PostgreSQL、插件、kong.conf、Nginx 模板和许可证类型,再阅读目标发行线的 升级路径 与 破坏性变更。官方测试的跨版本路径会变化,不能长期假定任意两个版本都可直升。
DB-less 和 Data Plane 适合滚动或新集群切流:用目标版本解析同一声明,在隔离环境执行正反探针,启动新节点,比较配置摘要与插件,再逐步转流。失败时把入口流量切回仍保留的旧节点。Traditional 或 Hybrid Control Plane 涉及数据库 migration;kong migrations 不可逆,执行后不能靠换回旧镜像恢复。资源允许时优先建立独立新集群并保留旧数据库恢复路径;无论采用哪种流程,都要在迁移前冻结配置写入并完成隔离恢复演练。
Hybrid 通常先升级 Control Plane,再滚动 Data Plane,并按官方 CP/DP 兼容矩阵控制混合版本窗口。窗口内“请求仍成功”可能只是旧 Data Plane 使用旧缓存,必须额外确认新配置能下发。插件代码、schema 和依赖要与 Gateway 一起进入兼容矩阵。
可用备份至少包含:数据库原生备份与声明式导出、kong.conf、部署清单、证书/密钥引用、自定义插件、外部 Secret 依赖,以及启用加密时单独保护的 keyring。恢复验收要在隔离目标上核对实体数量/摘要、插件、凭据引用、blue/green 请求与失败反例。Konnect 的底层数据库由服务方管理,客户侧退出和恢复依赖其支持的声明导出与 API,不能照搬 pg_restore。
把管理权限、凭据、数据和成本一起治理
Admin API、Kong Manager、Konnect token、声明导出、LMDB、数据库备份和访问日志都可能包含消费者凭据、证书或内部拓扑,应按高敏资产处理。生产管理入口只对专用管理网络开放,自动化使用独立服务身份与最小 scope,人员离职和流水线退役时单独撤销;轮换凭据时先建立新凭据、验证新调用方、停止旧凭据,再删除并确认旧凭据持续失败。
开源仓库的许可证只能说明对应源码 artifact 的权利,不能外推 Enterprise 二进制、插件、Konnect、基础镜像和商业支持。上线前分别核对目标 artifact 的 LICENSE/NOTICE、依赖与 SBOM,并由采购/法务确认订阅、再分发、数据处理和退出条款。价格、套餐、托管区域、保留期和支持承诺都从 当前官方价格与合同入口 读取,不写进长期配置模板。
成本不只是一组网关实例:还包括 PostgreSQL、跨区与出口流量、日志/Trace、证书与 Secret 系统、自定义插件维护、管理服务订阅和双集群升级窗口。按团队、环境和 API 产品标记资源;观察请求量、连接数、响应字节、插件耗时、日志量、数据库负载与配置规模的趋势;季度复查闲置 Control Plane、Data Plane、域名、负载均衡、备份和长期 token。
精确清理实验资源
先保存必要证据,再确认 Compose 项目名与实际资源,避免误删其他项目:
docker compose -p kong40 ps
docker compose -p kong40 logs --no-color kong > kong40-kong.log
docker compose -p kong40 down --remove-orphans
docker ps -a --filter label=com.docker.compose.project=kong40
docker network ls --filter label=com.docker.compose.project=kong40
docker volume ls --filter label=com.docker.compose.project=kong40这个实验没有声明持久卷,down 后不应留下项目容器、网络或 volume。只有过滤结果明确属于 kong40 时,才删除残留对象;不要使用无项目过滤的 docker system prune。随后删除本地合成 kong.yml、Compose 文件和脱敏日志,撤销实验 token/证书,确认 8000、8001 不再监听。
共享或生产环境的对象清理按引用逆序执行:先停流并验证无请求,再撤销 Consumer Credential,删除 Route 级插件与 Route,确认 Service 不再被引用后删除 Service,最后处理上游、证书、Data Plane、Control Plane、数据库备份和 DNS/负载均衡。每一步都保存对象 ID、变更人、配置摘要和删除后的 GET/请求证据;“页面上看不见”不等于凭据、备份、日志索引和计费资源已经消失。
