使⽤shell在多服务器上批量操作
需求
⽇常⼯作中,我们常需要同时在多台服务器上执⾏同样的命令,如对⽐⽇志、检查服务等。这就需要我们有服务器批量操作的能⼒。
两年前写过⼀篇⽂章,使⽤ shell 的expect命令进⾏ ssh 登陆,这种⽅式的灵活性确实⾮常⾼,但实现起来⽐较⿇烦,⽽且单进程阻塞的特性也是它的硬伤,如果使⽤它进⾏批
量操作,就需要启动多个 expect 进程,涉及到各个进程和主进程的双向通信,处理起来⾮常⿇烦。
不过我们可以借⽤ ssh 公钥登陆的能⼒,⽅便地实现在多个服务器上批量执⾏命令。
转载随意,⽂章会持续修订,请注明来源地址:。
SSH 协议
说公钥登陆之前,先来说⼀下 SSH 协议。
SSH 是⼀种⽹络协议,我们常说的 ssh ⼀般指其实现,即 OpenSSH,在 shell 中,也就是 ssh 命令。
SSH
Secure Shell(安全外壳协议,简称SSH)是⼀种加密的⽹络传输协议,可在不安全的⽹络中为⽹络服务提供安全的传输环境。 SSH通过在⽹络中建⽴安全隧道来实
现SSH客户端与服务器之间的连接。
SSH 的原理跟 HTTPS 差不多,都是基于 TCP 和⾮对称加密进⾏的应⽤层协议。它跟 HTTPS 的不同之处在于 HTTPS 通过数字证书和数字证书认证中⼼来防⽌中间⼈攻击,⽽
ssh 服务器的公钥没有⼈公证,只能通过其公钥指纹来⼈⼯确定其⾝份。
如下图所⽰,我们第⼀次使⽤ ssh 登陆某台服务器时, ssh 会提⽰我们验证服务器的公钥指纹。
当我们验证此公钥指纹是我们要登陆的服务器后,服务器的公钥会被添加到~/.ssh/known_hosts⾥,再登陆时,ssh 检测到是已认证服务器后就会跳过公钥验证阶段。
建连过程
关于通信加密的概念,我在之前的⽂章也有所介绍,参见:。⾄于 SSH 协议的建连过程,则可以参阅:。
总结起来主要包括以下步骤:
TCP 三次握⼿
SSH 协议版本协商
客户端与服务端的公钥交换
加密算法协商
客户端使⽤对称加密的密钥认证
客户端与服务端安全通信
我使⽤ tcpdump + wireshark 抓包并查看了⼀下其 SSH 的建连过程,如下图所⽰:
不得不再次感叹 tcpdump + wireshark 是学习⽹络协议的真神器。
ssh ⼯具
ssh
作为⼯具, ssh 分为服务端和客户端,在服务端,它是sshd,⼀般占⽤ 22 端⼝。我们平常使⽤的是其客户端,⼀般⽤法为ssh user@host,然后根据 ssh 的提⽰,我们输⼊密码后
登陆到服务器。
它的功能⾮常强⼤,看其⽀持参数就知道了。
ssh [-1246AaCfGgKkMNnqsTtVvXxYy] [-b bind_address] [-c cipher_spec] [-D [bind_address:]port] [-E log_file] [-e escape_char] [-F configfile] [-I pkcs11] [-i identity_file] [-J [user@]host[:port]] [-L address] [-l login_name] [-m mac_spec] 介绍完了 SSH 协议和 ssh 命令,终于说到公钥登陆了。
公钥登陆
理解了⾮对称加密的原理后,再公钥登陆会⾮常简单。由于公私钥是唯⼀的⼀对,在客户端保障⾃⼰私钥安全的情况下,服务端通过公钥就可以完全确定客户端的真实性,所以
要实现公钥登陆,我们就要先⽣成⼀个公私密钥对。
通过ssh-keygen命令来⽣成密钥对,为了让步骤更完整,我把它们暂时保存到⼯作⽬录,默认会保存到~/.ssh⽬录。
~ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/Users/zbs/.ssh/id_rsa): ./test
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in ./test.
ssh工具安卓下载Your public key has been saved in ./test.pub.
The key fingerprint is:
SHA256:xxxxx/B17z/xxxxxx zbs@zbs.local
The key's randomart image is:
+---[RSA 2048]----+
|    o+*.. EO*    |
|  ....          |
|    oo+    .o++.o|
+----[SHA256]-----+
~ ls ./test*
./test    ./test.pub
把私钥⽂件 ./test 的内容放到客户端的 ~/.ssh/id_rsa,再使⽤密码试登陆到服务器后,将公钥内容./test.pub⾥的内容放到服务器的 ~/.ssh/authorized_keys。
再次登陆时,ssh 会⾃动使⽤⾃⼰的私钥来认证,也就避免了输出密码。
批量操作
公钥登陆帮我们避免了每次登陆服务器要输出密码的⿇烦,它同时也解决了每个登陆会话都会同步阻塞的问题,这样我们就可以利⽤ ssh 的ssh user@host command⽅式来直接在服务器上执⾏命令。
同时,在我们拥有⼀个 ip 列表的情况下,使⽤ for 循环遍历 ip 列表,在多个服务器上批量执⾏命令也就成为了可能。
关于批量执⾏,已经有很多开源⼯具了,如使⽤ python 编写的,C++ 编写的(帮同事做个⼴告)等。
多服务器⽂件合并
前⼏天,帮同事在多个服务器上查⽇志,需要把在多个服务器上查到的⽇志都汇总到同⼀台机器上进⾏统计分析。我是⽤ pssh 登陆的多个服务器,由于⽇志量太⼤,查出来的结果输出到终端上再复制有些不现实,⽽使⽤重定向,结果⼜会重定向到各⾃的服务器。
scp
这时候可以使⽤scp,scp 跟 ssh 是同⼀家族的命令,也是基于 SSH 协议实现的安全传输协议。只要在各个服务器之间互相保存着对⽅的公钥,就可以跟 ssh 命令⼀样,实现免密操作。
scp 的常见⽤法是scp src dst,其中远程路径可以表⽰为user@host:/path。在批量登陆的情况下,可以使⽤ grep 等命令先把结果⽂件输⼊到⼀个⽂件中,再使⽤ scp 命令将其复制到同⼀台服务器。
为了避免各个服务器的⽂件名冲突,可以使⽤uuidgen | xargs -I {} scp result.log root@ip:/result/{} 将各个服务器的结果复制到不同的⽂件中,再使⽤ cat 将 result ⽂件夹中的⽂件合并到⼀块。
nc
当然,⼤多数情况下,我们的服务器之间并不会互相保存公钥,不过nc命令可以完美解决这个问题。
nc 的-k选项,可以让 nc 服务端在⽂件传输结束后保持连接不关闭。这样,我们使⽤nc -k -4l port > result.log启动⼀个 nc 服务端,再使⽤grep xxx info.log | nc ip port即可实现结果数据的合并。
⼩结
本⽂介绍的各个⼯具还是属于开发的⼩打⼩闹,了解多⼀些⼯具总是好的。如果做运维⼯作的话,还是需要依赖 OPS 平台集成更多功能,实现完整的⾃动化。
关于本⽂有什么疑问可以在下⾯留⾔交流,如果您觉得本⽂对您有帮助,欢迎关注我的或。您也可以在我的右上⾓点击Watch并选择Releases only项来订阅我的博客,有新⽂章发布会第⼀时间通知您。