云数据库SSL卸载网关架构:基于Nginx Stream模块实现全透明加密与连接池优化
背景与挑战
云数据库通常建议启用SSL加密传输,但客户端直连数据库时,TLS握手带来的CPU开销和延迟不可忽视。尤其在高并发场景下,每个连接都要重复协商密钥,直接拖垮数据库端的极限性能。更常见的是,大量遗留应用不支持或懒得配置证书链,运维人员被迫开启明文端口,风险极高。
本方案使用Nginx Stream模块作为透明SSL卸载网关,将TLS终结在Nginx层,后端与云数据库通过内网明文通信(或轻量TLS),从而集中管理证书、复用SSL会话、统一连接池。实测在轻云互联的云数据库实例(内网VPC延迟<0.1ms)上,SSL卸载后数据库负载降低约30%,QPS提升25%以上。
架构设计要点
- 正向代理模式:客户端 -> Nginx(公网/内网) -> 云数据库(内网)
- SSL终结层:Nginx负责SSL握手,使用共享内存缓存session,后续客户端连接复用密钥,跳过完整握手。
- 数据库连接池接管:Nginx通过
upstream模块的keepalive指令保持与云数据库的长连接,避免每次请求重建TCP+MySQL握手。 - 全透明协议:Nginx仅透传MySQL/PostgreSQL协议包,客户端无需修改连接串(只要指向Nginx的公网IP及SSL端口)。
关键Nginx配置详解
1. SSL卸载与会话缓存
stream {
upstream db_backend {
server 10.10.1.100:3306 max_conns=200;
keepalive 64; # 保持64条空闲连接
}
server {
listen 33061 ssl;
ssl_certificate /etc/nginx/ssl/db_fullchain.crt;
ssl_certificate_key /etc/nginx/ssl/db_privkey.pem;
# 共享SSL session缓存,减少重复握手
ssl_session_cache shared:SSL:64m;
ssl_session_timeout 12h;
ssl_session_tickets on; # 配合session ticket,进一步优化客户端复用
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers on;
proxy_pass db_backend;
proxy_protocol off; # 若需要客户端真实IP可开启proxy_protocol,但云数据库需支持
# 连接超时控制
proxy_connect_timeout 5s;
proxy_timeout 30s;
}
}
核心优化点:ssl_session_cache shared:SSL:64m 用内存共享缓存所有worker进程的SSL session,而不是每个进程单独缓存,避免因多进程导致的session失效;ssl_session_tickets on 允许服务端签发session ticket,客户端本地缓存,彻底消除后续请求的SSL握手。
2. 连接池与健康检查
stream {
upstream db_backend {
zone db 256k; # 共享状态区,支持动态健康检查
server 10.10.1.100:3306 weight=5;
server 10.10.1.101:3306 backup; # 备用只读节点
# 主动健康检查(需nginx-plus或第三方模块,以下为示意)
# health_check interval=5 passes=2 fails=3;
}
server {
listen 33061 ssl;
...
proxy_pass db_backend;
}
}
通过zone分配共享内存,健康检查模块(若使用nginx-plus或openresty)能实时摘除故障的数据库节点。后端max_conns=200限制单个后端最大连接数,防止雪崩。
3. 日志与审计
stream {
log_format db_proxy '$remote_addr [$time_local] $protocol $status $bytes_sent $bytes_received $session_time';
access_log /var/log/nginx/db_access.log db_proxy buffer=32k flush=5s;
server {
...
access_log /var/log/nginx/db_ssl.log db_proxy;
}
}
所有流经网关的数据库操作都能被记录,方便安全审计和慢连接排查。注意buffer和flush减少磁盘I/O。
性能调优实战
- worker_processes:直接绑定轻云互联云服务器物理核,
auto+worker_cpu_affinity auto,避免内核迁移。 - 连接复用:
proxy_buffering off(stream模块默认无buffering),配合keepalive使数据库端TCP连接数从10万降至几百。 - sysctl调优:
# 增大网络连接队列 net.core.somaxconn = 65535 net.ipv4.tcp_max_syn_backlog = 65535 # 减少TIME_WAIT(客户端到Nginx段) net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_fin_timeout = 15 - 内存优化:关闭Nginx不必要的模块(如ngx_http_*),只保留stream及ssl模块,减少内存碎片。
故障排查排错记录
上线初期遇到SSL session缓存命中率极低的问题,只有5%。排查发现多个worker进程共享SSL缓存时,客户端IP分布不均匀导致缓存频繁被淘汰。解决方案:调大ssl_session_cache到128m,同时开启ssl_session_tickets后命中率上升到92%。
另一个坑:后端云数据库的wait_timeout低(默认8小时),但Nginx的proxy_timeout设置为30s,导致后端不断释放空闲连接,keepalive命中率下降。将数据库参数wait_timeout=86400并重启后解决。
更隐蔽的问题:部分客户端(如Java驱动)使用的TLS版本不支持session ticket,导致这些客户端每次都是完整握手。我们在Nginx配置中ssl_session_tickets on同时降级支持ssl_session_cache作为回退,兼容所有客户端。
建议:部署前使用openssl s_client -connect 网关IP:33061 -reconnect验证session复用情况。
总结
这套基于Nginx Stream的SSL卸载网关已稳定运行在轻云互联云数据库集群上,将数据库侧的SSL计算开销完全剥离,同时简化了证书管理。后续可扩展为读写分离架构:在upstream中混合主库和只读从库,利用split_clients模块按查询类型分流——但这是另一篇了。