1. 本地 Docker 环境里 Traefik 反向代理到底解决什么问题
如果你手上有三四个容器化的小服务,每个都往宿主机上映射不同端口,时间一长自己都记不清哪个端口对应哪个服务。Traefik 反向代理加负载均衡要解决的就是这件事:所有流量只从 80/443 进来,由 Traefik 根据域名或路径自动分发到对应容器,容器增减时路由规则跟着 Docker 事件自动更新,不用手动改 Nginx 配置再 reload。
Traefik 是一款面向云原生场景的反向代理与负载均衡工具,它能直接监听 Docker 守护进程的事件流,容器一启动就自动注册路由,容器一销毁路由就下线。适合谁用?我建议这几类人优先考虑:本地同时跑多个 Web 服务需要统一入口的开发者、想给自建服务加 HTTPS 又不想手写证书配置的运维、以及正在学容器编排想找一个比 Nginx 更省心入口网关的人。
这篇教程的场景很明确:在本地 Docker 环境里部署 Traefik,完成反向代理与负载均衡配置,同时用 TaoToken 统一 Key 和 API 通道接入上游服务。所谓统一 Key,就是把多个上游服务的鉴权收敛到一处,Traefik 作为入口统一持有凭证,后端容器不用各自散落一堆 Key。下面从零开始,每一步都给可复制的配置。
先明确整体结构:Traefik 容器负责监听 80 和 8080(仪表盘),通过挂载/var/run/docker.sock感知其他容器;两个 whoami 容器作为后端,用 Docker Compose 的deploy.replicas或直接起两个服务来模拟负载均衡;TaoToken 的 API 通道作为上游服务之一接入,验证统一 Key 的转发是否生效。整套东西跑在一台本地机器上,不需要公网域名也能验证。
我试过把 Traefik 和 Nginx 放在同一台机器上对比,Traefik 的优势在于配置是声明式的,写在容器 label 里,跟服务定义放在一起,迁移时复制 compose 文件就行。Nginx 则要单独维护一份 conf,服务多了容易和实际容器状态脱节。当然 Traefik 也有学习成本,label 写错一个字符路由就不生效,所以下面每个配置我都会说明关键字段的作用。
在动手之前,确认本地已经装好 Docker 和 Docker Compose。用下面两条命令检查版本,能正常输出版本号即可:
docker --version docker compose version如果docker compose version报错,说明你的 Compose 还是旧版独立二进制,把命令换成docker-compose version即可,后续所有docker compose也相应替换。版本建议 Docker 20.10 以上、Compose v2 以上,低版本对 label 语法支持可能有差异。
2. TaoToken 统一 Key 与 API 通道的前置准备
在把 Traefik 配起来之前,先把上游服务的鉴权通道准备好,否则后面验证负载均衡时没有真实后端可打。TaoToken 在这里扮演的角色是统一 API 通道:你不需要在每个后端容器里各配一套 Key,而是让 Traefik 在转发时统一携带凭证,后端只认 Traefik 这一个入口。
先拿到访问凭证。打开官网 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,注册登录后进入控制台。控制台地址是 https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console ,在里面找到 API Keys 管理页,新建一个 Key 并复制保存。这个 Key 就是后面配置里要填的凭证,格式通常是一串以特定前缀开头的字符串。
拿到 Key 之后,确认 API 通道的基础地址。TaoToken 的 API 入口是 https://taotoken.net/api ,注意这个地址不带任何查询参数,直接作为 Base URL 使用。如果你用的是 OpenAI 兼容的客户端,Base URL 填这个,Key 填刚才复制的那个,模型 ID 按你实际要调用的填。这三件套——Base URL、Key、Model ID——在后面 Traefik 的转发配置里会用到。
这里要强调一个容易踩的坑:Base URL 和官网首页是两个不同的地址。首页是给人看的,API 是给程序调的,配置里千万别把首页地址填进去,否则请求会返回 HTML 而不是 JSON。我在早期配置时就犯过这个错,curl 出来一堆网页源码,排查了半天才发现地址填错。
如果你打算长期跑编码类或 Agent 类任务,可以顺带了解一下 Coding Plan,地址是 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan ,它适合需要持续调用模型的场景。不过本篇的重点是 Traefik 接入,Coding Plan 只是顺带提一句,不影响主流程。
前置准备的最后一步是规划目录结构。在本地建一个工作目录,比如~/traefik-lab,里面放三样东西:Traefik 的静态配置文件、docker-compose.yml、以及一个空的 acme.json(如果后面要配 HTTPS)。目录结构大概是这样:
traefik-lab/ ├── docker-compose.yml ├── traefik.yml ├── dynamic.yml └── acme.jsontraefik.yml是静态配置,定义入口点、Provider、API 仪表盘这些启动时就确定的东西;dynamic.yml是动态配置,定义中间件、路由规则这些可以热更新的东西。分开写的好处是改动态配置不用重启 Traefik。acme.json 先建空文件并设权限 600,后面配 HTTPS 时用得上。
3. 可复制的 docker-compose 与 traefik.yml 配置
这一节是全文的核心,所有配置都可以直接复制粘贴。先写静态配置文件traefik.yml,它定义 Traefik 启动时加载的参数:
# traefik.yml 静态配置 entryPoints: web: address: ":80" websecure: address: ":443" dashboard: address: ":8080" api: dashboard: true insecure: true providers: docker: endpoint: "unix:///var/run/docker.sock" exposedByDefault: false network: traefik-net file: filename: /etc/traefik/dynamic.yml watch: true log: level: INFO accessLog: {}逐段说明。entryPoints定义了三个入口:web 监听 80 走 HTTP,websecure 监听 443 走 HTTPS,dashboard 监听 8080 专门给仪表盘用。把仪表盘单独放一个入口点,比混在 80 端口上更清晰,也方便后面加访问控制。api.dashboard: true开启仪表盘,insecure: true表示仪表盘不强制 HTTPS,本地开发够用,生产环境要关掉并加认证。
providers.docker是 Traefik 感知容器的关键。endpoint指向 Docker 套接字,exposedByDefault: false表示容器默认不暴露,必须显式打traefik.enable=true标签才会被 Traefik 接管,这个设置能避免误暴露不该暴露的容器。network: traefik-net指定 Traefik 只发现接入这个网络的容器,隔离性更好。providers.file加载动态配置文件并开启 watch,改完文件自动生效。
接着写动态配置文件dynamic.yml,这里放中间件和统一鉴权头:
# dynamic.yml 动态配置 http: middlewares: taotoken-auth: headers: customRequestHeaders: Authorization: "Bearer sk-你的TaoTokenKey" ratelimit: rateLimit: average: 100 burst: 50 routers: api-router: rule: "PathPrefix(`/v1`)" entryPoints: - web service: taotoken-api middlewares: - taotoken-auth - ratelimit services: taotoken-api: loadBalancer: servers: - url: "https://taotoken.net/api" passHostHeader: true这段配置做了三件事。taotoken-auth中间件给所有经过它的请求自动加上Authorization头,把 TaoToken 的 Key 统一注入,后端服务不用各自持有 Key。ratelimit中间件限制平均 100 请求每秒、突发 50,防止某个客户端把上游打爆。api-router把路径以/v1开头的请求路由到taotoken-api服务,并挂上两个中间件。taotoken-api服务的loadBalancer.servers指向 TaoToken 的 API 地址,passHostHeader: true保留原始 Host 头。
注意Authorization里的 Key 要换成你在控制台复制的真实 Key。生产环境不建议把 Key 明文写在 dynamic.yml 里,可以用环境变量或 Docker secret 注入,这里为了演示直观就先明文写,你实际用时记得改。
最后写docker-compose.yml,把 Traefik 和两个后端服务串起来:
# docker-compose.yml services: traefik: image: traefik:v3.0 container_name: traefik restart: unless-stopped ports: - "80:80" - "443:443" - "8080:8080" volumes: - /var/run/docker.sock:/var/run/docker.sock:ro - ./traefik.yml:/etc/traefik/traefik.yml:ro - ./dynamic.yml:/etc/traefik/dynamic.yml:ro - ./acme.json:/acme.json networks: - traefik-net labels: - "traefik.enable=true" - "traefik.http.routers.dashboard.rule=Host(`traefik.localhost`)" - "traefik.http.routers.dashboard.service=api@internal" - "traefik.http.routers.dashboard.entrypoints=dashboard" whoami1: image: traefik/whoami container_name: whoami1 networks: - traefik-net labels: - "traefik.enable=true" - "traefik.http.routers.whoami.rule=Host(`whoami.localhost`)" - "traefik.http.services.whoami.loadbalancer.server.port=80" whoami2: image: traefik/whoami container_name: whoami2 networks: - traefik-net labels: - "traefik.enable=true" - "traefik.http.routers.whoami.rule=Host(`whoami.localhost`)" - "traefik.http.services.whoami.loadbalancer.server.port=80" networks: traefik-net: driver: bridge关键点解释。Traefik 容器挂载 Docker 套接字时加了:ro只读,减少安全风险。三个端口映射分别对应 HTTP、HTTPS、仪表盘。whoami1和whoami2用了完全相同的路由规则和 service 名,Traefik 会把它们识别为同一个 service 的两个后端实例,自动做负载均衡——这是 Traefik 的巧妙之处,同名 service 的多个容器会被合并成一个负载均衡池。
traefik.http.services.whoami.loadbalancer.server.port=80告诉 Traefik 后端容器监听的是 80 端口。如果后端监听其他端口,这里要改。traefik.http.routers.whoami.rule=Host(...)定义路由规则,两个容器写一样的规则,Traefik 自动合并。
启动前先建好 acme.json 并设权限:
touch acme.json chmod 600 acme.json然后启动整套服务:
docker compose up -d用docker compose ps确认三个容器都是 running 状态。如果 Traefik 起不来,用docker compose logs traefik看日志,常见问题是配置文件路径挂载错误或 YAML 缩进不对。
4. 验证请求分发与健康检查的具体动作
配置写完不算完,得实际验证路由和负载均衡是否按预期工作。先验证仪表盘能不能打开。因为用的是traefik.localhost这个域名,需要在 hosts 文件里加一条解析,或者直接用 localhost 加端口访问。用 curl 验证仪表盘 API:
curl -s http://localhost:8080/api/rawdata | head -c 500这条命令返回 Traefik 的原始配置数据,能看到 routers、services、middlewares 的注册情况。如果返回 JSON 且里面能找到whoami相关的 router,说明 Docker Provider 正常工作。如果返回空或报错,检查 Traefik 日志里有没有provider connection error。
接着验证 whoami 的路由。因为whoami.localhost需要 DNS 解析,最省事的办法是加 hosts:
# Linux/macOS echo "127.0.0.1 whoami.localhost traefik.localhost" | sudo tee -a /etc/hostsWindows 则编辑C:\Windows\System32\drivers\etc\hosts加同样一行。加完后用 curl 请求:
curl -s http://whoami.localhost多请求几次,观察返回的Hostname字段:
for i in $(seq 1 6); do curl -s http://whoami.localhost | grep Hostname; done如果负载均衡生效,你会看到 Hostname 在 whoami1 和 whoami2 之间交替出现。Traefik 默认用轮询策略,两个后端实例会轮流接请求。如果每次都返回同一个 Hostname,说明两个容器没被识别成同一个 service,检查两个容器的 label 里 service 名是否完全一致。
再验证 TaoToken 统一 Key 的转发。请求/v1路径,看 Traefik 是否自动加上了 Authorization 头并转发到上游:
curl -s -o /dev/null -w "%{http_code}\n" http://localhost/v1/models如果返回 200 或 401,说明请求确实到了上游。返回 401 通常意味着 Key 无效或没注入成功,返回 404 则可能是路由规则没匹配上。想看得更细,用-v看请求头:
curl -v http://localhost/v1/models 2>&1 | grep -i authorization正常情况应该能看到Authorization: Bearer sk-...被自动加上。这一步验证的就是统一 Key 的核心价值:客户端不用带 Key,Traefik 在中间层注入。
健康检查方面,Traefik 本身不主动探测后端健康状态,但可以通过 Docker 的 healthcheck 配合。给 whoami 容器加 healthcheck:
healthcheck: test: ["CMD", "wget", "-q", "--spider", "http://localhost:80"] interval: 10s timeout: 3s retries: 3加了 healthcheck 后,不健康的容器会被 Docker 标记,Traefik 感知到后会把流量只发给健康实例。验证方法是手动停掉 whoami1,再连续请求,看是否所有请求都落到 whoami2:
docker stop whoami1 for i in $(seq 1 4); do curl -s http://whoami.localhost | grep Hostname; done如果四次都返回 whoami2 的 Hostname,说明故障转移生效。验证完记得docker start whoami1恢复。
最后验证 HTTPS。如果你配了 Let's Encrypt,本地域名无法申请公网证书,可以用自签名证书测试。在 dynamic.yml 里加一个自签名证书配置,或者直接用 Traefik 的默认证书。本地测试 HTTPS 主要验证 443 入口点是否监听:
curl -sk https://localhost -o /dev/null -w "%{http_code}\n"-k跳过证书校验,返回 404 或 200 都说明 443 端口通了。生产环境再换成真实证书。
5. 本篇常见报错排查对照
配置过程中最容易撞上的几个报错,这里逐个对照排查。
第一个是401 Unauthorized。请求/v1路径返回 401,说明请求到了上游但鉴权没过。排查顺序:先确认 dynamic.yml 里Authorization头的 Key 是不是完整复制,有没有多余空格;再确认中间件有没有正确挂到 router 上,检查middlewares列表里taotoken-auth的拼写;最后用curl -v看请求头里到底有没有 Authorization。如果头没加上,多半是中间件名和 router 里引用的名字不一致。
第二个是local proxy failed或502 Bad Gateway。这通常意味着 Traefik 找不到后端。检查后端容器和 Traefik 是否在同一个 Docker 网络里,docker network inspect traefik-net看容器列表。如果后端不在网络里,Traefik 无法通过容器名解析到它。另一个原因是loadbalancer.server.port填错了,后端实际监听 8080 你填了 80,连接自然失败。
第三个是error while reading choices或类似的 JSON 解析错误。这种报错一般出现在你直接请求上游 API 时,返回的不是预期 JSON。原因通常是 Base URL 填错,把官网首页地址当成了 API 地址。确认配置里用的是https://taotoken.net/api而不是带其他路径的地址。如果用的是 OpenAI 兼容客户端,检查 Base URL 末尾有没有多余的斜杠。
第四个是 OAuth 或认证相关的报错。如果你用的是 Claude Code 这类工具接入,报错可能提示 OAuth 失败。这类工具通常需要三件套齐全:Base URL、Key、Model ID。缺任何一个都会认证失败。Base URL 填https://taotoken.net/api,Key 填控制台复制的,Model ID 按工具要求填。三个都确认无误后重启工具再试。
第五个是仪表盘打不开。访问http://localhost:8080返回 404 或连接拒绝。先确认 Traefik 容器的 8080 端口有没有映射出来,docker compose ps看端口列。再确认api.insecure: true有没有配,没配的话仪表盘默认走 HTTPS 且需要认证。如果端口映射正常但返回 404,检查 dashboard router 的 entrypoints 是不是写成了dashboard,和 entryPoints 里定义的名字对上。
第六个是路由不生效,请求返回 404。最常见的原因是容器 label 里traefik.enable=true没写,或者exposedByDefault: false下忘了打这个标签。另一个原因是路由规则里的 Host 和你请求的 Host 不匹配,比如规则写whoami.localhost但你请求的是localhost。用curl -H "Host: whoami.localhost" http://localhost可以绕过 DNS 直接测。
排查时善用 Traefik 日志。把log.level调成 DEBUG,重启后docker compose logs -f traefik能看到每条请求匹配了哪个 router、走了哪个 service。仪表盘里的 HTTP 页面也能看到实时请求和路由匹配情况,比翻日志直观。
6. 把统一入口接到你的实际项目里
跑通上面的 demo 之后,把 Traefik 接到真实项目里其实只是替换后端的事。你现有的容器只要接入traefik-net网络,打上traefik.enable=true和路由规则 label,就能被 Traefik 接管。原来映射到宿主机的端口可以去掉,所有流量统一从 80 进来。
统一 Key 这块,如果你的项目里有多个上游服务需要不同凭证,可以在 dynamic.yml 里定义多个中间件,每个中间件注入不同的 Authorization 头,然后按路径或域名挂到不同 router 上。这样客户端只需要访问 Traefik,凭证管理全部收敛到一处,换 Key 时只改 dynamic.yml 一个文件,不用挨个改后端配置。
需要提醒的是,dynamic.yml 里明文写 Key 只适合本地开发。生产环境建议用 Docker secret 或环境变量注入,Traefik 支持从文件读取环境变量。另外api.insecure: true在生产环境要关掉,仪表盘加上 basicAuth 中间件保护。
如果你在配置过程中卡在某个报错上,可以对照第 5 节的排查清单逐条过。大部分问题出在 label 拼写、网络不通、端口填错这三类。把 Traefik 日志开到 DEBUG,基本能定位到具体是哪条规则没匹配上。
后续想扩展的话,Traefik 还支持 TCP/UDP 路由、gRPC 负载均衡、以及和 Consul 等服务发现集成。本地玩熟了再往生产迁移,配置结构基本不用大改,把静态配置里的 entryPoints 换成真实域名和证书即可。