批量执行前须验证ssh免密连接:检查authorized_keys权限为600、.ssh目录为700;用ssh -o batchmode=yes测试;避免依赖ssh config别名;pdsh适合快速巡检,clustershell适合结构化输出;ansible中gather_facts: false可提速但丧失条件判断能力;务必捕获stderr和退出码。

用 ssh 批量执行命令前,先确认连接是否真能免密
很多人以为配好 ssh-copy-id 就万事大吉,结果批量跑脚本时一半机器卡在密码提示——其实 ssh 默认会尝试所有可用密钥,一旦某台服务器上残留了旧公钥或 ~/.ssh/authorized_keys 权限不对(比如是 644),就会退化到密码认证。
- 务必在每台目标机上检查:
ls -l ~/.ssh/authorized_keys,必须是 600;~/.ssh目录必须是 700 - 测试单点连接:运行
ssh -o ConnectTimeout=3 -o BatchMode=yes user@host 'echo ok',加BatchMode=yes能立刻暴露是否还在等密码 - 别依赖本地
~/.ssh/config别名做批量——脚本里写死 IP 或主机名更可控,避免 config 解析失败导致跳过某些节点
用 pdsh 还是 clustershell?看你的错误容忍和输出格式需求
pdsh 启动快、依赖少,但默认把所有主机输出混在一起,出错时难定位哪台挂了;clustershell 原生支持分组、异步、带前缀的结构化输出,但 Python 依赖多,部分老系统没预装。
- 快速巡检(比如查磁盘):用
pdsh -w "node[01-10]" 'df -h /' | grep -v '^$',加-R ssh显式指定后端 - 需要区分每台结果:用
clush -w "node[01-10]" --output-dir /tmp/clush-out 'uptime',它会为每台生成独立文件 - 注意
pdsh的-f并发数别设太高,超过 50 容易触发目标机sshd的MaxStartups限制,表现为随机断连
用 ansible 批量管理时,gather_facts: false 不是省时间那么简单
Ansible 默认每台都跑一遍 fact 收集(相当于执行几十条 unamelsb_releasedf),对老旧或资源紧张的服务器,这一步可能超时或拖慢整个 play。关掉它确实快,但代价是不能用 ansible_facts 变量做条件判断。
- 纯命令类任务(如重启服务、改配置):直接加
gather_facts: false,再用delegate_to: localhost配合shell模块做简单判断 - 需要跨平台适配(比如 centos vs debian 包管理器):保留
gather_facts: true,但用fact_path指向缓存目录,避免每次重采 - 别在
vars:里硬编码路径,用{{ ansible_facts['distribution'] }}动态拼接,否则换发行版就炸
日志和错误捕获不能只靠 stdout,stderr 丢掉等于没管
批量执行时,一条 curl 失败或 systemctl start 报错,往往只写进 stderr,而很多封装脚本默认不合并或不保存它,结果看起来“全成功”,实际一半服务根本没起来。
- 用
ssh手动跑命令时,加上2>&1:例如ssh host 'systemctl restart nginx 2>&1' - 用
pdsh时加-R ssh: -o LogLevel=Error减少干扰日志,但关键命令自己重定向:pdsh -w node[1-3] 'apt update 2>&1 | tail -5' - Ansible 中别只信
register变量,加ignore_errors: yes后一定要检查result.stderr和result.rc,rc != 0不一定代表失败(比如grep没匹配到也返回 1)
批量管理最麻烦的不是命令怎么发,而是你怎么确信每台都按你预期的状态运行了——stderr、退出码、进程存在性、端口监听状态,这些信号一个都不能少盯。