如何基于MySQL表数据批量执行bash的ssh与scp命令?
我之前维护过十几台同配置的Web服务器,跟你遇到过一模一样的问题——硬编码命令太蠢,加机器就得改脚本。下面给你几个落地的方案,完美解决从MySQL拉取服务器信息批量执行ssh/scp的需求:
第一步:先把服务器信息规范存入MySQL
首先得建个结构清晰的表,我一般这么设计:
CREATE TABLE servers ( id INT AUTO_INCREMENT PRIMARY KEY, ip VARCHAR(15) NOT NULL UNIQUE, username VARCHAR(50) NOT NULL, ssh_port INT NOT NULL DEFAULT 22, deploy_path VARCHAR(200) NOT NULL -- 可选字段,存远程部署路径,后续更灵活 );
把所有服务器的IP、登录用户、SSH端口都插入表中,以后新增机器直接插表就行,完全不用碰脚本。
方案一:逐条拉取命令,循环执行(最稳妥)
这是我日常用得最多的方式,能精准控制每条命令的执行状态,还能加错误处理。核心思路是让MySQL输出每条完整的ssh/scp命令(单独一行),然后用bash循环逐条执行。
比如批量执行SCP上传应用包的脚本:
#!/bin/bash # 配置MySQL连接信息 DB_USER="your_db_user" DB_PASS="your_db_password" DB_NAME="your_ops_db" LOCAL_APP_PATH="/local/your_app.tar.gz" # 从MySQL拉取所有SCP命令,-N参数用于去掉查询结果的表头 mysql -u${DB_USER} -p${DB_PASS} ${DB_NAME} -N -e " SELECT CONCAT( 'scp -P ', ssh_port, ' ${LOCAL_APP_PATH} ', username, '@', ip, ':', deploy_path ) AS cmd FROM servers;" | while read cmd; do echo "=== 正在执行: $cmd ===" # 执行命令并捕获结果 if $cmd; then echo "✅ 执行成功" else echo "❌ 执行失败,错误码: $?" # 可选:失败后记录日志或发送告警 echo "$(date) - FAIL: $cmd" >> /var/log/deploy_errors.log fi done
如果是批量执行SSH远程命令(比如重启应用服务),只需把MySQL查询里的CONCAT内容改成对应的SSH命令即可:
SELECT CONCAT( 'ssh -p ', ssh_port, ' ', username, '@', ip, ' "sudo systemctl restart your_app.service"' ) AS cmd FROM servers;
方案二:生成临时脚本执行(适合简单场景)
如果不需要复杂的错误处理,也可以把所有命令导出成临时shell脚本,直接执行:
#!/bin/bash DB_USER="your_db_user" DB_PASS="your_db_password" DB_NAME="your_ops_db" LOCAL_APP_PATH="/local/your_app.tar.gz" # 生成临时执行脚本 mysql -u${DB_USER} -p${DB_PASS} ${DB_NAME} -N -e " SELECT CONCAT( 'scp -P ', ssh_port, ' ${LOCAL_APP_PATH} ', username, '@', ip, ':', deploy_path ) AS cmd FROM servers;" > /tmp/batch_deploy.sh # 给脚本加执行权限并运行 chmod +x /tmp/batch_deploy.sh echo "=== 开始批量执行 ===" bash /tmp/batch_deploy.sh # 执行完成后清理临时文件 rm /tmp/batch_deploy.sh
方案三:并行执行(适合大量服务器)
如果服务器数量多(比如几十台),串行执行效率太低,可以用xargs实现并行执行:
mysql -u${DB_USER} -p${DB_PASS} ${DB_NAME} -N -e " SELECT CONCAT( 'scp -P ', ssh_port, ' ${LOCAL_APP_PATH} ', username, '@', ip, ':', deploy_path ) AS cmd FROM servers;" | xargs -I {} -P 8 bash -c "{}"
这里-P 8表示同时启动8个进程并行执行,你可以根据服务器性能调整这个数字。
避坑提醒
- 必须配置SSH免密登录:不然每条命令都会弹出密码提示,批量执行直接卡壳。可以用
ssh-copy-id把本地公钥批量传到所有服务器。 - 特殊字符转义:如果用户名或IP包含特殊字符(比如空格),记得在MySQL的CONCAT里用
QUOTE()函数包裹变量,避免bash解析出错:SELECT CONCAT( 'scp -P ', QUOTE(ssh_port), ' ${LOCAL_APP_PATH} ', QUOTE(username), '@', QUOTE(ip), ':', QUOTE(deploy_path) ) AS cmd FROM servers; - 一定要记日志:批量操作过程中出现问题时,日志是排查的关键,建议把执行结果统一写入日志文件。
内容的提问来源于stack exchange,提问作者Pierre
相关产品推荐
相关产品推荐

