☰
linux集群如何批量ssh,亲测可用
2026/9/27 21:07:32 网站建设 项目流程

按照本文批量ssh,需要所有服务器用来ssh互信的账户和密码是一样的,不一样的改一下,后期公钥配置好之后可以再改回来,因为批量的话要向所有主机推送文件和交互,还没有免密,只能用密码

1、首先我们要安装一个自动化的集成套件,我们需要使用这个套件完成ssh批量免密时的自动操作,选一台执行机就行,不需要所有节点安装

yum install -y expect sshpass

2、准备一个shell脚本,名字自定义我的叫ssh_all.sh

#!/bin/sh DEST_USER=$1 PASSWORD=$2 HOSTS_FILE=$3 if [ $# -ne 3 ]; then echo "用法:" echo "$0 远程用户 远程密码 主机文件" exit 1 fi SSH_DIR=~/.ssh SCRIPT_PREFIX=./tmp # 1. 在当前用户的主目录下,准备.ssh目录 ,并保证权限只有自己有 mkdir $SSH_DIR chmod 700 $SSH_DIR # 2. 生成一个统一的ssh密钥 TMP_SCRIPT=$SCRIPT_PREFIX.sh { echo "#!/usr/bin/expect" echo "spawn ssh-keygen -b 1024 -t rsa" echo "expect *key*" echo "send \r" if [ -f $SSH_DIR/id_rsa ]; then echo "expect *overwrite*" echo "send y\r" fi echo "expect *passphrase*" echo "send \r" echo "expect *again:" echo "send \r" echo "interact" } > $TMP_SCRIPT chmod +x $TMP_SCRIPT /usr/bin/expect $TMP_SCRIPT rm $TMP_SCRIPT # 3. 生成authorized_keys文件 cat $SSH_DIR/id_rsa.pub >> $SSH_DIR/authorized_keys # 4. 设置authorized_keys文件权限为600 chmod 600 $SSH_DIR/authorized_keys # 5. 将文件复制到其他主机 for ip in $(cat $HOSTS_FILE) do if [ "x$ip" != "x" ]; then TMP_SCRIPT=${SCRIPT_PREFIX}.$ip.sh # 检查known_hosts val=`ssh-keygen -F $ip` if [ "x$val" == "x" ]; then echo "$ip 不在 $SSH_DIR/known_hosts 中, 需要添加" val=`ssh-keyscan $ip 2>/dev/null` if [ "x$val" == "x" ]; then echo "ssh-keyscan $ip 失败!" else echo $val >> $SSH_DIR/known_ hosts fi fi echo "将 $SSH_DIR 复制到 $ip" { echo "#!/usr/bin/expect" echo "spawn scp -r $SSH_DIR $DEST_USER@$ip:~/" echo "expect *assword*" echo "send $PASSWORD\r" echo "interact" } > $TMP_SCRIPT chmod +x $TMP_SCRIPT /usr/bin/expect $TMP_SCRIPT rm $TMP_SCRIPT echo "复制完成." fi done echo 完成

3、准备集群ips文件,一行一个ip,如下

192.168.43.186 192.168.43.187 192.168.43.188

4、将shell脚本赋权

chmod 777 ssh_all.sh

5、运行,参数1是用来ssh的用户,参数2是该用户密码,参数3是集群ips文件

./ssh_all.sh root 123456 ./host_list

如果脚本运行正常会出现如下日志,注意我没有截全,最后会出现几个100%的进度条,之后用测试ssh效果就可以了


上面这个脚本是单线程执行,而且没有专注处理公钥身份唯一问题,可以应用于测试集群上跑一跑,如果你要正式的部署商用化集群,用下面的这个脚本

#!/bin/bash# 1. 处理脚本需要的参数以及所需服务检查# 免密用的用户DEST_USER=""# 密码PASSWORD=""# 节点列表文件HOSTS_FILE="/opt/hosts"# 默认并行度为1,可通过参数调整PARALLEL_JOBS=1# 公钥等文件存放的路径(根据用户名动态设置,root用户为/root/.ssh,普通用户为/home/<user>/.ssh)SSH_DIR=""if[$#-lt4];thenecho"+ 用法:$0-u 远程用户 -pw 远程密码 [-ipf 主机文件,默认使用 /opt/hosts] [-t 并行度]"exit1fiwhile[[$#-gt0]];docase"$1"in-u)#用户if[[-n"$2"&&"$2"!=-*]];then#设置文件,并将参数整体向左移动2位DEST_USER="$2"shift2elseecho"错误: -u 需要指定用户"exit1fi;;-pw)# 密码if[[-n"$2"&&"$2"!=-*]];then#设置文件,并将参数整体向左移动2位PASSWORD="$2"shift2elseecho"错误: -pw 需要指定密码"exit1fi;;-ipf)#主机文件if[[-n"$2"&&"$2"!=-*]];thenHOSTS_FILE=$(realpath"$2")shift2elseecho"错误: -ipf 需要指定主机文件"exit1fi;;-t)#并行度if[[-n"$2"&&"$2"!=-*]];thenPARALLEL_JOBS=$2shift2elseecho"错误: -t 需要指定并行度"exit1fi;;-*)# 其他参数,属于检测到未知选项echo"错误: 未知选项$1"exit1;;esacdone# 根据目标用户设置SSH目录if["$DEST_USER"="root"];thenexportSSH_DIR="/root/.ssh"elseexportSSH_DIR="/home/$DEST_USER/.ssh"fi#获取 HOMEif[-z"${TOOLS_HOME}"];thenexportTOOLS_HOME="$(cd "$(dirname"$0")"/..;pwd)" fi #确保本地日志路径存在 if [ ! -d${TOOLS_HOME}/logs/ssh_all ]; then echo -e "+\e[1;35;44m生成日志路径\e[0m" mkdir -p${TOOLS_HOME}/logs/ssh_all fi #expect 交互式模拟套件 echo -e "+\e[1;35;44m开始检测当前系统必要软件\e[0m" if rpm -q expect >/dev/null 2>&1; then echo "+expect环境存在" else echo "+expect环境不完整,开始yum安装" yum install -y expect fi #sshpass ssh密码输入工具 if rpm -q sshpass >/dev/null 2>&1; then echo "+ sshpass 环境存在" else echo "+ sshpass 环境不完整,开始yum安装" yum install -y sshpass fi # 2. 让所有节点,生成自己的SSH密钥(如果密钥不存在) generate_key_with_stats() { local ip=$1local user=$2local password=$3echo -e "+\e[1;35;44m[$ip]\e[0m 通知生成密钥" # 检查密钥是否存在 # sshpass 需要yum安装 本身是一个和expect类似的交互套件,用来专门托管ssh命令,提供密码填充功能 # -p "$password" 直接提供访问密码 # ssh 托管ssh服务 # -o StrictHostKeyChecking=no -o ConnectTimeout=10 跳过ssh客户端认证 设置连接超时为10秒 # "$user@$ip" 目标机器 # "test-f~/.ssh/id_rsa&&test-f~/.ssh/id_rsa.pub" 检查两个文件是否存在 # 这个 if 判断的是 ssh 的返回,test命令如果文件不存在状态码返回 非 0 if sshpass -p "$password" ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 "$user@$ip" \ "test-f~/.ssh/id_rsa&&test-f~/.ssh/id_rsa.pub" 2>/dev/null; then echo -e "+\e[1;35;44m[$ip]\e[0m 密钥已存在,跳过" return 0 fi # 生成密钥 if sshpass -p "$password" ssh -o StrictHostKeyChecking=no "$user@$ip" \ "mkdir-p~/.ssh&&chmod700~/.ssh&&\ssh-keygen-trsa-b2048-N''-f~/.ssh/id_rsa-C'$user@$ip'>/dev/null2>&1"; then echo -e "+\e[1;35;44m[$ip]\e[0m 密钥生成成功" echo ""return0elseecho-e"+\e[1;35;44m[$ip]\e[0m 密钥生成失败"echo""return1fi}# 导出方法,让并行子进程跑export-fgenerate_key_with_statsecho""echo-e"+\e[1;34;47m开始构造所有主机密钥 generate_key_with_stats all\e[0m "cat"$HOSTS_FILE"|grep-v'#'|xargs-I{}-P"$PARALLEL_JOBS"bash-c'generate_key_with_stats "$@" '_{}"$DEST_USER""$PASSWORD"echo-e"+\e[1;34;47m所有主机密钥构造结束 generate_key_with_stats all\e[0m "echo""# 3. 回收所有公钥,把公钥写在all_authorized_keys文件里,权限600,700也可以,不过不需要执行600就够了echo"+ 构造新的临时文件 build$SSH_DIR下的 all_authorized_keys 与 all_known_hosts"# 保险起见这里先清理一下rm-f$SSH_DIR/all_authorized_keys${SSH_DIR}/all_known_hoststouch$SSH_DIR/all_authorized_keystouch$SSH_DIR/all_known_hostsecho"+ 设置权限 chmod 600 all_authorized_keys all_known_hosts"chmod600$SSH_DIR/all_authorized_keyschmod600$SSH_DIR/all_known_hostsforipin$(cat${HOSTS_FILE}|grep-v'#');doecho-e"+\e[1;35;44m[$ip]\e[0m 正在回收公钥/密钥"# 收集公钥sshpass-p"$PASSWORD"ssh-oConnectTimeout=3"$DEST_USER@$ip""cat ~/.ssh/id_rsa.pub">>$SSH_DIR/all_authorized_keys# 收集主机密钥ssh-keyscan-H$ip>>$SSH_DIR/all_known_hostsdoneecho"+ 新的临时文件构造完成 build all_authorized_keys all_known_hosts"echo""echo"+ 检查本地是否存在原有密钥对文件,如果存在则不再额外处理"# 确保本地 authorized_keys 和 known_hosts 存在,新用户首次执行时这两个文件可能不存在# [ 命令 ] 这里不是 if ,该语法是一个单独的命令格式,等价于 test 命令,返回 0(真)或 1(假)[!-f"${SSH_DIR}/authorized_keys"]&&touch"${SSH_DIR}/authorized_keys"&&chown"$DEST_USER":"$DEST_USER""${SSH_DIR}/authorized_keys"&&chmod700"${SSH_DIR}/authorized_keys"&&echo"+ 本地 authorized_keys 不存在,已自动创建"[!-f"${SSH_DIR}/known_hosts"]&&touch"${SSH_DIR}/known_hosts"&&chown"$DEST_USER":"$DEST_USER""${SSH_DIR}/known_hosts"&&chmod700"${SSH_DIR}/known_hosts"&&echo"+ 本地 known_hosts 不存在,已自动创建"echo""echo"+ 追加本地结果文件 into authorized_keys known_hosts 原有的免密串只要在执行机上,就并不会被覆盖丢失,其他同步节点如有差异会丢失,因此请注意免密执行的环境"cat${SSH_DIR}/all_authorized_keys>>${SSH_DIR}/authorized_keyscat${SSH_DIR}/all_known_hosts>>${SSH_DIR}/known_hostsecho"+ 追加结果文件完成 into authorized_keys known_hosts"echo""# 4. 并行分发所有节点,当前的所有用户登录公钥和ssh客户端密钥process_host(){localip=$1localuser=$2localpassword=$3#提示输出 开始echo-e"+\e[1;35;44m[$ip]\e[0m 开始执行免密"# 使用expect进行SCP 把 ~/.ssh 下含有所有节点公钥以及ssh客户端私钥的文件同步所有节点的 ~/.ssh/ 路径下# set timeout 30 设置目标服务或命令响应超时时间为30秒# spawn scp -r 文件路径 $user@$ip:~/ 启用一个新的子进程,发送文件# expect 规定发生交互时的动作# \"*assword*\" 当遇到 assword 关键字时 发送密码和回车# \"yes/no\" 当遇到yes/no 关键子时 发送yes和回车# timeout 如果超时 推出返回 状态码 1# eof 不超时正常结束交互 wait关键字用来表明等待spawn子进程结束 catch表明捕获可能出现的错误 result声明保存的变量 exit [lindex \$result 3] 表示获取第四位的子线程状态码为expect的返回码expect-c" set timeout 30 spawn scp$SSH_DIR/authorized_keys$SSH_DIR/known_hosts$user@$ip:~/.ssh/ expect {\"*assword*\"{ send\"$password\r\"exp_continue }\"yes/no\"{ send\"yes\r\"exp_continue } timeout { exit 1 } eof { catch wait result exit [lindex \$result3] } } ">${TOOLS_HOME}/logs/ssh_all/${ip}.log2>&1if[$?-eq0];thenecho-e"+\e[1;35;44m[$ip]\e[0m 执行免密结束\e[1;32;44m成功\e[0m"echo""return0elseecho-e"+\e[1;35;44m[$ip]\e[0m 执行免密结束\e[1;37;41m失败\e[0m"echo""return1fi}# 导出函数和执行关键参数,以便在子shell中使用export-fprocess_hostecho""echo-e"+\e[1;34;47m开始并行分发SSH密钥到各节点\e[0m"#xargs centos7系统内置 、 -I 设置行数据占位表达式 、-P 并行度 、 bash -c 开始把函数动态重组成一个脚本,后面是参数,执行是会被 “$@” 展开,_ 缺省函数名称部分,{} 是前面的占位符号,后米两个就是用户和密码cat$HOSTS_FILE|grep-v'#'|xargs-I{}-P$PARALLEL_JOBSbash-c'process_host "$@"'_{}"$DEST_USER""$PASSWORD"echo-e"+\e[1;34;47m完成所有节点的SSH密钥分发\e[0m"echo""# 5.删除临时文件echo"+ 临时文件清理 rm -f all_authorized_keys all_known_hosts"rm-f${SSH_DIR}/all_authorized_keys${SSH_DIR}/all_known_hostsecho"完成免密"exit0

这个脚本使用起来就很直观了

./ssh_all.sh-ipf./ips-uroot-pw123456-t2

如果在免密中部分节点因为网络等问题失败了,单独执行那些失败的就行,使用这个脚本批量完成后,所有的节点都有自己的公钥

免密完成后,检查操作用户主目录下的.ssh文件夹,正常来说里面会有四个文件

  • id_rsa.pub:存放了本台节点的自己的公钥信息,内容通常是ssh-rsa 公钥串内容 用户@节点,ssh-rsa表示公钥串的类型,用户@节点在这个文件中只是用来标识这个串是那个节点那个用户的而已,在此只有节点自身和用户自己,所以没有其他特殊含义
  • authorized_keys:这个文件里数据和id_rsa.pub中格式是一样的,不过意义不同,该文件中的公钥串,放的是其他可连接登录到本机的其他机器的公钥,相比之下最后一位的用户@节点则标识当前串来自于那个节点和用户
  • known_hosts:这个文件中存放着的也是公钥,但是它和authorized_keys里公钥的所属对象不一样,它不是.ssh文件夹中用户的公钥,而是其他服务器ssh客户端的公钥,有自己的生成和验证逻辑不用人为操心。通俗的讲,known_hosts文件用来验证连接到服务器后客户端和目标服务器做一次公钥检查,来确定连接到服务器的客户端就是原本的客户端,而不是被中间人攻击了,authorized_keys解决的是那些机器可以用某个用户来登录
  • id_rsa:这个是私钥,用来证明当前节点自身,通常不会给别人,在免密登录操作上用不到

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询