一個(gè)小小的下劃線,讓我排查了一個(gè)多小時(shí)。希望這篇文章能幫助遇到類似問題的開發(fā)者少走彎路。
背景
我們的微服務(wù)架構(gòu)分為兩個(gè) Docker Compose 項(xiàng)目:
- 基礎(chǔ)設(shè)施層:Nacos、Redis、RabbitMQ 等中間件
- 應(yīng)用服務(wù)層:Gateway、Auth、Order 等業(yè)務(wù)服務(wù)
兩個(gè)項(xiàng)目通過同一個(gè) Docker 外部網(wǎng)絡(luò)進(jìn)行通信:
networks:
edniutrans_dl:
external: true
name: edniutrans_dl
問題現(xiàn)象
應(yīng)用服務(wù)啟動(dòng)時(shí),向 Nacos 注冊失敗,報(bào) HTTP 400 Bad Request 錯(cuò)誤:
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 控制臺(tái)可以正常訪問
- Nacos 日志沒有任何報(bào)錯(cuò)
- 從應(yīng)用容器可以 ping 通 nacos_dl
排查過程
1. 確認(rèn) Nacos 服務(wù)正常
從 Nacos 容器內(nèi)部測試服務(wù)注冊,成功:
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"
# 結(jié)果:成功 ?
2. 從應(yīng)用容器測試,失敗
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"
# 結(jié)果: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"
# 結(jié)果:成功 ?
關(guān)鍵發(fā)現(xiàn):用 IP 地址能成功,用主機(jī)名失?。?/p>
4. 查看詳細(xì)請求信息
使用 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 包含下劃線 _!
根本原因
根據(jù) RFC 952 和 RFC 1123 規(guī)范,合法的主機(jī)名(hostname)只能包含:
- 字母
a-z、A-Z - 數(shù)字
0-9 - 連字符
-(不能在開頭或結(jié)尾)
下劃線 _ 是不允許出現(xiàn)在主機(jī)名中的!
雖然 Linux 系統(tǒng)和 Docker DNS 對此比較寬容(能解析),但 Nacos 內(nèi)嵌的 Tomcat 服務(wù)器嚴(yán)格遵循 HTTP 規(guī)范,當(dāng)檢測到 Host 頭包含非法字符時(shí),直接返回 400 Bad Request。
這就解釋了為什么:
- ? 用 IP 地址訪問成功(Host 頭是 IP,沒有非法字符)
- ? 用
nacos_dl訪問失?。℉ost 頭包含下劃線) - ? 從容器內(nèi)部用 127.0.0.1 訪問成功(Host 頭是 127.0.0.1)
解決方案
將容器名/服務(wù)名中的下劃線去掉或替換為連字符:
# ? 錯(cuò)誤示例
services:
nacos_dl:
image: nacos/nacos-server:2.0.3
# ...
# ? 正確示例
services:
nacosdl: # 或者 nacos-dl
image: nacos/nacos-server:2.0.3
# ...
同時(shí)更新所有引用該服務(wù)的配置:
environment:
spring.cloud.nacos.config.server-addr: nacosdl:8848
spring.cloud.nacos.discovery.server-addr: nacosdl:8848
重啟服務(wù)后,問題解決!
命名規(guī)范建議
| ? 避免 | ? 推薦 |
|---|---|
nacos_dl |
nacosdl 或 nacos-dl
|
redis_master |
redis-master 或 redismaster
|
mq_server |
mq-server 或 mqserver
|
my_service |
my-service 或 myservice
|
受影響的場景
以下場景可能因主機(jī)名包含下劃線而出問題:
| 場景 | 表現(xiàn) |
|---|---|
| Nacos / Consul / Eureka | 服務(wù)注冊返回 400 |
| Nginx 反向代理 | 默認(rèn)拒絕帶下劃線的 Host |
| Tomcat 應(yīng)用 | 返回 400 Bad Request |
| SSL/TLS 證書 | 無法簽發(fā)證書 |
| 某些 DNS 服務(wù)器 | 解析失敗 |
為什么這個(gè)問題難以發(fā)現(xiàn)?
-
DNS 能正常解析 -
ping nacos_dl成功,讓人以為網(wǎng)絡(luò)沒問題 -
錯(cuò)誤信息不明確 - Nacos 只返回
400 Bad Request,沒有具體原因 - Nacos 日志無報(bào)錯(cuò) - 服務(wù)端日志看不到任何異常
- 本地測試能通過 - 從 Nacos 容器內(nèi)部用 127.0.0.1 測試是成功的
總結(jié)
這個(gè)問題的排查過程告訴我們:
- 遵循標(biāo)準(zhǔn)很重要 - HTTP 規(guī)范雖然枯燥,但關(guān)鍵時(shí)刻能幫你定位問題
- 對比測試是利器 - 用 IP 和 hostname 分別測試,快速縮小問題范圍
- 善用 curl -v - 查看完整的 HTTP 請求響應(yīng),發(fā)現(xiàn)隱藏的問題
最重要的一點(diǎn):在 Docker 容器命名、域名、主機(jī)名中,永遠(yuǎn)不要使用下劃線!用連字符 - 代替。
如果這篇文章幫你避開了這個(gè)坑,歡迎點(diǎn)贊分享~