Nginx负载均衡:为多个大模型实例分配请求

admin 2026-07-19 04:30:27 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文详细介绍了使用Nginx为多个大模型实例配置负载均衡的要点与最佳实践。核心结论是:大模型负载均衡不能简单套用普通Web短请求配置,必须考虑长连接、流式响应、分钟级推理超时、实例显存差异及故障摘除等特殊场景。关键发现包括:需确保后端实例在模型名、API结构、上下文限制等方面完全等价;推荐使用leastconn算法;必须关闭proxybuffering和gzip以支持流式响应;重试需谨慎避免POST请求重复执行。可操作建议是:使用文中提供的upstream和server配置模板,并根据实际环境调整超时参数和健康检查策略。 综合评分: 88 文章分类: 解决方案,实战经验,WEB安全,安全建设,其他


cover_image

Nginx 负载均衡:为多个大模型实例分配请求

点击关注👉 点击关注👉

马哥Linux运维

2026年7月18日 18:00 广东

在小说阅读器读本章

去阅读

Nginx 负载均衡:为多个大模型实例分配请求

单个大模型实例完成接口验证后,下一步通常是启动多个实例,再由统一入口分配请求。表面上这只是给 Nginx 写一个 upstream,实际却涉及长连接、流式响应、分钟级推理超时、实例显存差异、在途请求排空、重试安全和故障摘除。如果仍按普通 Web 短请求配置,很容易出现首 token 正常但中途断流、代理缓存导致“假流式”、故障实例反复接单或 POST 请求被错误重放。

本文以 Nginx 开源版、三个 OpenAI 兼容大模型实例为例:10.20.0.11:800010.20.0.12:800010.20.0.13:8000。占位符统一写成 <变量名>,例如 <API域名><证书路径> 和 <配置备份目录> 必须替换成实际值。后端可以是 SGLang、vLLM 或其他兼容服务,但同一个 upstream 中的模型名、聊天模板、上下文限制和 API 行为必须一致。

一、先判断什么可以均衡

负载均衡器只能在“等价后端”之间安全分流。若三个实例加载了不同权重、不同量化精度或不同聊天模板,即使都返回 HTTP 200,输出行为也可能不同。上线前应核对以下信息:

| 检查项 | 应满足的条件 | 不一致的后果 | | — | — | — | | 模型逻辑名 | /v1/models 返回一致 | 客户端随机报模型不存在 | | API 结构 | 状态码、SSE 格式一致 | SDK 间歇性解析失败 | | 上下文和输出上限 | 同一业务池一致 | 长请求随机成功或失败 | | 聊天模板 | 同一版本 | 输出风格和能力漂移 | | GPU 容量 | 明确是否同构 | 等权分配可能让小实例过载 | | 超时策略 | 服务端、Nginx、客户端协调 | 中间层提前断开 |

先逐个直连后端,不经过 Nginx。下面脚本检查 TCP、健康接口和模型列表,并把失败节点明确列出;/health 是否存在应以实际推理框架为准。

bash

#!/usr/bin/env bash
set&nbsp;-euo pipefail

BACKENDS=(
&nbsp;&nbsp;"http://10.20.0.11:8000"
&nbsp;&nbsp;"http://10.20.0.12:8000"
&nbsp;&nbsp;"http://10.20.0.13:8000"
)

for&nbsp;backend&nbsp;in&nbsp;"${BACKENDS[@]}";&nbsp;do
&nbsp;&nbsp;echo&nbsp;"Checking&nbsp;${backend}"
&nbsp; curl -fsS --connect-timeout 2 --max-time 5&nbsp;"${backend}/health"&nbsp;>/dev/null
&nbsp; curl -fsS --connect-timeout 2 --max-time 10&nbsp;"${backend}/v1/models"&nbsp;| jq -r&nbsp;'.data[].id'
done

三个节点返回模型名一致只是必要条件,还应发送同一条低随机性请求确认响应结构。不要把输出文本完全一致作为判定条件,因为浮点计算、并行策略和采样实现仍可能带来差异。

bash

for&nbsp;host&nbsp;in&nbsp;10.20.0.11 10.20.0.12 10.20.0.13;&nbsp;do
&nbsp; curl -fsS&nbsp;"http://${host}:8000/v1/chat/completions"&nbsp;\
&nbsp; &nbsp; -H&nbsp;'Content-Type: application/json'&nbsp;\
&nbsp; &nbsp; -d&nbsp;'{
&nbsp; &nbsp; &nbsp; "model":"<模型名>",
&nbsp; &nbsp; &nbsp; "messages":[{"role":"user","content":"只回复 ready"}],
&nbsp; &nbsp; &nbsp; "temperature":0,
&nbsp; &nbsp; &nbsp; "max_tokens":16,
&nbsp; &nbsp; &nbsp; "stream":false
&nbsp; &nbsp; }'&nbsp;| jq -c&nbsp;'{model,finish_reason:.choices[0].finish_reason,usage}'
done

二、检查 Nginx 版本与编译能力

不同发行版打包的 Nginx 模块和默认路径可能不同。部署前记录版本、编译参数和加载中的主配置,尤其要确认 TLS、真实 IP 或状态模块是否存在。

bash

nginx -v
nginx -V 2>&1 |&nbsp;tr&nbsp;' '&nbsp;'\n'&nbsp;|&nbsp;sort
sudo&nbsp;nginx -T > /tmp/nginx-effective-config.txt

nginx -T 会输出完整配置,可能包含内部域名、证书路径或认证信息,文件应按敏感配置处理。不要把未经脱敏的输出粘贴到公开工单。

三、选择分配算法

Nginx 开源版常用算法有轮询、least_connip_hash 和带 consistent 的 hash。大模型请求耗时差异很大,一个请求可能 2 秒,也可能持续数分钟,因此简单轮询只按请求数量分配,无法反映已有长请求。least_conn 通常更适合同构推理实例:新请求优先发给当前活动连接较少的节点。

least_conn 仍不了解 token 数、KV Cache 占用或排队长度。一个“只有一个连接”的超长上下文请求可能比多个短请求更重,所以它是可用基线,不是最优调度器。若业务要求会话级缓存亲和,可以按可信的会话头做一致性哈希,但会降低故障切换时的缓存命中稳定性,也可能造成热点。

下面配置建立一个同构模型池。max_fails=2 fail_timeout=20s 是被动故障判定:在失败窗口达到条件后,节点会暂时不可用。Nginx 开源版本身不提供商业版那种内置主动健康检查。

nginx

upstream&nbsp;llm_backend {
&nbsp; &nbsp; least_conn;
&nbsp; &nbsp;&nbsp;zone&nbsp;llm_backend&nbsp;64k;

&nbsp; &nbsp;&nbsp;server&nbsp;10.20.0.11:8000&nbsp;max_fails=2&nbsp;fail_timeout=20s;
&nbsp; &nbsp;&nbsp;server&nbsp;10.20.0.12:8000&nbsp;max_fails=2&nbsp;fail_timeout=20s;
&nbsp; &nbsp;&nbsp;server&nbsp;10.20.0.13:8000&nbsp;max_fails=2&nbsp;fail_timeout=20s;

&nbsp; &nbsp;&nbsp;keepalive&nbsp;64;
}

zone 让 worker 共享 upstream 运行状态,适合多 worker 场景。keepalive 64 是每个 worker 缓存的空闲上游连接上限,不是全局并发限制,也不是业务请求上限。

异构实例可以设置权重,但权重应来自同请求形态的容量测试,而不是按显存大小简单换算。例如强实例权重 2、弱实例权重 1:

nginx

upstream&nbsp;llm_backend_weighted {
&nbsp; &nbsp; least_conn;
&nbsp; &nbsp;&nbsp;zone&nbsp;llm_backend_weighted&nbsp;64k;

&nbsp; &nbsp;&nbsp;server&nbsp;10.20.0.21:8000&nbsp;weight=2&nbsp;max_fails=2&nbsp;fail_timeout=20s;
&nbsp; &nbsp;&nbsp;server&nbsp;10.20.0.22:8000&nbsp;weight=1&nbsp;max_fails=2&nbsp;fail_timeout=20s;
&nbsp; &nbsp;&nbsp;keepalive&nbsp;64;
}

不要在同一池混入不同模型,并试图依靠权重解决功能差异。不同模型应进入不同 upstream,再按路径、域名或经过校验的模型字段路由。

四、为非流式和流式 API 编写代理配置

大模型 OpenAI 兼容接口通常使用普通 HTTP POST;流式返回采用 Server-Sent Events,而不是 WebSocket。关键点是关闭响应缓冲、设置足够的读取超时、传递请求 ID,并让 upstream keepalive 正常复用。

下面是核心 server 配置。proxy_set_header Connection "" 清除客户端逐跳连接头,配合 HTTP/1.1 使用上游长连接。proxy_buffering off 保证流式数据尽快转发,proxy_request_buffering off 则避免大请求体全部落完后才发送给后端。

nginx

server&nbsp;{
&nbsp; &nbsp;&nbsp;listen&nbsp;443&nbsp;ssl http2;
&nbsp; &nbsp;&nbsp;server_name&nbsp;<API域名>;

&nbsp; &nbsp;&nbsp;ssl_certificate&nbsp; &nbsp; &nbsp;<证书路径>;
&nbsp; &nbsp;&nbsp;ssl_certificate_key&nbsp;<私钥路径>;

&nbsp; &nbsp;&nbsp;client_max_body_size&nbsp;10m;
&nbsp; &nbsp;&nbsp;client_body_timeout&nbsp;30s;

&nbsp; &nbsp;&nbsp;location&nbsp;/v1/ {
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;proxy_pass&nbsp;http://llm_backend;
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;proxy_http_version&nbsp;1.1;
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;proxy_set_header&nbsp;Connection&nbsp;"";
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;proxy_set_header&nbsp;Host&nbsp;$host;
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;proxy_set_header&nbsp;X-Real-IP&nbsp;$remote_addr;
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;proxy_set_header&nbsp;X-Forwarded-For&nbsp;$proxy_add_x_forwarded_for;
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;proxy_set_header&nbsp;X-Forwarded-Proto&nbsp;$scheme;
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;proxy_set_header&nbsp;X-Request-ID&nbsp;$request_id;

&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;proxy_connect_timeout&nbsp;3s;
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;proxy_send_timeout&nbsp;60s;
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;proxy_read_timeout&nbsp;600s;

&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;proxy_request_buffering&nbsp;off;
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;proxy_buffering&nbsp;off;
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;gzip&nbsp;off;

&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;proxy_next_upstream&nbsp;error&nbsp;timeout http_502 http_503 http_504;
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;proxy_next_upstream_tries&nbsp;2;
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;proxy_next_upstream_timeout&nbsp;5s;
&nbsp; &nbsp; }
}

proxy_read_timeout 600s 是两次上游读取操作之间允许的空闲时间,不是整个推理请求的绝对总时长。超时应根据业务最大生成时间设置,并与客户端、API 网关和推理服务端协调。值过短会中断正常长生成,值过长则会让异常连接占用资源更久。

较新的 Nginx 版本推荐用独立的 http2 on; 指令,部分旧版本仍使用示例中的 listen 443 ssl http2;。上线前通过 nginx -v 和当前版本文档确认语法;若替换写法,必须再次执行 nginx -t,不能同时保留互相冲突的监听配置。

重试必须特别谨慎。Chat Completions 使用 POST,Nginx 默认不会在请求已经发送给上游后将非幂等请求转交下一个节点,除非显式加入 non_idempotent。不要为了提高“成功率”添加 non_idempotent:模型请求可能在首节点继续消耗 GPU,同时第二节点又执行一遍,造成重复计费、双重工具调用或业务副作用。上述重试主要覆盖建连前错误和可安全切换的情况。

如果推理框架会设置 X-Accel-Buffering: no,可以保留;但网关端仍应显式关闭缓冲,避免不同后端行为不一致。不要对 SSE 开启 gzip,压缩层可能增加缓冲和首 token 延迟。

五、拆分健康检查与业务流量

健康检查不应进入通用 /v1/ 负载均衡路径后再由任意节点回答,否则只能证明“至少一个节点活着”。运维检查应逐个直连后端,用户入口健康则可以返回 Nginx 自身状态,或代理到轻量模型列表接口。

为负载均衡入口增加一个不触发推理的本地健康端点:

nginx

location&nbsp;= /gateway-health {
&nbsp; &nbsp;&nbsp;access_log&nbsp;off;
&nbsp; &nbsp;&nbsp;default_type&nbsp;text/plain;
&nbsp; &nbsp;&nbsp;return&nbsp;200&nbsp;"ok\n";
}

这个端点只证明 Nginx worker 能响应,不证明后端模型可用。监控系统应同时保留网关健康、逐实例健康和真实低频合成请求三层探测,避免单一探针误判。

六、记录能形成证据链的访问日志

默认 combined 日志缺少 upstream 地址、上游耗时和请求总耗时,无法回答“请求到底落在哪台、慢在网关还是后端”。建议增加 JSON 日志,记录请求 ID、状态码、上游节点和时延。不要记录请求体、Authorization 或模型输出。

nginx

log_format&nbsp;llm_json escape=json
&nbsp;&nbsp;'{'
&nbsp; &nbsp;&nbsp;'"time":"$time_iso8601",'
&nbsp; &nbsp;&nbsp;'"request_id":"$request_id",'
&nbsp; &nbsp;&nbsp;'"remote_addr":"$remote_addr",'
&nbsp; &nbsp;&nbsp;'"method":"$request_method",'
&nbsp; &nbsp;&nbsp;'"uri":"$uri",'
&nbsp; &nbsp;&nbsp;'"status":$status,'
&nbsp; &nbsp;&nbsp;'"request_length":$request_length,'
&nbsp; &nbsp;&nbsp;'"bytes_sent":$bytes_sent,'
&nbsp; &nbsp;&nbsp;'"request_time":$request_time,'
&nbsp; &nbsp;&nbsp;'"upstream_addr":"$upstream_addr",'
&nbsp; &nbsp;&nbsp;'"upstream_status":"$upstream_status",'
&nbsp; &nbsp;&nbsp;'"upstream_connect_time":"$upstream_connect_time",'
&nbsp; &nbsp;&nbsp;'"upstream_header_time":"$upstream_header_time",'
&nbsp; &nbsp;&nbsp;'"upstream_response_time":"$upstream_response_time"'
&nbsp;&nbsp;'}';

access_log&nbsp;/var/log/nginx/llm_access.json llm_json;
error_log&nbsp; /var/log/nginx/llm_error.log&nbsp;warn;

$upstream_header_time 可以近似帮助观察首个响应头到达时间,但不等同于模型首 token 时延;SSE 首 token 需要客户端或可观测组件在数据帧层记录。若发生上游重试,部分 upstream 变量会包含逗号分隔的多段值,这正是判断是否切换过节点的证据。

通过 jq 汇总最近日志中的节点分布与状态码。示例只读取日志,不修改服务。

bash

sudo&nbsp;tail&nbsp;-n 5000 /var/log/nginx/llm_access.json \
&nbsp; | jq -r&nbsp;'[.upstream_addr, (.status|tostring)] | @tsv'&nbsp;\
&nbsp; |&nbsp;sort&nbsp;|&nbsp;uniq&nbsp;-c |&nbsp;sort&nbsp;-nr

找出总耗时较长且后端响应时间也长的请求,进一步用 request ID 对照后端日志。

bash

sudo&nbsp;jq -c&nbsp;'select((.request_time // 0) > 30) |
&nbsp; {time,request_id,status,request_time,upstream_addr,upstream_response_time}'&nbsp;\
&nbsp; /var/log/nginx/llm_access.json |&nbsp;tail&nbsp;-n 100

若 request_time 很高而 upstream_response_time 较低,可能是客户端慢读、请求体上传慢或日志字段类型解析问题;若二者都高,再看模型队列、输入输出 token 和 GPU 指标。根因必须由多项证据共同支持。

七、限流、连接上限与过载保护

Nginx 的 limit_req 统计请求次数,不理解 token 成本。它适合限制突发调用,不能替代基于 token、用户配额和模型队列长度的业务限流。按 API Key 限流时不能把明文 Key 写进日志;若认证层已经把调用方映射为内部租户 ID,可以按可信请求头分区。

nginx

map&nbsp;$http_x_tenant_id&nbsp;$tenant_key&nbsp;{
&nbsp; &nbsp;&nbsp;default&nbsp;$http_x_tenant_id;
&nbsp; &nbsp; "" &nbsp; &nbsp; &nbsp;$binary_remote_addr;
}

limit_req_zone&nbsp;$tenant_key&nbsp;zone=llm_per_tenant:20m&nbsp;rate=2r/s;
limit_conn_zone&nbsp;$tenant_key&nbsp;zone=llm_conn_per_tenant:20m;

server&nbsp;{
&nbsp; &nbsp;&nbsp;location&nbsp;/v1/ {
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;limit_req&nbsp;zone=llm_per_tenant burst=10&nbsp;nodelay;
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;limit_conn&nbsp;llm_conn_per_tenant&nbsp;8;

&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;proxy_pass&nbsp;http://llm_backend;
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;# 其余代理参数与主配置保持一致
&nbsp; &nbsp; }
}

只有当 X-Tenant-ID 由可信认证网关注入,并且外部客户端不能伪造时才能作为限流键。直接信任公网客户端自报的租户头,会让调用方轻易绕过限制。连接上限也需考虑一个用户并行流式请求的合理需求。

为限流设置清晰状态码,便于客户端区分过载和服务故障。

nginx

limit_req_status&nbsp;429;
limit_conn_status&nbsp;429;

客户端收到 429 后应指数退避并加入抖动;立即无限重试会把暂时过载放大成重试风暴。

八、按会话做一致性哈希的适用边界

如果后端启用了前缀缓存,并且大量多轮请求具有稳定公共前缀,可以用一致性哈希提高同一会话回到同一实例的概率。先由应用生成不可猜测、稳定且长度受限的 X-Session-ID,再配置:

nginx

upstream&nbsp;llm_session_backend {
&nbsp; &nbsp;&nbsp;zone&nbsp;llm_session_backend&nbsp;64k;
&nbsp; &nbsp;&nbsp;hash&nbsp;$http_x_session_id&nbsp;consistent;

&nbsp; &nbsp;&nbsp;server&nbsp;10.20.0.11:8000&nbsp;max_fails=2&nbsp;fail_timeout=20s;
&nbsp; &nbsp;&nbsp;server&nbsp;10.20.0.12:8000&nbsp;max_fails=2&nbsp;fail_timeout=20s;
&nbsp; &nbsp;&nbsp;server&nbsp;10.20.0.13:8000&nbsp;max_fails=2&nbsp;fail_timeout=20s;
&nbsp; &nbsp;&nbsp;keepalive&nbsp;64;
}

一致性哈希不会同步 KV Cache;节点重启或扩缩容后,请求可能落到新节点并重新计算。某个超大租户也可能形成热点。因此应比较缓存命中、负载倾斜和故障恢复,而不是因为存在多轮对话就默认启用粘性。

九、配置变更必须走完整闭环

直接修改生产配置再重启风险很高。Nginx 支持平滑 reload,应采用“备份、生成新文件、语法检查、reload、验证、回滚”的顺序。reload 会让新 worker 使用新配置,旧 worker 处理完已有连接后退出,适合保护长流式请求。

下面脚本备份主配置目录并执行语法检查;备份目录要位于受控存储,不能把私钥复制到无权限保护的位置。

bash

#!/usr/bin/env bash
set&nbsp;-euo pipefail

NGINX_DIR="/etc/nginx"
BACKUP_ROOT="<配置备份目录>"
STAMP="$(date +%Y%m%d-%H%M%S)"
BACKUP_DIR="${BACKUP_ROOT}/nginx-${STAMP}"

sudo&nbsp;install -d -m 0700&nbsp;"${BACKUP_DIR}"
sudo&nbsp;cp&nbsp;-a&nbsp;"${NGINX_DIR}/."&nbsp;"${BACKUP_DIR}/"
sudo&nbsp;nginx -t
echo&nbsp;"Backup created:&nbsp;${BACKUP_DIR}"

修改后先查看差异,再测试并 reload。systemctl reload 不应在 nginx -t 失败时执行。

bash

sudo&nbsp;diff -u <旧配置文件> <新配置文件> ||&nbsp;true
sudo&nbsp;nginx -t
sudo&nbsp;systemctl reload nginx
sudo&nbsp;systemctl status nginx --no-pager

立刻验证本机入口、TLS、模型列表、非流式和流式请求。--resolve 可在 DNS 切换前将域名临时解析到指定网关 IP,同时保持正确的 Host 和 TLS SNI。

bash

curl --fail --silent --show-error \
&nbsp; --resolve&nbsp;'<API域名>:443:<网关IP>'&nbsp;\
&nbsp; https://<API域名>/gateway-health

curl --fail --silent --show-error \
&nbsp; --resolve&nbsp;'<API域名>:443:<网关IP>'&nbsp;\
&nbsp; https://<API域名>/v1/models | jq .

如果出现错误,先回滚配置并再次语法检查。回滚会改变新请求路由,但已经建立的流式连接可能仍由旧 worker 继续服务;应观察 worker 和连接排空,不要立即强杀。

bash

sudo&nbsp;rsync -a --delete <配置备份目录>/nginx-<时间戳>/ /etc/nginx/
sudo&nbsp;nginx -t
sudo&nbsp;systemctl reload nginx
curl -fsS https://<API域名>/gateway-health

rsync --delete 会删除目标中备份不存在的配置,属于高风险操作。执行前必须确认源、目标都是精确目录,先用 rsync -ani --delete dry-run 查看差异;若只改了单个文件,更安全的方式是只恢复该文件。

十、实例维护与连接排空

下线后端前,先确认目标节点和影响范围。最安全的方法是先把该 server 标为 down 或临时从 upstream 移除,语法检查并 reload;新请求不再进入该节点,已有连接由旧 worker 尽量处理完。

维护版 upstream 示例:

nginx

upstream&nbsp;llm_backend {
&nbsp; &nbsp; least_conn;
&nbsp; &nbsp;&nbsp;zone&nbsp;llm_backend&nbsp;64k;

&nbsp; &nbsp;&nbsp;server&nbsp;10.20.0.11:8000&nbsp;max_fails=2&nbsp;fail_timeout=20s;
&nbsp; &nbsp;&nbsp;server&nbsp;10.20.0.12:8000&nbsp;down;
&nbsp; &nbsp;&nbsp;server&nbsp;10.20.0.13:8000&nbsp;max_fails=2&nbsp;fail_timeout=20s;
&nbsp; &nbsp;&nbsp;keepalive&nbsp;64;
}

reload 后从访问日志确认没有新的请求分配到维护节点。不能只看一次 curl,应观察一个覆盖最长常见请求的时间窗口。

bash

sudo&nbsp;nginx -t &&&nbsp;sudo&nbsp;systemctl reload nginx
sudo&nbsp;tail&nbsp;-F /var/log/nginx/llm_access.json \
&nbsp; | jq --unbuffered&nbsp;'select(.upstream_addr | contains("10.20.0.12:8000"))'

确认新请求停止且在途请求排空后,才停止模型实例。停止会中断尚未完成的请求,因此应在业务低峰、告知影响并保留快速恢复命令。

维护结束后,先直连验证节点,再去掉 down 并灰度恢复。开源 Nginx 静态配置不能直接按百分比慢启动;可以先给恢复节点较低权重,观察错误率和时延后再恢复正常配置。

十一、常见故障定位

1. 502 Bad Gateway

502 表示 Nginx 没有从上游获得有效响应,常见原因包括端口未监听、连接被拒绝、进程退出或响应头无效。先从网关主机验证路由和 TCP,再直连健康接口。

bash

ip route get 10.20.0.11
nc -vz -w 2 10.20.0.11 8000
curl -v --connect-timeout 2 --max-time 10 \
&nbsp; http://10.20.0.11:8000/health
sudo&nbsp;grep -F&nbsp;'10.20.0.11:8000'&nbsp;/var/log/nginx/llm_error.log |&nbsp;tail&nbsp;-n 50

只有日志出现 connect() failed (111: Connection refused) 且直连端口失败,才能确认是连接拒绝;不能把所有 502 都归结为“后端挂了”。

2. 504 Gateway Timeout

504 通常与等待上游响应超时有关。用 request ID 对照 Nginx 和后端日志,再检查是否为长排队、长生成或 GPU 异常。盲目增大 proxy_read_timeout 只能掩盖问题。

bash

REQUEST_ID="<请求ID>"

sudo&nbsp;grep -F&nbsp;"${REQUEST_ID}"&nbsp;/var/log/nginx/llm_access.json
sudo&nbsp;grep -F&nbsp;"${REQUEST_ID}"&nbsp;/var/log/nginx/llm_error.log
ssh <后端主机>&nbsp;"journalctl -u <模型服务名> --since '-15 min' --no-pager | grep -F '${REQUEST_ID}'"

远程命令要求目标主机已配置受控 SSH 权限。生产环境更推荐集中日志平台,避免运维账号在多台推理节点上拥有不必要权限。

3. 流式响应一次性返回

先直连后端使用 curl -N,再经过 Nginx 对比。如果直连逐段返回而代理一次性返回,重点检查所有中间层的缓冲、压缩和 CDN 策略。

bash

curl -N -sS http://10.20.0.11:8000/v1/chat/completions \
&nbsp; -H&nbsp;'Content-Type: application/json'&nbsp;\
&nbsp; -d&nbsp;'{"model":"<模型名>","messages":[{"role":"user","content":"逐项列出五条检查"}],"stream":true,"max_tokens":128}'

curl -N -sS https://<API域名>/v1/chat/completions \
&nbsp; -H&nbsp;'Content-Type: application/json'&nbsp;\
&nbsp; -d&nbsp;'{"model":"<模型名>","messages":[{"role":"user","content":"逐项列出五条检查"}],"stream":true,"max_tokens":128}'

4. 请求分配严重不均

先从日志按节点统计请求数和总处理时间。仅请求数不均不一定是故障:least_conn 会根据活动连接变化,节点处理速度、请求长度和失败重试都会影响分布。

bash

sudo&nbsp;jq -r&nbsp;'select(.upstream_addr != "") |
&nbsp; [.upstream_addr, (.request_time|tostring)] | @tsv'&nbsp;\
&nbsp; /var/log/nginx/llm_access.json \
&nbsp; | awk -F&nbsp;'\t'&nbsp;'{count[$1]++; sum[$1]+=$2}
&nbsp; &nbsp; &nbsp; END {for (h in count) printf "%s requests=%d total_time=%.3f\n", h, count[h], sum[h]}'

若某节点请求少且连接失败多,检查服务健康;若请求数少但总处理时间接近其他节点,说明它可能承担了更长请求;若使用会话哈希,则检查租户或会话热点。

十二、自动巡检多个节点

下面脚本检查配置语法、入口健康、逐后端健康和模型名一致性。它不会自动 reload 或摘除节点,因为自动修改路由可能扩大误判影响;发现异常后由监控告警和变更流程处理。

bash

#!/usr/bin/env bash
set&nbsp;-euo pipefail

EXPECTED_MODEL="${EXPECTED_MODEL:-<模型名>}"
GATEWAY_URL="${GATEWAY_URL:-https://<API域名>}"
BACKENDS=(
&nbsp;&nbsp;"http://10.20.0.11:8000"
&nbsp;&nbsp;"http://10.20.0.12:8000"
&nbsp;&nbsp;"http://10.20.0.13:8000"
)

sudo&nbsp;nginx -t >/dev/null
curl -fsS --max-time 5&nbsp;"${GATEWAY_URL}/gateway-health"&nbsp;>/dev/null

failed=0
for&nbsp;backend&nbsp;in&nbsp;"${BACKENDS[@]}";&nbsp;do
&nbsp;&nbsp;if&nbsp;! curl -fsS --max-time 5&nbsp;"${backend}/health"&nbsp;>/dev/null;&nbsp;then
&nbsp; &nbsp;&nbsp;echo&nbsp;"CRITICAL: health failed&nbsp;${backend}"&nbsp;>&2
&nbsp; &nbsp; failed=1
&nbsp; &nbsp;&nbsp;continue
&nbsp;&nbsp;fi
&nbsp;&nbsp;if&nbsp;! curl -fsS --max-time 10&nbsp;"${backend}/v1/models"&nbsp;\
&nbsp; &nbsp; &nbsp; | jq -e --arg m&nbsp;"${EXPECTED_MODEL}"&nbsp;'.data[] | select(.id == $m)'&nbsp;>/dev/null;&nbsp;then
&nbsp; &nbsp;&nbsp;echo&nbsp;"CRITICAL: model mismatch&nbsp;${backend}"&nbsp;>&2
&nbsp; &nbsp; failed=1
&nbsp;&nbsp;fi
done

exit&nbsp;"${failed}"

巡检成功并不代表容量充足。还应监控 Nginx 4xx/5xx、连接数、请求时延、各 upstream 错误、模型队列、首 token 时延、输出 token 速率、GPU 利用率和显存。只有把网关与推理实例指标放在同一时间线上,才能区分入口故障、网络故障和模型过载。

十三、上线验收与回滚标准

上线前应完成以下验证:所有后端模型和配置等价;直连请求通过;Nginx 配置语法正确;SSE 不被缓存;POST 没有启用非幂等重试;超时覆盖业务需求但有限;访问日志含 request ID、upstream 和耗时;限流键不能被客户端伪造;维护节点可先摘流再停止;旧配置备份可恢复;TLS 私钥权限未因备份扩大。

回滚标准同样要量化:入口健康恢复、核心请求成功、错误率回落、流式输出正常、请求重新分布到旧池、没有继续向故障节点分流。Nginx reload 成功只是过程证据,不是业务恢复结论。

对大模型服务来说,负载均衡不是“平均分配每一个请求”,而是在不同长度、不同成本的推理任务之间维持可用性。Nginx 能提供可靠入口和基础调度,但 token 成本、队列深度、前缀缓存和租户配额仍需要推理平台与业务层共同治理。

文末阅读福利

仅目前来说,无论是运维人转型提升,还是零基础想转行IT,最好的岗位就是云计算运维&SRE岗位。

为了帮助大家早日快速入门云计算运维领域,给大家整理了一套【最新运维资料】高级运维工程师必备技能资料包(文末一键免费领取),内容有多详实丰富看下图!

1.38张最全工程师技能图谱

2.面试大礼包

3.Linux书籍

内容比较多,就不一一展示了

以上所有资料获取请扫码:

识别上方二维码

备注:2026最新运维资料

100%免费领取

(是扫码领取,不是在公众号后台回复,别看错了哦)


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:马哥Linux运维 点击关注👉 点击关注👉《Nginx 负载均衡:为多个大模型实例分配请求》

评论:0   参与:  0