1. Traefik in Docker 为什么总在“最后一公里”翻车
Traefik 在 Docker 里跑起来只要几行 compose,但真正让请求稳定落到容器上,往往卡在几个很具体的点上:路由规则写了却不生效、Let's Encrypt 证书一直签不下来、中间件套上去反而出现重定向循环。这篇就围绕这三个高频报错,把可复制的静态配置、动态配置骨架,以及 TaoToken 统一 Key 的接入示例一次讲清楚,让你照着改就能定位问题。
Traefik 本质上是一个“会自己看 Docker 标签”的反向代理。你给容器打上traefik.enable=true和路由规则,它就去 Docker API 里读这些标签,自动生成路由。听起来很省事,但正因为配置分散在静态文件、动态文件、容器标签三个地方,任何一处对不上,表现都是“访问不通”,排查起来容易懵。
适合谁看:已经在 Docker 里跑 Traefik、但被路由或证书问题卡住的同学;准备把多个 AI 服务(比如模型对话、Coding Plan 这类接口)统一收口到一个域名后面的同学;以及想用 TaoToken 统一 Key 管理多家模型调用、又不想在网关层反复改配置的同学。
下面所有配置我都按“静态配置 + 动态配置 + 容器标签”三层来组织,这样出问题时你能快速判断是哪一层没生效。TaoToken 的接入点放在动态配置里,因为它本质就是一个上游 HTTP 服务,用 Traefik 的loadBalancer指向它即可。
2. 前置准备:TaoToken 统一 Key 与目录结构
在动 Traefik 之前,先把上游服务准备好。TaoToken 提供统一的 API 入口,你只需要一个 Key 就能调用多种模型,省去在网关里为每家模型单独配一套转发规则。官网地址是 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 基址是 https://taotoken.net/api 。
先去控制台创建一个 API Key,路径在 https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite ,Key 管理页在 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite 。拿到形如sk-xxxx的 Key 后,先别急着塞进 Traefik,用 curl 单独验证一次,确认 Key 本身可用,避免后面把“Key 无效”误判成“Traefik 路由错”。
curl -s https://taotoken.net/api/v1/models \ -H "Authorization: Bearer sk-你的Key" | head -c 400如果返回模型列表,说明 Key 和网络都正常。这一步很关键,因为 Traefik 报错信息经常只告诉你 502 或 504,不会告诉你上游其实是 401。
目录结构建议这样放,静态配置和动态配置分开,方便 Traefik 热加载动态部分:
traefik/ ├── docker-compose.yml ├── traefik.yml # 静态配置 ├── dynamic/ │ └── conf.yml # 动态配置(路由、中间件、服务) └── acme.json # 证书存储,权限必须 600acme.json一定要提前创建并改权限,否则证书签发会静默失败:
touch traefik/acme.json && chmod 600 traefik/acme.json3. 可复制配置:静态骨架 + 动态骨架 + TaoToken 接入
3.1 静态配置 traefik.yml
静态配置决定 Traefik 启动时加载什么,改完要重启容器。这里开启 Docker provider、文件 provider,并配好 Let's Encrypt 的 HTTP 挑战。
entryPoints: web: address: ":80" websecure: address: ":443" providers: docker: endpoint: "unix:///var/run/docker.sock" exposedByDefault: false file: directory: "/etc/traefik/dynamic" watch: true certificatesResolvers: myresolver: acme: email: your-email@example.com storage: /acme.json httpChallenge: entryPoint: web api: dashboard: trueexposedByDefault: false是重点。默认 true 时,所有容器都会被 Traefik 尝试接管,容易和别的服务冲突;设成 false 后,只有显式打了traefik.enable=true的容器才会被代理。
3.2 动态配置 dynamic/conf.yml
动态配置负责路由、中间件和上游服务。TaoToken 作为一个外部服务写在这里,用loadBalancer指向它的 API 地址。
http: routers: taotoken-router: rule: "Host(`ai.example.com`)" entryPoints: - websecure service: taotoken-svc tls: certResolver: myresolver middlewares: - taotoken-headers services: taotoken-svc: loadBalancer: servers: - url: "https://taotoken.net" passHostHeader: true middlewares: taotoken-headers: headers: customRequestHeaders: Authorization: "Bearer sk-你的Key"这里把 Key 放在中间件的customRequestHeaders里,客户端请求 Traefik 时不用带 Key,由网关统一注入。注意passHostHeader: true,否则上游可能因为 Host 不匹配返回 404。
3.3 docker-compose.yml
services: traefik: image: traefik:v3.0 container_name: traefik restart: unless-stopped ports: - "80:80" - "443:443" volumes: - /var/run/docker.sock:/var/run/docker.sock:ro - ./traefik.yml:/etc/traefik/traefik.yml:ro - ./dynamic:/etc/traefik/dynamic:ro - ./acme.json:/acme.json networks: - web networks: web: external: truesocket挂载用:ro只读,减少权限风险。网络用 external,保证 Traefik 和业务容器在同一个网络里,这是“服务发现失败”最常见的根因。
4. 验证请求:从路由到证书逐项确认
配置写完别直接开浏览器,按顺序验证能省很多时间。
第一步,确认 Traefik 自己起来了,看日志有没有 provider 加载成功:
docker logs traefik --tail 50 | grep -i "provider\|error"第二步,确认路由被识别。访问 dashboard(如果开了)或直接看 API:
curl -s http://localhost:8080/api/http/routers | head -c 500第三步,验证 TaoToken 上游是否通。先绕过 Traefik 直连一次,再通过 Traefik 域名访问,对比结果:
curl -s https://ai.example.com/api/v1/models \ -H "Authorization: Bearer sk-你的Key" | head -c 300如果直连通、走 Traefik 不通,问题就在 Traefik 层;如果直连也不通,先回去检查 Key 和网络。
第四步,证书验证。用 openssl 看证书链是否完整:
echo | openssl s_client -connect ai.example.com:443 -servername ai.example.com 2>/dev/null | openssl x509 -noout -issuer -dates签发成功会显示 Let's Encrypt 的 issuer 和有效期。如果这里报错,基本就是acme.json权限或 80 端口没通。
5. 本篇常见错误逐项排查
5.1 路由未生效:标签写了但 404
现象是访问域名返回 404,dashboard 里看不到对应 router。先检查容器是否在 Traefik 同一个网络:
docker inspect 业务容器名 --format '{{json .NetworkSettings.Networks}}'如果网络名和 Traefik 的不一致,路由标签再多也没用。其次检查标签是否写全,traefik.http.routers.xxx.rule里的 router 名要和 service 名对应上。还有一点容易忽略:exposedByDefault: false时,容器必须显式加traefik.enable=true。
5.2 证书签发失败:一直卡在 pending
最常见原因是 80 端口没对外开放,HTTP 挑战需要外部能访问http://你的域名/.well-known/acme-challenge/。用 curl 从外网测一下:
curl -I http://ai.example.com/.well-known/acme-challenge/test如果返回 404 而不是连接失败,说明 Traefik 收到了请求但没匹配到挑战路由,检查 entryPoint 名字是否和静态配置里一致。另一个坑是acme.json权限不是 600,Traefik 会拒绝写入,日志里会有unable to store certificate之类的提示。
5.3 中间件不匹配:重定向循环
浏览器报ERR_TOO_MANY_REDIRECTS,通常是 Traefik 配了 HTTPS 重定向,后端服务自己也强制跳 HTTPS,两边来回踢。解决办法是只在一层做重定向。如果 Traefik 已经用redirectScheme中间件,后端就不要再配强制跳转。
http: middlewares: redirect-to-https: redirectScheme: scheme: https permanent: true把这个中间件挂到 web entryPoint 的路由上,websecure 的路由不要再挂,否则自己跳自己。
5.4 TaoToken 接入返回 401 或 502
401 一般是 Key 没注入成功,检查中间件是否真的挂到了 router 上,名字有没有拼错。502 则是 Traefik 连不上上游,确认loadBalancer里的 url 是https://taotoken.net而不是带路径的地址,路径部分交给路由规则处理。如果上游要求特定 Host,passHostHeader要设 true。
6. 收口与后续动作
把上面三层配置跑通后,你其实已经有一个能统一收口 AI 请求的网关:客户端只认一个域名,Key 在网关层注入,换模型或加服务只改动态配置,不用动客户端。后续如果要长期跑编码类或 Agent 类任务,可以了解下 Coding Plan,地址是 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite ,它更适合持续性的调用场景。想先验证模型对话效果,可以直接在 https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_content=chat&utm_campaign=rewrite 里试。接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,遇到参数细节可以对照查。
最后留一个我踩过的坑:Traefik 的静态配置改完必须重启容器,动态配置才会热加载,别改完静态文件就等它自动生效,那样只会浪费半小时。