☰
TaoToken 场景下的 Traefik 实战:K3S+Rancher 中按端口映射不同内部服务根路径
2026/10/2 16:33:03 网站建设 项目流程

1. 为什么 K3S 里 Ingress 做不到「80 端口给 A、8080 端口给 B」

先把问题摆清楚。K3S 默认自带 Traefik 作为 IngressController,你写一个 Ingress 或者 IngressRoute,它加载的规则是全局的。也就是说,不管你从哪个主机端口进来,只要流量最终落到这个 IngressController 上,它匹配的规则集是同一套。

我举个具体例子。集群里有两个服务:usercenter和ordersvc,两个服务的 UI 都从根路径/开始加载资源。你希望http://主机:80/打到 usercenter,http://主机:8080/打到 ordersvc。如果你只写 Ingress:

apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: usercenter-ingress spec: rules: - http: paths: - path: / pathType: Prefix backend: service: name: usercenter port: number: 80

再来一个 ordersvc 的 Ingress,同样写path: /。这时候 Traefik 会怎么处理?两个规则冲突,通常只有一个生效,或者按优先级随机命中。你开 80 和 8080 两个端口,它们都指向同一个 IngressController,所以两个端口的行为完全一样——要么都到 usercenter,要么都到 ordersvc。

有人会说,那我用traefik.ingress.kubernetes.io/rewrite-target重写路径行不行?对于 API 类服务可能行,但对于前端 UI 就不行了。因为浏览器加载页面后,JS/CSS 请求的还是根路径/static/app.js,这个请求回到 Traefik 后,又会被同一套规则匹配,可能被转发到另一个服务去。结果就是 A 的页面加载了 B 的 JS,白屏或者报错。

NodePort 能解决端口区分的问题,但它有个硬伤:如果服务本身只跑 HTTP,而整个集群要求对外统一 HTTPS,NodePort 就没法在入口层做 TLS 终止了。你总不能在每个服务里都配一遍证书。

所以真正的需求是:在集群入口处,按主机端口区分流量,每个端口独立转发到不同的内部服务根路径,并且能统一挂证书。这件事用 K3S 自带的 Traefik IngressController 做不到,但用「独立 Traefik 实例 + L4 负载均衡」可以做到。

下面我把整套流程拆成可复制的步骤。你不需要改 K3S 自带的 Traefik,而是额外部署两个「纯文件配置」的 Traefik Pod,让它们不监听 Kubernetes 事件,只按配置文件转发。然后用 K3S 的 ServiceLB(Klipper)把主机端口 80 和 8080 分别指向这两个 Traefik Pod。

2. TaoToken 前置:把入口 endpoint 统一到 API 通道

在开始配 Traefik 之前,先说一下联调阶段的一个常见需求。你本地或者集群里跑的服务,很多时候需要调用大模型 API 做测试。如果每个服务各自配一套 Key 和 Base URL,联调起来很乱。我习惯把入口统一到一个 API 通道上,这样 Traefik 转发到后端服务后,后端服务只需要认一个 endpoint。

TaoToken 的 API 地址是https://taotoken.net/api,控制台在https://taotoken.net/console,API Keys 管理在https://taotoken.net/api-keys。你可以在控制台里创建一个 Key,然后在后端服务的环境变量里统一写:

export OPENAI_BASE_URL=https://taotoken.net/api export OPENAI_API_KEY=sk-你的Key

如果你用的是 Claude Code 或者类似的编码工具,模型对话入口在https://taotoken.net/model,Coding Plan 在https://taotoken.net/coding-plan。这些地址在联调时可以直接作为后端服务的上游 endpoint。

为什么要在这里提这个?因为 Traefik 做端口映射后,你可能会把某个端口专门留给「AI 网关」类的服务。比如 8080 端口映射到 ordersvc,而 ordersvc 内部需要调用模型 API。这时候 ordersvc 的配置里写 TaoToken 的 API 地址,Traefik 只负责把外部流量转到 ordersvc,ordersvc 再出站调用 TaoToken。这样入口和出站是两条独立的链路,排障时不会混在一起。

如果你需要看接入文档,地址是https://taotoken.net/doc。Claude Code 的 Anthropic 兼容入口在https://taotoken.net/claudecode-anthropic。这些链接在后面的 CTA 部分还会用到,这里先记一下。

现在回到 Traefik。我们的目标很明确:部署两个独立的 Traefik 实例,一个监听 80 转发到 usercenter,一个监听 8080 转发到 ordersvc。它们不接管 Ingress,只读文件配置。

3. 可复制配置:Traefik 文件模式 + ServiceLB 端口映射

这一节是核心,所有配置都可以直接复制。我按顺序来:先建 ConfigMap,再建 Deployment,最后建 Service。

3.1 创建 Traefik 配置文件 ConfigMap

K3S 自带的 Traefik 配置在kube-system命名空间下,你可以先看一下它的结构:

kubectl -n kube-system get configmap traefik -o yaml

但我们要新建两个独立的 ConfigMap,分别给 A 和 B 用。注意关键点:去掉[kubernetes]段,这样 Traefik 就不会去监听 Kubernetes 的 Ingress 事件,变成一个纯文件配置的转发器。

先写 A 的配置,保存为traefik-a.toml:

logLevel = "DEBUG" [traefikLog] filePath = "/tmp/traefik.log" format = "json" [accessLog] filePath = "/tmp/access.log" format = "json" defaultEntryPoints = ["http"] [entryPoints] [entryPoints.http] address = ":80" [ping] entryPoint = "http" [file] [backends] [backends.usercenter] [backends.usercenter.servers.server1] url = "http://usercenter.default.svc.cluster.local:80" weight = 10 [frontends] [frontends.usercenter] entryPoints = ["http"] passHostHeader = true backend = "usercenter" [frontends.usercenter.routes] [frontends.usercenter.routes.route0] rule = "PathPrefix:/"

再写 B 的配置,保存为traefik-b.toml:

logLevel = "DEBUG" [traefikLog] filePath = "/tmp/traefik.log" format = "json" [accessLog] filePath = "/tmp/access.log" format = "json" defaultEntryPoints = ["http"] [entryPoints] [entryPoints.http] address = ":80" [ping] entryPoint = "http" [file] [backends] [backends.ordersvc] [backends.ordersvc.servers.server1] url = "http://ordersvc.default.svc.cluster.local:80" weight = 10 [frontends] [frontends.ordersvc] entryPoints = ["http"] passHostHeader = true backend = "ordersvc" [frontends.ordersvc.routes] [frontends.ordersvc.routes.route0] rule = "PathPrefix:/"

注意两个配置里的url都指向了集群内 Service 的 DNS 名称。格式是<service>.<namespace>.svc.cluster.local:<port>。如果你的服务在别的命名空间,把default换成对应的命名空间。

然后创建 ConfigMap:

kubectl create configmap traefik-config-a --from-file=traefik.toml=traefik-a.toml -n default kubectl create configmap traefik-config-b --from-file=traefik.toml=traefik-b.toml -n default

3.2 部署两个独立 Traefik Deployment

接下来创建 Deployment。这里用 Traefik 2.x 的镜像,因为 K3S 默认也是 2.x 系列,配置语法一致。

apiVersion: apps/v1 kind: Deployment metadata: name: traefik-a namespace: default spec: replicas: 1 selector: matchLabels: app: traefik-a template: metadata: labels: app: traefik-a spec: containers: - name: traefik image: traefik:v2.9 args: - --configfile=/config/traefik.toml ports: - name: http containerPort: 80 volumeMounts: - name: config mountPath: /config volumes: - name: config configMap: name: traefik-config-a

保存为traefik-a-deploy.yaml,然后同样写一份traefik-b-deploy.yaml,把traefik-a全部替换成traefik-b,ConfigMap 换成traefik-config-b。

应用:

kubectl apply -f traefik-a-deploy.yaml kubectl apply -f traefik-b-deploy.yaml

检查 Pod 是否 Running:

kubectl get pods -l app=traefik-a kubectl get pods -l app=traefik-b

3.3 用 ServiceLB 暴露主机端口

K3S 自带 ServiceLB(Klipper),你创建一个type: LoadBalancer的 Service,它就会自动在主机上监听对应端口。这里我们要把主机 80 映射到 traefik-a 的 80,主机 8080 映射到 traefik-b 的 80。

先建 traefik-a 的 Service:

apiVersion: v1 kind: Service metadata: name: traefik-a-lb namespace: default spec: type: LoadBalancer ports: - name: http port: 80 targetPort: 80 protocol: TCP selector: app: traefik-a

再建 traefik-b 的 Service,注意port改成 8080:

apiVersion: v1 kind: Service metadata: name: traefik-b-lb namespace: default spec: type: LoadBalancer ports: - name: http port: 8080 targetPort: 80 protocol: TCP selector: app: traefik-b

应用后查看:

kubectl get svc traefik-a-lb traefik-b-lb

你应该能看到EXTERNAL-IP显示的是节点 IP,端口分别是 80 和 8080。

3.4 关于 HTTPS 的补充配置

如果你的服务需要 HTTPS,可以在 Traefik 配置里加 entryPoints.https 和证书。证书可以通过 Secret 挂载到 Pod 里,然后在 toml 里引用:

[entryPoints] [entryPoints.http] address = ":80" [entryPoints.https] address = ":443" [entryPoints.https.tls] [[entryPoints.https.tls.certificates]] certFile = "/ssl/tls.crt" keyFile = "/ssl/tls.key"

然后在 Deployment 里挂载 Secret 到/ssl。Service 的 ports 也要加上 443。这部分和端口映射的逻辑是正交的,你按需加就行。

4. 验证请求:kubectl 命令 + curl 实测

配置完了,怎么确认真的生效?我分三步验证。

4.1 确认 Pod 和 Service 状态

kubectl get pods -l app=traefik-a -o wide kubectl get pods -l app=traefik-b -o wide kubectl get svc traefik-a-lb traefik-b-lb -o wide

重点看EXTERNAL-IP和PORT(S)。如果EXTERNAL-IP是<pending>,说明 ServiceLB 还没分配,等几秒再看。如果一直是 pending,检查 K3S 的 ServiceLB 组件是否正常:

kubectl -n kube-system get pods | grep svclb

4.2 从集群内部 curl 测试

先起一个临时 Pod 做测试:

kubectl run curl-test --image=curlimages/curl -it --rm -- sh

进去后分别请求两个 Service:

curl -v http://traefik-a-lb.default.svc.cluster.local:80/ curl -v http://traefik-b-lb.default.svc.cluster.local:8080/

你应该能看到 A 返回 usercenter 的响应,B 返回 ordersvc 的响应。如果返回 404,说明 Traefik 的 backend 配置有问题,检查url是否写对了 Service 名称和端口。

4.3 从主机 curl 测试

在 K3S 节点上直接执行:

curl -v http://localhost:80/ curl -v http://localhost:8080/

如果节点有多个网卡,用节点 IP 代替 localhost。这一步验证的是主机端口到 Traefik Pod 的链路。如果 80 和 8080 返回了不同服务的响应,说明端口映射成功。

4.4 验证根路径资源加载

对于前端 UI 服务,光看首页返回还不够,要确认 JS/CSS 也走对了。你可以用 curl 请求一个静态资源:

curl -v http://localhost:80/static/app.js curl -v http://localhost:8080/static/app.js

如果两个返回的内容不同(比如文件大小、内容哈希不一样),说明根路径资源没有串。如果返回一样,检查两个服务的静态资源路径是否真的不同,或者 Traefik 的passHostHeader是否影响了后端路由。

4.5 查看 Traefik 日志

如果请求没通,看日志是最快的:

kubectl logs -l app=traefik-a --tail=50 kubectl logs -l app=traefik-b --tail=50

日志里会显示请求匹配了哪个 frontend、转发到了哪个 backend。如果看到no matching frontend,说明 rule 没匹配上;如果看到backend not found,说明 backend 名称写错了。

5. 本篇常见错排查:401、local proxy failed、reading choices、OAuth

这一节列几个我在实际部署和联调中踩过的坑,以及对应的报错和解决方式。

5.1 401 Unauthorized

这个报错通常出现在后端服务调用模型 API 的时候。比如 ordersvc 里配置了OPENAI_API_KEY,但 Key 无效或者没传。检查方式:

kubectl exec -it deploy/ordersvc -- env | grep -i key

确认OPENAI_BASE_URL和OPENAI_API_KEY都设置正确。如果你用的是 TaoToken 的 API 通道,Base URL 应该是https://taotoken.net/api,Key 从https://taotoken.net/api-keys获取。注意不要多空格,也不要漏掉sk-前缀。

5.2 local proxy failed

这个报错一般出现在 Traefik 转发到后端 Service 的时候。原因可能是:

  • Service 名称写错,比如usercenter.default.svc.cluster.local写成了usercenter.default.svc。
  • 端口写错,Service 暴露的是 8080,你写了 80。
  • 后端 Pod 没 Ready,Endpoints 为空。

排查命令:

kubectl get endpoints usercenter -n default kubectl describe svc usercenter -n default

如果 Endpoints 是空的,检查 Pod 的 readinessProbe 是否通过。

5.3 reading choices

这个报错通常出现在调用模型 API 返回流式响应时,客户端解析出错。如果你在 ordersvc 里用了 OpenAI SDK,检查是否设置了stream: true但客户端没处理 SSE。可以先关掉 stream 测试:

response = client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": "test"}], stream=False )

如果非流式正常,流式报错,检查客户端库版本和网络中间件是否缓冲了响应。Traefik 默认不缓冲,但如果你在 Traefik 和后端之间加了别的代理,可能会有影响。

5.4 OAuth 回调失败

如果你的服务用了 OAuth 登录,回调地址通常是http://主机:端口/callback。端口映射后,回调地址要写实际访问的端口。比如 80 端口的服务,回调写http://your-host/callback;8080 端口的服务,回调写http://your-host:8080/callback。如果写错了,OAuth 提供方会拒绝回调。

另外,如果你在 Traefik 层做了 HTTPS 终止,后端服务收到的请求是 HTTP,但 OAuth 回调需要 HTTPS。这时候要在 Traefik 配置里加passHostHeader = true,并且后端服务要信任X-Forwarded-Proto头。

5.5 CC Switch / Cline MCP / Codex auth.json 的三件套

如果你在联调时用到了 CC Switch、Cline MCP 或者 Codex 的auth.json,记住配置三件套:Base URL + Key + Model ID。

以 Codex 的auth.json为例:

{ "openai_api_key": "sk-你的Key", "openai_base_url": "https://taotoken.net/api", "model": "gpt-4o-mini" }

Cline MCP 的配置类似,在 settings 里填:

{ "mcpServers": { "taotoken": { "url": "https://taotoken.net/api", "apiKey": "sk-你的Key", "model": "gpt-4o-mini" } } }

CC Switch 的配置在~/.cc-switch/config.json,同样三个字段。缺一个都会导致 401 或者 model not found。

6. 把入口 endpoint 统一到 TaoToken 做联调

最后说一下联调阶段的收尾。Traefik 把外部流量按端口分到了不同服务,但服务内部如果要调用模型 API,建议统一走 TaoToken 的 API 通道。这样做的好处是:你只需要维护一个 Key,换模型或者换 Key 的时候不用改每个服务的配置。

具体操作:

  1. 在https://taotoken.net/api-keys创建一个 Key。
  2. 在每个需要调用模型的后端服务里,设置环境变量:
OPENAI_BASE_URL=https://taotoken.net/api OPENAI_API_KEY=sk-你的Key
  1. 如果服务用的是 Anthropic 协议,Base URL 换成https://taotoken.net/claudecode-anthropic。
  2. 重启服务,用 curl 测试:
curl -X POST https://taotoken.net/api/v1/chat/completions \ -H "Authorization: Bearer sk-你的Key" \ -H "Content-Type: application/json" \ -d '{"model":"gpt-4o-mini","messages":[{"role":"user","content":"ping"}]}'

如果返回正常,说明 API 通道通了。然后你从主机 80 或 8080 访问服务,服务内部再调 TaoToken,整条链路就串起来了。

如果你需要长期跑编码 Agent,可以看一下 Coding Plan:https://taotoken.net/coding-plan。模型对话入口在https://taotoken.net/model,接入文档在https://taotoken.net/doc。控制台在https://taotoken.net/console,API Keys 在https://taotoken.net/api-keys。

整套流程走下来,你得到的是:主机 80 端口访问 usercenter 根路径,8080 端口访问 ordersvc 根路径,两个服务互不干扰,各自可以独立挂证书,后端服务统一走 TaoToken API 通道做联调。配置全部可复制,验证命令全部可执行。如果遇到端口没生效,先查 ServiceLB 的 Pod,再查 Traefik 日志,最后查后端 Endpoints,三步定位。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询