Nginx | 负载均衡策略:ip_hash / hash 会话保持实践

知识是人生的灯塔,只有不断学习,才能照亮前行的道路

📢 大家好,我是 WeiyiGeek,一名深耕安全运维开发(SecOpsDev)领域的技术从业者,致力于探索DevOps与安全的融合(DevSecOps),自动化运维工具开发与实践,企业网络安全防护,欢迎各位道友一起学习交流、一起进步 🚀,若此文对你有帮助,一定记得点个关注⭐与小红星❤️或加入到作者知识星球『 全栈工程师修炼指南』,转发收藏学习不迷路 😋 。

负载均衡策略:ip_hash / hash 会话保持

描述:由于 Round Robin 负载均衡(轮询)算法无法保证来自同一客户端的请求总是被转发到同一个上游服务器,因此只能用于AKF中的X轴水平扩展,若想实现Z轴扩展以确保同一个客户端请求总是转发到同一个上游服务器,从而实现会话保持(Session Persistence),则需要使用 IP Hash 负载均衡算法。

温馨提示:若文章代码块中存在乱码或不能复制,请联系作者,也可通过文末的阅读原文链接,加入知识星球中阅读,原文链接:https://articles.zsxq.com/id_a2l6pacq0flr.html

上一章节中,在介绍 upstream 模块时我们提到过 ip_hashhash 负载均衡算法,其中:

  • ip_hash 负载均衡算法是根据客户端的 IP 地址进行哈希运算,不受权重影响,即使用$remote_addr变量作为关键字,对于IPV4地址使用前3字节作为关键字,IPV6 则使用完整地址,此模块默认编译进 Nginx 核心,可通过 --without-http_upstream_ip_hash_module 编译选项禁用。

  • hash 负载均衡算法是基于任意字符串参数或变量关键字进行哈希运算,不受权重影响,此模块默认编译进 Nginx,可通过 --without-http_upstream_hash_module 编译选项禁用。

go 复制代码
# 基于客户端IP地址哈希的负载平衡方法。
Syntax: ip_hash;
Default: ---
Context: upstream

# 基于哈希键值的服务器组指定负载均衡算法。
Syntax: hash key [consistent];
Default: ---
Context: upstream

温馨提示:在 Nginx 中要从代理链(如 CDN、负载均衡器、反向代理)中提取真实的客户端 IP 地址,替换 Nginx 接收到的连接 IP 地址,需使用 set_real_ip_from 指令指定可信代理服务器的 IP 段,并使用 real_ip_header 指令指定包含真实客户端 IP 的 HTTP 头部字段,最后使用 real_ip_recursive 指令启用递归查找真实 IP,相关知识在【Nginx | 核心知识150讲,百万并发下性能优化之深挖十一个处理阶段对应模块指令实践(上篇)】文章中讲解过,就不再累述了。

值得注意的是,使用 set_real_ip_fromreal_ip_header 指令来设置真实的客户端 IP 地址,其相关指令依赖于 realip 模块,默认未编译进 Nginx,可通过 --with-http_realip_module 选项启用,否则下述实践时可能会出现如下错误提示:nginx: [emerg] unknown directive "set_real_ip_from"

go 复制代码
cd /usr/local/src/nginx-1.29.0
./configure --prefix=/usr/local/nginx --user=nginx --group=nginx --sbin-path=/usr/sbin/nginx --conf-path=/usr/local/nginx/conf/nginx.conf --pid-path=/usr/local/nginx/nginx.pid --error-log-path=/var/log/nginx/logs/error.log --http-log-path=/var/log/nginx/logs/access.log --lock-path=/var/run/nginx.lock --modules-path=/usr/local/nginx/modules --with-http_stub_status_module --with-http_realip_module
make && make install

目的:利用 Nginx 反向代理验证 ip_hashhash 负载均衡算法。

操作步骤

步骤 01.创建两个测试服务器(213、214),213主机监听 8010 端口,214主机分别监听8011和8012端口。

go 复制代码
# 编辑主配置文件,添加 include 指令,引入自定义配置文件目录。
vim /usr/local/nginx/conf/nginx.conf
....
include /usr/local/nginx/conf.d/*.conf;
....

# 创建测试服务器配置文件目录,并写入两个监听服务器的配置。
mkdir /usr/local/nginx/conf.d/
# 10.20.172.214 主机
tee /usr/local/nginx/conf.d/server.conf <<'EOF'
server {
  listen 8011;
  server_name localhost _;
  default_type text/plain;
return 200 'From 8011 server response!\n';
}

server {
  listen 8012;
  server_name localhost _;
  default_type text/plain;
return 200 'From 8012 server response!\n';
}
EOF

# 10.20.172.213 主机
tee /usr/local/nginx/conf.d/server.conf <<'EOF'
server {
  listen 8010;
  server_name localhost _;
  default_type text/plain;
return 200 'From 10.20.172.213:8011 server response!\n';
}
EOF

步骤 02.编辑 Nginx 配置文件,反向代理请求时使用 ip_hash 负载均衡算法。

go 复制代码
tee /usr/local/nginx/conf.d/proxy_server_iphash.conf <<'EOF'
# 创建上游服务器
upstream backend {
# 基于客户端IP地址哈希的负载平衡方法
  ip_hash;
  server 127.0.0.1:8011 weight=2;  # 权重为2,ip_hash 负载均衡算法不受权重影响
  server 127.0.0.1:8012;  # 缺省权重为1,ip_hash 负载均衡算法不受权重影响
# 其它后端服务组,最大失败次数为2,暂停10秒不向其转发请求
  server 10.20.172.213:8010 max_fails=2 fail_timeout=10s max_conns=5; 
  keepalive 10;  # 设置与上游服务器的长连接,最多保持10个空闲的保活连接
  keepalive_timeout 60s; # 设置与上游服务器的长连接,空闲连接的超时时间
}

# 创建监听用于反向代理
server {
  listen 80;
  server_name test.weiyigeek.top;
  access_log /var/log/nginx/test.log main;
  error_log test.error.log info;

# 指定可信任的代理服务器 IP 段,只有来自 10.20.172.0/24 网段的请求(CDN/代理地址),Nginx 才会处理其 X-Forwarded-For 头
  set_real_ip_from 10.20.172.0/24;

# 启用递归查找真实 IP  
# off(默认):取 X-Forwarded-For 的最后一个 IP
# on:从右向左遍历,跳过所有可信代理 IP,取第一个不可信 IP
  real_ip_recursive on; 

# 指定包含真实 IP 的 HTTP 头部字段
  real_ip_header X-Forwarded-For;

  location / {
    proxy_pass http://backend;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Connection "";
    proxy_http_version 1.1;
  }
}
EOF

步骤 03.重启 Nginx 服务,使用 curl 验证 ip_hash 负载均衡算法, 由结果可知,来自同一客户端的无论多少次请求总是被转发到同一个上游服务器,不受权重影响,另外根据不同客户端IP请求通过ip_hash计算转发到的上游服务器也不同。

go 复制代码
# 214 主机模拟请求
for i in $(seq 1 5);do curl http://test.weiyigeek.top;done
# 模拟不同客户端IP请求
for i in $(seq 1 5);do curl -H 'X-Forwarded-For: 100.10.2.215' http://test.weiyigeek.top;done
for i in $(seq 1 5);do curl -H 'X-Forwarded-For: 100.10.12.215' http://test.weiyigeek.top;done

weiyigeek.top-ip_hash 负载均衡算法效果图

步骤 04.再编辑 Nginx 配置文件,反向代理请求时使用 hash 负载均衡算法。

go 复制代码
mv /usr/local/nginx/conf.d/proxy_server_iphash.conf{,.bak}
tee /usr/local/nginx/conf.d/proxy_server_hash.conf <<'EOF'
# 创建上游服务器
upstream backend {
# 基于指定字符串 hash 的负载平衡方法。
hash user_$arg_user; # 基于 user_拼接请求url中的user参数进行哈希运算,不受权重影响
# hash 负载均衡算法不受权重影响
  server 127.0.0.1:8011 weight=2; 
  server 127.0.0.1:8012;
# 最大失败次数为2,暂停10秒不向其转发请求。
  server 10.20.172.213:8010 max_fails=2 fail_timeout=10s max_conns=5;
  keepalive 10;  # 设置与上游服务器的长连接,最多保持10个空闲的保活连接。
  keepalive_timeout 60s; # 设置与上游服务器的长连接,空闲连接的超时时间。
}

# 创建监听用于反向代理
server {
  listen 80;
  server_name test.weiyigeek.top;
  access_log /var/log/nginx/test.log main;
  error_log test.error.log info;
  location / {
    proxy_pass http://backend;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Connection "";
    proxy_http_version 1.1;
  }
}
EOF

步骤 06.重启 Nginx 服务,使用 curl 验证 hash 负载均衡算法, 由结果可知,来自同一客户端相同的user请求参数总是被转发到同一个上游服务器,不受权重以及客户端IP地址影响。

go 复制代码
# 重启服务
nginx -s reload

# 模拟请求
for i in $(seq 1 5);do curl http://test.weiyigeek.top?user=weiyigeek;done
for i in $(seq 1 5);do curl -H 'X-Forwarded-For: 100.10.2.215' http://test.weiyigeek.top?user=weiyigeek;done
for i in $(seq 1 5);do curl -H 'X-Forwarded-For: 100.10.2.215' http://test.weiyigeek.top?user=WeiyiGeek;done
 for i in $(seq 1 5);do echo -n "user_WeiyiGeek${i}:";curl -H 'X-Forwarded-For: 100.10.2.215' http://test.weiyigeek.top?user=WeiyiGeek${i};done

weiyigeek.top-hash负载均衡算法效果图

至此,作者针对 ip_hash 和 hash 负载均衡算法的测试实践就完成了,由上可知,使用哈希算法可确保某一类请求始终由同一台上游服务器处理,即使服务器离线,请求依然按原规则转发,从而保障一致性。

温馨提示:使用哈希算法时,不能直接从 upstream 配置中移除已下线的服务器,若强行移除会导致哈希环变化,影响原有请求的路由映射,可能导致大量缓存失效或会话中断,引发严重后果。当然若想避免这种情况,可以继续学习下一节中一致性哈希算法,它缓解了节点变动带来的大规模路由重分布问题。

END

加入:作者【全栈工程师修炼指南】知识星球

『 全栈工程师修炼指南』星球,主要涉及全栈工程师(Full Stack Development)实践文章,持续更新包括但不限于企业SecDevOps和网络安全等保合规、安全渗透测试、编程开发、云原生(Cloud Native)、物联网工业控制(IOT)、人工智能Ai,从业书籍笔记,人生职场认识等方面资料或文章。

Q: 加入作者【全栈工程师修炼指南】星球后有啥好处?

✅ 将获得作者最新工作学习实践文章以及网盘资源。

✅ 将获得作者珍藏多年的全栈学习笔记(需连续两年及以上老星球友,也可单次购买)

✅ 将获得作者专门答疑学习交流群,解决在工作学习中的问题。

✅ 将获得作者远程支持(在作者能力范围内且合规)。

获取:作者工作学习全栈笔记

作者整理了10年的工作学习笔记(涉及网络、安全、运维、开发),需要学习实践笔记的看友,可添加作者微信或者回复【工作学习实践笔记】,当前价格¥299,除了获得从业笔记的同时还可进行问题答疑以及每月远程技术支持,希望大家多多支持,收获定大于付出!

知识推荐 往期文章

若文章对你有帮助,请将它转发给更多的看友,若有疑问的小伙伴,可在评论区留言你想法哟 💬!

相关推荐
eggrall1 小时前
Linux 基础开发工具 —— 解锁高效开发的底层密钥
linux·运维·服务器
weixin_307779131 小时前
基于AWS安全组的两层架构访问控制设计与实现
运维·云原生·架构·云计算·aws
秋深枫叶红1 小时前
嵌入式第三十篇——数据结构——哈希表
数据结构·学习·算法·哈希算法
哇哈哈&1 小时前
rabbitmq最简单的安装方法
linux·运维·centos
虎头金猫1 小时前
从杂乱到有序,Paperless-ngx 加个cpolar更好用
linux·运维·人工智能·docker·开源·beautifulsoup·pandas
源之缘-OFD先行者2 小时前
定制化 Live555 实战:按需开发低耗 RTSP 服务器,完美适配 C# 项目
运维·服务器·c#
wifi chicken2 小时前
Linux Wlan无线网络开发之DHCP预留功能 实操demo
linux·运维·服务器
段帅龙呀2 小时前
服务器因BIOS设置导致无法正常做RAID修复
运维·服务器·raid
Dovis(誓平步青云)2 小时前
《Linux 网络实战手册:从 TCP/IP 协议栈到 UDP网络通信》
linux·网络·tcp/ip