Kubernetes集群部署traefik Ingress:从零搭建到TaoToken统一API入口

发布时间:2026/10/18 6:20:38
Kubernetes集群部署traefik Ingress:从零搭建到TaoToken统一API入口
1. 为什么要在 K8s 里给 AI 工具留一个统一入口集群里跑着好几个 AI 相关的服务一个本地模型推理的 Deployment、一个给团队用的对话前端、还有几个定时跑批的 Agent 任务。每个服务都挂 NodePort 的话端口号记都记不住更别说对外只暴露一个域名了。这时候 Ingress 就是那个「集群大门」——外部请求先到 Ingress再按域名和路径转发到对应的 ServiceService 再落到具体 Pod。Ingress 本身只是一堆路由规则真正干活的是 Ingress Controller。它像个监工持续跟 Kubernetes API 打交道感知 Service、Pod 的增减然后动态刷新反向代理配置。Traefik 就是其中一款 Controller它跟 K8s 原生集成得比较顺支持 CRD比如 IngressRoute配置改完不用重启进程就能生效对需要频繁调整路由的 AI 服务来说省事不少。这篇要交付的东西很具体一套能直接kubectl apply的 Traefik 部署 YAMLRBAC、DaemonSet、UI一个把集群内 AI 服务暴露出去的 IngressRoute 示例以及把 TaoToken 的 API 地址接进这套入口的配置。最后用 curl 验证两件事Ingress 路由通不通、TaoToken 的 API 能不能从集群里正常调通。适合已经在跑 K8s、想给 AI 工具收口统一入口的开发者假设你手上有可用的集群和 kubectl 权限。我试过在测试集群里用 DaemonSet 方式铺 Traefik节点打 label 那步最容易漏漏了 Pod 就一直 Pending下面会把这个坑写清楚。2. 部署前的准备RBAC、节点标签与 TaoToken 入口规划Traefik 要能实时感知集群里的 Service 和 Pod 变化就得有权限去读这些资源。K8s 里管权限的是 RBAC核心就四个概念Role 是命名空间内的权限集合ClusterRole 是集群级别的RoleBinding 把角色绑到用户ClusterRoleBinding 则是集群级别的绑定。Traefik 需要跨命名空间看资源所以用 ClusterRoleBinding 把 cluster-admin 绑给它的 ServiceAccount 最省事生产环境建议按最小权限收紧这里为了先跑通先用 cluster-admin。先建 ServiceAccount 和绑定文件名ingress-rbac.yamlapiVersion: v1 kind: ServiceAccount metadata: name: ingress namespace: kube-system --- kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: ingress subjects: - kind: ServiceAccount name: ingress namespace: kube-system roleRef: kind: ClusterRole name: cluster-admin apiGroup: rbac.authorization.k8s.io注意 apiVersion 用的是rbac.authorization.k8s.io/v1老教程里常见的v1beta1在新版本集群已经被移除了直接 apply 会报no matches for kind。这是第一个容易踩的版本坑。接着给要跑 Traefik 的节点打标签。DaemonSet 配合 nodeSelector 能让每个符合条件的节点都跑一个 Traefik Pod这样任意节点的 80 端口都能接流量。假设你的边缘节点是10.200.3.106和10.200.3.107kubectl label node 10.200.3.106 traefikproxy kubectl label node 10.200.3.107 traefikproxy kubectl get nodes --show-labels | grep traefik打完之后--show-labels里应该能看到traefikproxy。如果这步忘了后面 DaemonSet 的 Pod 会因为 nodeSelector 匹配不到节点而一直 Pendingkubectl describe pod里会写0/2 nodes are available: 2 node(s) didnt match node selector。关于 TaoToken 的入口规划TaoToken 提供的是兼容 OpenAI 风格的 APIBase URL 是https://taotoken.net/api模型对话、Coding Plan、控制台、API Keys 这些页面都在官网https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content下。集群内的 AI 工具要调它有两种接法一是让 Pod 直接出网访问公网 API二是通过 Traefik 做一个内部统一入口再转发出去。这篇两种都会给到先保证集群内服务能通过一个稳定的内部域名访问到统一 API 入口。3. 可复制的 Traefik DaemonSet 与 IngressRoute 配置先部署 Traefik 本体。用 DaemonSet 而不是 Deployment是为了让每个边缘节点都有一份配合 hostNetwork 直接占用宿主机 80 端口省掉一层 Service 转发。文件名traefik-daemonset.yamlapiVersion: apps/v1 kind: DaemonSet metadata: name: traefik-ingress-lb namespace: kube-system labels: k8s-app: traefik-ingress-lb spec: selector: matchLabels: k8s-app: traefik-ingress-lb template: metadata: labels: k8s-app: traefik-ingress-lb name: traefik-ingress-lb spec: terminationGracePeriodSeconds: 60 hostNetwork: true restartPolicy: Always serviceAccountName: ingress containers: - image: traefik:v2.10 name: traefik-ingress-lb resources: limits: cpu: 200m memory: 128Mi requests: cpu: 100m memory: 64Mi ports: - name: http containerPort: 80 hostPort: 80 - name: admin containerPort: 8080 hostPort: 8080 args: - --api.insecuretrue - --api.dashboardtrue - --providers.kubernetesingresstrue - --providers.kubernetescrdtrue - --entrypoints.web.address:80 nodeSelector: traefik: proxy这里跟老版本 Traefik 1.x 的写法差别很大1.x 用--web、--kubernetes这种参数2.x 改成了--api.insecure、--providers.kubernetesingress这种点分结构。如果你照着旧教程写--web.address:8580容器会直接启动失败日志里报command traefik error: unknown flag: --web。UI 端口也从 8580 变成了 8080别搞混。apply 之后确认 Pod 起来了kubectl apply -f ingress-rbac.yaml kubectl apply -f traefik-daemonset.yaml kubectl get pods -n kube-system -l k8s-apptraefik-ingress-lb -o wide两个节点各一个 Running 就对了。接着配 IngressRoute。Traefik 2.x 推荐用 CRD 的 IngressRoute比原生 Ingress 表达力强能直接写中间件。假设集群里有个 AI 对话前端 Service 叫ai-chat-service端口 80文件名ai-chat-ingressroute.yamlapiVersion: traefik.containo.us/v1alpha1 kind: IngressRoute metadata: name: ai-chat-route namespace: default spec: entryPoints: - web routes: - match: Host(ai.internal.local) kind: Rule services: - name: ai-chat-service port: 80如果你更习惯原生 Ingress等价写法是这样文件名ai-chat-ingress.yamlapiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: ai-chat-ingress namespace: default spec: rules: - host: ai.internal.local http: paths: - path: / pathType: Prefix backend: service: name: ai-chat-service port: number: 80注意networking.k8s.io/v1里 backend 结构变了老教程的serviceName/servicePort平铺写法在 v1 里已经废弃得改成service.name/service.port.number否则 apply 报unknown field。现在把 TaoToken 的 API 地址接进来。集群内的 AI 工具需要调https://taotoken.net/api可以在应用的环境变量里直接配也可以走 Traefik 做一个内部转发。环境变量方式最直接在 Deployment 里加env: - name: OPENAI_BASE_URL value: https://taotoken.net/api - name: OPENAI_API_KEY valueFrom: secretKeyRef: name: taotoken-secret key: api-keyAPI Key 别硬编码在 YAML 里用 Secret 存kubectl create secret generic taotoken-secret \ --from-literalapi-key你的Key如果你想让集群内所有工具都通过一个内部域名访问可以在 Traefik 里加一条指向外部服务的 IngressRoute配合 ExternalName ServiceapiVersion: v1 kind: Service metadata: name: taotoken-api namespace: default spec: type: ExternalName externalName: taotoken.net --- apiVersion: traefik.containo.us/v1alpha1 kind: IngressRoute metadata: name: taotoken-route namespace: default spec: entryPoints: - web routes: - match: Host(api.internal.local) PathPrefix(/api) kind: Rule services: - name: taotoken-api port: 443这样集群内工具把 Base URL 写成http://api.internal.local/api就能走 Traefik 转发到 TaoToken。三件套记牢Base URL 是https://taotoken.net/apiKey 从控制台https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content生成Model ID 按你用的模型填比如对话类填对应模型名。API Keys 管理页在https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content。4. 验证 Ingress 路由与 TaoToken API 连通性配置都 apply 完之后先确认资源状态kubectl get ingressroute -A kubectl get ingress -A kubectl get svc -n defaultIngressRoute 应该显示ai-chat-route原生 Ingress 显示ai-chat-ingress。然后从集群外任意能访问到节点 IP 的机器上测路由。因为 Traefik 用 hostNetwork 占了节点 80 端口直接打节点 IP 加 Host 头就行curl -I -H Host: ai.internal.local http://10.200.3.106期望返回HTTP/1.1 200 OK如果后端是 nginx 类服务还能看到Server: nginx之类的头。换另一个节点10.200.3.107再打一次结果应该一致说明 DaemonSet 两个副本都在正常工作。如果返回404 Not Found多半是 Host 头没匹配上 IngressRoute 里的Host(...)规则检查域名拼写。如果返回502 Bad Gateway说明路由匹配到了但后端 Service 没通去查ai-chat-service的 Endpoints 是不是空的kubectl get endpoints ai-chat-serviceEndpoints 为空通常是 Service 的 selector 跟 Pod 的 label 对不上或者 Pod 没 Ready。接着验证 TaoToken API 从集群内能不能调通。起一个临时 Pod 测kubectl run curl-test --imagecurlimages/curl --rm -it --restartNever -- \ curl -s -o /dev/null -w %{http_code} \ -H Authorization: Bearer 你的Key \ https://taotoken.net/api/models返回200就说明集群出网和 Key 都正常。如果走的是内部转发那条 IngressRoute把地址换成http://api.internal.local/api/models再测一次。想看具体返回内容去掉-o /dev/null -w那部分直接看 JSON。再验证一次模型对话接口确认不只是列表能通kubectl run curl-chat --imagecurlimages/curl --rm -it --restartNever -- \ curl -s https://taotoken.net/api/chat/completions \ -H Content-Type: application/json \ -H Authorization: Bearer 你的Key \ -d {model:你的模型ID,messages:[{role:user,content:ping}]}返回体里带choices字段就说明整条链路通了。这一步能同时验证 Ingress 转发如果走了内部域名和 TaoToken 的 API 可用性。5. 常见报错排查401、local proxy failed 与 reading choices部署和联调阶段最容易撞上的几个报错逐个拆。401 Unauthorized。调 TaoToken API 时返回 401先看 Authorization 头格式对不对必须是Bearer加 Key中间一个空格别漏。然后确认 Key 没过期、没被删。在控制台https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content里核对一下。如果 Key 是从 Secret 挂进来的检查 Secret 里的值有没有多余换行kubectl get secret taotoken-secret -o jsonpath{.data.api-key} | base64 -d | xxd | tail -2末尾出现0a就是多了换行用--from-literal重建时注意别带。local proxy failed。这个报错通常出现在集群内 Pod 访问外部 API 时说明出网链路有问题。先确认 Pod 所在节点能不能出网kubectl run net-test --imagecurlimages/curl --rm -it --restartNever -- \ curl -s -o /dev/null -w %{http_code} https://taotoken.net/api/models如果这里就失败检查节点的 DNS 解析和网络策略。CoreDNS 挂了会导致域名解析不了kubectl get pods -n kube-system -l k8s-appkube-dns看状态。另外 NetworkPolicy 如果限制了 egress也会拦掉出网流量kubectl get networkpolicy -A确认一下。reading choices 相关报错。调用对话接口时如果报解析choices失败一般是返回体不是预期的 JSON 结构。常见原因有两个一是请求打到了错误的路径比如把/api漏了或者多拼了/v1TaoToken 的 Base URL 是https://taotoken.net/api对话接口是/api/chat/completions二是 Key 无效时返回的是错误 JSON里面没有choices字段客户端却按成功结构去解析。先用 curl 看原始返回curl -s https://taotoken.net/api/chat/completions \ -H Authorization: Bearer 你的Key \ -H Content-Type: application/json \ -d {model:你的模型ID,messages:[{role:user,content:hi}]} | head -c 500看到error字段就先解决鉴权或模型 ID 问题看到choices才说明请求本身没问题。OAuth 相关报错。如果你用的是 Claude Code 这类工具接 TaoToken报 OAuth 失败通常是认证方式没配对。这类工具走的是 API Key 认证而不是 OAuth 流程检查配置文件里是不是误开了 OAuth 模式。以 Claude Code 为例配置里要明确用 API KeyBase URL 指向https://taotoken.net/api。如果工具同时支持多种认证确保选的是 Key 模式。Traefik Pod 一直 Pending。回到第 2 节说的节点标签问题kubectl describe pod看 Events如果是didnt match node selector补打标签即可。如果是Insufficient cpu把 DaemonSet 里的 resources requests 调小。IngressRoute 不生效。先确认 Traefik 启动参数里带了--providers.kubernetescrdtrue没带的话它根本不认 IngressRoute 这个 CRD。然后确认 CRD 本身装没装kubectl get crd | grep traefik没有的话需要先 apply Traefik 的 CRD 定义文件否则 IngressRoute 创建会报no matches for kind。6. 把统一入口用起来从验证到日常接入路由和 API 都验证通过之后日常接入就简单了。集群内的 AI 工具统一把 Base URL 指向https://taotoken.net/api或内部转发域名Key 从 Secret 注入Model ID 按需填。新加一个服务要暴露时复制一份 IngressRoute 改match规则和services名字就行Traefik 会自动感知不用重启。需要长期跑编码类 Agent 或者批量任务的可以看下 Coding Plan 页面https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content按用量规划比单次调用省心。想先在浏览器里试模型效果的模型对话入口在https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content首页就能进。接入文档在https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content里面有各语言的调用示例。最后留一个实用习惯每次改完 IngressRoute用kubectl get ingressroute -A -o yaml确认一下实际生效的规则比翻 dashboard 快。Traefik 的 dashboard 在http://节点IP:8080/dashboard/能看到所有 router 和 service 的实时状态排查路由问题时比猜快得多。