You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Docker容器中PHP-FPM通过SSH隧道连接远程MySQL数据库问题

Docker中PHP API通过SSH隧道连接远程MySQL的解决方案

我刚帮朋友处理过类似的场景,Docker容器里跑PHP API连需要SSH隧道的MySQL,用密码验证的话确实有几个容易踩的坑,给你梳理下可行的方案:

第一步:确保容器内有必要的工具

默认的PHP Docker镜像(比如php:fpm)是不带sshpass和openssh-client的,所以得先在Dockerfile里安装它们:

# 假设用Debian系的PHP镜像
RUN apt-get update && apt-get install -y --no-install-recommends sshpass openssh-client \
    && rm -rf /var/lib/apt/lists/*

--no-install-recommends是为了减少镜像体积,用完清理apt缓存也很重要。

方案一:容器启动时预先建立SSH隧道(推荐用于API服务)

这种方式最稳定,适合长期运行的API服务,思路是先启动SSH隧道后台运行,再启动PHP服务。

1. 编写启动脚本

创建一个start.sh脚本,放在项目根目录:

#!/bin/bash
set -e # 遇到错误就退出

# 配置参数,建议用环境变量传递,不要硬编码!
SSH_PASSWORD=${SSH_PASSWORD:-"你的默认SSH密码"}
SSH_USER=${SSH_USER:-"ssh_username"}
SSH_HOST=${SSH_HOST:-"ssh_server_ip"}
REMOTE_MYSQL_HOST=${REMOTE_MYSQL_HOST:-"mysql_server_ip"}
LOCAL_MYSQL_PORT=${LOCAL_MYSQL_PORT:-3307}

# 建立SSH隧道:把远程MySQL的3306端口映射到容器本地的3307端口
# -f:后台运行;-N:不执行远程命令;-L:端口映射;-o:跳过主机指纹验证(第一次连接必备)
sshpass -p "$SSH_PASSWORD" ssh -f -N -L 127.0.0.1:$LOCAL_MYSQL_PORT:$REMOTE_MYSQL_HOST:3306 \
    -o StrictHostKeyChecking=no \
    $SSH_USER@$SSH_HOST

# 启动PHP服务(这里以php-fpm为例,根据你的环境调整)
exec php-fpm

注意:绝对不要在生产环境硬编码密码,用Docker环境变量传递更安全,比如启动容器时加-e SSH_PASSWORD=xxx。

2. 修改Dockerfile

把脚本复制到容器里,设置为启动命令:

COPY start.sh /usr/local/bin/start.sh
RUN chmod +x /usr/local/bin/start.sh
CMD ["start.sh"]

3. 配置Aura/SQL连接

因为隧道已经把远程MySQL映射到容器的127.0.0.1:3307,所以Aura/SQL的连接配置要指向这个本地端口:

use Aura\Sql\Connector\MysqlConnector;

$connector = new MysqlConnector();
$connection = $connector->connect([
    'host' => '127.0.0.1',
    'port' => 3307, // 和脚本里的LOCAL_MYSQL_PORT一致
    'dbname' => 'your_database_name',
    'username' => 'mysql_username', // 远程MySQL的用户名
    'password' => 'mysql_password', // 远程MySQL的密码
]);

方案二:PHP代码中动态建立隧道(适合脚本/低流量场景)

如果你的服务是脚本或者低流量的API,可以在代码里直接启动隧道,但要注意性能和权限问题:

// 用环境变量读取配置,避免硬编码
$sshPassword = getenv('SSH_PASSWORD');
$sshUser = getenv('SSH_USER');
$sshHost = getenv('SSH_HOST');
$remoteMysqlHost = getenv('REMOTE_MYSQL_HOST');
$localPort = 3307;

// 启动SSH隧道
$descriptorspec = [
    0 => ['pipe', 'r'],
    1 => ['pipe', 'w'],
    2 => ['pipe', 'w'],
];

$process = proc_open(
    "sshpass -p '$sshPassword' ssh -N -L $localPort:$remoteMysqlHost:3306 $sshUser@$sshHost -o StrictHostKeyChecking=no",
    $descriptorspec,
    $pipes
);

// 给隧道一点时间建立连接
sleep(2);

// 检查隧道是否成功启动
if (!is_resource($process)) {
    die('Failed to establish SSH tunnel');
}

// 初始化Aura/SQL连接
$connector = new \Aura\Sql\Connector\MysqlConnector();
$connection = $connector->connect([
    'host' => '127.0.0.1',
    'port' => $localPort,
    'dbname' => 'your_database_name',
    'username' => 'mysql_username',
    'password' => 'mysql_password',
]);

// 执行你的数据库操作...
// $result = $connection->fetchAll('SELECT * FROM your_table');

// 脚本结束时关闭隧道
proc_terminate($process);
proc_close($process);

⚠️ 注意:这种方式每次请求都会建立新隧道,性能开销大,而且要确保PHP的proc_open函数没被禁用(检查php.ini里的disable_functions)。

关键注意事项

  1. 安全性优化:生产环境绝对不要用明文密码的sshpass,建议改用SSH密钥对:
    • 生成密钥对:ssh-keygen -t rsa -b 4096
    • 把公钥传到SSH服务器的~/.ssh/authorized_keys
    • 把私钥挂载到容器的/root/.ssh/id_rsa,然后修改ssh命令去掉sshpass:ssh -i /root/.ssh/id_rsa ...
  2. 隧道存活检测:如果SSH隧道意外断开,你的API会连不上MySQL,可以在启动脚本里加个循环检测,比如每隔5分钟检查隧道进程,不存在就重启:
    while true; do
        # 检查隧道进程是否存在
        pgrep -f "ssh -f -N -L" || sshpass -p "$SSH_PASSWORD" ssh -f -N -L ...
        sleep 300
    done &
    
    把这段加到start.sh里,后台运行检测循环。
  3. 主机指纹验证:生产环境不要用-o StrictHostKeyChecking=no,可以预先把SSH服务器的指纹加到容器的/root/.ssh/known_hosts里,比如在Dockerfile里添加:
    RUN mkdir -p /root/.ssh && echo "ssh_server_ip ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQ..." > /root/.ssh/known_hosts
    

内容的提问来源于stack exchange,提问作者donfifty

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 12:35:33