TaoToken 场景下的 Traefik 实战:K3S+Rancher 中按端口映射不同内部服务根路径
1. 为什么 K3S 里 Ingress 做不到「80 端口给 A、8080 端口给 B」先把问题摆清楚。K3S 默认自带 Traefik 作为 IngressController你写一个 Ingress 或者 IngressRoute它加载的规则是全局的。也就是说不管你从哪个主机端口进来只要流量最终落到这个 IngressController 上它匹配的规则集是同一套。我举个具体例子。集群里有两个服务usercenter和ordersvc两个服务的 UI 都从根路径/开始加载资源。你希望http://主机:80/打到 usercenterhttp://主机:8080/打到 ordersvc。如果你只写 IngressapiVersion: 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而整个集群要求对外统一 HTTPSNodePort 就没法在入口层做 TLS 终止了。你总不能在每个服务里都配一遍证书。所以真正的需求是在集群入口处按主机端口区分流量每个端口独立转发到不同的内部服务根路径并且能统一挂证书。这件事用 K3S 自带的 Traefik IngressController 做不到但用「独立 Traefik 实例 L4 负载均衡」可以做到。下面我把整套流程拆成可复制的步骤。你不需要改 K3S 自带的 Traefik而是额外部署两个「纯文件配置」的 Traefik Pod让它们不监听 Kubernetes 事件只按配置文件转发。然后用 K3S 的 ServiceLBKlipper把主机端口 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/consoleAPI Keys 管理在https://taotoken.net/api-keys。你可以在控制台里创建一个 Key然后在后端服务的环境变量里统一写export OPENAI_BASE_URLhttps://taotoken.net/api export OPENAI_API_KEYsk-你的Key如果你用的是 Claude Code 或者类似的编码工具模型对话入口在https://taotoken.net/modelCoding Plan 在https://taotoken.net/coding-plan。这些地址在联调时可以直接作为后端服务的上游 endpoint。为什么要在这里提这个因为 Traefik 做端口映射后你可能会把某个端口专门留给「AI 网关」类的服务。比如 8080 端口映射到 ordersvc而 ordersvc 内部需要调用模型 API。这时候 ordersvc 的配置里写 TaoToken 的 API 地址Traefik 只负责把外部流量转到 ordersvcordersvc 再出站调用 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 配置文件 ConfigMapK3S 自带的 Traefik 配置在kube-system命名空间下你可以先看一下它的结构kubectl -n kube-system get configmap traefik -o yaml但我们要新建两个独立的 ConfigMap分别给 A 和 B 用。注意关键点去掉[kubernetes]段这样 Traefik 就不会去监听 Kubernetes 的 Ingress 事件变成一个纯文件配置的转发器。先写 A 的配置保存为traefik-a.tomllogLevel 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.tomllogLevel 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换成对应的命名空间。然后创建 ConfigMapkubectl create configmap traefik-config-a --from-filetraefik.tomltraefik-a.toml -n default kubectl create configmap traefik-config-b --from-filetraefik.tomltraefik-b.toml -n default3.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-bConfigMap 换成traefik-config-b。应用kubectl apply -f traefik-a-deploy.yaml kubectl apply -f traefik-b-deploy.yaml检查 Pod 是否 Runningkubectl get pods -l apptraefik-a kubectl get pods -l apptraefik-b3.3 用 ServiceLB 暴露主机端口K3S 自带 ServiceLBKlipper你创建一个type: LoadBalancer的 Service它就会自动在主机上监听对应端口。这里我们要把主机 80 映射到 traefik-a 的 80主机 8080 映射到 traefik-b 的 80。先建 traefik-a 的 ServiceapiVersion: 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改成 8080apiVersion: 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 apptraefik-a -o wide kubectl get pods -l apptraefik-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 svclb4.2 从集群内部 curl 测试先起一个临时 Pod 做测试kubectl run curl-test --imagecurlimages/curl -it --rm -- sh进去后分别请求两个 Servicecurl -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 apptraefik-a --tail50 kubectl logs -l apptraefik-b --tail50日志里会显示请求匹配了哪个 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/apiKey 从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 没 ReadyEndpoints 为空。排查命令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( modelgpt-4o-mini, messages[{role: user, content: test}], streamFalse )如果非流式正常流式报错检查客户端库版本和网络中间件是否缓冲了响应。Traefik 默认不缓冲但如果你在 Traefik 和后端之间加了别的代理可能会有影响。5.4 OAuth 回调失败如果你的服务用了 OAuth 登录回调地址通常是http://主机:端口/callback。端口映射后回调地址要写实际访问的端口。比如 80 端口的服务回调写http://your-host/callback8080 端口的服务回调写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 的时候不用改每个服务的配置。具体操作在https://taotoken.net/api-keys创建一个 Key。在每个需要调用模型的后端服务里设置环境变量OPENAI_BASE_URLhttps://taotoken.net/api OPENAI_API_KEYsk-你的Key如果服务用的是 Anthropic 协议Base URL 换成https://taotoken.net/claudecode-anthropic。重启服务用 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 Planhttps://taotoken.net/coding-plan。模型对话入口在https://taotoken.net/model接入文档在https://taotoken.net/doc。控制台在https://taotoken.net/consoleAPI Keys 在https://taotoken.net/api-keys。整套流程走下来你得到的是主机 80 端口访问 usercenter 根路径8080 端口访问 ordersvc 根路径两个服务互不干扰各自可以独立挂证书后端服务统一走 TaoToken API 通道做联调。配置全部可复制验证命令全部可执行。如果遇到端口没生效先查 ServiceLB 的 Pod再查 Traefik 日志最后查后端 Endpoints三步定位。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →