多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

大模型网关公网接入踩坑:用OCI免费L7负载均衡器解决超时与端口难题

大模型网关公网接入踩坑:用OCI免费L7负载均衡器解决超时与端口难题 最近在搭大模型网关把鉴权、限流、路由这些核心功能都跑通之后本以为把服务往公网一挂就能收工结果被“域名、端口、超时”这三件事按在地上反复摩擦。网关跑在本机时一切正常Streaming SSE 输出流畅前端打字机效果赏心悦目一旦套上 Cloudflare 免费 CDN100 秒超时准时掐断长连接改用非标准端口直连又撞上防火墙和证书问题最后我把目光放到 OCI 的免费 L7 云负载均衡器上才算真正破局。这篇文章就把这一路的踩坑、折中方案和最终架构完整记录下来。不整虚的直接讲我试过的方案、踩过的坑、改过的配置给同样正在做大模型网关的人一个可以直接抄作业的参考。1. 先理清楚大模型网关到底卡在哪1.1 网关的超时敏感点在哪里大模型网关不是普通 Web 服务。它做的事是接收客户端的对话请求完成身份校验、API Key 鉴权、上下文组装然后把请求转给后端模型服务自己部署的推理引擎或者云厂商模型接口再把模型生成的 Token 流式地推回给客户端。这个链路里最敏感的就是那一根流式连接。关键点在于 SSE 长连接。客户端打开一个 HTTP 连接后服务端可以持续推送多个事件常见实现是 OpenAI 兼容格式的/v1/chat/completions端点返回text/event-stream。这种连接的生命周期可能从几百毫秒到几十秒甚至几分钟。普通 Web 后端对请求-响应时间没那么敏感——页面 3 秒打不开用户就走人但大模型网关如果 60 秒没返回任何字节客户端可能直接报超时。还有“首字延迟”和“Token 间静默”这两个隐藏杀手。模型在读题、思考的时候流里可能没有任何字节。深度推理类模型在思考阶段不发第一个 Token 是常有的事有时候思考几十秒才开始输出。此时连接是“活”的TCP 已建立但没有应用层数据。中间任何一层转发组件如果按“空闲超时”来管理连接就会把这根连接误判成死连接直接掐掉。这就是为什么大模型网关的超时配置和普通 API 服务完全不是一回事。1.2 域名、端口和超时是怎么纠缠在一起的生产环境必然要域名和证书。于是你必须在入口处做一个“终止 TLS、转发流量”的组件。常见的三个选择云厂商的负载均衡器/网关如 OCI LB、ALB自建 Nginx/OpenResty 反向代理CDN如 CloudflareCDN 看着最方便免费、自带防护、证书自助、还能在网页上点点点。但它有一个大模型网关最不需要的特性——缓存以及一个最致命的限制——100 秒超时。这个限制不是 Nginx 的 60s 那种可配置超时而是 CDN 边缘节点对源站的请求超时策略用户改不了。端口的问题随之而来Cloudflare 免费版只代理白名单端口443 之外想开新端口受限而源站直接裸奔高位端口又暴露源站 IP证书也要自己搞。域名、端口、超时三件事一旦纠缠在一起就成了“牵一发动全身”的难题。改超时要动入口组件换端口要动客户端和证书改域名解析又要考虑是否绕开 CDN 防护。每一步都有连锁反应这正是很多人卡壳的原因。1.3 破局思路从 CDN 思维切到 LB 思维我最后想明白一件事大模型网关的业务模型决定了它不需要 CDN。CDN 存在的价值是“边缘缓存 就近分发”而大模型的流式响应是动态生成、一一对应、无法复用的缓存命中率趋近于 0。你真正需要的是一个稳定、低延迟、不截断长连接的公网入口这正是负载均衡器Load Balancer的本职工作。想通这一点之后方案就清晰了把 Cloudflare 从“代理模式”降级为“DNS-only”让流量直连 OCI 免费 L7 负载均衡器TLS 证书在 LB 上终结LB 后端挂内部的大模型网关节点。没有 CDN 的 100 秒限制端口不再依赖 Cloudflare 白名单超时参数完全由自己掌控。这个思路看起来简单但如果你一直陷在“怎么调 Cloudflare 配置”的思维里是根本转不过来的。2. Cloudflare 的 100 秒限制与端口白名单2.1 100 秒限制是怎么发生的先说现象。我当时的架构是 Cloudflare 代理橙色云→ 源站 Nginx → 大模型网关。本地用 curl 调 SSE 接口40 秒内能正常收到 Token一旦流量走到 Cloudflare 边缘只要源站在 100 秒内没有输出任何数据连接就会被掐断。客户端表现为HTTP 响应头已经返回Content-Type: text/event-stream但字节流卡住过一会儿客户端报Error: 524。524 是 Cloudflare 的“源站超时”状态码意思是“Cloudflare 边缘已经连上了源站也在等待响应但是源站迟迟没有给完整的响应头或数据最终触发了超时”。而 522 是“边缘连不上源站”523 是“源站端口拒绝连接”520 是“源站返回了 Cloudflare 无法解析的响应”。很多人看到 524 第一反应是查 Nginx 的proxy_read_timeout其实上游压根没到 Nginx 那层是 Cloudflare 边缘主动发的错误。这个区分很重要排查方向错的话能折腾一整天。注意一个细节100 秒超时针对的是“边缘到源站之间抓取完整响应的等待时间”。SSE 场景下如果边缘已经收到了event-stream响应头、正在持续接收数据只要两次数据之间的间隔没超过 100 秒流理论上不会被断问题出在模型思考阶段的“静默窗口”。但实测中即使没有静默窗口只要响应总时长接近 100 秒也会被拦——因为很多实现是按整个响应耗时算的而不是按空闲时长算。所以我对待大模型网关的态度是不要赌“刚好 99 秒”这种边界条件直接把依赖换掉。2.2 端口白名单与端口折中的真实体验Cloudflare 免费版代理的端口不是随便开的只放行一个固定名单。官方长期以来支持的端口大致如下协议支持端口HTTP80、8080、8880、2052、2082、2086、2095HTTPS443、2053、2083、2087、2096、8443表格里的 2053、2083、2087、2096、8443 就是大家口口相传的“可以用 HTTPS 走非标准端口”的来源。但问题是这些端口在 Cloudflare 边缘依然受 100 秒超时规则约束它们只是端口号不同代理逻辑完全一样。换句话说把服务从 443 搬到 8443解决不了超时问题。那“端口折中”到底是什么我在实战中看到的做法有两种。第一种是“流量分流”普通短请求模型列表、额度查询、健康检查走 Cloudflare 代理的 443流式对话请求用 DNS-only 灰云直连源站的某个高位端口比如 7443两边共存。第二种是“全量灰云”把所有域名记录都改成 DNS-only相当于 Cloudflare 只做 DNS 解析流量直连源站。这两种都能绕开 100 秒限制但都属于治标不治本后面我会讲它们各自埋了哪些雷。2.3 折中方案翻车的三个典型坑第一个坑是“高位端口被运营商或企业防火墙拦截”。你以为用了 7443 就万事大吉但很多企业网络、云服务商的安全策略默认只放行 443/80高位端口在用户侧直接连不通。我试过把网关放到 8443结果自己从某办公网络测试时连接直接 timeout最后排查半天发现是出站防火墙。对大模型网关来说客户端环境不可控443 是最稳的选择次之是 8443 这类还算常见的 HTTPS 端口。第二个坑是“灰云直连等于裸奔”。Cloudflare 橙色云至少还能帮你挡一部分扫描和 CC 流量切成灰云后源站 IP 直接暴露在公网扫描、爆破、DDoS 都会找上门。大模型网关通常还连着内部数据库和模型推理服务一旦被打穿影响面比普通站点大得多。你省下的那点 CDN 配置成本可能还不够一次故障修复的时间成本。第三个坑是“证书和 IP 变动”。直连模式下证书要么每三个月手动续一次 Lets Encrypt要么迁移到付费证书而源站如果是动态 IP灰云模式下 DNS 记录的 TTL 变化也会带来明显的中断窗口。CDN 模式下这些都由 Cloudflare 消化了只要切回直连所有运维负担全回到自己身上。我当时被证书续期和 IP 变更折腾了两次之后彻底放弃了这个方向。2.4 Cloudflare Tunnel 在超时问题上帮不上忙顺便说一句 Tunnel。很多人被介绍用cloudflared把内网服务暴露出来以为可以绕过端口限制——Tunnel 确实不需要源站开放入站端口但它仍然把流量接入 Cloudflare 边缘的 HTTP 代理免费版同样受 100 秒超时约束。也就是说Tunnel 解决的是“没有公网 IP / 不想暴露源站”的问题解决不了“长连接被截断”的问题。我在排查cloudflared的时候还经常遇到两类问题容器部署时环境变量没传对cloudflared报Unable to reach the origin service或者 Tunnel 创建后 DNS 记录迟迟不生效curl 直接 connection timeout。这类问题本质是配置链路问题和网关本身的超时配置没关系别混在一起查。如果你已经在用 Tunnel最好先确认一下免费版的超时限制再决定要不要拿它承载大模型流式接口。3. 换个思路免费 L7 负载均衡器为什么能破局3.1 OCI Always Free 免费额度盘点Oracle Cloud Infrastructure 的 Always Free 资源是我目前见过对个人开发者最友好的免费套餐之一。它包含两台 1/8 OCPU 1GB 内存的 AMD 小机、合计 4 OCPU 24GB 内存的 Ampere ARM 实例、200GB 块存储、每月 10TB 出站流量还有一个 L7 负载均衡器固定 10 Mbps 带宽。10Mbps 对大模型网关的入口带宽来说确实不大但通常够用——HTTP 协议层模型传输是文本 JSON一个 Token 也就几个字节实测跑 100 并发以内完全没问题。关键是这个 L7 负载均衡器完全免费没有隐藏的流量计费出站流量走的是 10TB/月的额度。我一开始也担心“免费的东西是不是很弱”实际用下来发现它具备生产级 LB 的基本能力SSL 证书终结、HTTP/HTTPS 监听器、后端健康检查、会话保持、负载均衡策略、空闲超时设置。对个人项目和小团队来说它比自建 Nginx 多活方案的运维成本低得多比 Cloudflare 代理又灵活得多。提示创建资源时务必看清楚Always Free Eligible标签。OCI 控制台里很多组件会同时显示收费版本手滑选了 Flex 100 Mbps 的 LB月底账单会教做人。创建完也可以到费用中心复核一遍确保所有资源都是免费额度内。3.2 L7 负载均衡器和 CDN 的区别搞清楚 L7 LB 和 CDN 的差异才能理解为什么它适合大模型网关。CDN 是“缓存 分布式边缘节点 代理”L7 LB 是“流量分发 TLS 终结 健康检查”通常部署在数据中心内部或云区域入口。两者的设计目标是不同的。CDN 适合“可缓存、只读、接近用户的静态资源”L7 LB 适合“动态生成、长连接、需要稳定回源路径”的业务。大模型网关的响应内容对每个用户都不一样缓存毫无价值反而因为多了一级代理多了一层超时约束。L7 LB 则是标准的七层网关连接从建立到关闭都在后台掌控只要配置合理就不会主动掐断长连接。我把两者的对比整理成表方便你做选型判断维度CDNCloudflare 代理L7 负载均衡器OCI LB核心能力边缘缓存、就近分发、DDoS 防护流量分发、TLS 终结、健康检查对动态流式响应缓存命中率 0徒增链路天然支持长连接不会截断超时策略免费版 100 秒写死空闲超时/请求超时可控端口白名单限制监听器端口自定义走 443 即可适用场景静态资源、站点加速API 网关、流式接口、长连接这个认知非常重要。很多人在选型时被“CDN 免费、还有防护”吸引没有意识到自己的业务和 CDN 的核心价值完全错配。我个人的看法是大模型网关这类业务入口组件选择的第一优先级应该是“连接可管理和可预测”而不是“缓存能力强”。L7 LB 从设计目标上就是为动态流量而生的。3.3 超时时间为什么完全由自己掌控Cloudflare 免费版的 100 秒限制是写死在边缘节点上的用户没有任何调整入口。有人总想靠换边缘节点来“优化”但超时规则在每个边缘节点上都生效换哪个节点都绕不开。OCI L7 LB 则把超时策略完整暴露给了用户监听器层面可以设置空闲超时Idle Timeout和请求超时后端集层面可以设置健康检查超时。也就是说你可以根据自己的业务特征决定“一条连接空闲多久才判死”。大模型网关最怕的不是数据流速慢而是上游思考时的静默。只要模型在持续输出连接就不会空闲而即使是思考停顿LB 默认的 300 秒空闲超时也比 Cloudflare 的 100 秒宽裕得多。实测下来我把空闲超时保持默认SSE 长连接从建立到结束持续 3~5 分钟完全没有出现被中间层掐断的情况。如果你跑的是特别长的多轮 Agent 任务可以把空闲超时继续放大把它当作业务参数而不是平台限制来调。另外L7 LB 天然支持 WebSocket 升级。如果你的网关同时暴露了 WebSocket 接口很多新客户端为了双向通信会优先走 WSLB 会正确转发Upgrade: websocket头而 Cloudflare 免费版对 WebSocket 又有额外的空闲限制。这一点也是选 LB 而不是 CDN 的加分项。4. 实操OCI 免费 L7 LB 搭建大模型网关入口4.1 前置创建 VCN 和安全列表在 OCI 控制台里先创建 VCN规划一个公网子网用于放 LB一个私网子网用于放后端网关节点。最简单的做法是LB 放公网子网后端 VM 放私网子网。两张子网之间通过安全列表Security List控制流量。安全列表的规则非常重要常见错误是“创建完 LB 一切正常但请求一直 502/504”最后发现是漏了一条允许 LB 所在子网的流量访问后端 VM 的 8080 端口。我建议至少配置两条规则公网子网入站允许 443 (TCP) 来自0.0.0.0/0用于客户端访问私网子网入站允许 8080 (TCP) 来自“公网子网 CIDR”或者 LB 的网络安全组 NSG用于 LB 健康检查和流量转发。后端 VM 不要暴露任何公网端口。安全列表生效有短暂的传播延迟改完规则后稍等半分钟再测试别改完立刻断言“没生效”。4.2 创建负载均衡器监听器、后端集、健康检查进入 OCI 控制台Networking → Load Balancer → Create Load Balancer。关键选择如下类型选Layer 7也就是 HTTP/HTTPS 负载均衡器Visibility 选Public并分配一个保留公网 IPReserved Public IP这样做 A 记录时 IP 不会变Bandwidth Shape 选Micro 10 MbpsAlways Free放在公网子网里。创建成功后进入 LB 详情页先配置Backend Set后端协议选 HTTP端口 8080负载均衡策略用加权轮询单节点时权重无意义日后扩两个节点就体现出来了。健康检查策略写GET /healthz端口 8080期望返回 200。健康检查超时设 3 秒、间隔 10 秒、重试 3 次这样网关挂了 30 秒内会被自动摘除流量不会打到坏节点上。然后配置Listener协议 HTTPS端口 443选择之前上传的服务证书见 4.3。如果有 WebSocket 需求确认升级策略保持开启或者在后端 Nginx 阶段处理Upgrade和Connection头。4.3 接入域名与 HTTPS 证书域名解析有两种做法。其一直接在 DNS 服务商那里把api.example.com加一条 A 记录指向 LB 的公网保留 IP其二如果你还想保留 Cloudflare 做 DNS把记录设为DNS-only灰云也就是不启用橙色代理。灰云模式下 Cloudflare 只做解析流量直连 LB因此不会再有 100 秒限制。我最终选择的是第二种既保留了 Cloudflare 的 DNS 管理和免费证书签发能力又避开了边缘代理的超时。证书方面最简单的方式是让 LB 做 TLS 终结把域名证书PEM 格式含私钥上传到 OCI Load Balancer 的证书管理HTTPS 监听器引用它。证书过期前记得续期替换。如果懒得手动续可以用 OCI 的 Certificates 服务配置自动续期或者干脆在 LB 上用 HTTP 监听器 后端 Nginx 做内部 TLS客户端这边仍然由 LB 终结证书刷新不用动后端。我个人的经验是证书生命周期管理放在 LB 层后端网关完全不用感知 TLS。后续换证书只需在 OCI 控制台换一次不用登录服务器改 Nginx也不用重启网关容器对服务可用性影响最小。4.4 后端网关接入 LB 并验证后端 VM 上大模型网关比如一个跑在 8080 端口的容器服务监听内网地址即可。如果用 Docker注意把端口 bind 到0.0.0.0:8080而不是127.0.0.1否则 LB 的健康检查会失败但也不要 bind 到公网网卡配合安全列表保证只有 LB 能访问。验证链路时可以在后端加一个简单的GET /healthz接口返回 JSON先本地 curl 确认再看 LB 的后端集页面是否显示健康Backend Set 的 backend 状态变绿。然后本地 curl 走公网域名测试curl -N https://api.example.com/v1/chat/completions-N会禁用 curl 的缓冲让 SSE 流数据实时打印。如果这一步正常再模拟一个长流式请求观察 100 秒、200 秒、300 秒时连接是否还在。实测下来只要客户端和后端都配置正确LB 不会主动掐连接。最后再提醒一句验证时记得看后端 access log 和 LB 访问日志确认流量真的经过 LB 而不是直连后端。我踩过把 curl 打到后端内网 IP 上测试结果全链路没问题但一旦走公网域名就 504 的坑。提示OCI 的 LB 本身也有请求处理上限10 Mbps 免费带宽不要硬扛大流量压测。如果后面并发上来了优先考虑扩容到更高 bandwidth shape 或换 ARM 大机做后端而不是一味调大超时时间。4.5 常见错误502/504 的排查顺序我在搭的过程中至少遇到三回 502/504排查经验按优先顺序排一下第一看 LB 后端集是否健康不健康说明安全列表没放行或者健康检查路径不对第二看 LB 到后端的网络连通性可以在后端 VM 上用tcpdump抓包确认是否收到健康检查请求第三看后端服务是否真的在监听内网端口ss -lntp检查 8080第四看监听器协议和后端协议是否匹配比如监听器是 HTTPS后端是 HTTP中间证书转发没问题。按照这个顺序基本半小时内能定位比瞎改配置快得多。5. 常见问题与排查技巧实录5.1 超时现象速查表把云上大模型网关最常见的超时现象、状态码和排查方向整理成一张表遇到问题时先对号入座现象典型状态码可能原因排查方向边缘提示源站超时524CDN 等待源站响应超过 100 秒检查模型推理是否卡住换 L7 LB 避免 CDN 限制边缘连不上源站522源站防火墙封了 CDN IP 段放行 CDN IP 段或改用 LB 直连源站端口拒绝连接523源站未监听对应端口检查ss -lntp、安全列表请求一直 502/504502 / 504LB 到后端不通或后端无响应超时优先看健康检查状态再查安全列表客户端报“信号灯超时时间已到”无TCP 层超时TLS 握手阶段丢包/MTU 异常检查 MTU、路由链路换大包测试客户端 git clone 报failed to connect ... port 443: 连接超时无目标仓库链路不好或本地网络限制换镜像源/设置镜像变量别硬等SSE 流 60 秒后断无Nginx/网关响应超时设置过短调大proxy_read_timeout/ LB 空闲超时这张表里524/522/523 是 CDN 专属错误码502/504 是通用网关错误。很多人把 524 当成 Nginx 超时去改配置改了半天没用——Nginx 返回超时是 504不是 524。分清错误码来源是排查效率的分水岭。5.2 几个“看似无关”的超时根因搜索热词里有一堆看似八竿子打不着的超时问题mtu过大导致tls超时、linux 127秒超时、cannot connect to host huggingface.co:443 ssl:default [信号灯超时时间已到]。它们其实有一个共同点超时不等于服务端卡死很多时候是网络路径上的某个环节在丢包。举几个例子。MTU 导致的 TLS 超时TLS 握手阶段需要交换证书消息数据量可以到几千字节。如果路径 MTU 设置过大比如 1500 但实际链路不支持这么大的分片大包会被静默丢弃或分片后重组失败表现为“连接看起来建立了但一直读不到证书直到超时”。排查方法是用ping -M do -s 1472测各种包大小找到能通过的 MTU 阈值然后调整网络接口或隧道的 MSS。这个坑在容器网络、云 VPC 里尤其常见。Linux 的“127 秒超时”TCP 底层 SYN 重传的默认策略会导致客户端在无法连接目标时大约 127 秒后才返回失败。这本质上不是应用层超时而是内核在按指数退避重试连接。如果你希望应用“快速失败”可以调小net.ipv4.tcp_syn_retries或设置连接超时反过来如果不想在弱网环境下频繁断连就保持默认。大模型网关的客户端 SDK 大多有自己的 connect timeout一般设 30~60 秒足够别依赖内核的 127 秒兜底。一些海外代码仓库、模型下载站点经常出现在超时热搜里本质都是链路或 DNS 解析问题。通用的处理方式是先用dig或nslookup确认 DNS 解析是否正常再测 TCP 连接的握手耗时如果链路确实不稳定换区域更近的镜像源或下载端点而不是反复重试。游戏的更新器响应超时也是同理——不是更新服务器坏了是链路波动换个时段或换镜像通常能解决。5.3 超时参数设计的通用套路超时问题看起来复杂剥开来看就是三段话等待多久才放弃、谁负责等待、失败后怎么处理。大模型网关的客户端 SDK、网关层、LB、后端模型服务各有各的超时参数四者之间如果设置不当就会形成“上级超时小于下级超时”的连锁截断。我建议按这个顺序设置客户端 connect timeout 设 2~5 秒read timeout 设 120 秒以上SSE 场景甚至可以设 300 秒不要让客户端在连接阶段干等网关层把proxy_read_timeout或 LB 的空闲超时设得比客户端 read timeout 长一些因为中间层不能比两端更早放弃后端模型服务则关心推理最大时长超时后返回 507 或 408并保证日志里能分辨是“用户取消”还是“模型超时”。精神内核是超时要分级不能一刀切。连接建立阶段可以快速失败读数据阶段必须宽容真正到了推理执行阶段才谈业务超时。这个原则不止适用于大模型网关普通 Web 后端、数据库连接池remove-abandoned这类参数、甚至桌面端弹窗自动关闭设计逻辑都是同一个——先分阶段再配时间。5.4 我踩过的坑和最终体会写到最后分享几条最实用的经验。第一别迷信 CDN。大模型网关的动态流式业务和 CDN 能力错配Cloudflare 免费版的 100 秒限制迟早会坑你一次。我的体会是入口选型先判断“业务是否需要缓存”需要缓存才考虑 CDN需要长连接稳定转发L7 LB 才是正解。第二端口折中只能当应急方案。把服务挂到 8443/2053 这类端口上看似简单实际是在和客户端网络环境赌运气更稳妥的做法是让所有流量都走 443把端口问题交给负载均衡器和证书体系去解决。OCI 免费 L7 LB 就是那个既能让你走 443、又不限制长连接时间的选择。第三超时排查一定要先分清“谁发的错误码”。CDN 的 520/522/523/524、网关的 502/504、内核的 TCP 超时、应用层的 read timeout来源完全不一样。排查顺序永远是先确认错误码来自哪一层再看对应的日志和抓包最后才动配置。我见过太多人一上来就调 Nginx 的proxy_read_timeout结果问题根本不在那。这个架构跑了一段时间之后最大的感受是Cloudflare 留给我的身份从“流量入口”降级成了“DNS 管理面板”OCI L7 LB 则承担了真正的入口职责。整个链路变成用户 - Cloudflare DNS灰云- OCI L7 LB443 终止证书- 后端网关8080 内网- 大模型推理服务。没有 100 秒限制没有端口白名单超时参数都在自己手里SSE 流想保持多久保持多久。如果你现在正在被同样的三件套折磨建议照着这个思路重搭一遍入口大概率能少走我这几周的弯路。
返回列表