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
 
# 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-datanginx 用户)必须对 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:

Bash
 
sudo nginx -s reload

然后你可以从远程或其他客户端连接 Nginx 监听的端口:

Bash
 
mysql -u your_user -p -h <Nginx服务器IP> -P 33060

5. 为什么这样做?

这种架构常用于以下场景:

  1. 安全隔离: MySQL 只监听 UDS(不对网络开放),通过 Nginx 暴露受控的 TCP 端口。

  2. 负载均衡: 可以在 upstream 中配置多个本地或远程节点。

  3. 连接审计: 利用 Nginx 的日志功能记录所有进入数据库的连接情况。

需要我帮你写一段脚本来自动检测并修正 Nginx 对 MySQL Socket 的访问权限吗?

 

找不到 /var/run/mysqld/mysqld.sock 文件通常意味着 MySQL 没有开启 Unix Socket 监听,或者 Socket 文件被配置在了其他路径

我们可以按照以下步骤定位并修复:


1. 查找真实的 Socket 路径

MySQL 可能将文件放在了不同的地方(例如 /tmp//var/lib/mysql/)。请运行以下命令寻找:

Bash
 
# 方案 A: 直接在系统中搜索 .sock 文件
find / -name "mysqld.sock" 2>/dev/null

# 方案 B: 检查 MySQL 进程启动参数
ps aux | grep mysqld | grep socket

2. 进入数据库查询(最准确)

如果你能通过 TCP 登录数据库,直接问 MySQL 它把文件放哪了:

Bash
 
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):

  1. 找到 [mysqld] 部分。

  2. 确保包含以下配置:

    Ini, TOML
     
    [mysqld]
    user            = mysql
    pid-file        = /var/run/mysqld/mysqld.pid
    socket          = /var/run/mysqld/mysqld.sock  # 显式指定路径
    port            = 3306
    datadir         = /var/lib/mysql
    
  3. 创建目录并授权(如果目录不存在):

    Bash
     
    sudo mkdir -p /var/run/mysqld
    sudo chown mysql:mysql /var/run/mysqld
    
  4. 重启 MySQL 服务

    Bash
     
    sudo systemctl restart mysql
    

4. 权限与 AppArmor(潜在障碍)

由于你之前的记录中提到过想要禁用 AppArmor,这里有个关键点:

AppArmor 经常会限制 Nginx 访问 /var/run/mysqld/ 目录。

  • 检查 Nginx 是否被拒绝:

    Bash
     
    dmesg | 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 路径:

Nginx
 
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 块:

Nginx
 
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 必须有权读写它。请运行以下命令确保权限开放:

Bash
 
# 查看当前权限
ls -l /tmp/mysql.sock

# 确保所有用户(包括 nginx)都能读写该 socket
sudo chmod 777 /tmp/mysql.sock

3. 处理 Systemd 隔离问题 (PrivateTmp)

这是最容易被忽视的一点。如果 Nginx 的 systemd 服务开启了 PrivateTmp=true,它将看不到系统的 /tmp,而是看到一个属于它自己的私有 /tmp

检查方法:

查看 Nginx 的服务配置:

Bash
 
systemctl cat nginx | grep PrivateTmp
  • 如果输出 PrivateTmp=yes

    Nginx 永远连不上 /tmp/mysql.sock

  • 解决方案:

    1. 最稳妥的做法是将 MySQL 的 Socket 路径改回 /var/run/mysqld/mysqld.sock(修改 my.cnf 并重启)。

    2. 或者临时关闭 Nginx 的 PrivateTmp(不推荐,有安全风险):

      运行 systemctl edit nginx,添加:

      Ini, TOML
       
      [Service]
      PrivateTmp=false
      

      然后执行 systemctl daemon-reload && systemctl restart nginx


4. 验证代理是否成功

配置生效并重启 Nginx (nginx -s reload) 后,你可以尝试通过 Nginx 的端口登录:

Bash
 
# 在本地或远程尝试通过 TCP 端口连接 Nginx 转发过来的 MySQL
mysql -u root -p -h 127.0.0.1 -P 33060

最后的一点建议:

既然你之前提到过 AppArmor,如果配置后依然显示 Connection refusedPermission denied,请检查 /var/log/nginx/error.log。如果日志里提到 (13: Permission denied),那大概率是 AppArmor 还在拦截 Nginx 访问 /tmp 下的文件。

需要我帮你写一个快速测试脚本,来验证 Nginx 到底能不能“看见”这个 /tmp/mysql.sock 吗?

 

 

posted @ 2026-03-27 10:03  技术颜良  阅读(54)  评论(0)    收藏  举报