李涛自留地
回到顶部
首页 > 建站 > nginx配置负载均衡的几种写法与生产环境踩坑记录

nginx配置负载均衡的几种写法与生产环境踩坑记录

文章目录

上周四晚上十点多,我正准备关电脑,监控群里有人贴截图说后台一直转圈打不开。赶紧 ssh 上去看,两台后端挂了一台,nginx 还在把一半请求往那台身上打。最早那版 nginx配置负载均衡 只写了轮询,故障转移压根没配,这就是那天晚上翻车的原因,后来我又踩了两个坑才把它理顺。

nginx 负载均衡的几种调度算法

nginx配置负载均衡 里能选的调度算法不多,但够用。默认的 round-robin 按顺序往后端轮,流量均匀的时候最省事;least_conn 挑当前连接数最少的那台,长连接多的场景比轮询靠谱得多。

叠放的配置文件分成多路分流接口,nginx配置负载均衡

ip_hash 按客户端 IP 做哈希,同一个 IP 固定落到同一台机器上。老系统把 session 存在本地内存里,你可能就得靠它兜底。least_time 和 sticky 是商业版才有的,开源版没这两个;hash 开源版倒是有,加 consistent 参数就是一致性哈希,别把这两个记混,拿不准的以官方文档为准。

我的习惯是:没状态的服务用 least_conn,有本地 session 的用 ip_hash,缓存类又想按 URL 固定落点,就在 upstream 里写一行 hash $request_uri consistent。选算法是 nginx配置负载均衡 的第一步,别急着复制别人的配置块。

顺带说一句,调度算法只管新连接去哪,跟健康检查是两码事。很多人把这两件事混在一起调,调半天没效果,其实方向一开始就偏了,我头两年也这么干过。

我当时以为 least_conn 能自动避开挂掉的后端,结果发现它只是不把新连接发给挂的那台,早先连上的连接照样晾在那里等超时。

upstream 里的 weight 怎么配

weight 默认都是 1。两台机器配置不一样时,把性能好的那台调高,它接的请求自然就多。调 weight 是第一步,动之前先想清楚比例:3 比 1 就是七五对二五,不是三七开。weight 是 nginx配置负载均衡 里最常改的参数,改完记得拿日志对一遍。

第二步才轮到 max_fails 和 fail_timeout。前者是失败几次算不可用,后者是判定之后隔离多久,默认值分别是 1 次和 10 秒。高并发下 10s 实在太短,我上线头一天就被这个默认值坑过。

我现在固定用这一份,backup 那台平时不接流量,只在主机全挂时顶上:

upstream backend {
    least_conn;
    server 10.0.0.11:8080 weight=3 max_fails=2 fail_timeout=30s;
    server 10.0.0.12:8080 weight=1 backup;
    keepalive 32;
}

backend 是 upstream 的名字,proxy_pass 里写 http://backend 就能引用。weight=3 配 weight=1 是七五比二五,具体比例按机器核数和内存来,别硬搬数字。

down 这个标记也常用。机器要维护就手动标 down,比直接删掉整行 server 省事,恢复时删一个单词就行。我拿它做过灰度,先标一台观察半小时,确认没有单点问题再放回去。

用 keepalive 记得在 location 里补上 proxy_http_version 1.1 和 proxy_set_header Connection "",不然长连接建不起来。这个坑我踩过一次,配置看着一点问题没有,就是连接数上不去。

真出问题时,先用 curl 直接打后端 IP,确认服务本身还活着,再回来查 nginx 这一层,能少绕不少路。

拿日志核对流量到底分没分对

nginx配置负载均衡 改完别急着收工,先确认请求真按预期分了。默认的 access_log 里没有后端地址,你得在 log_format 里加 $upstream_addr,才能看到每个请求最终落到哪台机器。

俯视终端窗口与旁边滚动的日志流,nginx配置负载均衡

最省事的写法是在 http 块里留一行格式定义,再让 access_log 指向这个名字:

log_format lb '$remote_addr $upstream_addr $request $status';
access_log /var/log/nginx/access.log lb;

改完 reload,用下面这条看生效之后的配置到底长什么样:

nginx -T | sed -n '/upstream backend/,/}/p'
nginx -t && nginx -s reload

输出是 include 展开后的完整 upstream 段,重点看 server 那几行的 weight 和 backup 标记跟你写的是否一致。改错文件、改错环境这种事我干过不止一回。

流量分布也能数出来:把 access_log 里 $upstream_addr 那一列切出来排序去重,正常应该接近你配的权重比例。某台明显偏多,多半是它一直在超时重试。

还有个前置条件得先说清楚。如果你在 nginx 前面套了一层四层代理,real_ip 要先配好,不然日志里的客户端地址全是代理地址,按 IP 哈希的那套直接用不了。

日志这步最没意思,但少了它,你根本说不清 nginx配置负载均衡 改完之后到底生效了没有。

后端挂了 nginx 多久才发现

nginx配置负载均衡 的故障转移不是瞬间的事。nginx 要等一次请求真的失败,才把 max_fails 加一,累计到阈值才把那台标记成不可用。探测频率直接决定故障被发现的快慢。

我当时以为配了 backup 就万事大吉,结果前端还是报了十几秒的 502。那会儿我以为是有请求正好打到正在挂的那台、重试次数不够,后来翻文档才发现方向错了:proxy_next_upstream 的默认值本来就是 error timeout。

分叉后停住的那一路接口与日志流,nginx配置负载均衡

真正要查的是两件事。一件是 proxy_next_upstream_tries 有没有被谁显式调小,它默认是 0,0 表示不限次;另一件是那 502 到底谁发的,如果是后端自己返回 502,nginx 默认不会换机器,得把 http_502 加进 proxy_next_upstream 的列表里,它才肯重试。

重试这条路本身有代价:请求可能执行两次,下单这类写接口宁可报错也别重试,不然退款单能把你折腾到半夜,我吃过这个亏。

还有个细节:被标记 down 的机器,nginx 会按 fail_timeout 隔一阵子再试一次,不是永久下线。所以后端修好了不用改配置,等一个周期就行。

日志里 502 和 504 要分开看,502 是后端把连接拒了,504 是等到超时。这两类的处理方式完全不同,混在一起查容易白跑一趟。

我现在改配置的固定动作

改之前先备份,改完 nginx -t 过一遍语法,再 nginx -s reload。reload 是平滑的,老的 worker 处理完手上的请求才退出,不会把用户连接掐断。这两条我写成了 shell alias,手比脑子快的时候不容易漏。

老实说,nginx配置负载均衡 最容易翻车的不是算法选错,是改完不看日志、不验证权重。我现在每次动 upstream,都顺手抓十分钟 access_log 对一遍,对不上就回滚。

配置文件我放在 /etc/nginx/conf.d/ 下一个站点一个文件,出问题一眼就知道是哪块。回滚也简单,把备份 cp 回去再 reload,我一般留三份历史,够用了。

改完 upstream 先抓十分钟日志,对不上就回滚,别等告警来找你。

若非作者标注皆为李涛自留地原创文章,转载或复制请以超链接形式并注明出处。