一个小小的下划线,让我排查了一个多小时。希望这篇文章能帮助遇到类似问题的开发者少走弯路。
背景
我们的微服务架构分为两个 Docker Compose 项目:
- 基础设施层:Nacos、Redis、RabbitMQ 等中间件
- 应用服务层:Gateway、Auth、Order 等业务服务
两个项目通过同一个 Docker 外部网络进行通信:
networks:
edniutrans_dl:
external: true
name: edniutrans_dl
问题现象
应用服务启动时,向 Nacos 注册失败,报 HTTP 400 Bad Request 错误:
NacosException: failed to req API:/nacos/v1/ns/instance
after all servers([nacos_dl:8848]) tried:
ErrCode:400, ErrMsg: HTTP Status 400 – Bad Request
奇怪的是:
- Nacos 控制台可以正常访问
- Nacos 日志没有任何报错
- 从应用容器可以 ping 通 nacos_dl
排查过程
1. 确认 Nacos 服务正常
从 Nacos 容器内部测试服务注册,成功:
docker exec -it nacos_dl curl -X POST \
"http://127.0.0.1:8848/nacos/v1/ns/instance?serviceName=test&ip=127.0.0.1&port=8080"
# 结果:成功 ✅
2. 从应用容器测试,失败
docker exec -it app-container curl -X POST \
"http://nacos_dl:8848/nacos/v1/ns/instance?serviceName=test&ip=172.22.0.3&port=8080"
# 结果:400 Bad Request ❌
3. 换成 IP 地址测试,成功
docker exec -it app-container curl -X POST \
"http://172.22.0.8:8848/nacos/v1/ns/instance?serviceName=test&ip=172.22.0.3&port=8080"
# 结果:成功 ✅
关键发现:用 IP 地址能成功,用主机名失败!
4. 查看详细请求信息
使用 curl -v 查看完整的 HTTP 请求:
docker exec -it app-container curl -v \
"http://nacos_dl:8848/nacos/v1/ns/instance/list?serviceName=test"
输出:
> GET /nacos/v1/ns/instance/list?serviceName=test HTTP/1.1
> Host: nacos_dl:8848
> User-Agent: curl/7.60.0
> Accept: */*
>
< HTTP/1.1 400
问题锁定:Host 请求头是 nacos_dl:8848,其中 nacos_dl 包含下划线 _!
根本原因
根据 RFC 952 和 RFC 1123 规范,合法的主机名(hostname)只能包含:
- 字母
a-z、A-Z - 数字
0-9 - 连字符
-(不能在开头或结尾)
下划线 _ 是不允许出现在主机名中的!
虽然 Linux 系统和 Docker DNS 对此比较宽容(能解析),但 Nacos 内嵌的 Tomcat 服务器严格遵循 HTTP 规范,当检测到 Host 头包含非法字符时,直接返回 400 Bad Request。
这就解释了为什么:
- ✅ 用 IP 地址访问成功(Host 头是 IP,没有非法字符)
- ❌ 用
nacos_dl访问失败(Host 头包含下划线) - ✅ 从容器内部用 127.0.0.1 访问成功(Host 头是 127.0.0.1)
解决方案
将容器名/服务名中的下划线去掉或替换为连字符:
# ❌ 错误示例
services:
nacos_dl:
image: nacos/nacos-server:2.0.3
# ...
# ✅ 正确示例
services:
nacosdl: # 或者 nacos-dl
image: nacos/nacos-server:2.0.3
# ...
同时更新所有引用该服务的配置:
environment:
spring.cloud.nacos.config.server-addr: nacosdl:8848
spring.cloud.nacos.discovery.server-addr: nacosdl:8848
重启服务后,问题解决!
命名规范建议
| ❌ 避免 | ✅ 推荐 |
|---|---|
nacos_dl |
nacosdl 或 nacos-dl
|
redis_master |
redis-master 或 redismaster
|
mq_server |
mq-server 或 mqserver
|
my_service |
my-service 或 myservice
|
受影响的场景
以下场景可能因主机名包含下划线而出问题:
| 场景 | 表现 |
|---|---|
| Nacos / Consul / Eureka | 服务注册返回 400 |
| Nginx 反向代理 | 默认拒绝带下划线的 Host |
| Tomcat 应用 | 返回 400 Bad Request |
| SSL/TLS 证书 | 无法签发证书 |
| 某些 DNS 服务器 | 解析失败 |
为什么这个问题难以发现?
-
DNS 能正常解析 -
ping nacos_dl成功,让人以为网络没问题 -
错误信息不明确 - Nacos 只返回
400 Bad Request,没有具体原因 - Nacos 日志无报错 - 服务端日志看不到任何异常
- 本地测试能通过 - 从 Nacos 容器内部用 127.0.0.1 测试是成功的
总结
这个问题的排查过程告诉我们:
- 遵循标准很重要 - HTTP 规范虽然枯燥,但关键时刻能帮你定位问题
- 对比测试是利器 - 用 IP 和 hostname 分别测试,快速缩小问题范围
- 善用 curl -v - 查看完整的 HTTP 请求响应,发现隐藏的问题
最重要的一点:在 Docker 容器命名、域名、主机名中,永远不要使用下划线!用连字符 - 代替。
如果这篇文章帮你避开了这个坑,欢迎点赞分享~