纯靠一台Nginx对外提供服务的架构,在业务量小的时候没任何问题,可一旦这台机器挂了,整个站点就彻底无法访问;即便用Keepalived做了主备,机房级别的故障依然无解。把华为云ELB放在Nginx前面,让云上负载均衡负责流量分发和健康剔除,Nginx专注做七层转发、路由重写和证书卸载,这种分层组合是目前比较主流的做法。下面详细拆解整套部署思路和配置细节。

一、架构设计:为什么ELB在外层、Nginx在内层
很多刚接触云架构的同学容易纠结:有了ELB还要Nginx干什么?其实两者定位不同。华为云ELB是托管的负载均衡服务,天然多可用区容灾,不占用你的计算资源,但它本质上是标准化的流量分发,灵活度有限。Nginx则是一台完全由你掌控的七层网关,可以写复杂的location规则、做灰度分流、改写请求头、对接鉴权逻辑,这是ELB给不了的。
所以合理的分层是:ELB挂在公网入口,接收用户流量,通过健康检查探测后端Nginx组的存活状态;Nginx集群部署在VPC内网,做精细化的七层处理后再转发给真正的应用服务器。这样一来,ELB解决了Nginx自身的单点和带宽瓶颈问题,Nginx保留了架构的灵活性,各干各的活。
还有一点值得注意:ELB同时支持四层(TCP/UDP)和七层(HTTP/HTTPS)监听。混合部署时通常建议在ELB上配四层转发,把TLS卸载放到Nginx这一层做,这样证书管理、SNI多域名配置都在自己手里,改起来不用动云控制台。当然如果你的证书变化很少、又想省掉Nginx的SSL开销,也可以反过来在ELB做七层终结,两种方案都成立,取决于运维习惯。
二、网络规划与安全组配置
网络层面先规划好三段:ELB所在的外部网络(云上自动处理)、Nginx集群所在的VPC子网、应用服务器所在的子网。建议给Nginx单独划一个子网,与应用层隔离,后续做安全组规则会清爽很多。
安全组是新手最容易踩坑的地方。核心原则是:Nginx的安全组必须放通ELB的后端转发网段。华为云ELB(独享型)访问后端服务器时,源IP处理方式有保留客户端真实IP和还原两种模式,如果后端服务器通过100.125.0.0/16网段的健康检查探测,安全组里必须放通这个网段,否则健康检查会一直失败,后端权重再高也收不到流量。典型的Nginx安全组规则如下:
入方向规则: 协议 端口 源地址 说明 TCP 80 0.0.0.0/0 (若ELB为四层透传)HTTP流量 TCP 443 0.0.0.0/0 HTTPS流量 TCP 80 100.125.0.0/16 ELB健康检查网段,必须放通 出方向规则: 全部 全部 0.0.0.0/0 Nginx需访问后端应用与外部API
应用服务器的安全组则只放通来自Nginx子网的流量,比如Nginx子网是192.168.1.0/24,那么应用安全组的8080端口源地址就填这个网段。这样即使ELB被人误操作直连了应用,流量也进不来,链路清晰且安全。
另外一个细节:如果选择在ELB上做四层TCP监听并开启源IP透传(TOA),Nginx拿到的remote_addr就是用户真实IP;如果ELB做了七层或SNAT,则需要在Nginx里通过realip模块还原真实IP,下面配置里会讲到。
三、ELB监听器与Nginx的具体配置
先看云上部分。在华为云控制台创建独享型ELB,添加监听器:协议选TCP,前端端口443,后端端口443,后端服务器组挂上两台以上Nginx的ECS。健康检查协议建议用TCP探测443端口,间隔5秒,正常阈值3次,这样一台Nginx宕机后大约15秒内会被自动摘除。如果想在应用层确认Nginx配置正确,也可以用HTTP探测,路径设为一个轻量的健康检查接口,比如/healthz。
再看Nginx侧的完整配置示例,包含了真实IP还原、证书卸载、后端转发几个关键点:
# nginx.conf 核心片段
http {
# ELB七层模式下还原客户端真实IP
set_real_ip_from 100.125.0.0/16; # 华为云ELB健康检查与转发网段
set_real_ip_from 192.168.0.0/16; # VPC内网段
real_ip_header X-Forwarded-For;
real_ip_recursive on;
upstream app_servers {
server 192.168.2.10:8080 weight=3 max_fails=2 fail_timeout=10s;
server 192.168.2.11:8080 weight=3 max_fails=2 fail_timeout=10s;
server 192.168.2.12:8080 weight=1 backup; # 备用节点
keepalive 64; # 与后端保持长连接,降低连接开销
}
server {
listen 443 ssl http2;
server_name www.ipipp.com;
ssl_certificate /etc/nginx/certs/server.pem;
ssl_certificate_key /etc/nginx/certs/server.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_session_cache shared:SSL:20m;
location / {
proxy_pass http://app_servers;
proxy_http_version 1.1;
proxy_set_header Connection "";
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_connect_timeout 3s;
proxy_read_timeout 30s;
}
location /healthz {
access_log off;
return 200 "ok";
}
}
}
这段配置有几个点值得展开。upstream里的keepalive配合proxy_http_version 1.1和Connection为空这两行,能让Nginx到后端的连接复用起来,高并发时能明显降低TIME_WAIT数量。backup参数指定的那台服务器平时不接流量,只有前面两台全挂了才顶上,适合放一台配置较低的应急机器。
健康检查接口/healthz建议单独写,且关闭访问日志。ELB如果配了HTTP探测会周期性请求它,不关日志的话access.log会被探测请求刷爆,排查问题时会很痛苦。
四、会话保持、灰度发布与故障排查
会话保持有两种实现位置。在ELB上可以开启会话保持(七层监听基于cookie,四层基于源IP哈希),但混合架构下更推荐在Nginx用ip_hash或一致性哈希做,因为Nginx离应用更近,规则可以按location粒度控制,某些接口需要粘性、某些不需要,分得很细。要注意的是如果ELB已经做了源IP哈希,Nginx再叠一层ip_hash等于双重哈希,分布效果反而变差,二选一即可。
灰度发布是这套架构的亮点场景。比如只让1%的用户看到新版本,可以在Nginx里按cookie或用户ID哈希分流:
# 按用户ID尾号灰度:尾号为7的用户走新版本
map $cookie_uid $backend_pool {
~7$ new_version;
default stable_version;
}
upstream stable_version {
server 192.168.2.10:8080;
}
upstream new_version {
server 192.168.2.20:8080; # 新版本服务器组
}
server {
listen 443 ssl;
server_name www.ipipp.com;
location / {
proxy_pass http://$backend_pool;
}
}
排查故障时按链路顺序走:先看ELB后端服务器健康状态是否正常,如果显示异常,去Nginx机器上curl一下健康检查路径,多半是安全组没放通100.125.0.0/16;健康状态正常但访问502,则查Nginx到应用的连通性,telnet后端8080端口,同时看Nginx的error.log里有没有connect timed out;如果是偶发性504,重点检查proxy_read_timeout是否小于后端慢接口的处理时间,以及ELB监听器的空闲超时设置,默认往往只有60秒,长请求场景需要调大。
最后提一个容量问题:ELB和Nginx是两级转发,整体超时时间要成梯度设置,Nginx的proxy_read_timeout应该小于ELB的空闲超时,这样慢请求会先在Nginx层被熔断并返回明确的错误码,而不是在ELB层被静默掐断,用户端只看到一个含义模糊的连接重置。把这些细节提前想清楚,整套混合架构跑起来会稳定很多。