在Ubuntu服务器上配置Nginx反向代理和负载均衡,核心就是通过upstream模块把多台后端应用服务器串联起来,再配合health_check机制自动剔除故障节点。整个配置流程分为三步:安装Nginx、编写upstream和proxy_pass规则、设置健康检查策略。下面直接给你完整的操作方案和原理拆解。

一、Ubuntu环境下Nginx的安装与基础准备

先确保系统是Ubuntu 20.04或22.04,执行以下命令安装Nginx:

sudo apt update
sudo apt install nginx -y

安装完成后,确认Nginx版本是否支持被动健康检查(默认就支持)和主动健康检查模块(需要编译安装nginx_upstream_check_module或使用开源版本的nginx-healthcheck-plugin)。对于大多数场景,Nginx自带的被动健康检查(fail_timeout + max_fails)已经够用。验证安装:

nginx -v

确认版本号后,进入配置目录:

cd /etc/nginx/

Ubuntu的Nginx主配置文件是nginx.conf,但实际业务配置建议放在/etc/nginx/conf.d/目录下新建独立文件,比如proxy_loadbalance.conf,方便管理和维护。

二、upstream反向代理与负载均衡的核心配置

打开或新建配置文件,在http块内编写upstream定义。这是整个反向代理的心脏部分:

upstream backend_servers {
    # 负载均衡策略:默认轮询(round-robin),也可选least_conn(最少连接)、ip_hash(IP哈希)
    least_conn;

    server 192.168.1.101:8080 weight=5 max_fails=3 fail_timeout=30s;
    server 192.168.1.102:8080 weight=3 max_fails=3 fail_timeout=30s;
    server 192.168.1.103:8080 weight=2 max_fails=3 fail_timeout=30s;

    # 备用服务器,主节点全挂时才启用
    server 192.168.1.104:8080 backup;
}

这里有几个关键参数需要理解:weight是权重,数值越大分配的请求越多;max_fails表示在fail_timeout时间内失败多少次就认为该节点不可用;fail_timeout是判定节点不可用的时间窗口,同时也是节点恢复后重新尝试的间隔。least_conn策略特别适合后端服务器性能不均的场景,它会把新请求分配给当前连接数最少的节点。

接下来在server块中配置反向代理:

server {
    listen 80;
    server_name yourdomain.com;

    location / {
        proxy_pass http://backend_servers;
        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 X-Forwarded-Proto $scheme;

        # 超时设置,防止后端响应慢导致Nginx阻塞
        proxy_connect_timeout 10s;
        proxy_send_timeout 30s;
        proxy_read_timeout 60s;
    }
}

proxy_set_header这几行非常重要,它把客户端的真实IP和协议信息传递给后端,否则后端看到的全部是Nginx的内网IP。超时参数根据业务调整,如果是API接口建议proxy_read_timeout设长一些。

三、健康检查的两种实现方式详解

Nginx的健康检查分被动和主动两种。被动健康检查是Nginx原生支持的,通过max_fails和fail_timeout自动实现。当Nginx转发请求到某个后端节点,如果连续失败max_fails次,就在fail_timeout时间内不再给该节点分配请求。这是最简单也最常用的方式。

主动健康检查需要额外模块支持。如果你用的是开源的tengine或者编译了healthcheck模块,可以这样配置:

upstream backend_servers {
    server 192.168.1.101:8080 max_fails=3 fail_timeout=30s;
    server 192.168.1.102:8080 max_fails=3 fail_timeout=30s;

    # 主动健康检查配置
    check interval=5000 rise=2 fall=3 timeout=3000 type=http;
    check_http_send "GET /health HTTP/1.0\r\n\r\n";
    check_http_expect_alive http_2xx http_3xx;
}

check interval=5000表示每5秒检测一次;rise=2表示连续2次成功才认为节点恢复;fall=3表示连续3次失败才标记为不可用;type=http表示用HTTP方式检测;check_http_send是发送的探测请求,check_http_expect_alive定义了哪些响应码算正常。这种方式的优势是不依赖真实用户请求,能更快发现故障。

如果你没有编译healthcheck模块,也可以用一个替代方案:在后端应用自己暴露一个/health接口返回200,然后通过Nginx的被动检查机制来间接实现。或者用cron脚本定期curl后端接口,失败则通过API动态修改Nginx upstream(需要配合openresty或lua模块)。

四、配置验证与重载的正确姿势

每次改完配置文件,必须先测试语法再重载,否则一个括号错误就会导致Nginx挂掉:

sudo nginx -t

看到"syntax is ok"和"test is successful"才能执行重载:

sudo systemctl reload nginx

注意是reload不是restart,reload是平滑重载,不会中断现有连接。如果你需要彻底重启(比如改了监听端口),才用restart。

五、日志监控与故障排查实战

负载均衡跑起来之后,日志是你的眼睛。Nginx的访问日志和错误日志默认在/var/log/nginx/目录下。建议在upstream级别开启日志:

upstream backend_servers {
    server 192.168.1.101:8080;
    server 192.168.1.102:8080;

    # 记录每次转发到哪个节点
    # 需要在http块中配置log_format
}

在http块中定义带upstream信息的日志格式:

log_format main '$remote_addr - $remote_user [$time_local] '
                '"$request" $status $body_bytes_sent '
                '"$http_referer" "$http_user_agent" '
                'upstream: $upstream_addr '
                'upstream_status: $upstream_status '
                'request_time: $request_time';

access_log /var/log/nginx/access.log main;

$upstream_addr会记录实际转发到的后端IP和端口,$upstream_status记录后端返回的状态码,$request_time记录整个请求耗时。通过这些字段你可以清楚看到流量分配是否均衡、哪个节点响应慢、哪个节点在报错。

日常排查命令:查看哪些后端节点被标记为不可用:

sudo nginx -T 2>/dev/null | grep -A 20 "upstream"

实时监控错误日志:

tail -f /var/log/nginx/error.log

如果发现某个节点频繁被踢出,先检查后端服务本身是否稳定,再检查网络连通性(ping、telnet端口),最后看是不是weight设置不合理导致某节点过载。

六、进阶优化:会话保持与SSL终结

如果你的后端应用需要会话保持(比如用户登录后需要一直访问同一台服务器),把least_conn改成ip_hash:

upstream backend_servers {
    ip_hash;
    server 192.168.1.101:8080;
    server 192.168.1.102:8080;
}

ip_hash根据客户端IP计算哈希值,同一IP始终路由到同一后端。但要注意,如果后端节点增减,哈希会重新分配,可能导致大量会话丢失。生产环境更推荐用Redis共享session或者在应用层做无状态设计。

SSL终结也是Nginx反向代理的常见场景,在server块中加上SSL配置:

server {
    listen 443 ssl;
    server_name yourdomain.com;

    ssl_certificate /etc/nginx/ssl/yourdomain.crt;
    ssl_certificate_key /etc/nginx/ssl/yourdomain.key;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;

    location / {
        proxy_pass http://backend_servers;
        # ...其余proxy配置同上
    }
}

SSL证书可以用certbot免费申请,配置好后HTTP请求建议301跳转到HTTPS,保证传输安全。

七、常见踩坑点与经验总结

第一,不要把所有后端服务器都设成相同weight,如果机器配置不同,一定要按性能比例分配权重。第二,fail_timeout不要设太短,比如设成5秒,网络抖动一下就把节点踢了,流量全压到剩余节点反而更危险,建议30秒起步。第三,如果后端是WebSocket应用,必须加这几行:

proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";

否则WebSocket连接会直接断开。第四,生产环境建议至少三台后端服务器,两台做负载一台做backup,单点故障风险太高。第五,定期用ab或wrk做压力测试,验证负载均衡是否真正生效,而不是只看配置觉得没问题。

总结一下,Ubuntu上Nginx反向代理和负载均衡的配置并不复杂,核心就是upstream定义加proxy_pass转发,健康检查用被动机制基本够用,主动检查需要额外模块支持。把日志配好、参数调对、测试做足,这套方案在中小型项目里完全能扛住日均百万级请求。