使用 Nginx Stream 模块代理 TCP 连接,实现数据库端口转发和负载均衡。

应用场景

  • 端口映射:本地 33078 → 远程 MySQL 3306
  • 负载均衡:多个数据库实例分流
  • 连接日志:记录所有数据库连接信息
  • 安全加固:隐藏真实数据库地址

Nginx Stream 配置

完整配置示例

/etc/nginx/nginx.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;

# 增加文件描述符限制
worker_rlimit_nofile 65535;

events {
worker_connections 10240;
}

# HTTP 配置(Web 服务)
http {
# ... HTTP 配置保留 ...
include /etc/nginx/sites-enabled/*;
}

# Stream 配置(TCP/UDP 代理)
stream {
# 日志格式
log_format proxy '$remote_addr [$time_local] '
'$protocol $status $bytes_sent $bytes_received '
'$session_time "$upstream_addr" '
'"$upstream_bytes_sent" "$upstream_bytes_received" "$upstream_connect_time"';

access_log /var/log/nginx/tcp-access.log proxy;
error_log /var/log/nginx/tcp-error.log;
open_log_file_cache off;

# MySQL 代理
server {
listen 33078;
proxy_pass mysql.example.com:3306;
proxy_timeout 300m;
proxy_connect_timeout 10s;
}

# PostgreSQL 代理
server {
listen 54320;
proxy_pass postgres.example.com:5432;
proxy_timeout 300m;
}

# Redis 代理
server {
listen 63790;
proxy_pass redis.example.com:6379;
proxy_timeout 600s;
}

# MongoDB 代理
server {
listen 27018;
proxy_pass mongodb.example.com:27017;
proxy_timeout 300m;
}
}

配置说明

日志字段

字段 说明
$remote_addr 客户端 IP
$time_local 本地时间
$protocol 协议(TCP/UDP)
$status 连接状态
$bytes_sent 发送字节数
$bytes_received 接收字节数
$session_time 会话时长
$upstream_addr 后端服务器地址
$upstream_connect_time 连接时间

超时设置

1
2
proxy_timeout 300m;           # 会话超时(5 小时)
proxy_connect_timeout 10s; # 连接超时(10 秒)

负载均衡配置

多个 MySQL 实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
stream {
# 定义后端服务器组
upstream mysql_backend {
# 负载均衡策略:least_conn(最少连接)
least_conn;

server mysql1.example.com:3306 weight=2 max_fails=3 fail_timeout=30s;
server mysql2.example.com:3306 weight=1 max_fails=3 fail_timeout=30s;
server mysql3.example.com:3306 backup; # 备用服务器
}

server {
listen 33078;
proxy_pass mysql_backend;
proxy_timeout 300m;
}
}

负载均衡策略

策略 说明
round-robin 轮询(默认)
least_conn 最少连接数
hash $remote_addr IP 哈希(会话保持)
random 随机

IP Hash(会话保持)

1
2
3
4
5
upstream mysql_backend {
hash $remote_addr consistent;
server mysql1.example.com:3306;
server mysql2.example.com:3306;
}

SSL/TLS 加密

添加 SSL 层

1
2
3
4
5
6
7
8
9
10
11
stream {
server {
listen 33078 ssl;
proxy_pass mysql.example.com:3306;

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

访问控制

IP 白名单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
stream {
# 定义允许的 IP
geo $allowed_ip {
default 0;
192.168.1.0/24 1;
10.0.0.0/8 1;
}

server {
listen 33078;

# 检查 IP
if ($allowed_ip = 0) {
return 403;
}

proxy_pass mysql.example.com:3306;
}
}

使用示例

连接 MySQL

1
2
3
4
5
# 直接连接(原始)
mysql -h mysql.example.com -P 3306 -u user -p

# 通过代理连接
mysql -h proxy.example.com -P 33078 -u user -p

连接 PostgreSQL

1
psql -h proxy.example.com -p 54320 -U user -d database

连接 Redis

1
redis-cli -h proxy.example.com -p 63790

查看连接日志

1
2
3
4
5
# 实时查看
tail -f /var/log/nginx/tcp-access.log

# 示例输出:
# 192.168.1.100 [29/Sep/2024:15:34:32 +0800] TCP 200 1024 2048 300.123 "mysql.example.com:3306" "512" "1536" "0.003"

性能优化

连接池配置

1
2
3
4
5
6
worker_rlimit_nofile 65535;  # 增加文件描述符

events {
worker_connections 10240; # 增加连接数
use epoll; # Linux 使用 epoll
}

缓冲区调优

1
2
3
4
5
6
7
8
9
10
stream {
server {
listen 33078;
proxy_pass mysql.example.com:3306;

proxy_buffer_size 16k;
proxy_upload_rate 0;
proxy_download_rate 0;
}
}

健康检查

需要 Nginx Plus 或第三方模块:

1
2
3
4
5
6
upstream mysql_backend {
server mysql1.example.com:3306;

# Nginx Plus 健康检查
health_check interval=5s fails=3 passes=2;
}

开源版替代方案:使用外部脚本定期检测。

注意事项

  • Stream 模块默认编译,无需额外安装
  • 与 HTTP 配置在同一个 nginx.conf 文件中
  • 日志文件单独配置,避免与 HTTP 日志混淆
  • 超时时间根据实际业务调整(长连接可设置更大值)
  • 生产环境建议启用 SSL/TLS
  • 定期清理日志文件,避免磁盘占满

参考资料