ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

k8s 使用 traefik 暴露 http 服务和 tcp 服务:TaoToken 统一 Key 接入实践

k8s 使用 traefik 暴露 http 服务和 tcp 服务:TaoToken 统一 Key 接入实践 1. 为什么要在 k8s 里用 Traefik 同时暴露 HTTP 和 TCP 服务在 k8s 集群里做流量入口很多人第一反应是 nginx-ingress。但当你遇到「同一个入口既要转发 HTTP 又要转发 TCP」的场景时nginx-ingress 的配置会变得很别扭——TCP 端口得靠 ConfigMap 全局声明改一次要重启端口冲突排查起来也费劲。Traefik 在这件事上更顺手它原生支持 IngressRouteHTTP和 IngressRouteTCPTCP两种 CRDHTTP 走七层路由TCP 走四层转发互不干扰。这篇要解决的问题很具体一个 k8s 集群用 Traefik 作为统一入口既要把内部 Web 服务通过域名暴露出去又要把 MySQL、Redis 这类 TCP 服务通过固定端口暴露出去。同时外部访问链路里涉及模型 API 调用时用 TaoToken 的统一 Key 和 API 通道来收敛凭证管理避免每个服务各配一套 Key。适合谁看已经跑过 k8s、装过 Helm、对 Ingress 有基本概念但被 Traefik 的 TCP 入口配置卡住的同学。如果你还没装过 Traefik下面从 Helm 安装开始一步步来照着做能跑通。核心检索词先明确k8s 使用 traefik 暴露 http 服务和 tcp 服务关键在于 entryPoints 的预定义和两类 CRD 的配合。Traefik 和 nginx-ingress 最大的区别是TCP 端口必须在部署时通过 additionalArguments 写死不能运行时动态加。这一点决定了整个配置思路。我试过在测试集群里反复调这套配置踩过的坑主要集中在端口别名对不上、entryPoints 名字写错、以及 IngressRouteTCP 的 match 规则理解偏差。下面把完整链路拆开讲。2. TaoToken 统一 Key 接入的前置准备在讲 Traefik 配置之前先把外部访问链路里的凭证问题解决掉。集群里如果有服务需要调用大模型 API比如内部的知识库问答、代码助手、Agent 工具每个服务单独配 Key 会带来两个麻烦一是 Key 散落在各个 Secret 里难轮换二是不同模型供应商的 Base URL 和鉴权格式不统一改起来到处动。TaoToken 在这里的角色是一个统一的 API 通道你拿一个 Key通过统一的 Base URL 访问背后对接不同模型。对于 k8s 里的服务来说只需要在环境变量或 Secret 里配一组Base URL Key Model ID不用关心上游是谁。前置准备分三步。第一步拿到 API Key。访问控制台创建https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentconsole在 API Keys 页面生成一个 Key形如sk-xxxx。这个 Key 后面会写进 k8s Secret。第二步确认 Base URL。TaoToken 的 API 端点是https://taotoken.net/api注意这个地址不带任何查询参数直接作为 OpenAI 兼容接口的 base_url 使用。如果你用的是 Anthropic 风格的调用比如 Claude Code端点路径会不同参考接入文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentdoc第三步确定 Model ID。不同模型的 ID 不一样在模型对话页面可以直接试https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentmodels把这三样东西准备好后面在 k8s 里创建 Secret 时会用到。这里先给一个 Secret 的 YAML 模板你可以直接改apiVersion: v1 kind: Secret metadata: name: taotoken-secret namespace: default type: Opaque stringData: TAOTOKEN_API_KEY: sk-你的实际Key TAOTOKEN_BASE_URL: https://taotoken.net/api TAOTOKEN_MODEL_ID: 你的模型ID创建命令kubectl apply -f taotoken-secret.yaml验证 Secret 是否创建成功kubectl get secret taotoken-secret -o jsonpath{.data.TAOTOKEN_API_KEY} | base64 -d能打印出你的 Key 就说明没问题。这一步做完集群内的服务就可以通过挂载这个 Secret 来读取统一凭证不用在每个 Deployment 里硬编码。需要提醒的是Secret 默认是 base64 编码不是加密生产环境建议开启 etcd 加密或配合外部密钥管理。测试环境这样够用。3. Traefik 部署与 HTTP/TCP 入口的可复制配置这一节是重头戏。Traefik 的部署方式我用 Helm因为 values.yaml 改起来直观。先加 repo 并拉 charthelm repo add traefik https://helm.traefik.io/traefik helm repo update helm pull traefik/traefik tar -zxvf traefik-*.tgz进入解压后的目录改 values.yaml。下面按关键段落说明。3.1 端口与 entryPoints 配置Traefik 的入口点entryPoints决定了它监听哪些端口。HTTP 用 web80和 websecure443dashboard 用 traefik9000TCP 自定义端口用 additionalArguments 声明。ports: traefik: port: 9000 expose: true exposedPort: 9000 protocol: TCP web: port: 8000 expose: true exposedPort: 80 protocol: TCP websecure: port: 8443 expose: true exposedPort: 443 protocol: TCP metrics: port: 9100 expose: false exposedPort: 9100 protocol: TCP这里expose: true表示这些端口会通过 Service 暴露出来。web 和 websecure 是 HTTP 流量的入口traefik 是 dashboard。3.2 Service 暴露方式集群外要访问Service 类型选 NodePortservice: enabled: true type: NodePort annotations: {} annotationsTCP: {} annotationsUDP: {} labels: {} spec: {} loadBalancerSourceRanges: [] externalIPs: []NodePort 会在每个节点上开一个高位端口映射到 Service。部署后用kubectl get svc能看到分配的端口。3.3 hostNetwork 与 ingressClasshostNetwork: truehostNetwork 设为 true 让 Traefik Pod 直接用宿主机网络这样自定义 TCP 端口能直接绑定到节点上省去 NodePort 的端口映射层。如果你的环境不允许 hostNetwork可以关掉但 TCP 端口访问要走 NodePort。ingressClass 配置providers: kubernetesIngress: enabled: true allowExternalNameServices: false allowEmptyServices: false ingressClass: mytraefikingressClass: mytraefik这个值要和后面 Ingress 资源里 annotation 的kubernetes.io/ingress.class一致否则 Traefik 不认。3.4 自定义 TCP 端口关键Traefik 不支持运行时动态加 TCP 端口必须在部署时声明。这里配两个additionalArguments: - --entrypoints.myport32000.Address:32000 - --entrypoints.myport32001.Address:32001myport32000和myport32001是端口别名后面 IngressRouteTCP 的 entryPoints 字段要填这个别名不是端口号。这是最容易搞错的地方。3.5 允许绑定 80 端口securityContext: capabilities: drop: [ALL] add: [NET_BIND_SERVICE] readOnlyRootFilesystem: true runAsGroup: 0 runAsNonRoot: false runAsUser: 0NET_BIND_SERVICE能力允许容器绑定 1024 以下端口。如果你不用 hostNetwork 且 exposedPort 是 80这个必须加。改完 values.yaml 后部署helm install mytraefik . -n default查看 Pod 和 Servicekubectl get pods -l app.kubernetes.io/nametraefik kubectl get svc mytraefikdashboard 访问地址是节点IP:NodePort/dashboard/#/。比如 NodePort 是 30332节点 IP 是 10.10.101.140就访问http://10.10.101.140:30332/dashboard/#/。注意路径里的#/不能少。4. HTTP 与 TCP 服务的验证请求与成功结果配置部署完接下来验证两类流量是否真的通了。4.1 HTTP 服务验证先创建一个测试用的 Deployment 和 ServiceapiVersion: apps/v1 kind: Deployment metadata: name: web-demo namespace: default spec: replicas: 1 selector: matchLabels: app: web-demo template: metadata: labels: app: web-demo spec: containers: - name: nginx image: nginx:alpine ports: - containerPort: 80 --- apiVersion: v1 kind: Service metadata: name: web-demo namespace: default spec: selector: app: web-demo ports: - port: 80 targetPort: 80然后创建 IngressRouteTraefik 的 CRD比原生 Ingress 更灵活apiVersion: traefik.containo.us/v1alpha1 kind: IngressRoute metadata: name: web-demo-route namespace: default spec: entryPoints: - web routes: - match: Host(demo.local) kind: Rule services: - name: web-demo port: 80应用后用 curl 验证。因为用的是 Host 匹配需要手动指定 Host 头curl -H Host: demo.local http://10.10.101.140:30332/如果返回 nginx 的欢迎页 HTML说明 HTTP 链路通了。你也可以在本地 hosts 文件里加一条10.10.101.140 demo.local然后直接curl http://demo.local:30332/。4.2 TCP 服务验证TCP 服务用 IngressRouteTCP。先部署一个测试 MySQL 或直接用 nc 起个监听apiVersion: apps/v1 kind: Deployment metadata: name: tcp-demo namespace: default spec: replicas: 1 selector: matchLabels: app: tcp-demo template: metadata: labels: app: tcp-demo spec: containers: - name: tcp-server image: alpine command: [sh, -c, while true; do echo hello from tcp | nc -l -p 3306; done] ports: - containerPort: 3306 --- apiVersion: v1 kind: Service metadata: name: tcp-demo namespace: default spec: selector: app: tcp-demo ports: - port: 3306 targetPort: 3306创建 IngressRouteTCPapiVersion: traefik.containo.us/v1alpha1 kind: IngressRouteTCP metadata: name: tcp-demo-route namespace: default spec: entryPoints: - myport32000 routes: - match: HostSNI(*) services: - name: tcp-demo port: 3306注意entryPoints填的是myport32000这个别名不是 32000。match: HostSNI(*)表示不区分 SNI所有 TCP 连接都转发。验证用 telnet 或 nctelnet 10.10.101.140 32000或者nc -v 10.10.101.140 32000如果看到hello from tcp输出说明 TCP 链路通了。用 telnet 的话连上后随便输入字符回车也能看到服务端返回。4.3 结合 TaoToken 的调用验证如果集群内有服务要调模型 API可以在 Pod 里用 curl 验证 TaoToken 通道kubectl run curl-test --rm -it --imagecurlimages/curl -- sh进入容器后curl https://taotoken.net/api/v1/chat/completions \ -H Authorization: Bearer $TAOTOKEN_API_KEY \ -H Content-Type: application/json \ -d { model: $TAOTOKEN_MODEL_ID, messages: [{role: user, content: ping}] }环境变量从 Secret 挂载。返回正常 JSON 就说明统一 Key 通道可用。这一步把 Traefik 暴露的外部入口和 TaoToken 的 API 通道串起来了外部流量进 Traefik内部服务出流量走 TaoToken。5. 本篇常见报错排查401、local proxy failed、reading choices、OAuth配置过程中最容易撞上的几类报错逐个拆。5.1 401 Unauthorized调用 TaoToken API 返回 401通常是 Key 没传对。检查三点第一Header 格式。必须是Authorization: Bearer sk-xxxxBearer 和 Key 之间一个空格不能少。第二Key 是否被 base64 解码污染。从 Secret 读取时如果直接用了 base64 字符串就会 401。验证方法kubectl exec -it pod -- env | grep TAOTOKEN看输出的 Key 是不是sk-开头。如果是乱码说明 Secret 挂载方式有问题改用envFrom或valueFrom。第三Base URL 是否多了斜杠。https://taotoken.net/api后面不要加/否则拼出来的路径可能是//v1/chat/completions部分网关会拒绝。5.2 local proxy failed这个报错一般出现在 Claude Code 或某些 CLI 工具里提示本地代理失败。原因通常是工具配置了本地代理端口但那个端口没有服务在监听。检查工具的配置文件比如~/.claude/settings.json或环境变量HTTP_PROXY把代理配置去掉直连 TaoToken 的 Base URL。如果是 k8s 里的 Pod 报这个错检查 Pod 的HTTP_PROXY环境变量是否指向了一个不存在的 sidecar。5.3 reading choices 相关报错类似error reading choices或cannot read property choices of undefined这是解析响应时字段对不上。常见原因第一Model ID 写错。TaoToken 返回的响应里choices字段存在但如果模型 ID 无效可能返回错误结构。去模型对话页面确认正确的 Model ID。第二响应被中间层截断。如果 Traefik 或其它网关对响应体大小有限制大响应会被截。检查 Traefik 的buffering配置。第三Content-Type 不对。请求头必须是application/json少了这个有些网关会返回 HTML 错误页解析自然失败。5.4 OAuth 相关报错如果你用的是 Claude Code 这类需要 OAuth 的工具报 OAuth 错误通常是因为工具默认走 Anthropic 官方 OAuth 流程而你要走 TaoToken 的 API Key 模式。需要在工具配置里切换认证方式把ANTHROPIC_BASE_URL指向 TaoToken 的对应端点并用 API Key 替代 OAuth token。Claude Code 的接入配置参考文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentdoc配置三件套要写全Base URL、API Key、Model ID。缺一个都会报错。5.5 Traefik 侧常见问题IngressRouteTCP 不生效先检查entryPoints填的是别名还是端口号。填端口号32000是错的必须填myport32000。TCP 端口连不上检查 Traefik Pod 是否用了 hostNetwork。如果没用32000 端口不会绑定到节点上需要走 NodePort 映射。dashboard 打不开检查访问路径是否带了/dashboard/#/以及 NodePort 是否正确。6. 长期编码与 Agent 场景的接入建议把 Traefik 的 HTTP/TCP 入口和 TaoToken 统一 Key 通道搭好之后这套组合在长期编码和 Agent 场景里会越来越顺手。对于需要长期跑编码助手的团队建议用 Coding Plan 来管理额度比按量计费更可控https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentcoding-plan在 k8s 里部署 Agent 服务时把 TaoToken 的 Secret 挂到 Pod 里Agent 通过环境变量读取 Base URL 和 Key。这样换模型、换额度套餐时只需要改 Secret不用动 Deployment。API Keys 管理页面在这里可以创建多个 Key 做环境隔离测试/生产分开https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentapi-keys接入文档里有各语言 SDK 的配置示例Python、Node.js、Go 都有https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentdoc实测下来Traefik 的 IngressRouteTCP 配合 hostNetwork 是最省心的 TCP 暴露方案端口别名一旦配对后续加服务只是多写一个 CRD。唯一要记住的是TCP 端口必须在部署时声明不能动态加。规划端口时留几个余量比如 32000-32010后面加服务不用重新部署 Traefik。最后一个小技巧IngressRouteTCP 的match: HostSNI(*)适合不区分域名的场景。如果你要按 SNI 区分多个 TCP 服务把*换成具体域名但前提是客户端支持 SNI。MySQL 客户端默认不带 SNI所以数据库类服务还是用*加端口区分更实际。
返回列表