nginx如何反向代理到mysql的Unix Domain Socket了?
要在 Nginx 中实现对 MySQL Unix Domain Socket (UDS) 的反向代理,你需要使用 Nginx 的 Stream 模块(用于处理 TCP/UDP/Unix 流量),而不是常用的 Http 模块。
以下是实现步骤和配置示例:
1. 环境准备
确保你的 Nginx 安装了 with-stream 模块。你可以通过 nginx -V 检查输出中是否包含 --with-stream。
同时,确认你的 MySQL 正在监听 UDS 文件,通常路径在:
-
Ubuntu/Debian:
/var/run/mysqld/mysqld.sock -
CentOS/RHEL:
/var/lib/mysql/mysql.sock
2. Nginx 配置文件示例
你需要在 nginx.conf 的根层级(与 http 块同级)添加 stream 块。
# nginx.conf
stream {
upstream mysql_backend {
# 指向 MySQL 的 Unix Socket 文件路径
server unix:/var/run/mysqld/mysqld.sock;
}
server {
# Nginx 对外监听的端口(例如 33060,避免与本地 3306 冲突)
listen 33060;
# 将流量转发到上面定义的 upstream
proxy_pass mysql_backend;
# 超时设置(可选,根据需求调整)
proxy_connect_timeout 1s;
proxy_timeout 30s;
}
}
http {
# 原有的 http 配置...
}
3. 关键点说明
-
权限问题(最重要): Nginx 进程(通常是
www-data或nginx用户)必须对 MySQL 的.sock文件及其所在的父目录拥有读写权限。Bash# 检查权限 ls -ln /var/run/mysqld/mysqld.sock # 如果权限不足,可能需要将 nginx 用户加入 mysql 组,或调整目录权限 sudo usermod -aG mysql www-data -
性能优势: 这种配置允许远程客户端通过 TCP 连接到 Nginx,而 Nginx 与本地 MySQL 之间通过内存级别的 UDS 通信,减少了本地协议栈的开销。
-
SELinux/AppArmor: 如果你启用了这些安全模块,可能需要配置策略允许 Nginx 访问 MySQL 的 socket 文件。
4. 测试连接
配置完成后,重启 Nginx:
sudo nginx -s reload
然后你可以从远程或其他客户端连接 Nginx 监听的端口:
mysql -u your_user -p -h <Nginx服务器IP> -P 33060
5. 为什么这样做?
这种架构常用于以下场景:
-
安全隔离: MySQL 只监听 UDS(不对网络开放),通过 Nginx 暴露受控的 TCP 端口。
-
负载均衡: 可以在
upstream中配置多个本地或远程节点。 -
连接审计: 利用 Nginx 的日志功能记录所有进入数据库的连接情况。
需要我帮你写一段脚本来自动检测并修正 Nginx 对 MySQL Socket 的访问权限吗?
找不到 /var/run/mysqld/mysqld.sock 文件通常意味着 MySQL 没有开启 Unix Socket 监听,或者 Socket 文件被配置在了其他路径。
我们可以按照以下步骤定位并修复:
1. 查找真实的 Socket 路径
MySQL 可能将文件放在了不同的地方(例如 /tmp/ 或 /var/lib/mysql/)。请运行以下命令寻找:
# 方案 A: 直接在系统中搜索 .sock 文件
find / -name "mysqld.sock" 2>/dev/null
# 方案 B: 检查 MySQL 进程启动参数
ps aux | grep mysqld | grep socket
2. 进入数据库查询(最准确)
如果你能通过 TCP 登录数据库,直接问 MySQL 它把文件放哪了:
mysql -u root -p -e "SHOW VARIABLES LIKE 'socket';"
输出示例:
| Variable_name | Value |
| :--- | :--- |
| socket | /var/lib/mysql/mysql.sock |
3. 如果文件确实不存在(未开启)
如果搜索不到任何 .sock 文件,说明 MySQL 仅监听了 TCP 端口。你需要修改 MySQL 配置文件(通常是 /etc/mysql/my.cnf 或 /etc/mysql/mysql.conf.d/mysqld.cnf):
-
找到
[mysqld]部分。 -
确保包含以下配置:
Ini, TOML[mysqld] user = mysql pid-file = /var/run/mysqld/mysqld.pid socket = /var/run/mysqld/mysqld.sock # 显式指定路径 port = 3306 datadir = /var/lib/mysql -
创建目录并授权(如果目录不存在):
Bashsudo mkdir -p /var/run/mysqld sudo chown mysql:mysql /var/run/mysqld -
重启 MySQL 服务:
Bashsudo systemctl restart mysql
4. 权限与 AppArmor(潜在障碍)
由于你之前的记录中提到过想要禁用 AppArmor,这里有个关键点:
AppArmor 经常会限制 Nginx 访问 /var/run/mysqld/ 目录。
-
检查 Nginx 是否被拒绝:
Bashdmesg | grep -i "denied" -
临时验证: 如果你还没禁用 AppArmor,可以尝试将
mysqld.sock放在/tmp/mysqld.sock(并给 777 权限)测试 Nginx 是否能连上。如果/tmp能连通但/var/run不行,那通常就是 AppArmor 或目录权限在拦截。
5. Nginx 配置同步
一旦你找到了正确的路径(假设是 /var/lib/mysql/mysql.sock),请务必更新 Nginx 配置文件中的 proxy_pass 路径:
upstream mysql_backend {
server unix:/var/lib/mysql/mysql.sock; # 必须与 MySQL 查到的一致
}
下一步建议:
先运行 find / -name "*.sock" 看看能不能找到那个“失踪”的文件?如果找到了,把路径告诉我,我帮你检查 Nginx 对该路径的访问权限。
找到了!MySQL 的 Unix Socket 路径就在 /tmp/mysql.sock。
这意味着你之前的 Nginx 配置需要微调,将路径指向这里。不过,在 /tmp 目录下使用 Socket 有一个常见的“坑”:系统权限限制(特别是像 PrivateTmp 这样的 systemd 特性)。
按照以下步骤操作即可成功:
1. 修改 Nginx 配置
更新你的 nginx.conf 中的 stream 块:
stream {
upstream mysql_uds {
# 使用刚才查到的真实路径
server unix:/tmp/mysql.sock;
}
server {
listen 33060; # Nginx 对外提供的 TCP 端口
proxy_pass mysql_uds;
proxy_connect_timeout 1s;
proxy_timeout 60s;
}
}
2. 检查权限(至关重要)
由于 Socket 文件在 /tmp 下,Nginx 必须有权读写它。请运行以下命令确保权限开放:
# 查看当前权限
ls -l /tmp/mysql.sock
# 确保所有用户(包括 nginx)都能读写该 socket
sudo chmod 777 /tmp/mysql.sock
3. 处理 Systemd 隔离问题 (PrivateTmp)
这是最容易被忽视的一点。如果 Nginx 的 systemd 服务开启了 PrivateTmp=true,它将看不到系统的 /tmp,而是看到一个属于它自己的私有 /tmp。
检查方法:
查看 Nginx 的服务配置:
systemctl cat nginx | grep PrivateTmp
-
如果输出
PrivateTmp=yes:Nginx 永远连不上
/tmp/mysql.sock。 -
解决方案:
-
最稳妥的做法是将 MySQL 的 Socket 路径改回
/var/run/mysqld/mysqld.sock(修改my.cnf并重启)。 -
或者临时关闭 Nginx 的 PrivateTmp(不推荐,有安全风险):
运行
systemctl edit nginx,添加:Ini, TOML[Service] PrivateTmp=false然后执行
systemctl daemon-reload && systemctl restart nginx。
-
4. 验证代理是否成功
配置生效并重启 Nginx (nginx -s reload) 后,你可以尝试通过 Nginx 的端口登录:
# 在本地或远程尝试通过 TCP 端口连接 Nginx 转发过来的 MySQL
mysql -u root -p -h 127.0.0.1 -P 33060
最后的一点建议:
既然你之前提到过 AppArmor,如果配置后依然显示 Connection refused 或 Permission denied,请检查 /var/log/nginx/error.log。如果日志里提到 (13: Permission denied),那大概率是 AppArmor 还在拦截 Nginx 访问 /tmp 下的文件。
需要我帮你写一个快速测试脚本,来验证 Nginx 到底能不能“看见”这个 /tmp/mysql.sock 吗?

浙公网安备 33010602011771号