说说LoadBalancer- Nginx 配置 SSL 证书:HTTPS 负载均衡完整实操(学习笔记)

最近在做优化的时候涉及到了这块内容,觉得值得写下来,方便以后翻阅。

👋 大家好,欢迎来到我的技术博客! 📚 在这里,我会分享学习笔记、实战经验与技术思考,力求用轻松的方式讲清楚麻烦的问题。 🎯 本文将围绕LoadBalancer这个话题展开,希望能为你带来一些启发或实用的参考。 🌱 无论你是刚入门的新手,还是正在进阶的开发者,希望你都能有所收获!

文章目录

打包与启动 第二步:获取 SSL 证书 第三步:配置 Nginx 负载均衡 第四步:实现健康检查(Health Check) 第五步:会话保持(Session Persistence) 第六步:性能调优与安全加固 第七步:监控与日志分析 常见问题排查 高级话题:多层负载均衡总结

LoadBalancer- Nginx 配置 SSL 证书:HTTPS 负载均衡完整实操

在现代 Web 应用架构中,高可用性安全性可扩展性是三大核心诉求。而 Nginx 作为反向代理与负载均衡器,配合 SSL/TLS 证书实现 HTTPS 加密通信,已成为构建安全、高效 Web 服务的黄金组合 🔒。

本文将带你从零开始,完整实操如何用 Nginx 搭建支持 HTTPS 的负载均衡系统,并集成 Java 后端服务(Spring Boot),涵盖证书申请、Nginx 配置、健康检查、会话保持、性能调优等关键环节。无论你是 DevOps 工程师、后端开发者,还是系统架构师,都能从中获得实用技能 💪。


为什么需要 HTTPS 负载均衡?

在传统 HTTP 架构中,客户端直接与后端服务器通信,存在以下问题:

  • 数据明文传输:用户密码、支付信息等敏感数据易被窃听;
  • 中间人攻击(MITM):攻击者可篡改请求或响应;
  • 单点故障:一台服务器宕机导致整个服务不可用;
  • 性能瓶颈:单台服务器无法应对高并发流量。
引入 HTTPS 负载均衡后,上述问题迎刃而解:

加密通信:SSL/TLS 保障数据机密性与完整性;
高可用:多台后端服务器自动分担负载,故障自动剔除;
性能提升:Nginx 高效处理连接,释放后端资源;
统一入口:所有流量经由 Nginx 路由,便于监控、限流、WAF 集成。

🌐 根据 Let’s Encrypt 官方数据,截至 2024 年,全球超过 3 亿个网站已启用 HTTPS,安全已成为 Web 标配。

架构概览

咱们采用如下典型架构:

HTTPS

Client

Nginx Load Balancer

Backend Server 1:8081

Backend Server 2:8082

Backend Server 3:8083

Database

  • Nginx:运行在公网 IP 上,监听 443 端口,处理 SSL 终止(SSL Termination);
  • 后端服务:三台 Spring Boot 应用,运行在内网,仅监听 HTTP(8081~8083);
  • 数据库:共享存储,非本文重点,略过。
💡 SSL Termination(SSL 终止) 是指在负载均衡器上解密 HTTPS 流量,后端以 HTTP 通信。这减轻了后端服务器的 CPU 负担(SSL 加解密开销大),同时简化证书管理。

第一步:准备后端 Java 服务

咱们先搭建一个轻松的 Spring Boot 应用,用于模拟真实业务逻辑。

创建 Spring Boot 项目

使用 Spring Initializr 快速生成项目,依赖选择 Spring Web

pom.xml(Maven)


    4.0.0

    com.example
    backend-service
    1.0.0
    jar

        org.springframework.boot
        spring-boot-starter-parent
        3.2.0

            org.springframework.boot
            spring-boot-starter-web

                org.springframework.boot
                spring-boot-maven-plugin

主启动类

package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class BackendApplication {
    public static void main(String[] args) {
        SpringApplication.run(BackendApplication.class, args);
    }
}

控制器:返回服务标识与时间

package com.example.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

@RestController
public class HealthController {

    @Value("${server.port}")
    private int port;

    @GetMapping("/api/health")
    public String healthCheck() {
        String time = LocalDateTime.now()
                .format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        return String.format("Backend Service on port %d - Time: %s", port, time);
    }

    @GetMapping("/api/user")
    public String getUser() {
        return "User data from backend on port " + port;
    }
}

配置文件 application.yml

server:
  port: 8081  # 实际部署时,每台机器修改为不同端口

⚠️ 留意:在生产环境中,每台后端服务器应运行在不同端口或不同主机上。本地测试可用同一主机不同端口模拟。

打包与启动

# 打包
mvn clean package -DskipTests

# 启动三个实例(Linux/macOS)
nohup java -jar target/backend-service-1.0.0.jar --server.port=8081 > app1.log 2>&1 &
nohup java -jar target/backend-service-1.0.0.jar --server.port=8082 > app2.log 2>&1 &
nohup java -jar target/backend-service-1.0.0.jar --server.port=8083 > app3.log 2>&1 &

验证服务是否正常:

curl http://localhost:8081/api/health
# 输出:Backend Service on port 8081 - Time: 2024-06-01 10:00:00


第二步:获取 SSL 证书

SSL 证书有多种

前提条件

  • 你拥有一个域名(如 example.com);
  • 该域名已解析到你的 Nginx 服务器公网 IP;
  • 服务器开放 80 和 443 端口(Let’s Encrypt 验证需 80 端口)。

使用 Certbot 申请证书

Certbot 是 Let’s Encrypt 官方推荐的客户端工具。

安装 Certbot(Ubuntu/Debian)

sudo apt update
sudo apt install certbot -y

申请证书(Standalone 模式)
⚠️ 留意:Standalone 模式会临时占用 80 端口,确保 Nginx 未运行或已停止。

sudo systemctl stop nginx  # 如果 Nginx 正在运行
sudo certbot certonly --standalone -d your-domain.com

按提示输入邮箱、同意条款,成功后证书将保存在:

/etc/letsencrypt/live/your-domain.com/
├── cert.pem      # 服务器证书
├── chain.pem     # 中间证书
├── fullchain.pem # 服务器证书 + 中间证书(Nginx 用这个)
└── privkey.pem   # 私钥

🔐 安全提示:privkey.pem 是私钥,必须严格保护,权限设为 600。
自动续期

Let’s Encrypt 证书有效期 90 天,需自动续期:

# 测试续期
sudo certbot renew --dry-run

# 添加定时任务(每天凌晨 2 点检查)
echo "0 2 * * * /usr/bin/certbot renew --quiet && systemctl reload nginx" | sudo crontab -

📚 更多关于 Certbot 的使用,可参考 官方文档。

第三步:配置 Nginx 负载均衡

现在,咱们配置 Nginx 作为 HTTPS 负载均衡器。

安装 Nginx

sudo apt install nginx -y
sudo systemctl start nginx
sudo systemctl enable nginx

编写 Nginx 配置文件

创建 /etc/nginx/sites-available/https-loadbalancer

# 定义上游服务器组
upstream backend_servers {
    # 轮询(默认)
    server 127.0.0.1:8081;
    server 127.0.0.1:8082;
    server 127.0.0.1:8083;

    # 可选:开启健康检查(需 Nginx Plus 或第三方模块)
    # 本文使用开源版,后续用脚本实现健康检查
}

# HTTP 重定向到 HTTPS
server {
    listen 80;
    server_name your-domain.com;

    # ACME 挑战路径(用于证书续期)
    location /.well-known/acme-challenge/ {
        root /var/www/certbot;
    }

    # 强制跳转 HTTPS
    location / {
        return 301 https://$host$request_uri;
    }
}

# HTTPS 服务
server {
    listen 443 ssl http2;
    server_name your-domain.com;

    # SSL 证书
    ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;

    # 推荐的 SSL 配置(来自 Mozilla SSL Config Generator)
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
    ssl_prefer_server_ciphers off;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;

    # HSTS(HTTP Strict Transport Security)
    add_header Strict-Transport-Security "max-age=63072000" always;

    # 日志
    access_log /var/log/nginx/https-access.log;
    error_log /var/log/nginx/https-error.log;

    # 负载均衡代理
    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;

        # 超时设置
        proxy_connect_timeout 5s;
        proxy_send_timeout 10s;
        proxy_read_timeout 10s;
    }

    # 健康检查端点(可选,供外部监控)
    location /lb-health {
        access_log off;
        default_type text/plain;
        return 200 "OK\n";
    }
}

启用配置

# 创建符号链接
sudo ln -s /etc/nginx/sites-available/https-loadbalancer /etc/nginx/sites-enabled/

# 测试配置
sudo nginx -t

# 重载 Nginx
sudo systemctl reload nginx

✅ 成功标志:访问 https://your-domain.com/api/health,应看到后端返回内容,且浏览器地址栏显示锁图标 🔒。

第四步:实现健康检查(Health Check)

Nginx 开源版不支持主动健康检查(需 Nginx Plus),但我们可以通过 Shell 脚本 + Cron 实现。

健康检查脚本

创建 /usr/local/bin/nginx-health-check.sh

#!/bin/bash

# 后端服务器列表
BACKENDS=("127.0.0.1:8081" "127.0.0.1:8082" "127.0.0.1:8083")
NGINX_CONF="/etc/nginx/conf.d/backend-upstream.conf"
TEMP_CONF="/tmp/backend-upstream.conf"

# 检查单个后端
check_backend() {
    local backend=$1
    if curl -sf http://$backend/api/health > /dev/null; then
        echo "    server $backend;" >> $TEMP_CONF
        echo "$(date): $backend is UP"
    else
        echo "$(date): $backend is DOWN"
    fi
}

# 生成新的 upstream 配置
echo "upstream backend_servers {" > $TEMP_CONF
for backend in "${BACKENDS[@]}"; do
    check_backend $backend
done
echo "}" >> $TEMP_CONF

# 比较新旧配置,若不同则重载 Nginx
if ! cmp -s $TEMP_CONF $NGINX_CONF; then
    cp $TEMP_CONF $NGINX_CONF
    nginx -t && systemctl reload nginx
    echo "$(date): Nginx reloaded due to backend status change"
fi

# 清理临时文件
rm -f $TEMP_CONF

赋予执行权限:

chmod +x /usr/local/bin/nginx-health-check.sh

添加定时任务

每 10 秒检查一次(Cron 最小粒度为分钟,可用循环脚本):

# 创建守护脚本 /usr/local/bin/health-check-daemon.sh
#!/bin/bash
while true; do
    /usr/local/bin/nginx-health-check.sh
    sleep 10
done

# 后台运行
nohup /usr/local/bin/health-check-daemon.sh > /var/log/health-check.log 2>&1 &

🔄 现在,当某台后端宕机,Nginx 会在 10 秒内将其从 upstream 中移除,实现自动故障转移。

第五步:会话保持(Session Persistence)

某些应用(如购物车)要求用户始终路由到同一后端服务器。Nginx 支持基于 IP 哈希Cookie 的会话保持。

方案一:IP Hash(简单但不完美)

修改 upstream:

upstream backend_servers {
    ip_hash;  # 基于客户端 IP 哈希
    server 127.0.0.1:8081;
    server 127.0.0.1:8082;
    server 127.0.0.1:8083;
}

⚠️ 缺点:NAT 网络下多个用户共享同一公网 IP,导致负载不均。

方案二:基于 Cookie(推荐)

使用 sticky 模块(需编译 Nginx 时加入 --add-module),但开源版不支持。替代方案:应用层生成 sticky cookie

Java 后端生成 Sticky Cookie

修改控制器:

@GetMapping("/api/login")
public ResponseEntity login(HttpServletResponse response) {
    // 生成唯一 session ID
    String sessionId = UUID.randomUUID().toString();

    // 设置 sticky cookie,值为当前端口(简化)
    ResponseCookie cookie = ResponseCookie.from("SERVERID", String.valueOf(port))
            .path("/")
            .maxAge(Duration.ofHours(1))
            .httpOnly(true)
            .secure(true) // 仅 HTTPS 传输
            .build();

    response.addHeader("Set-Cookie", cookie.toString());
    return ResponseEntity.ok("Logged in");
}

Nginx 根据 Cookie 路由

map $cookie_SERVERID $backend_port {
    "8081" 8081;
    "8082" 8082;
    "8083" 8083;
    default 8081; # 默认轮询
}

server {
    listen 443 ssl;
    # ... 其他配置 ...

    location / {
        # 如果有 SERVERID cookie,直连对应后端
        if ($backend_port) {
            proxy_pass http://127.0.0.1:$backend_port;
            break;
        }
        # 否则走负载均衡
        proxy_pass http://backend_servers;
        # ... proxy_set_header ...
    }
}

🧩 这种方式灵活,但需应用配合。更优雅的做法是使用 Redis 共享 Session,彻底解决会话保持问题。

第六步:性能调优与安全加固

Nginx 性能调优

# /etc/nginx/nginx.conf
worker_processes auto;
worker_rlimit_nofile 65535;

events {
    worker_connections 10240;
    use epoll; # Linux 高效事件模型
    multi_accept on;
}

http {
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    keepalive_requests 100;

    # Gzip 压缩
    gzip on;
    gzip_vary on;
    gzip_min_length 1024;
    gzip_types text/plain application/json application/javascript text/css;
}

安全加固

1. 隐藏 Nginx 版本号

server_tokens off;

2. 限制请求方法

if ($request_method !~ ^(GET|HEAD|POST)$ ) {
    return 405;
}

3. 防止 DDoS

limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
location /api/ {
    limit_req zone=api burst=20 nodelay;
}

4. CSP 头(内容安全策略)

add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'";

🛡️ 更多安全实践,可参考 OWASP Secure Headers Project。

第七步:监控与日志分析

日志格式优化

自定义日志格式,包含后端响应时间:

log_format detailed '$remote_addr - $remote_user [$time_local] '
                   '"$request" $status $body_bytes_sent '
                   '"$http_referer" "$http_user_agent" '
                   'rt=$request_time uct="$upstream_connect_time" '
                   'uht="$upstream_header_time" urt="$upstream_response_time"';

access_log /var/log/nginx/detailed.log detailed;

使用 Prometheus + Grafana 监控

虽然 Nginx 开源版无内置指标,但可通过 nginx-prometheus-exporter(需 Nginx Plus)或解析日志实现。

简易方案:用 Shell 脚本统计 5xx 错误:

# 每分钟统计 5xx 错误数
grep '" 5[0-9][0-9] ' /var/log/nginx/https-access.log | wc -l


常见问题排查

1. HTTPS 访问返回 502 Bad Gateway

  • 检查后端服务是否运行:curl http://127.0.0.1:8081
  • 检查 Nginx 是否能访问后端:telnet 127.0.0.1 8081
  • 查看 Nginx 错误日志:tail -f /var/log/nginx/error.log

2. 证书无效或过期

  • 检查证书路径是否正确;
  • 执行 sudo certbot renew --dry-run 测试续期;
  • 确保域名 DNS 解析正确。

3. 负载不均

  • 检查 upstream 配置是否启用了 ip_hash 或其他策略;
  • 使用 abwrk 压测验证分布。

高级话题:多层负载均衡

对于超大型系统,可采用 L4 + L7 混合架构

渲染错误: Mermaid 渲染失败: Parse error on line 2: ...4 Load Balancer

(e.g., AWS ALB, HAPr -----------------------^ Expecting 'SQE', 'DOUBLECIRCLEEND', 'PE', '-)', 'STADIUMEND', 'SUBROUTINEEND', 'PIPE', 'CYLINDEREND', 'DIAMOND_STOP', 'TAGEND', 'TRAPEND', 'INVTRAPEND', 'UNICODE_TEXT', 'TEXT', 'TAGSTART', got 'PS'

  • L4 层(传输层):基于 IP+端口分发,性能极高;
  • L7 层(应用层):Nginx 做细粒度路由、SSL 终止、WAF。
☁️ 云服务商(如 AWS、Azure)给出托管负载均衡器,可与 Nginx 结合使用。

总结

通过本文,你已掌握:

✅ 使用 Spring Boot 构建可扩展的后端服务;
✅ 通过 Let’s Encrypt 免费获取 SSL 证书;
✅ 配置 Nginx 实现 HTTPS 负载均衡;
✅ 实现健康检查、会话保持、性能调优;
✅ 加固安全、监控日志。

这套架构已在无数生产环境中验证,兼具安全性可靠性成本效益。下一步,你可以探索:

  • 将 Nginx 替换为 EnvoyTraefik(云原生网关);
  • 使用 Kubernetes Ingress 管理负载均衡;
  • 集成 WAF(如 ModSecurity)防御 Web 攻击。
🌟 记住:安全不是功能,而是持续的过程。定期更新证书、打补丁、审计配置,才能构筑真正的防线。

现在,就去部署你的 HTTPS 负载均衡系统吧!🚀


🙌

暂时整理到这里。以上都是个人理解,可能有疏漏,欢迎指正。

评论 (0)

暂无评论