1. 从 401 到 EnvoyFilter:Istio 出站 AI API 的 Key 贴法
最近关于 AI 数据中心电子垃圾被低估的讨论,让算力基础设施的隐性成本再次被关注;在 Istio 管 AI 数据中心服务网格时,另一个隐性成本是出站 Key 的落点。你可能遇到:应用 Pod 内curl -H "Authorization: Bearer $KEY" https://taotoken.net/api/...正常,但同一镜像进入网格后返回401 Unauthorized,或者upstream connect error or disconnect/reset before headers。istioctl proxy-config route能看到taotoken.net路由,问题往往在出站 HTTP_FILTER 没有把 Key 贴到 Envoy 代理的请求头上。TaoToken 官网入口:https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_content=istio_intro 。接入事实先固定:去官网拿 Key,Base URL 填 https://taotoken.net/api,Key 占位符统一用YOUR_API_KEY。下面以服务网格视角,把 ServiceEntry、DestinationRule、EnvoyFilter、Claude Code settings.json、Codex config.toml 和 CC Switch 三件套串起来。
在 Istio 里,出站任务不是“配一个代理地址”这么简单。Sidecar 会拦截 Pod 出站流量,Envoy 的 listener、filter chain、route、cluster 会按服务注册表决定转发路径。外部 AI API 如果不先注册为 ServiceEntry,在REGISTRY_ONLY出站策略下会直接失败;即使注册了,如果 Key 只在应用代码里设置,而应用又经过一层出口代理,Header 也可能在跳数中被覆盖或丢弃。所以更稳的思路是:把taotoken.net注册为外部服务,再用 EnvoyFilter 在 SIDECAR_OUTBOUND 的 HTTP 阶段统一贴 Key。注意,这个方案最适合明文 HTTP 出站,或者由出口网关做 TLS 终止后再注入;如果应用直接发起 HTTPS,Sidecar 默认只看到 TLS 字节流,看不到Authorization这个 HTTP Header,EnvoyFilter 不会生效。这个边界后面会展开。
2. ServiceEntry + DestinationRule:让 taotoken.net 成为可治理的外部服务
先解决“能不能出去”的问题。很多集群默认outboundTrafficPolicy.mode是ALLOW_ANY,但生产网格常被收紧为REGISTRY_ONLY,这时所有未注册的外部域名都会被 Sidecar 拒绝。TaoToken 的 Base URL 是https://taotoken.net/api,所以需要把taotoken.net显式加入服务注册表。可以从 TaoToken 官网准备 Key:https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_content=istio_serviceentry ,然后创建下面的 ServiceEntry。示例命名空间用ai-workloads,你可以替换成自己的业务命名空间。
apiVersion: networking.istio.io/v1beta1 kind: ServiceEntry metadata: name: taotoken-api namespace: ai-workloads spec: hosts: - taotoken.net location: MESH_EXTERNAL ports: - number: 80 name: http protocol: HTTP resolution: DNS endpoints: - address: taotoken.net ports: http: 443 --- apiVersion: networking.istio.io/v1beta1 kind: DestinationRule metadata: name: taotoken-api namespace: ai-workloads spec: host: taotoken.net trafficPolicy: portLevelSettings: - port: number: 80 tls: mode: SIMPLE sni: taotoken.net这段配置表达三件事:
ServiceEntry把taotoken.net声明为网格外部服务,location: MESH_EXTERNAL告诉 Istio 它不是网格内工作负载。ports里声明80/http,是为了让 Sidecar 能按 HTTP 协议解析请求,这样 EnvoyFilter 才能在 HTTP_FILTER 阶段添加 Header。endpoints里把http映射到上游443,表示 Sidecar 实际连接 TaoToken 的 TLS 端口。DestinationRule对 80 端口做SIMPLETLS 发起,sni: taotoken.net保证 SNI 正确。应用侧可以请求http://taotoken.net/api,网格内这一段由 Sidecar 接管,网格外这一段由 Envoy 以 TLS 连接 TaoToken。
如果你坚持在应用里配置https://taotoken.net/api,那就不适合用这个 EnvoyFilter 集中贴 Key 的模式,因为 Sidecar 对 HTTPS 默认是透传。更推荐的做法是:应用层直接带 Key,或者流量先到出口网关,由网关终止 TLS 后再注入 Header。把“服务注册”和“Key 注入”分开看,排障会清晰很多。
验证服务注册是否生效,可以用:
kubectl get serviceentry,destinationrule -n ai-workloads | grep taotoken istioctl proxy-config cluster deploy/ai-client -n ai-workloads | grep taotoken istioctl proxy-config route deploy/ai-client -n ai-workloads | grep taotoken如果cluster里没有outbound|80||taotoken.net,先不要查 Key,先查 ServiceEntry 的 namespace、hosts、ports 和 Sidecar 是否归属于该 namespace。
3. EnvoyFilter 出站注入:Lua 给 taotoken.net/api 贴 Authorization 与 x-api-key
下面进入核心部分:在 SIDECAR_OUTBOUND 的 HTTP_FILTER 链中插入 Lua Filter,匹配:authority为taotoken.net或taotoken.net:80,并且:path以/api开头的请求,然后替换或添加authorization与x-api-key。Key 用YOUR_API_KEY占位,真实值从 TaoToken 控制台创建。官网拿 Key 的入口同样可以在这里使用:https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_content=istio_envoyfilter 。
apiVersion: networking.istio.io/v1alpha3 kind: EnvoyFilter metadata: name: taotoken-outbound-key namespace: ai-workloads spec: workloadSelector: labels: app: ai-client configPatches: - applyTo: HTTP_FILTER match: context: SIDECAR_OUTBOUND listener: portNumber: 80 filterChain: filter: name: envoy.filters.network.http_connection_manager subFilter: name: envoy.filters.http.router patch: operation: INSERT_BEFORE value: name: envoy.filters.http.lua typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua inlineCode: | function envoy_on_request(request_handle) local host = request_handle:headers():get(":authority") or "" local path = request_handle:headers():get(":path") or "" if (host == "taotoken.net" or host == "taotoken.net:80") and string.find(path, "^/api") then request_handle:headers():replace("authorization", "Bearer YOUR_API_KEY") request_handle:headers():replace("x-api-key", "YOUR_API_KEY") end end几个关键点:
context: SIDECAR_OUTBOUND表示只处理 Sidecar 出站方向,不影响入站请求。portNumber: 80与前面的 ServiceEntry 端口对应。如果你改成 443,要同步确认 Sidecar 是否真的以 HTTP 解析该流量。INSERT_BEFORE配合subFilter: envoy.filters.http.router,目的是让 Lua Filter 在路由转发前执行。request_handle:headers():replace会覆盖已有 Header。如果应用已经带了错误的Authorization,这里会纠正;如果应用完全没带,这里会补上。x-api-key与Authorization同时写入,是为了兼容不同上游鉴权习惯。实际使用时按 TaoToken 文档选择一种即可,避免上游因为多个鉴权头产生歧义。
应用侧如果要配合这个模式,可以把请求发到http://taotoken.net/api,由 Sidecar 做 TLS 发起。示例:
curl -sS http://taotoken.net/api/v1/models \ -H "Content-Type: application/json"此时应用不需要自己设置 Key,Key 由 EnvoyFilter 在出站时贴上。验证 Lua 是否加载:
istioctl proxy-config listener deploy/ai-client -n ai-workloads --port 80 -o json | grep -i lua istioctl proxy-config route deploy/ai-client -n ai-workloads -o json | grep -i taotoken istioctl proxy-config cluster deploy/ai-client -n ai-workloads -o json | grep -i taotoken如果 listener 中没有envoy.filters.http.lua,优先检查workloadSelector.labels是否匹配目标 Pod,以及 EnvoyFilter 所在 namespace 是否与工作负载一致。EnvoyFilter 不是全局配置,它需要匹配到具体 Sidecar 才会下发。
4. Claude Code settings.json 与 ANTHROPIC_*:客户端侧也能直连 TaoToken
服务网格解决的是集群内出站治理,Claude Code 这类本地或开发机工具则更常直接配置环境变量。TaoToken 给出的接入点是 Base URL 用https://taotoken.net/api,Key 用YOUR_API_KEY。Claude Code 推荐在settings.json中通过ANTHROPIC_*环境变量配置,不要把 Codex 的配置混进来。可以先去 TaoToken 官网拿 Key:https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_content=istio_client 。
Claude Code 的~/.claude/settings.json示例:
{ "env": { "ANTHROPIC_BASE_URL": "https://taotoken.net/api", "ANTHROPIC_AUTH_TOKEN": "YOUR_API_KEY" } }如果你的版本还识别ANTHROPIC_API_KEY,也可以按官方文档选择对应字段,但不要同时写多个来源,以免实际请求使用了旧值。模型名按 TaoToken 控制台当前可用模型填写,例如放到ANTHROPIC_MODEL,但不要凭记忆写不存在的模型名。配置完成后,重启 Claude Code 或重新打开终端,让环境变量生效。
在 Istio 网格内运行 Claude Code 时,要注意两套配置的关系:
- 如果 Claude Code 直接配置
ANTHROPIC_BASE_URL=https://taotoken.net/api,它会自己发 HTTPS 请求并自带鉴权头。这种模式不依赖 EnvoyFilter 注入 Key。 - 如果 Claude Code 被配置为请求
http://taotoken.net/api,并且 Sidecar 已按前面的 ServiceEntry 和 DestinationRule 处理,那么 Key 可以由 EnvoyFilter 统一贴。但这种模式要求应用侧不额外覆盖Authorization。 - 两种模式不要同时启用。否则可能出现应用带了一个 Key,EnvoyFilter 又替换成另一个 Key,最后排查时不知道是谁生效。
一个常见的 401 场景是:ANTHROPIC_BASE_URL写成了https://taotoken.net/api/v1,而 SDK 又自动追加/v1,最终请求路径变成/api/v1/v1/...。因此先保持 Base URL 为https://taotoken.net/api,路径拼接交给 SDK 或文档说明。另一个场景是开发机代理、容器环境变量、Claude Code settings 三处各写了一份 Key,旧 Key 覆盖新 Key。排障时先执行env | grep ANTHROPIC,确认最终生效值。
5. Codex config.toml 与 CC Switch 三件套:不要把 ANTHROPIC_* 配给 Codex
Codex 的配置体系与 Claude Code 不同。Codex 更常见的是config.toml加自定义 provider,而不是ANTHROPIC_*。把 Claude Code 的ANTHROPIC_BASE_URL、ANTHROPIC_AUTH_TOKEN直接塞给 Codex,通常不会生效,还会让排障方向跑偏。Codex 示例:
model_provider = "taotoken" model = "按 TaoToken 控制台可用模型填写" [model_providers.taotoken] name = "TaoToken" base_url = "https://taotoken.net/api" env_key = "TAOTOKEN_API_KEY"然后在 shell 或系统环境变量中设置:
export TAOTOKEN_API_KEY="YOUR_API_KEY"如果你使用 Windows PowerShell:
$env:TAOTOKEN_API_KEY="YOUR_API_KEY"这里的关键是:Codex 用model_providers指定 provider,用env_key指向 API Key 环境变量,用base_url指向 TaoToken 的https://taotoken.net/api。不要在 Codex 里写ANTHROPIC_*,除非某个版本明确支持且你已核对文档。字段名错误时,Codex 往往会回退到默认 provider,表现为请求没有走到 TaoToken,或者鉴权失败。
CC Switch 的三件套可以理解为:供应商、Key、模型。不同版本的 CC Switch 界面可能不同,但核心配置通常一致:
- 供应商名称:
TaoToken - Base URL:
https://taotoken.net/api - API Key:
YOUR_API_KEY - 默认模型:按 TaoToken 控制台当前可用模型选择
如果你在 CC Switch 里同时管理 Claude Code 与 Codex,建议分两组配置保存:Claude Code 组使用ANTHROPIC_*,Codex 组使用model_providers.taotoken与TAOTOKEN_API_KEY。不要把一个组的 Key 变量名复制到另一个组。切换后要重启对应工具,并确认进程实际读到了新的配置。可以用最小请求验证:
curl -sS https://taotoken.net/api/v1/models \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json"如果这条命令能通,说明 Key 和 Base URL 没问题,再把问题缩小到 Codex 的 TOML 字段或 Claude Code 的 settings.json 路径。
6. 排障:EnvoyFilter 不生效、TLS 透传、Header 覆盖与 401
Istio 出站排障不要一上来就改 Key,按链路顺序查更快。
第一,查 EnvoyFilter 是否下发。kubectl get envoyfilter -n ai-workloads只能证明资源存在,不能证明已生效。用istioctl proxy-config listener deploy/ai-client -n ai-workloads --port 80 -o json看 HTTP_FILTER 链里有没有 Lua。没有就检查workloadSelector.labels、namespace、context: SIDECAR_OUTBOUND和portNumber。
第二,查流量是否真的以 HTTP 进入 Sidecar。EnvoyFilter 的 Lua 在 HTTP Connection Manager 解析请求后执行。如果应用直接发 HTTPS,Sidecar 默认只看到 TLS 透传,:authority、:path、authorization都不存在,Lua 不会触发。这就是为什么前面强调两种模式:应用层带 Key,或者出口网关/明文 HTTP 加 TLS 发起。
第三,查 ServiceEntry 协议。protocol: HTTP和protocol: TLS会导致完全不同的 Filter 链。写错协议时,路由可能看似存在,但 HTTP_FILTER 不执行。resolution: DNS要求域名可解析;如果集群 DNS 无法解析taotoken.net,先修 DNS 或改用静态 endpoint。
第四,查 Header 覆盖。应用、Sidecar、出口网关、上游 SDK 都可能设置Authorization。如果 Lua 用replace,它会覆盖旧值;如果你希望保留应用自带 Key、只在缺失时补充,可以把 Lua 改成先get再add。但生产上更建议统一出口,避免多来源 Header 竞争。
第五,查 401 的具体返回。401 invalid api key表示请求已经到了上游但 Key 不对;401 missing api key表示 Header 没贴上;403可能是 Key 权限或模型权限;404常见于 Base URL 路径拼接错误。把curl -v的请求路径、Header 名、响应体一起看,不要只看状态码。
第六,查 Sidecar 日志和 Envoy 管理端口。可以临时提高日志级别:
istioctl proxy-config log deploy/ai-client -n ai-workloads --level http:debug,lua:debug kubectl logs deploy/ai-client -n ai-workloads -c istio-proxy --tail=200如果看到lua filter相关日志,说明 Filter 已进入执行阶段;如果完全没有,说明请求没有以 HTTP 形式进入出站 HCM。
7. 安全落地:Key 轮换、Secret 引用与出站审计
把YOUR_API_KEY直接写在 EnvoyFilter 里只适合实验。生产环境至少要做到三件事:Key 不进入 Git、Key 可轮换、出站可审计。Kubernetes 里可以先用 Secret 管理:
apiVersion: v1 kind: Secret metadata: name: taotoken-key namespace: ai-workloads type: Opaque stringData: api-key: YOUR_API_KEY应用侧通过envFrom或 volume 读取,而不是把 Key 写死到镜像。如果坚持在网格层注入,建议把注入点放到出口网关,由网关统一读取 Secret,并在网关侧做审计和限流。Sidecar 内的静态 Lua 适合验证链路,不适合长期生产。TaoToken 官网的控制台可以创建和轮换 Key:https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_content=istio_security 。
还要注意模型调用日志中可能包含 Prompt、响应片段或工具调用参数。出站审计不要只记录 Header,还要对 Body 做采样和脱敏。服务网格可以做的是:记录目标 host、路径、状态码、响应时间、上游 cluster;应用侧记录模型名、Token 用量和请求 ID。两边用同一个请求 ID 关联,排障时才能从 Envoy 一路查到业务。
Key 轮换时,建议用双 Key 过渡:先在 TaoToken 控制台创建新 Key,更新 Secret 或网关配置,观察旧 Key 调用量归零,再禁用旧 Key。不要直接删除旧 Key,否则正在运行的 Pod 可能因配置未热更新而中断。对于 Claude Code、Codex、CC Switch 这些本地工具,Key 轮换后要同步更新 settings.json、config.toml 对应的环境变量和 CC Switch 配置,避免本地缓存旧值。
8. 从模型对话到 Claude Code 文档:一次配通的最短路径
如果你现在要完整跑通一条链路,可以按这个顺序走:
- 先去模型对话页确认 TaoToken 当前可用模型和调用方式:https://taotoken.net/models/detail/chat?utm_source=taotoken_aicg_blog_end&utm_content=istio_chat
- 如果你需要长期写代码、频繁调用,查看 Coding Plan 是否匹配你的使用强度:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=istio_plan
- 在控制台创建 API Key,并把
YOUR_API_KEY替换成真实值:https://taotoken.net/console/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=istio_key - Claude Code 用户按官方文档配置
settings.json和ANTHROPIC_*:https://taotoken.net/doc/ClaudeCodeAnthropic?utm_source=taotoken_aicg_blog_end&utm_content=istio_claude_doc
回到 Istio 这条线,最小落地顺序是:ServiceEntry 注册taotoken.net,DestinationRule 配置 TLS 发起,EnvoyFilter 在 SIDECAR_OUTBOUND 的 80 端口 HTTP_FILTER 中插入 Lua,应用请求走http://taotoken.net/api,Key 由 Lua 统一替换为Bearer YOUR_API_KEY和x-api-key: YOUR_API_KEY。客户端侧则分开配置:Claude Code 用ANTHROPIC_BASE_URL=https://taotoken.net/api与ANTHROPIC_AUTH_TOKEN=YOUR_API_KEY,Codex 用config.toml的model_providers.taotoken与TAOTOKEN_API_KEY,CC Switch 按供应商、Key、模型三件套保存。把这两条线分开,出站 401 就不再是玄学问题,而是可以按 listener、filter、route、cluster、header 一层层定位的配置问题。