Traefik 一直 404:labels 全对,问题出在健康检查

1412 字
7 分钟
Traefik 一直 404:labels 全对,问题出在健康检查

新服务部署完,Traefik 怎么都不给它路由:域名访问返回 404 page not found,而同一台机器上其他服务全部正常。

从怀疑隧道查到怀疑 provider 配置,折腾了一个多小时。真正的原因是我给容器写了一条错误的健康检查命令。

一、症状#

走域名进来是 404:

Terminal window
curl -s -o /dev/null -w "%{http_code}\n" -H "Host: myapp.example.com" \
http://192.168.1.10:8888/api/v1/something
# 404

直连容器端口是正常的:

Terminal window
curl -s -o /dev/null -w "%{http_code}\n" http://192.168.1.10:8130/api/v1/something
# 401 ← 路径对,只是没带 API Key

同一个后端,两条路径,一个通一个不通。

二、错的排查顺序:从外到内查了六轮#

每一步的判断都是对的,只是顺序错了,六轮下来一个多小时。

先排掉最外面那一层#

一、怀疑 Cloudflare 隧道配错了。返回的 404 page not found 是纯文本、没有 HTML,这是 Go 程序的默认 404,而 Traefik 就是 Go 写的。说明请求已经到了 Traefik,只是没匹配上路由。隧道是通的。

再往里,labels、网络、入口点、provider#

二、怀疑 labels 没生效:

Terminal window
docker inspect <容器名> --format '{{json .Config.Labels}}' \
| python3 -m json.tool | grep -i traefik

traefik.enable=true、rule、entrypoints、service 全在。labels 没问题。

三、怀疑容器不在 Traefik 的网络上:

Terminal window
docker inspect <容器名> \
--format '{{range $k,$v := .NetworkSettings.Networks}}{{$k}}{{"\n"}}{{end}}'

traefik-net 在里面。网络没问题。

四、怀疑入口点名字对不上:

Terminal window
docker inspect <proxy容器> --format '{{json .Config.Cmd}}' | python3 -m json.tool

--entrypoints.web-cf.address=:8888 确实存在。入口点没问题。

五、怀疑 provider 上挂了 constraints。完整启动参数里只有 exposedbydefault=false 和 providers.docker.network,一条 constraint 都没有。也没问题。

六、重启 Traefik 强制重新扫描。还是 404。

三、直接问 Traefik 注册了什么#

不再猜,去拿它自己的 rawdata:

Terminal window
docker exec <proxy容器> wget -qO- http://localhost:8080/api/rawdata 2>/dev/null \
| python3 -c "import sys,json;d=json.load(sys.stdin);print(json.dumps({k:v for k,v in d.get('routers',{}).items() if 'myapp' in k},indent=2))"
# {}

空的。 不是「注册了但报错」,是根本没看见这个容器。

再查容器状态:

Terminal window
docker inspect <容器名> --format 'State={{.State.Status}} Health={{if .State.Health}}{{.State.Health.Status}}{{else}}none{{end}}'
# State=running Health=unhealthy

running 但 unhealthy。Traefik 的 docker provider 会跳过健康检查失败的容器。

服务其实活得好好的,直连端口返回 401 就是证明。只是我给它写的探针命令在那个镜像里不成立。

四、原因:一条跑不通的探针#

给一个 nginx 前端容器写了这条健康检查:

healthcheck:
test: ["CMD-SHELL", "wget -qO- http://localhost/ >/dev/null || exit 1"]

看着无害,实际在那个镜像里跑不通——可能是 busybox 版 wget 的行为不同,也可能是根路径本身不返回 200。总之探针永远失败,容器永远 unhealthy。

改法是删掉这段。官方 compose 本来就没给这个服务配健康检查,是我按「每个服务都要有 healthcheck」的习惯自己加上去的,而这个习惯写进过在 NAS 上用 Coolify 部署的十条约定。

五、三个教训#

没把握的探针不如不写#

一个永远失败的健康检查比没有健康检查危害大得多:它不报警,却会让 Traefik 跳过路由,还会让 depends_on: condition: service_healthy 的下游服务永远卡在 Created。

这两种症状都不指向健康检查本身,这是它难查的地方。

不同镜像里的工具不一样#

alpine 系有 wget 没 curl,distroless 两个都没有,nginx 官方镜像有 curl 但未必是你要的行为。加探针之前先进容器验一次:

Terminal window
docker exec <容器名> wget -qO- http://127.0.0.1:<端口>/
docker exec <容器名> curl -sf http://127.0.0.1:<端口>/

跑得通再往 compose 里写。

排查顺序应该反过来#

我从最外层的隧道查到最内层的 provider 配置,但成本最低、信息量最大的两条命令其实是:

Terminal window
# Traefik 到底注册了哪些 router
docker exec <proxy容器> wget -qO- http://localhost:8080/api/rawdata
# 容器自己是什么状态
docker ps --format '{{.Names}}\t{{.Status}}'

第二条尤其便宜——docker ps 的 Status 列直接写着 (unhealthy)。我看了很多遍那个输出,注意力全在 Up 上,没看括号里的字。

六、一份更实用的排查清单#

路由不通时按这个顺序,从便宜到贵:

Terminal window
# 1. 容器在跑吗?健康吗?(注意看 Up 后面的括号)
docker ps --format '{{.Names}}\t{{.Status}}' | grep <关键词>
# 2. 直连容器端口通吗?(绕开所有网关)
curl -s -o /dev/null -w "%{http_code}\n" http://192.168.1.10:<映射端口>/
# 3. Traefik 注册这条路由了吗?
docker exec <proxy容器> wget -qO- http://localhost:8080/api/http/routers 2>/dev/null \
| python3 -c "import sys,json;[print(r.get('name'),'|',r.get('status'),'|',r.get('error','')) for r in json.load(sys.stdin)]"
# 4. 打 Traefik 入口,带 Host 头(绕开 DNS 和隧道)
curl -s -o /dev/null -w "%{http_code}\n" -H "Host: myapp.example.com" http://192.168.1.10:8888/
# 5. 以上都对,再去查 labels、网络、入口点名字

第 1 步和第 2 步加起来不到十秒,能排掉一大半情况。

注意第 3 步列出来的 router 是按字母排序的,用 head -40 之类截断很容易把要找的那条切掉——我也在这儿浪费了一轮。

七、为什么这个坑藏得深#

根因很小,但症状(404)和原因(探针写错)之间隔着两层不相关的东西:Traefik 的服务发现,和 Docker 的健康状态。

换一句话说:labels 写对了,只保证「规则没错」,不保证「Traefik 看得见这个容器」。这两件事之间还夹着一道健康检查。

Traefik 跳过 unhealthy 容器这个行为是合理的,它不想把流量导给一个自己声称不健康的后端。问题在于「不健康」这个信号是我自己伪造的。

同一套网关此前还踩过五层别的问题,分层验证的顺序写在自建 Traefik 网关:HTTPS 打不开的完整排查路径里,这次的教训正好补上那份清单最前面缺的一步。

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

Traefik 一直 404:labels 全对,问题出在健康检查
https://blog.always1ov.com/posts/traefik-404-unhealthy-container/
作者
薯饼
发布于
2026-09-20
许可协议
CC BY-NC-SA 4.0