4. SSH免密与并行执行框架

当管理的主机从两三台增长到几十台甚至上百台时,逐台输入密码执行命令会迅速成为瓶颈。SSH 免密登录解决的是“不用输密码”的问题,而并行执行框架解决的是“一次下发、多机同时执行、结果集中回收”的问题。两者叠加,才构成批量运维的基础通道。本节围绕这条通道展开:先建立密钥与 ssh-agent 的信任链,再用 pssh/parallel-ssh 与 ClusterShell 完成多主机命令下发与结果收集。

一、免密登录的本质:公钥认证信任链

SSH 免密并不是“跳过认证”,而是把认证方式从口令换成密钥对。控制端生成一对密钥,私钥留在本地(默认 ~/.ssh/id_ed25519),公钥(id_ed25519.pub)追加写入目标主机的 ~/.ssh/authorized_keys。登录时目标主机用公钥加密一段挑战数据,控制端用私钥解密并回应,验证通过即放行。因此免密的前提是:控制端持有私钥、目标端信任对应公钥、权限正确。

权限是免密失败最常见的原因。OpenSSH 对权限非常严格:~/.ssh 应为 700,authorized_keys 应为 600,用户家目录不能被组或其他用户写。若权限过宽,sshd 会直接拒绝使用该密钥,日志中通常出现 Authentication refused: bad ownership or modes。排查免密问题时,应先用 ssh -vvv user@host 观察密钥是否被提供、是否被接受。

二、密钥分发:ssh-copy-id 与批量分发

单台分发用 ssh-copy-id 最稳妥,它会自动创建目录、设置权限并追加公钥,避免手工操作出错:

ssh-keygen -t ed25519 -C "ops@control" -f ~/.ssh/id_ed25519 -N ""
ssh-copy-id -i ~/.ssh/id_ed25519.pub ops@node01
ssh ops@node01 'hostname; id'

批量分发时,若目标主机尚未免密,就需要借助密码一次性推送。可用 sshpass 配合循环,或使用 Ansible 的 authorized_key 模块。以下为示例脚本,密码通过环境变量传入,避免写入命令历史:

export SSHPASS='ExamplePass!'
for h in node{01..10}; do
  sshpass -e ssh-copy-id -o StrictHostKeyChecking=no \
    -i ~/.ssh/id_ed25519.pub ops@$h
done

分发完成后应做一次连通性验证,确认 10 台全部免密成功,再进入并行执行阶段。验证命令可用 for h in node{01..10}; do ssh -o BatchMode=yes ops@$h true && echo "$h ok" || echo "$h fail"; done,其中 BatchMode=yes 禁止交互式提问,能真实反映免密是否生效。

三、ssh-agent:私钥的会话级托管

如果私钥设置了口令(passphrase),每次使用都要输入,批量场景下不可接受。ssh-agent 在内存中托管已解锁的私钥,后续 ssh 连接自动向 agent 请求签名,无需重复输入口令。典型用法:

eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519
ssh-add -l

需要注意 agent 的转发(ForwardAgent)与跳板场景:转发 agent 会把本地私钥能力暴露给远端,存在被滥用风险,生产环境应谨慎开启,更推荐在跳板机上直接部署密钥或使用 ProxyJump。对于自动化任务,通常使用无口令密钥配合严格的文件权限,而不是依赖 agent 常驻。

四、并行执行框架:pssh 与 ClusterShell

免密只是通道,批量下发还需要并行工具。parallel-ssh(pssh)套件包含 pssh(并行执行)、pscp(并行复制)、pslurp(并行回收)、pnuke(并行终止)等命令。它读取主机列表文件,按并发度同时连接并汇总输出:

cat > hosts.txt <<'EOF'
node01
node02
node03
EOF

pssh -h hosts.txt -i -p 10 -t 30 -l ops \
  'uptime; systemctl is-active sshd'

其中 -i 表示实时输出并等待全部完成,-p 10 控制并发进程数,-t 30 设置单机超时秒数,-l ops 指定登录用户。若不加 -i,pssh 只返回退出状态,适合“只关心成功与否”的场景。结果判断依据是:整体退出码为 0 表示所有主机成功;非 0 时需结合每台主机的输出定位失败节点。

ClusterShell 则更适合大规模、结构化结果处理。它用 nodeset 表达主机集合,支持把相同输出的主机折叠成一行,极大压缩结果:

clush -w node[01-10] -b --worker=yes 'cat /etc/redhat-release'
clush -w node[01-10] -g disk 'df -h / | tail -1'

-b 表示合并相同输出,-g 按组选择主机。当 10 台主机返回完全相同的发行版信息时,clush 只显示一行并标注 node[01-10],一眼即可看出“全部一致”;若某台不同,它会单独列出该主机,差异立刻暴露。这正是批量巡检最需要的效果。

SSH 免密信任链与并行执行数据流 控制端 control 私钥 id_ed25519(本地) ssh-agent 托管已解锁私钥 hosts.txt / nodeset 主机清单 ssh-copy-id 公钥分发 目标主机群 node[01-10] node01 authorized_keys node02 authorized_keys node03 authorized_keys … node10 权限:~/.ssh 700 | authorized_keys 600 | 家目录不可组写 认证:公钥加密挑战 → 私钥签名回应 → 放行 并行执行框架 pssh -h hosts.txt -i -p 10 -t 30 -l ops 'cmd' clush -w node[01-10] -b 'cmd' 命令下发 结果回收 结果收集与判读 退出码 0 → 全部成功 非 0 → 定位失败主机 clush -b → 相同输出折叠为 node[01-10] 差异主机单独列出 → 立即暴露异常节点

五、结果收集与判读要点

批量执行的价值最终体现在结果判读上。pssh 默认只给出每台主机的退出状态,加 -i 才显示输出;当主机数量多、输出长时,逐台阅读效率很低。ClusterShell 的 -b 合并机制把“相同结果”压缩,把“不同结果”突出,非常适合巡检类任务,例如检查服务状态、磁盘使用率、内核版本是否一致。

实际使用中还需注意三点:一是并发度 -p 不宜过大,否则控制端文件描述符和网络连接会吃紧,一般从 10~20 起步;二是超时 -t 要覆盖最慢主机的执行时间,避免误判;三是命令应尽量幂等、无交互,避免出现等待输入而挂起。对于需要回收文件的任务,可用 pslurp 或 clush --rcopy 把远端结果拉回本地统一分析。

建议把主机清单、并发度、超时、登录用户固化为脚本参数,并在每次批量操作前先对 1 台主机试跑,确认命令行为符合预期后再全量下发,可显著降低误操作风险。

综合来看,密钥分发建立信任、ssh-agent 解决私钥口令、pssh 与 ClusterShell 负责并行下发与结果聚合,四者构成一条完整的批量运维通道。掌握这条通道后,后续的配置同步、服务重启、日志采集等任务都可以在其上快速构建。