很多开发团队都会遇到这样的场景:业务系统部署在公有云上,但数据库服务器放在内网环境。直接暴露MySQL的3306端口显然不安全,黑客扫描到开放端口后可能尝试暴力破解。我见过太多因为数据库端口暴露导致的安全事件,轻则数据泄露,重则整个数据库被勒索加密。
这时候Nginx的Stream模块就像个智能门卫,它能在公网和内网之间建立加密通道。具体来说有三大优势:
去年我们有个电商项目就用了这个方案。当时客户要求供应商能实时查询库存数据,但又不愿意直接开放数据库访问。用Nginx转发后,供应商通过特定端口连接,我们在Nginx层还做了IP白名单限制,既满足了业务需求又保障了安全。
在开始配置前,先确认你的Nginx是否支持stream模块。执行这个命令查看编译参数:
bash复制nginx -V 2>&1 | grep -o with-stream
如果输出with-stream就说明已经支持。这里有个坑要注意:用yum/apt安装的Nginx通常自带stream模块,但源码编译安装的可能需要手动添加。我去年帮客户排查问题时就遇到过,他们自己编译的Nginx死活不生效,最后发现是漏了--with-stream参数。
如果确实缺少stream模块,需要重新编译。分享下我的标准编译参数:
bash复制./configure \
--prefix=/usr/local/nginx \
--user=nginx \
--group=nginx \
--with-http_ssl_module \
--with-http_realip_module \
--with-http_gzip_static_module \
--with-stream \
--with-stream_ssl_module
关键点在于最后两个参数:--with-stream启用基础模块,--with-stream_ssl_module支持SSL加密。编译完成后别急着重启,先用nginx -t测试配置文件,避免服务崩溃。
在nginx.conf的最外层(注意不是在http块内)添加stream配置:
nginx复制stream {
upstream mysql_backend {
server 192.168.1.100:3306 weight=1;
}
server {
listen 3307;
proxy_pass mysql_backend;
proxy_connect_timeout 3s;
proxy_timeout 1h;
}
}
这个配置实现了:
实际生产环境我建议增加这些参数:
nginx复制server {
listen 3307 so_keepalive=on;
proxy_pass mysql_backend;
proxy_buffer_size 16k;
# 连接控制
proxy_connect_timeout 10s;
proxy_timeout 2h;
# 流量限制
proxy_upload_rate 512k;
proxy_download_rate 2m;
# 错误处理
proxy_next_upstream on;
proxy_next_upstream_timeout 5s;
proxy_next_upstream_tries 3;
}
特别是proxy_upload_rate和proxy_download_rate这两个参数,能防止某个客户端耗尽所有带宽。曾经有个报表系统因为没有限速,一个全表查询把网络带宽占满了。
第一次启动时可能会遇到这个错误:
code复制bind() to 0.0.0.0:3307 failed (13: Permission denied)
这是因为Linux系统默认不允许非root进程监听1024以下端口。有三种解决方案:
bash复制setcap 'cap_net_bind_service=+ep' /usr/sbin/nginx
bash复制iptables -t nat -A PREROUTING -p tcp --dport 3307 -j REDIRECT --to-port 53307
如果系统启用了SELinux(用getenforce命令查看),需要执行:
bash复制semanage port -a -t http_port_t -p tcp 3307
这比直接关闭SELinux更安全。我总跟客户强调:安全机制不是我们的敌人,要学会和它们共处。
先用telnet检查端口是否开放:
bash复制telnet your_nginx_ip 3307
如果连接立即断开,可能是:
nginx -s reload推荐用mysql客户端实测:
bash复制mysql -h nginx_host -P 3307 -u dbuser -p
如果连接成功但查询报错,可能是:
proxy_set_header)经过多个项目实践,我总结了这些优化技巧:
连接池配置:
nginx复制upstream mysql_backend {
server 192.168.1.100:3306 max_conns=100;
keepalive 20;
}
max_conns限制最大连接数,keepalive维持长连接
TCP参数调优:
nginx复制server {
listen 3307 so_keepalive=30m::10;
tcp_nodelay on;
}
这个配置会:
监控指标:
在Nginx状态模块中添加:
nginx复制server {
listen 8080;
location /stream_status {
stream_status;
}
}
这样可以实时查看活跃连接数、流量等指标
只允许特定IP访问:
nginx复制server {
listen 3307;
allow 203.0.113.5;
allow 198.51.100.0/24;
deny all;
proxy_pass mysql_backend;
}
生成证书后配置:
nginx复制server {
listen 3307 ssl;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
proxy_pass mysql_backend;
}
防止暴力破解:
nginx复制limit_conn_zone $binary_remote_addr zone=mysql_conn:10m;
server {
listen 3307;
limit_conn mysql_conn 5;
proxy_pass mysql_backend;
}
每个IP最多允许5个并发连接
问题1:连接经常超时断开
proxy_timeout值是否过小wait_timeout参数是否小于Nginx的超时设置问题2:大数据量查询失败
proxy_buffer_size(建议16k-64k)proxy_upload_rate和proxy_download_ratemax_allowed_packet参数问题3:性能瓶颈定位
ss -tlnp查看连接状态记得有次客户反映查询变慢,最后发现是Nginx的buffer设置太小,导致大量小包传输。调整后性能提升了40%。