2026-09-16 重要更正与风险提示(务必先读) 1. Claude 也能走
bedrock-mantle:路径是/anthropic/v1/messages(Anthropic Messages 面),模型用 mantle 的短 ID(不带版本后缀、不带us.前缀),如anthropic.claude-opus-4-8、anthropic.claude-sonnet-5。此前 word文档/本文判定"Claude 不在 mantle"是因为拿 runtime 的长版本 ID(...-20250929-v1:0)去打 mantle,mantle 不认这种 ID → 404,被误读成"端点不支持 Claude"。实测(2026-09-16,us-east-1 与香港 ap-east-1 Lambda)均 200。 2. 地域门按"模型/厂商"判,不按"端点"判:香港出口下,OpenAI 系在 mantle 和 runtime 两个端点都被挡(400 unsupported countries);Claude 在两个端点都能过(200)。 3. ⚠️ 但 Claude 从香港能用属"意外",理论上不该放行,随时可能被收紧。生产架构不要依赖"Claude 从香港直连"这条——Claude 也走海外中继,既统一出口/鉴权/模型名,又不受这条政策变化影响。GPT 是明确被挡的,本来就必须走中继。
| 问题 | 结论(2026-09-15 实测) |
|---|---|
| 香港出口到底能不能调 | GPT 系(openai.*)从香港被挡(400 Access to OpenAI models is not allowed from unsupported countries,mantle 与 runtime 两端点都挡);Claude 从香港目前是通的(runtime Converse/Invoke 与 mantle /anthropic/v1/messages 均 200,2026-09-16 香港 Lambda 复测)。⚠️ Claude 能用是意外、随时可能收紧,架构不要依赖它。 |
| Claude 能不能走 mantle | 能。路径 /anthropic/v1/messages,用短 ID(anthropic.claude-opus-4-8 等)。这是对 word文档"Claude 不在 mantle"的更正(原判断用错了模型 ID 形式)。对本方案的影响:中继上 Claude 的 LiteLLM 后端可指向 runtime Converse(现方案,稳)或 mantle /anthropic/v1/messages(等价可选);One API 侧走 OpenAI 类型渠道不变,格式转换仍由 LiteLLM 做。 |
| 中继放哪 | 东京、新加坡、美东都能过门。实测经东京中转的总时延(53+150=203ms)与香港直连美东(205ms)几乎相同,中继不增加延迟;经新加坡多约 55ms(33+225=258ms),因为新加坡到美东要绕行。推荐东京,放美东效果等价(中继到 Bedrock 同区),新加坡也可用。 |
| 中继怎么做 | 一台 Ubuntu EC2 + 3 个容器(nginx / aws-sigv4-proxy / LiteLLM),挂 IAM 角色,无任何静态 AWS 密钥。一段脚本粘进 EC2"用户数据",开机 ~80 秒自动就绪。 |
| One API 侧改什么 | 只加两个 OpenAI 类型渠道(GPT、Claude 各一)+ 信任中继证书 + 把"失败重试次数"从 0 改成 3。One API 原生的 "AWS Bedrock" 渠道类型不能用(源码确认)。 |
| 高可用 | 推荐:中继层 = Auto Scaling Group(≥2 台、跨可用区、启动模板带安装脚本)+ ALB 固定入口;香港 One API 每个模型对同一 ALB 建两个渠道 + 失败重试 3 次。实测直接终止一台中继:ALB 16 秒摘除、ASG 3 分 45 秒补齐两台;期间 20 个请求 19 个成功(唯一失败是终止后 2 秒内那个)。滚动升级(实例刷新)期间请求零失败。香港 One API 不管一台还是多台,都只指向 ALB 域名。 |
| 原文档最大的坑 | ① 中继 IAM 角色缺 bedrock-mantle:CreateInference,GPT 路 403;② nginx 镜像自带的默认站点会抢 80 端口,ALB 健康检查 404;③ TLS 证书从哪来、One API 如何信任没交代(One API 不会跳过证书校验);④ GPT 渠道"密钥任意"等于中继裸奔;⑤ 高可用只讲了 LB,没讲实例挂了谁来补(需要 ASG)。全部已修正并实测。 |
本文所有文件在
deliverables/目录下,见 §6 文件清单。三步走每一步都给了 控制台点选版 和 命令行版,两者任选其一。读者定位:熟悉 Linux / docker / nginx / TLS 的运维同学,但没碰过 AWS。所以 docker compose、nginx upstream、openssl 自签这些不展开;AWS 专有名词在 §0.1 用你们熟悉的东西对照解释一遍,正文里第一次出现时也会点一下。
| AWS 名词 | 相当于你熟悉的 | 本方案里怎么用 | 坑 |
|---|---|---|---|
| Region(区域) | 一个独立机房集群,如 ap-northeast-1 东京、ap-east-1 香港、us-east-1 美东 | EC2 建在东京;Bedrock 端点调美东 | 控制台右上角要先切区域再找资源;香港区是"可选加入"区,新账号要先在账户设置里开启 |
| VPC / 子网 / 可用区(AZ) | 私有网络 / 网段 / 同城不同机房 | 全用每个区自带的默认 VPC,不用自己建;两台中继放不同 AZ | 每个区默认 VPC 网段都是 172.31.0.0/16,所以跨区 VPC 对等会冲突,本方案走公网 TLS 而不是内网 |
| 安全组 (Security Group) | 实例级有状态防火墙(只写允许规则,默认拒绝入站、放行出站) | 中继只放 香港机IP/32 → 443 | 规则改了立即生效,不用重启;来源可以填"另一个安全组"(ALB → 中继 80 就这么写) |
| EC2 / AMI / 实例类型 | 虚拟机 / 系统镜像 / 规格 | Ubuntu 24.04 官方 AMI,t3.small(2C2G) | t3.micro 只有 1G,LiteLLM 会 OOM |
| 用户数据 (User data) | cloud-init 开机脚本 | 把 install-relay.sh 整段贴进去,开机自动装好 |
只在首次启动跑一次;以 root 执行;日志在 /var/log/cloud-init-output.log |
| IAM 角色 + 实例配置文件 (Instance Profile) | 给"这台机器"发的短期凭证,类似 k8s 的 ServiceAccount / IRSA | 中继挂 bedrock-relay-role,容器里的 SDK 自动拿到临时 AK/SK,机器上不存任何密钥 |
控制台建角色时会自动建同名实例配置文件;CLI 要手动建并关联(脚本已做) |
| IAM 策略 | RBAC 的规则 JSON | bedrock:Invoke* + bedrock-mantle:CreateInference |
权限改动对已在跑的实例立即生效,不用重启 |
| IMDS / 元数据服务 / 跃点限制 | 169.254.169.254 上的本机信息接口,角色凭证从这里取 | 容器(docker bridge,多一跳)要取凭证 → 跃点限制设 2 | 默认 1,忘了改的话 LiteLLM/sigv4-proxy 报 "no credentials",nginx 却是好的,很迷惑 |
| Session Manager | 浏览器/CLI 里的 SSH,走 AWS 内部通道 | 不开 22 端口、不配密钥对也能进机器 | 需要角色带 AmazonSSMManagedInstanceCore(脚本已加);Ubuntu AMI 自带 agent |
| 弹性 IP (EIP) | 固定公网 IP | 生产给中继绑一个,否则 stop/start 会换 IP | 绑完要重签证书(gen-cert.sh),并更新香港机安全组/LB 配置 |
| ALB | 托管的七层 LB(类似 nginx/HAProxy as a service),自带健康检查、多 AZ | 方案 B 用;方案 A 不用 | 空闲超时默认 60s 要改 300s;POST 失败不会重试到另一台 |
| 目标组 (Target Group) | LB 的 upstream 列表 + 健康检查配置 | 两台中继:80,/healthz |
最短检查间隔 5 秒 |
| ACM | 证书管理器 | ALB 的证书必须放在 ACM;没域名就"导入"自签证书 | 自签证书 CN 限 64 字符,ALB 域名太长只能放 SAN |
| Bedrock / 模型访问权限 | 托管的模型推理 API | 美东 us-east-1;控制台"模型访问权限"页要先勾 Anthropic 与 OpenAI | 端点分两个:bedrock-runtime(原生 Converse/Invoke)和 bedrock-mantle(OpenAI 兼容),IAM 动作也不同 |
inference profile(us./global. 前缀) |
跨区路由的模型别名 | runtime 端点必须用带前缀的 ID;mantle 端点只认裸 ID | 记反了分别报 400 invalid model / 404 |
| 启动模板 (Launch Template) | 虚拟机的"镜像 + 规格 + 网卡 + 开机脚本"模板,带版本号 | ASG 用它拉起中继;改配置 = 发新版本 | 用户数据要 base64;Version=$Latest 才会跟着新版本 |
| Auto Scaling Group (ASG) | 保证"始终有 N 台按模板起的机器"的控制器,类似 k8s 的 ReplicaSet | 最少 2 台跨 AZ;健康检查类型选 ELB,ALB 判定不健康就换新机 | 换出来的是新 IP,所以前面必须有 ALB 这种固定入口;health-check-grace-period 要 ≥ 开机脚本时长(180s) |
| 实例刷新 (Instance Refresh) | 滚动发布 | 升级中继:发新版启动模板 → start-instance-refresh,一台一台换 | MinHealthyPercentage=50 保证始终有一台在线;实测期间请求零失败 |
| CloudWatch 告警 → 恢复/重启实例 | 监控 + 自愈动作 | 给中继加 StatusCheckFailed 两个告警 | 与容器 restart: always 配合就是"EC2 崩了自动回来" |
| 标签 (Tag) | label | 所有资源打 Project=bedrock-relay-poc |
99-cleanup.sh 按标签删,别漏打 |
| # | 原文 | 问题 | 修正 |
|---|---|---|---|
| A1 | §1/§5:Claude 走 One API 的 AWS Bedrock 渠道,base_url 指向中继 /claude |
不成立。源码 relay/adaptor/aws/adaptor.go:只用 Region + AK/SK 构造 bedrockruntime.New(...),没有任何 endpoint/base_url 入口;模型表 relay/adaptor/aws/claude/main.go 停在 claude-3-5,没有 Sonnet 4.5。runbook 已改,word文档 未同步。 |
Claude 用 OpenAI 类型渠道 → 中继 LiteLLM → Bedrock Converse(本文方案)。 |
| A2 | §2:两个端点同一把凭证即可 | 凭证是同一把,但 IAM 动作不同:mantle 端点要 bedrock-mantle:CreateInference,只给 bedrock:InvokeModel* 实测 403。 |
角色策略见 cli/bedrock-invoke-policy.json。 |
| A3 | §3 nginx:if ($http_x_relay_auth = ...) 做闸门;§5 说"两个渠道都加自定义头 X-Relay-Auth" |
One API 原版渠道 没有自定义请求头功能,X-Relay-Auth 发不出去,闸门永远 401。 | 用 One API 一定会发的 Authorization: Bearer <渠道密钥> 当闸门:GPT 路由 nginx 校验后再交给 sigv4-proxy 换成 SigV4;Claude 路由 LiteLLM 用 master_key 校验。 |
| A4 | §3 nginx 无 client_max_body_size |
nginx 默认 1MB,长 prompt / 带图片请求会 413。 | 设 20m。 |
| A5 | §3 rewrite ^/mantle/v1/(.*)$ /openai/v1/$1 vs runbook base_url=/mantle/openai + rewrite ^/mantle/(.*)$ /$1 |
两份文档路径约定不一致。 | 统一用 runbook 的:渠道 base_url https://<relay>/mantle/openai,nginx 去掉 /mantle 前缀。 |
| A6 | §4:"地域门只挡中国大陆/香港" | 精确化:按模型/厂商判,不按端点判——只挡 OpenAI 系;Claude 在 runtime 与 mantle 两端点从香港都 200(2026-09-16 香港 Lambda 复测)。 | 见 §0。但 Claude 从香港能用属意外,随时可能收紧,架构不要依赖;为了统一出口/鉴权/模型名、并规避这条政策风险,Claude 也走中继。 |
| A10 | §5/§8:"Claude 不在 Bedrock 的 /openai/v1,也不在 /anthropic/v1/messages(runtime+mantle 均 404)" |
对 mantle 的结论错了:错在拿 runtime 的长版本 ID 去打 mantle。mantle 只认短 ID,用 anthropic.claude-opus-4-8 打 bedrock-mantle .../anthropic/v1/messages 实测 200。(runtime 侧 Claude 确实只认原生 Converse/Invoke,这部分不变。) |
见 §0 更正与 §1.3。 |
| A7 | §1 高可用版:ALB + WAF + ECS Fargate | ECS Fargate 对不熟 AWS 的团队过重;ALB 保留,但原文没说"实例挂了谁来补",而且 ALB 对宕机节点有 10~20 秒 502 窗口且不会重发 POST(实测)。 | §3.3:ASG(自动补机)+ ALB + One API 双渠道重试兜窗口。 |
| A8 | §3/§6:ssl_certificate /etc/nginx/tls/fullchain.pem |
没说证书从哪来。客户多半没域名;One API 的 Go HTTP 客户端不跳过证书校验(源码无 InsecureSkipVerify),自签证书直接用会握手失败。 |
中继自签证书(脚本自动生成),One API 容器用 SSL_CERT_FILE 挂信任包(add-relay-cert.sh 一条命令)。 |
| A9 | §6 compose 用自定义 entrypoint 跑 envsubst | 不必要。 | nginx 官方镜像自带 /etc/nginx/templates/*.template → conf.d/ 的 envsubst 机制。 |
| # | 原文 | 问题 | 修正 |
|---|---|---|---|
| R1 | A 节:IAM Role 含 bedrock:InvokeModel + InvokeModelWithResponseStream |
缺 bedrock-mantle:CreateInference,GPT 路 403(实测)。 |
补上,见策略文件。 |
| R2 | B 节渠道 A:密钥"任意(mantle 侧由 sigv4-proxy 签,忽略此值)" | 中继 GPT 路 零鉴权:安全组之外没有第二道防线,且安全组放的是整台香港机。 | nginx 校验 Bearer 必须等于 RELAY_GPT_KEY(错钥匙实测 401)。 |
| R3 | nginx 只 listen 443 |
上 ALB 时要 listen 80,但 nginx 镜像自带 conf.d/default.conf 也监听 80 并抢默认站点 → ALB 健康检查 /healthz 得到 404(实测 Target.ResponseCodeMismatch)。 |
模板文件名用 default.conf.template,覆盖镜像自带文件。 |
| R4 | ./tls 挂 fullchain.pem / privkey.pem |
同 A8。 | 同 A8。 |
| R5 | 未提 EC2 元数据跳数 | 容器内(docker bridge)拿实例角色凭证,IMDSv2 hop limit 必须 ≥2,否则 LiteLLM/sigv4-proxy 拿不到凭证。 | 启动参数 HttpPutResponseHopLimit=2(控制台:高级详情 → 元数据响应跃点限制 = 2)。 |
| R6 | 验证命令给 Claude/GPT 都发 max_tokens |
GPT-5.6 经 mantle 与 OpenAI 官方一致:不接受 max_tokens,要 max_completion_tokens(实测 400 unsupported_parameter)。Claude 经 LiteLLM 两者皆可。 |
文档/测试脚本已改;若客户端改不了,可让 GPT 走 LiteLLM 的 gpt-5.6-converse(实测 200,见 §3.2)。 |
| R7 | 没提模型重定向 | 若要给用户暴露 gpt-5.6 这样的短名,除了填"模型重定向",别名也必须写进渠道的模型列表,否则 503 "无可用渠道"(实测)。 |
setup-channels.sh 已处理。 |
| R8 | 未提失败重试 | One API 的 RetryTimes 默认 0,是系统设置项(设置 → 运营设置 → 失败重试次数),不是环境变量。 |
设为 3(脚本已自动设置)。 |
| R9 | litellm:main-latest |
滚动 tag,重装可能换版本。 | 用 main-stable。 |
| R10 | "验证状态标注"最后一行:双机端到端未跑过 | 本次已跑通,见 §5 实测表。 | — |
| R11 | "地域门只挡大陆/香港(香港未亲测)" | 已亲测:GPT 挡、Claude 不挡。 | 同 A6。 |
| R12 | 多节点故障转移用 "LB nginx + 2 后端" | 结论对(实测零 502),但 LB nginx 放哪、后端 IP 变了谁改、后端挂了谁补 都没说:单独一台 nginx LB 是新的单点,写死 IP 又和自动补机冲突。 | 生产用 ASG + ALB(§3.3);nginx LB 只作为"没有 ASG 的过渡方案",以 sidecar 形式跟随每个 One API 节点。 |
| R13 | litellm.yaml 里 master_key 明文 sk-relay-claude |
建议随机。 | 脚本自动生成 32 位随机,也可预先指定。 |
aws-samples/bedrock-access-gateway 都行;LiteLLM 更通用、社区大。aws bedrock list-foundation-models --region us-east-1 实查,2026-09-15):| 用途 | 端点 | 要填的 ID |
|---|---|---|
GPT-5.6 走 mantle /openai/v1 |
bedrock-mantle | 裸 ID:openai.gpt-5.6-sol / -luna / -terra、openai.gpt-6-astra |
| Claude 走 runtime Converse(LiteLLM,现方案) | bedrock-runtime | 必须带前缀的 inference profile:us.anthropic.claude-sonnet-4-5-20250929-v1:0、us.anthropic.claude-opus-4-8、us.anthropic.claude-haiku-4-5-20251001-v1:0…(global. 前缀亦可) |
Claude 走 mantle /anthropic/v1/messages(可选后端) |
bedrock-mantle | 裸/短 ID:anthropic.claude-opus-4-8、anthropic.claude-opus-5、anthropic.claude-sonnet-5、anthropic.claude-haiku-4-5、anthropic.claude-fable-5、anthropic.claude-opus-4-7(bedrock-mantle .../v1/models 实查,2026-09-16;注意此列表与 runtime 的可用型号/版本不完全相同) |
| GPT 走 runtime Converse(LiteLLM 备选) | bedrock-runtime | us.openai.gpt-5.6-sol |
规则:mantle 只认短 ID,runtime 只认带前缀 profile ID。Claude 在 mantle 走 Anthropic Messages 面(/anthropic/v1/messages),不支持 OpenAI 的 /v1/chat/completions(实测 400 does not support the ... API);GPT 反之走 /openai/v1/chat/completions。列 mantle 模型清单需 bedrock-mantle:ListModels 权限(调用只需 CreateInference)。
3. 香港到底挡不挡:见 §0。
4. 中继放哪(从香港 EC2 与东京/新加坡 EC2 实测 TCP 建连时延):
| 路径 | TCP | 备注 |
|---|---|---|
| 香港 → 新加坡 | 33ms | |
| 香港 → 东京 | 53ms | |
| 香港 → 美西 us-west-2 | 145ms | |
| 香港 → 美东 us-east-1 | 205ms | |
| 东京 → 美东 bedrock-runtime / mantle | 150ms | |
| 新加坡 → 美东 | 225ms | |
| 端到端(香港 One API → 东京中继 → 美东 Bedrock → 回) | GPT 0.9~1.1s,Claude 1.5~1.9s | 短问答整体耗时,含模型推理 |
端点必须留在 us-east-1(GPT-5.6 的 mantle OpenAI 兼容端点只有美东,runbook 已实测)。 5. 要不要 Bedrock API Key(Bearer):不需要。IAM 角色 + sigv4-proxy/LiteLLM 自动签名,没有任何长期密钥要保管。API Key 只在"中继不在 AWS 上"时才有意义。
香港(客户现有 One API,1 台或多台都一样,不动业务逻辑) 东京
┌──────────────────────────────────────────┐ ┌─────────────────────────────────────────────────────────┐
│ One API 节点 ×N │ │ ALB(固定域名,HTTPS 443,安全组只放香港各节点出口 IP) │
│ 渠道 GPT-5.6 ×2 OpenAI类型 │ HTTPS │ 目标组 :80 /healthz 5s×2 │
│ base_url https://<ALB>/mantle/openai ──┼───────►│ ┌──────────────── Auto Scaling Group(min 2,跨 AZ)──────┐│
│ 密钥 = RELAY_GPT_KEY │ │ │ 中继 EC2(启动模板 = Ubuntu + install-relay.sh 用户数据) ││
│ 渠道 Claude ×2 OpenAI类型 │ │ │ nginx :80 ─┬ /mantle/* 校验Bearer → sigv4-proxy ──────┼┼─→ bedrock-mantle.us-east-1 /openai/v1 (GPT)
│ base_url https://<ALB>/claude ─────────┼───────►│ │ └ /claude/* → LiteLLM (master_key) ──────┼┼─→ bedrock-runtime.us-east-1 Converse (Claude)
│ 密钥 = RELAY_CLAUDE_KEY │ │ │ IAM 角色:bedrock:Invoke* + bedrock-mantle:CreateInference ││
│ 系统设置:失败重试 3 │ │ └─────────────────────────────────────────────────────────┘│
└──────────────────────────────────────────┘ │ 实例挂/不健康 → ASG 自动换新并注册回 ALB │
└─────────────────────────────────────────────────────────┘
https://<中继IP>/mantle/openai 与 https://<中继IP>/claude,中继 nginx 443 自签 TLS。RELAY_*_KEY 写在启动模板的用户数据里,所有中继一致。RELAY_*_KEY 只是"中继的门钥匙"。SSL_CERT_FILE 信任)。下面凡是 AWS 专有名词(角色、实例配置文件、安全组、跃点限制、Session Manager、EIP、ALB、目标组、ACM),含义与坑见 §0.1,正文不再重复解释。docker / nginx / openssl 部分默认你已熟悉。
控制台:IAM → 角色 → 创建角色 → 可信实体 AWS 服务 / EC2 → 下一步 → 先勾 AmazonSSMManagedInstanceCore(让你能在浏览器里点"连接 → Session Manager"进机器,不用密钥对)→ 角色名 bedrock-relay-role → 创建。再进该角色 → 添加权限 → 创建内联策略 → JSON → 粘贴 cli/bedrock-invoke-policy.json 全文 → 名字 bedrock-invoke。
命令行:bash cli/01-create-iam-role.sh
策略要点:
bedrock:InvokeModel*/Converse*加上bedrock-mantle:CreateInference(GPT 走 mantle 端点必需)。另外要在 Bedrock 控制台(us-east-1)确认已开通 Anthropic 与 OpenAI 模型访问("模型访问权限"页面)。
目的:在不涉及中继的情况下,证明 (a) 这个 AWS 账号能调 Claude 和 GPT,(b) 该 EC2 所在区的出口 IP 过得了地域门。建议就在你打算放中继的区(东京)开一台临时机;同一脚本在香港机上跑一遍,可以亲眼看到 GPT 被挡而 Claude 不挡。
控制台:EC2(右上角切到 东京 ap-northeast-1)→ 启动实例 → 名称 bedrock-test → AMI 选 Ubuntu Server 24.04 → 类型 t3.micro 即可 → 密钥对"不使用" → 网络:默认 VPC、不需要开任何入站端口 → 高级详情 → IAM 实例配置文件 = bedrock-relay-role → 启动。等状态检查通过后,选中实例 → 连接 → Session Manager → 连接,得到一个浏览器终端。然后:
# 把 step1-账号权限测试/bedrock-test.py 的全文粘进去保存(或 nano bedrock-test.py 粘贴),再:
python3 bedrock-test.py
命令行:(在自己电脑或任意有 AWS CLI 的地方)
REGION=ap-northeast-1; AMI=$(aws ssm get-parameter --region $REGION --name /aws/service/canonical/ubuntu/server/24.04/stable/current/amd64/hvm/ebs-gp3/ami-id --query Parameter.Value --output text)
aws ec2 run-instances --region $REGION --image-id $AMI --instance-type t3.micro --iam-instance-profile Name=bedrock-relay-role \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=bedrock-test}]' --query "Instances[0].InstanceId" --output text
# 然后 aws ssm start-session --region $REGION --target <实例ID>,进去后同上跑 python3 bedrock-test.py
判读(脚本自带 ✅/❌ 和说明):
| 编号 | 测的是 | 期望 | 不通时最可能的原因 |
|---|---|---|---|
| 1 | GPT-5.6 @ mantle /openai/v1 |
200 PONG | 403 bedrock-mantle:CreateInference → 角色策略缺;400 unsupported countries → 本机出口被挡(香港/大陆),GPT 必须经中继 |
| 2 | Claude @ runtime Converse | 200 PONG | 403 AccessDenied → 角色策略/模型访问权限;400 invalid model → 没带 us. 前缀 |
| 3 | Claude @ runtime InvokeModel | 200 PONG | 同上 |
| 4 | GPT @ runtime Converse | 200 PONG | 同 1 |
| 5 | Claude @ mantle /anthropic/v1/messages(短 ID) |
200 PONG | 404 → model ID 用错(别用 runtime 的长版本 ID);403 → 缺 bedrock-mantle:CreateInference。香港出口下也应 200(Claude 不被地域门挡;但这属意外,勿依赖) |
| 6 | mantle /v1/models 列表 |
200 | 403 → 缺 bedrock-mantle:ListModels(可选权限,不影响调用) |
| 7/8 | 对照组:Claude 走 mantle 的 OpenAI 面 / 用 runtime 长 ID 打 mantle | 400 / 404 才是预期 | Claude 在 mantle 不支持 /v1/chat/completions(400);mantle 不认长版本 ID(404) |
脚本只用 python3 标准库,自带 SigV4 签名,不需要 AWS CLI、不需要 pip。(原 curl 版在 Ubuntu 24.04 自带的 curl 8.5 上签名有 bug,已弃用。)
控制台:EC2(东京)→ 启动实例:
| 项 | 填什么 |
|---|---|
| 名称 | bedrock-relay-a |
| AMI | Ubuntu Server 24.04 LTS (amd64) |
| 实例类型 | t3.small(2GB 内存,LiteLLM 需要;t3.micro 会 OOM) |
| 密钥对 | 不使用(用 Session Manager) |
| 网络 → 安全组 | 新建 bedrock-relay-sg:入站 仅 HTTPS 443 来源 = <香港 One API 机公网IP>/32。不要开 22、不要 0.0.0.0/0 |
| 存储 | 16 GiB gp3 |
| 高级详情 → IAM 实例配置文件 | bedrock-relay-role |
| 高级详情 → 元数据版本 / 跃点限制 | IMDSv2 仅令牌;元数据响应跃点限制 = 2 |
| 高级详情 → 用户数据 | 粘贴 step2-中继机/install-relay.sh 全文,并把开头两行改成你的钥匙:RELAY_GPT_KEY="sk-relay-gpt-随便32位"、RELAY_CLAUDE_KEY="sk-relay-claude-随便32位"(留空也行,脚本会自动生成并打印) |
启动后约 80~90 秒就绪。连接 → Session Manager,运行:
sudo cat /opt/relay/.env # 看两把钥匙
sudo bash /opt/relay/selftest.sh # 期望:healthz=ok;[2] GPT PONG;[3] Claude PONG;[4] 401
sudo cat /opt/relay/tls/server.crt # 证书内容,香港机要信任它(也可由香港机自动抓取,见下)
命令行:
export HK_CIDR=<香港机公网IP>/32 RELAY_GPT_KEY=sk-relay-gpt-xxx RELAY_CLAUDE_KEY=sk-relay-claude-xxx
REGION=ap-northeast-1 NAME=bedrock-relay-a bash cli/02-launch-relay.sh
建议:给中继绑一个弹性 IP(EC2 → 弹性 IP → 分配 → 关联),否则停机再开 IP 会变。绑完在机上执行 sudo /opt/relay/gen-cert.sh && cd /opt/relay && sudo docker compose restart nginx 重新签发含新 IP 的证书。
中继机上到底有什么(/opt/relay/,完整内容见 step2-中继机/配置参考/):
| 文件 | 作用 |
|---|---|
docker-compose.yml |
三个容器:litellm(Claude 转换 + 签名)、sigv4mantle(给 GPT 路签 SigV4)、nginx(TLS、分流、钥匙校验) |
relay.conf.template |
nginx 配置:/mantle/* 校验 Bearer 后去前缀转 sigv4-proxy;/claude/* 转 LiteLLM;client_max_body_size 20m;proxy_buffering off(SSE) |
litellm.yaml |
claude-sonnet-4-5 → bedrock/us.anthropic.claude-sonnet-4-5-20250929-v1:0;备选 gpt-5.6-converse → bedrock/converse/us.openai.gpt-5.6-sol |
.env |
两把钥匙 + region(chmod 600) |
tls/server.crt|key |
自签证书,SAN = DNS:bedrock-relay + 本机公网/内网 IP |
gen-cert.sh / selftest.sh |
重签证书 / 自检 |
要加模型:改 litellm.yaml 加一段 model_name → sudo docker compose restart litellm;GPT 路不用改(mantle 直接认 openai.gpt-5.6-luna 等裸 ID),只需在 One API 渠道模型列表里加名字。
客户已有 One API(docker 部署) —— 只做三件事:
server.crt 追加到一个 PEM 文件(系统 CA + 中继证书),挂进容器并设 SSL_CERT_FILE。step2-香港OneAPI机/add-relay-cert.sh <中继IP> 会自动抓证书、追加、重启容器。手工版 compose 片段:
yaml
services:
one-api:
volumes:
- ./relay-ca-bundle.pem:/etc/ssl/certs/relay-ca-bundle.pem:ro
environment:
SSL_CERT_FILE: /etc/ssl/certs/relay-ca-bundle.pem # Go 程序读这个变量
RELAY_TIMEOUT: "300"
(relay-ca-bundle.pem = cat /etc/ssl/certs/ca-certificates.crt 中继server.crt,这样其它 https 渠道不受影响。)| 字段 | 渠道 A(GPT-5.6) | 渠道 B(Claude) |
|---|---|---|
| 类型 | OpenAI | OpenAI(不是 Anthropic、不是 AWS) |
| 名称 | GPT-5.6 via relay-a | Claude via relay-a |
| 代理 / Base URL | https://<中继IP>/mantle/openai |
https://<中继IP>/claude |
| 密钥 | RELAY_GPT_KEY |
RELAY_CLAUDE_KEY |
| 模型 | openai.gpt-5.6-sol(想给用户短名则再加 gpt-5.6) |
claude-sonnet-4-5(可再加 gpt-5.6-converse) |
| 模型重定向(可选) | {"gpt-5.6":"openai.gpt-5.6-sol"} |
— |
| 分组 | default | default |
或者命令行:./setup-channels.sh <中继IP> <RELAY_GPT_KEY> <RELAY_CLAUDE_KEY>(用管理令牌走 One API 的 /api/channel 接口,效果与上表一致,并顺手把重试次数设为 3、建一个测试令牌)。
3. 设置 → 运营设置 → 失败重试次数 = 3(默认 0;多渠道时才会自动换渠道)。
全新装 One API 做 PoC(本次测试就是这样):控制台在 ap-east-1 启动 Ubuntu 24.04 t3.small,用户数据粘 install-oneapi.sh,安全组只对你办公 IP 开 3000(或不开,用 Session Manager 端口转发);命令行 bash cli/03-launch-oneapi-hk.sh。装完 cd /opt/oneapi && ./add-relay-cert.sh <中继IP> && ./setup-channels.sh <中继IP> <GPT钥匙> <Claude钥匙>。
记得把香港机的公网 IP 填进中继安全组(3.2.1 的
HK_CIDR)。
cd /opt/oneapi && ./e2e-test.sh # 或 ./e2e-test.sh sk-<你的One API令牌>
期望四条非流式都 HTTP 200 且 content: "PONG",两条流式都逐块出 data: 行。本次实测输出(节选):
>>> openai.gpt-5.6-sol {"choices":[{"message":{"content":"PONG",... HTTP 200 总耗时 1.13s
>>> gpt-5.6(重定向) ...content":"PONG"... HTTP 200 总耗时 0.96s
>>> claude-sonnet-4-5 ...content":"PONG"... HTTP 200 总耗时 1.47s
>>> gpt-5.6-converse(备选) ...content":"PONG"... HTTP 200 总耗时 1.56s
>>> claude-sonnet-4-5 流式 data: {...delta":{"content":"1\n2\n3\n4"...}} ✔
>>> openai.gpt-5.6-sol 流式 data: {...delta":{"content":"1"}...} ✔
给业务方的两条提醒:① GPT-5.6 请求用 max_completion_tokens(发 max_tokens 会 400,与 OpenAI 官方一致);改不了的老客户端用 gpt-5.6-converse。② One API 日志会刷 model ratio not found,是计费倍率没配,去"设置 → 运营设置 → 模型倍率"给这几个模型名加倍率即可,不影响调用。
要解决的三件事:挂了不影响业务(多台 + 入口自动摘除)、挂了自动补(ASG)、升级不停机(实例刷新)。写死 IP 的 nginx LB 解决不了第二件,所以生产用 ASG + ALB。三种做法都实测过:
| 推荐:ASG + ALB + One API 双渠道重试 | 过渡:香港侧 nginx LB sidecar + 手工维护的 2 台中继 | 应急:只用 One API 多渠道 | |
|---|---|---|---|
| 实例挂了 | ALB 16s 摘除;ASG 3~4 分钟自动换新(实测) | ALB 无,nginx 立刻换台;坏机器要人手重开并改 LB 配置 | 随机换渠道重试 |
| 挂掉瞬间的请求 | ALB 窗口内会 502,One API 换渠道重试兜住;实测 20 个请求 19 成功,唯一失败在终止后 2 秒内 | nginx 原请求重发,实测 10/10 | 实测 9/10 |
| 升级 | 实例刷新滚动替换,实测期间零失败 | 两台错开手工 pull/up | 手工 |
| 香港 One API 多台 | 全部指向同一 ALB 域名,无差别 | 每个节点各跑一个 sidecar,各自维护 upstream | 每个节点各配渠道 |
| 新增组件 | 启动模板、ASG、ALB、目标组、ACM 证书、2 个安全组 | 每个 One API 节点 1 个 nginx 容器 | 无 |
| 月成本(东京) | ≈ $47(2×t3.small)+ ≈ $20(ALB)+ 流量 | ≈ $47 + 流量 | ≈ $47 |
| 适合 | 生产 | 没有 ALB/ASG 权限或想先跑起来的阶段 | 临时 |
命令行(一条命令建完):
export HK_CIDRS="<香港节点1 IP>/32 <香港节点2 IP>/32" # 所有 One API 节点的出口 IP
export RELAY_GPT_KEY=sk-relay-gpt-xxx RELAY_CLAUDE_KEY=sk-relay-claude-xxx
REGION=ap-northeast-1 bash step3-高可用/create-asg-alb.sh
脚本顺序做:ALB 安全组(443 ← 香港)→ 中继安全组(80 ← ALB 安全组)→ 目标组(/healthz,5s×2)→ ALB(空闲超时 300s)→ 自签证书导入 ACM + HTTPS 监听器 → 启动模板(AMI、t3.small、角色、跃点限制 2、install-relay.sh 作用户数据)→ ASG(min 2 / max 4,跨所有默认子网,健康检查类型 ELB,宽限 180s)。最后打印 ALB 域名。
控制台(东京,按同样顺序):
1. EC2 → 安全组:建 bedrock-relay-alb-sg(入站 443 ← 各香港 IP/32);建 bedrock-relay-asg-sg(入站 80 ← 来源选安全组 bedrock-relay-alb-sg)。
2. EC2 → 目标组 → 创建:实例、HTTP 80、默认 VPC;健康检查 /healthz,高级:间隔 5、超时 3、阈值 2/2。不选任何实例(ASG 会自动注册)。
3. EC2 → 负载均衡器 → 创建 ALB:面向互联网、默认 VPC 勾全部可用区、安全组只选 bedrock-relay-alb-sg、监听器 HTTPS 443 → 上面的目标组、证书:有域名选 ACM 证书;没域名先建 HTTP 80 监听器,拿到 DNS 名后按 §0.1 ACM 行生成自签、ACM → 导入,再加 HTTPS 监听器并删 HTTP。属性 → 空闲超时 300。
4. EC2 → 启动模板 → 创建:名称 bedrock-relay-lt;AMI Ubuntu 24.04;t3.small;不选密钥对;安全组 bedrock-relay-asg-sg;存储 16G gp3;高级:IAM 实例配置文件 bedrock-relay-role、元数据 v2 仅令牌、跃点限制 2、用户数据粘 install-relay.sh(开头先写两行 export RELAY_GPT_KEY=... RELAY_CLAUDE_KEY=...)。
5. EC2 → Auto Scaling 组 → 创建:选上面的模板(版本 Latest);网络选默认 VPC 全部子网;负载均衡 → 附加到现有目标组;健康检查类型勾 ELB,宽限期 180;组大小 期望 2 / 最小 2 / 最大 4;标签 Project=bedrock-relay-poc 勾"传播到实例"。
6. 约 3 分钟后目标组两台 healthy。
香港每个 One API 节点:
cd /opt/oneapi
./add-relay-cert.sh <ALB域名> # 自签证书才需要;用正规证书跳过
./setup-channels.sh <ALB域名> <GPT钥匙> <Claude钥匙> alb-1
./setup-channels.sh <ALB域名> <GPT钥匙> <Claude钥匙> alb-2 # 同一 ALB 第二套渠道,给 One API 重试用
控制台等价:每个模型建两个一模一样的 OpenAI 渠道(只差名字),设置 → 运营设置 → 失败重试次数 = 3。
验证(实测记录):
- ./failover-test.sh 20 全 ✅。
- 崩溃演练:aws ec2 terminate-instances --instance-ids <一台中继>(或控制台终止),同时 ./failover-test.sh 20。实测时间线:T+0 终止;T+16s ALB 标 unhealthy;T+28s 目标组只剩一台;T+2m10s ASG 判定失联、拉新机;T+3m45s 新机 healthy。请求 19/20,One API 日志 11 次 using channel #x to retry。
- 升级演练:aws autoscaling start-instance-refresh --auto-scaling-group-name bedrock-relay-asg --preferences '{"MinHealthyPercentage":50,"InstanceWarmup":90}',期间连续跑 failover-test.sh。实测约 5 分钟两台全部换成新实例,期间 120 个请求 0 失败。
日常运维:
- 改中继配置 / 升级镜像:改 install-relay.sh → aws ec2 create-launch-template-version --launch-template-name bedrock-relay-lt --source-version 1 --launch-template-data '{"UserData":"<base64>"}' → 上面的 instance refresh。控制台:启动模板 → 操作 → 修改模板(创建新版本)→ ASG → 实例刷新。
- 换钥匙:同上发新版模板 + 刷新,再改 One API 渠道密钥。
- 扩容:改 ASG 期望容量即可,ALB 自动注册。
- 看状态:ASG 活动历史、目标组健康页;建议给 ALB UnHealthyHostCount ≥ 1 和 HTTPCode_ELB_5XX_Count 加 CloudWatch 告警发到邮箱/企微。
- 已知限制:ALB 摘除坏节点前约 10 秒窗口,靠 One API 双渠道重试兜;极端情况(重试 3 次都落坏节点)仍会返回 502,业务侧保留客户端重试。
适合还没拿到 ALB/ASG 权限、或只有两台手工维护中继的阶段。在每个 One API 节点旁边起一个 relay-lb nginx 容器(docker-compose.override.yml),upstream 写死中继 IP,proxy_next_upstream ... non_idempotent 让失败的 POST 立刻换台。One API 指向 http://relay-lb:8080/...,不再需要 SSL_CERT_FILE。
cd /opt/oneapi
./add-relay-cert.sh <中继A IP>; ./add-relay-cert.sh <中继B IP>
sudo bash install-hk-lb.sh <中继A IP> <中继B IP>
./setup-channels.sh http://relay-lb:8080 <GPT钥匙> <Claude钥匙> lb
实测:停掉中继 A 的 nginx,随即 10 个请求 10/10。缺点:中继换 IP/重建后要到每个节点改 upstream 重跑脚本;坏机器不会自动补。One API 若跑在 k8s,等价做法是一个 nginx Deployment + Service。
restart: always:实例重启后自动拉起,实测 reboot 后 55 秒三容器 Up。StatusCheckFailed_Instance → 重启实例 告警;系统盘坏了要人手按 3.2.1 重开。| 项 | 做法 |
|---|---|
| 中继入口 | 安全组仅放香港机 IP 的 443(方案 B 再放 ALB 安全组的 80);不开 22(用 Session Manager) |
| 中继鉴权 | GPT 路 nginx 校验 Authorization: Bearer RELAY_GPT_KEY;Claude 路 LiteLLM 校验 master_key。错钥匙 401(实测) |
| AWS 凭证 | 只有实例角色,权限最小化(Invoke + mantle:CreateInference);机器上没有 AK/SK |
| 传输 | 香港→中继 TLS(自签,One API/LB 校验);中继→Bedrock TLS |
| 换钥匙 | ASG 方案:发新版启动模板 + 实例刷新;单机:改 /opt/relay/.env → docker compose up -d。然后改 One API 渠道密钥 |
| 换证书/换 IP | 中继:gen-cert.sh + restart nginx;香港:add-relay-cert.sh <IP> 追加 |
| 升级 | ASG 方案:实例刷新(滚动,实测零失败);单机:cd /opt/relay && sudo docker compose pull && sudo docker compose up -d |
| 日志 | 中继 docker compose logs -f nginx|litellm;LiteLLM 默认不落盘对话内容 |
| 成本 | 中继 t3.small 东京 ≈ $22/月/台 + 16GB 盘 ≈ $1.5;两台 ≈ $47/月;ALB ≈ $20/月 + LCU;出站流量另计(回答文本量小) |
| 模型倍率 | One API 设置里给 openai.gpt-5.6-sol、claude-sonnet-4-5 等配倍率,否则日志报 model ratio not found 且按默认倍率计费 |
| 项 | 结果 |
|---|---|
| 新加坡 EC2 直连:GPT@mantle、Claude@Converse/Invoke、GPT@Converse、SSE | ✅ 全 200 |
| 香港 EC2 直连:GPT@mantle、GPT@Converse | ❌ 400 Access to OpenAI models is not allowed from unsupported countries… |
| 香港 EC2 直连:Claude@Converse、Claude@InvokeModel | ✅ 200 |
| 香港 Lambda(ap-east-1,出口 IP 43.199.34.64)复测(2026-09-16):GPT@mantle、GPT@runtime | ❌ 400 unsupported countries(两端点都挡) |
| 同上:Claude@runtime Converse、Claude@runtime Invoke | ✅ 200 |
同上:Claude@mantle /anthropic/v1/messages(短 ID anthropic.claude-opus-4-8) |
✅ 200 —— 证明地域门按模型判、不按端点判;⚠️ Claude 从香港能用属意外,勿依赖 |
| mantle Claude 的模型 ID 形式(us-east-1 与香港均测):短 ID vs runtime 长版本 ID | 短 ID ✅ 200;长版本 ID ❌ 404(这是此前误判"Claude 不在 mantle"的根因) |
Claude 走 mantle 的 OpenAI 面 /v1/chat/completions |
❌ 400 does not support the '/v1/chat/completions' API(Claude 在 mantle 只吃 /anthropic/v1/messages) |
bedrock-mantle .../v1/models(GET,SigV4)列模型 |
✅ 200,含 6 个 Claude(opus-4-7/4-8/5、sonnet-5、haiku-4-5、fable-5);需 bedrock-mantle:ListModels 权限 |
中继角色只给 bedrock:Invoke* 时 GPT@mantle |
❌ 403 not authorized to perform: bedrock-mantle:CreateInference → 补权限后 ✅ |
| 中继一键脚本(用户数据)→ 三容器就绪 | ✅ 76~90 秒(3 台:relay-a/b、以及用最终版脚本从零重建的 relay-c) |
| 中继自检:healthz / GPT / Claude / 错钥匙 401 | ✅ 3 台全部通过 |
| 香港 One API → 中继 → Bedrock(GPT、gpt-5.6 重定向、Claude、gpt-5.6-converse) | ✅ 全 200,0.9~1.9s |
| 上述两条流式 SSE 经 One API + 中继 | ✅ 逐块到达 |
GPT 发 max_tokens |
❌ 400 unsupported_parameter(与 OpenAI 官方一致)→ 用 max_completion_tokens ✅ |
| 模型重定向别名不在模型列表 | ❌ 503 无可用渠道 → 加进列表 ✅ |
| 方案 C(4 渠道 + RetryTimes=3),停中继 A 后 10 请求 | 9/10(1 次三连抽到坏渠道) |
| 方案 B(ALB,健康检查 10s×2),停中继 A 后 | 发现前 502(16 请求中 7 失败,全部落在坏节点),发现后 ✅ |
方案 B ALB 健康检查打 80 /healthz |
初次 ❌ ResponseCodeMismatch(nginx 镜像默认站点抢 80)→ 覆盖 default.conf 后 ✅ healthy |
| 方案 B 经 ALB 的 SSE | ✅ |
| 方案 A(香港 nginx LB),停中继 A 后立刻 10 请求 + SSE | ✅ 10/10,0 失败;LB 日志 upstream server temporarily disabled |
中继 B reboot |
✅ 55 秒后三容器 Up、/healthz ok、ALB healthy |
ASG + ALB 崩溃演练:terminate-instances 一台中继 |
ALB T+16s unhealthy、T+28s 摘除;ASG T+2m10s 拉新机、T+3m45s healthy;期间 20 请求 19 成功(唯一失败在 T+2s),One API 换渠道重试 11 次 |
| ASG 实例刷新(滚动替换 2 台,MinHealthy 50%,warmup 90s) | 约 5 分钟换完两台(新实例 ID);期间 6 轮 × 20 = 120 个请求 0 失败 |
| One API 信任自签证书(SSL_CERT_FILE) | ✅(不配则 TLS 握手失败,源码无 InsecureSkipVerify) |
curl 8.5(Ubuntu 24.04 自带)--aws-sigv4 对含 : 的模型 ID |
❌ 签名不匹配(curl 8.18/8.21 正常)→ 改用 python 版测试脚本 |
deliverables/)Bedrock中继方案-修正版-2026-09-15.md ← 本文
step1-账号权限测试/
bedrock-test.py 第一步:只需 python3,自动取实例角色,SigV4 签名,✅/❌ 判读
step2-中继机/
install-relay.sh 中继一键安装(可直接作 EC2 用户数据);顶部可预设两把钥匙
配置参考/docker-compose.yml | relay.conf.template | litellm.yaml | gen-cert.sh | selftest.sh
step2-香港OneAPI机/
install-oneapi.sh 全新装 One API(含 SSL_CERT_FILE、RELAY_TIMEOUT、管理令牌)
add-relay-cert.sh <中继IP或ALB域名> 抓中继证书加入信任并重启 One API
setup-channels.sh <中继地址> <GPT钥匙> <Claude钥匙> [后缀] 用 API 建两个渠道 + 重试次数 + 测试令牌
e2e-test.sh 端到端验证(4 非流式 + 2 流式)
docker-compose.yml.参考
step3-高可用/
install-hk-lb.sh <中继IP...> 过渡方案:One API 节点旁起 nginx LB sidecar(docker-compose.override.yml)
create-asg-alb.sh 推荐方案:一键建 ALB/目标组/证书/启动模板/Auto Scaling Group
failover-test.sh [N] 连发 N 个请求统计成功率
cli/
bedrock-invoke-policy.json 中继角色的 IAM 策略(含 bedrock-mantle:CreateInference)
01-create-iam-role.sh 建角色 + 实例配置文件
02-launch-relay.sh 启动一台中继(安全组、AMI、hop limit、用户数据全自动)
03-launch-oneapi-hk.sh (可选)启动香港 PoC One API 机
99-cleanup.sh 按标签 Project=bedrock-relay-poc 删光所有测试资源(含 ASG/启动模板/ALB)
songquanpeng/one-api 最新版 justsong/one-api:latest 验证;new-api 等分支界面字段名略有不同,但"OpenAI 类型渠道 + base_url + 密钥"的逻辑一致)。add-relay-cert.sh 这一步。max_tokens → max_completion_tokens;不能则 GPT 走 gpt-5.6-converse。