1. 为什么要在 K8s 里用 Traefik 做反向代理
很多同学第一次接触 Kubernetes 的 Ingress,都是从 Nginx Ingress Controller 开始的。它稳定、资料多,但配置热更新依赖 reload,服务频繁上下线时,偶尔会看到几秒的 502。Traefik 走的是另一条路:它原生监听 Kubernetes API,Service、Pod、Endpoint 一有变化就自动感知,配置热重载,进程不重启,用户基本无感知。这就是我这次在集群里换用 Traefik 做反向代理的直接原因。
Traefik 是什么?一句话:面向微服务的 HTTP 反向代理和负载均衡器。它能对接 Docker、Swarm、Kubernetes、Consul、Etcd 等多种后端,自动发现服务并刷新路由。适合谁?适合已经在跑 K8s、想让外部流量按 Host/Path 精准落到后端服务、又不想天天手动改 Nginx 配置的运维和开发同学。
这次实践的目标有两个:第一,用 Traefik 的 IngressRoute 和 Middleware 把外部流量按域名和路径路由到集群内的 dashboard、Kibana 等服务;第二,把 AI 工具调用的 Base URL 统一改到 TaoToken 的 API 通道,让本地脚本、CI、Agent 都走同一个入口,省得每个工具单独配 Key。下面从环境准备一路写到 curl 验证和报错排查,命令和 YAML 都可以直接复制。
需要提前说明的是,Traefik 在 K8s 里有两种 CRD 风格:老的 Ingress(靠 annotation)和新的 IngressRoute(Traefik 自己的 CRD)。前者兼容性好,后者表达力强,Middleware 就是靠 CRD 才能用的。本文两种都会给,重点放在 IngressRoute + Middleware,因为路径重写、鉴权头注入这些操作,用 Middleware 比堆 annotation 清爽太多。
2. 部署 Traefik 与前置准备:IngressRoute 与 CRD 安装踩坑
环境假设你已经有一个能正常kubectl get nodes的集群,我用的是 kubeadm 搭的三节点。Traefik 官方提供了 Helm Chart,也提供了裸 YAML。为了看清每一步,我这里用官方 YAML 走一遍,再补 Helm 方式。
先拉官方的 k8s 示例目录,里面包含 RBAC、Deployment、Service 等文件:
cd /home/wanyang3/k8s/ git clone https://github.com/traefik/traefik.git ls traefik/docs/content/reference/dynamic-configuration/如果你用的是较新版本,CRD 需要单独安装,否则 IngressRoute 创建时会报no matches for kind "IngressRoute"。这一步是新手最容易漏的:
kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v2.11/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v2.11/docs/content/reference/dynamic-configuration/kubernetes-crd-rbac.yml装完 CRD,再部署 Traefik 本体。RBAC 先上,再上 Deployment:
kubectl create -f traefik-rbac.yaml kubectl create -f traefik-deployment.yaml kubectl get pods --all-namespaces -o wide | grep traefik正常会看到traefik-ingress-controller-xxx 1/1 Running。它同时开了 80(业务入口)和 8080(Dashboard)。查看 Service 暴露的 NodePort:
kubectl get service --all-namespaces | grep traefik输出类似traefik-ingress-service 10.96.8.191 <nodes> 80:30016/TCP,8080:30960/TCP。浏览器访问http://<node_ip>:30960/dashboard/#/,能看到 Dashboard。初次打开 Providers 下 Kubernetes 是空的,因为还没建任何路由规则,这是正常的。
这里有个细节值得说:Traefik 的 Dashboard 本身也是通过 IngressRoute 暴露的,官方示例里叫traefik-dashboard。如果你想让 Dashboard 走域名而不是 NodePort,可以自己写一条 IngressRoute,把 Host 设成traefik-ui.k8s,后端指向api@internal。这样访问http://traefik-ui.k8s/dashboard/就能进,比记端口号舒服。
前置准备里还有一件事:确认你的 Traefik 版本。kubectl get deploy traefik -n kube-system -o yaml | grep image看一下,v2 和 v3 的 CRD apiVersion 不一样,v2 是traefik.containo.us/v1alpha1,v3 改成了traefik.io/v1alpha1。写 YAML 时 apiVersion 写错,报错信息是no matches for kind,很多人会误以为是 CRD 没装,其实是版本对不上。我踩过的坑就是拿 v2 的示例往 v3 集群上套,折腾了半小时才发现是 apiVersion 的问题。
3. 可复制配置:IngressRoute、Middleware 与 TaoToken 通道片段
这一节是全文的核心,所有 YAML 和配置片段都可以直接改改就用。先看基于 Host 的 IngressRoute,把dashboard.k8s.traefik和ela.k8s.traefik分别路由到 dashboard 和 elasticsearch:
apiVersion: traefik.io/v1alpha1 kind: IngressRoute metadata: name: dashboard-ela-route namespace: kube-system spec: entryPoints: - web routes: - match: Host(`dashboard.k8s.traefik`) kind: Rule services: - name: kubernetes-dashboard port: 80 - match: Host(`ela.k8s.traefik`) kind: Rule services: - name: elasticsearch-logging port: 9200再看基于 Path 的路由,这里必须配合 Middleware 做路径重写,否则后端服务收到的路径会带上前缀,直接 404。Middleware 的stripPrefix就是干这个的:
apiVersion: traefik.io/v1alpha1 kind: Middleware metadata: name: strip-dashboard namespace: kube-system spec: stripPrefix: prefixes: - /dashboard --- apiVersion: traefik.io/v1alpha1 kind: IngressRoute metadata: name: my-k8s-route namespace: kube-system spec: entryPoints: - web routes: - match: Host(`my.k8s.traefik`) && PathPrefix(`/dashboard`) kind: Rule middlewares: - name: strip-dashboard services: - name: kubernetes-dashboard port: 80 - match: Host(`my.k8s.traefik`) && PathPrefix(`/kibana`) kind: Rule middlewares: - name: strip-kibana services: - name: kibana-logging port: 5601strip-kibana的 Middleware 和上面结构一样,把 prefixes 换成/kibana即可。注意match里用的是反引号,不是单引号,这是 Traefik 的规则语法,写错会报invalid rule。
接下来是 TaoToken 统一 API 通道的配置。TaoToken 提供 OpenAI 兼容的接口,Base URL 是https://taotoken.net/api,模型对话、Coding Plan、API Keys 都在控制台管理。你可以在本地工具里这样配:
{ "base_url": "https://taotoken.net/api", "api_key": "sk-你的TaoToken密钥", "model": "claude-sonnet-4-5" }如果你用的是 Claude Code 这类工具,配置通常放在~/.claude/settings.json或项目级 settings 里,把 Base URL 指向 TaoToken,Key 填控制台生成的即可。Codex 用户则改~/.codex/auth.json,结构类似:
{ "base_url": "https://taotoken.net/api", "api_key": "sk-你的TaoToken密钥" }三件套记牢:Base URL、Key、Model ID,缺一个都调不通。Model ID 以控制台模型列表为准,别照抄网上的旧名字。如果你在集群里跑 Agent,可以把这些值放进 Secret,再挂进 Pod 环境变量,避免硬编码。
4. 验证请求:curl 测路由与鉴权是否真的通了
配置写完不验证等于没写。先确认 IngressRoute 被 Traefik 识别:
kubectl get ingressroute --all-namespaces kubectl get middleware --all-namespaces然后从集群外 curl,验证 Host 路由。假设你的节点 IP 是 10.222.76.93,Traefik 的 web 入口 NodePort 是 30016:
curl -H "Host: dashboard.k8s.traefik" http://10.222.76.93:30016/ -I返回HTTP/1.1 200 OK或 302 跳转,说明 Host 路由生效。再测 Path 路由和 stripPrefix:
curl -H "Host: my.k8s.traefik" http://10.222.76.93:30016/dashboard/ -I如果后端 dashboard 返回 200,说明/dashboard前缀被正确剥离。如果返回 404,八成是 Middleware 没挂上,或者stripPrefix的 prefixes 写错了。
再验证 TaoToken 通道。用 curl 直接打模型对话接口:
curl https://taotoken.net/api/v1/chat/completions \ -H "Authorization: Bearer sk-你的TaoToken密钥" \ -H "Content-Type: application/json" \ -d '{ "model": "claude-sonnet-4-5", "messages": [{"role": "user", "content": "ping"}] }'返回里带choices数组和内容,就说明 Key 和 Base URL 都对。如果返回 401,是 Key 无效或没带Bearer前缀;返回 404,多半是路径写成了/v1/chat/completions之外的形式,检查 Base URL 是不是https://taotoken.net/api,别多加或少加/v1。
最后看 Traefik Dashboard,Providers 下应该能看到你新建的 IngressRoute 和 Middleware,Services 里能看到后端服务列表。点进去还能看到负载均衡策略,默认是 wrr(加权轮询)。如果你把某个服务扩到两个 Pod,Dashboard 里会实时多出一个 endpoint,不用刷新,这就是 Traefik 热重载的直观体现。
5. 本篇常见错排查:401、local proxy failed 与 reading choices
排错这块我按真实报错来对,都是我自己或群里同学遇到过的。
第一个,401 Unauthorized。分两种场景。如果是 curl Traefik 路由返回 401,说明后端服务自己有鉴权,比如 dashboard 需要 token,这跟 Traefik 无关,去后端解决。如果是 curl TaoToken 返回 401,检查三件事:Key 是否复制完整(别带空格)、请求头是否是Authorization: Bearer sk-xxx、Key 是否在控制台被禁用或额度耗尽。
第二个,local proxy failed。这个报错通常出现在本地工具通过代理访问 API 时,代理进程没起来或端口不对。排查顺序:先确认本地代理进程在跑,再确认工具的 Base URL 指向的是https://taotoken.net/api而不是某个本地端口。如果你在 K8s 里跑 Agent,检查 Pod 的 env 里 Base URL 有没有被旧值覆盖,kubectl exec进去env | grep -i base看一眼最直接。
第三个,reading choices相关报错,比如cannot read property 'choices' of undefined。这几乎都是响应体不是预期的 JSON 结构导致的。常见原因:Base URL 配错,请求打到了某个返回 HTML 的地址;或者 Model ID 写错,服务端返回了错误对象而不是正常响应。解决办法是先 curl 一次看原始返回,别只看工具封装后的报错。原始返回里如果有error字段,按里面的 message 定位。
第四个,IngressRoute 创建成功但访问 404。检查entryPoints是不是web(80)还是websecure(443),你 curl 的端口要和 entryPoint 对上。再检查match规则里的 Host 和你 curl 的-H "Host: ..."是否完全一致,大小写、点号都不能差。
第五个,Middleware 不生效。kubectl get middleware -n kube-system确认存在,然后看 IngressRoute 的middlewares字段引用的 name 和 namespace 是否对得上。跨 namespace 引用要写全,同 namespace 直接写 name。改完 Middleware 不用重启 Traefik,几秒内自动生效。
6. 把 AI 调用统一到 TaoToken 通道的长期用法
路由跑通之后,真正省事的是把 AI 工具的调用入口统一。以前每个工具一套 Key、一个 Base URL,换模型要改一堆地方。现在全部指向 TaoToken,Key 在控制台统一管理,模型切换只改 Model ID。
如果你要长期跑编码类 Agent,建议用 Coding Plan,额度更划算,适合高频调用。日常验证模型效果,用模型对话页面直接试,不用写代码。需要生成和管理 Key,去 API Keys 页面。接入细节和参数说明,看接入文档最准。这几个入口分别是:
- 模型对话:https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=model_chat
- Coding Plan:https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=coding_plan
- 控制台:https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=console
- API Keys:https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=api_keys
- 接入文档:https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=doc
在 K8s 里,我习惯把 TaoToken 的 Key 放进 Secret,Agent 的 Deployment 通过envFrom引用,这样换 Key 只改 Secret,不用动镜像。配合 Traefik 的 IngressRoute,外部流量进集群走 Traefik,集群内 Agent 出网走 TaoToken,两条链路各管各的,排查起来边界清晰。
最后留一个实用技巧:Traefik 的 Dashboard 里能看到每个路由的请求量和响应码分布,调 AI 接口时如果偶发 5xx,先看 Dashboard 是 Traefik 层的问题还是后端的问题,能省不少时间。路由规则改完,kubectl apply一下,Dashboard 秒级更新,不用重启任何 Pod,这就是当初换 Traefik 想要的效果。