最近在将一个基于HZero的微服务系统接入Kong API网关时,遇到了一系列令人抓狂的问题。本以为只是简单的路由配置工作,结果却演变成了一场持续数天的排查之旅。其中最典型的问题现象是:通过Kong网关发起的请求就像被黑洞吞噬一样,没有任何响应。
具体表现为:使用curl测试时,请求会卡在发送阶段,既不返回状态码,也没有响应体,连接还不会自动断开。这种"无响应"状态在微服务网关场景下特别具有迷惑性,因为很容易让人误以为是后端服务出了问题。
提示:当遇到网关请求无响应时,首先要区分是"完全没有连接"还是"连接已建立但响应慢"。这两种情况对应的排查方向完全不同。
首先检查了Kong中配置的路由规则:
这些配置看起来都很合理,理论上应该能正常工作。为了确保万无一失,还特意用PATCH请求重新确认了配置:
bash复制curl -X PATCH http://localhost:8001/routes/hzero-all \
-d '{"strip_path":true,"preserve_host":false}'
但问题依旧存在,这说明我们的初步假设——"可能是路由配置问题"——很可能是不正确的。
直接绕过Kong访问后端服务时,能够获得预期的401响应:
bash复制curl http://192.168.0.128:8080/iam/v1/...
# 返回:<oauth><status>PERMISSION_ACCESS_TOKEN_EXPIRED</status>...</oauth>
这个结果证实了:
因此,问题范围可以缩小到Kong网关本身或Kong与后端服务之间的通信环节。
查看Kong的error.log发现了关键线索:
code复制[error] ... upstream timed out (110: Connection timed out) while connecting to upstream,
upstream: "http://192.168.0.128:8080/iam/v1/..."
特别注意日志中的"while connecting to upstream"这个关键短语。它表明问题发生在TCP连接建立阶段,而不是请求已经发送后的等待响应阶段。
这里出现了一个容易被忽视的重要事实:Kong是运行在Docker容器中的,而我们测试用的curl命令是在宿主机上执行的。这两者的网络视角可能有本质区别。
验证方法:
bash复制telnet 192.168.0.128 8080
bash复制docker exec -it kong telnet 192.168.0.128 8080
更令人惊讶的是,在容器内甚至无法ping通目标IP:
bash复制docker exec -it kong ping 192.168.0.128
这清楚地表明:问题出在容器网络层面,而不是应用配置问题。
经过排查,发现团队为了安全加固,在/etc/docker/daemon.json中设置了:
json复制{
"iptables": false
}
这个设置的初衷是好的——防止Docker自动管理iptables规则,提高网络控制权。但副作用是:它完全破坏了Docker容器的网络功能。
正常情况下,Docker依赖iptables实现以下功能:
当iptables=false时,Docker不会自动添加关键的MASQUERADE规则,导致:
结果就是表现为"连接超时",实际上是因为TCP三次握手无法完成。
在宿主机上检查NAT表规则:
bash复制iptables -t nat -L -n | grep MASQUERADE
正常情况下应该看到类似这样的规则:
code复制MASQUERADE all -- 172.17.0.0/16 0.0.0.0/0
如果没有任何MASQUERADE规则,基本可以确认就是这个原因导致的问题。
最简单的解决方法是删除daemon.json中的iptables=false设置:
bash复制vim /etc/docker/daemon.json
bash复制systemctl restart docker
注意:reload可能不够,建议使用restart确保配置生效,但这会短暂中断所有容器服务。
如果确实需要保持iptables=false,可以手动添加必要的规则:
bash复制# 假设Docker使用的是默认的172.17.0.0/16网段
iptables -t nat -A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
然后确保规则持久化(写入启动脚本或使用iptables-persistent工具)。
解决网络问题后,又遇到了新的503错误:
xml复制<oauth>
<status>PERMISSION_SERVICE_ROUTE</status>
<message>This request mismatch any routes, uri: %2Fhzero-gateway%2Fiam%2F...</message>
</oauth>
这个错误表明后端服务接收到的路径包含了/hzero-gateway前缀,而我们的strip_path设置应该已经去掉了这个前缀。经过检查发现:
使用路由名称来避免混淆:
bash复制curl -X PATCH http://localhost:8001/routes/hzero-all \
-d '{"strip_path":true}'
然后验证配置是否生效:
bash复制curl http://localhost:8001/routes/hzero-all | jq .strip_path
# 应该返回true
Kong默认的限流提示是英文的,对中文用户不太友好:
json复制{"message": "API rate limit exceeded"}
bash复制curl http://localhost:8001/plugins | jq '.data[] | select(.name=="rate-limiting")'
bash复制curl -X PATCH http://192.168.0.95:8001/plugins/插件ID \
--data "config.error_message=您的请求过于频繁,请稍后再试"
如果需要返回JSON格式的中文提示,可以这样设置:
bash复制curl -X PATCH http://192.168.0.95:8001/plugins/插件ID \
--data 'config.error_message={"message":"您的请求过于频繁,请稍后再试"}' \
--data 'config.content_type=application/json'
通过这次排查经历,我总结了几点保障网关稳定性的建议:
监控指标:对以下关键指标设置监控
混沌工程:定期模拟网络分区、服务不可用等情况,验证系统的容错能力
渐进式发布:网关配置变更应该通过canary发布逐步验证
文档沉淀:将排查过程形成文档,建立内部知识库
在实际操作中,我还发现Kong的Admin API返回的配置有时会有缓存延迟,特别是在频繁修改配置时。因此,对于关键配置变更,建议:
这些经验虽然来源于特定技术栈,但背后的排查思路和方法论是通用的。无论是使用Kong、Nginx还是其他API网关,理解网络通信的基本原理和掌握系统的可观测性工具,都是快速定位和解决问题的关键。