天天看點

使用 shell 在多伺服器上批量操作

需求

日常工作中,我們常需要同時在多台伺服器上執行同樣的指令,如對比日志、檢查服務等。這就需要我們有伺服器批量操作的能力。

兩年前寫過一篇文章,shell實作SSH自動登陸 使用 shell 的 

expect

 指令進行 ssh 登陸,這種方式的靈活性确實非常高,但實作起來比較麻煩,而且單程序阻塞的特性也是它的硬傷,如果使用它進行批量操作,就需要啟動多個 expect 程序,涉及到各個程序和主程序的雙向通信,處理起來非常麻煩。

不過我們可以借用 ssh 

公鑰登陸

 的能力,友善地實作在多個伺服器上批量執行指令。

轉載随意,文章會持續修訂,請注明來源位址:https://zhenbianshu.github.io 。

SSH 協定

說公鑰登陸之前,先來說一下 SSH 協定。

SSH 是一種網絡協定,我們常說的 ssh 一般指其實作,即 OpenSSH,在 shell 中,也就是 ssh 指令。

SSH

Secure Shell(安全外殼協定,簡稱SSH)是一種加密的網絡傳輸協定,可在不安全的網絡中為網絡服務提供安全的傳輸環境。 SSH通過在網絡中建立安全隧道來實作SSH用戶端與伺服器之間的連接配接。

SSH 的原理跟 HTTPS 差不多,都是基于 TCP 和 非對稱加密進行的應用層協定。它跟 HTTPS 的不同之處在于 HTTPS 通過 

數字證書

 和 

數字證書認證中心

 來防止中間人攻擊,而 ssh 伺服器的公鑰沒有人公證,隻能通過其公鑰指紋來人工确定其身份。

如下圖所示,我們第一次使用 ssh 登陸某台伺服器時, ssh 會提示我們驗證伺服器的公鑰指紋。

使用 shell 在多伺服器上批量操作

當我們驗證此公鑰指紋是我們要登陸的伺服器後,伺服器的公鑰會被添加到 

~/.ssh/known_hosts

 裡,再登陸時,ssh 檢測到是已認證伺服器後就會跳過公鑰驗證階段。

建連過程

關于通信加密的概念,我在之前的文章也有所介紹,參見:再談加密-RSA非對稱加密的了解和使用。至于 SSH 協定的建連過程,則可以參閱:Protocol Basics: Secure Shell Protocol 。

總結起來主要包括以下步驟:

  • TCP 三次握手
  • SSH 協定版本協商
  • 用戶端與服務端的公鑰交換
  • 加密算法協商
  • 用戶端使用對稱加密的密鑰認證
  • 用戶端與服務端安全通信

我使用 tcpdump + wireshark 抓包并檢視了一下其 SSH 的建連過程,如下圖所示:

使用 shell 在多伺服器上批量操作

不得不再次感歎 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] [-O ctl_cmd] [-o option] [-p port] [-Q query_option] [-R address] [-S ctl_path] [-W host:port] [-w local_tun[:remote_tun]] [user@]hostname [command]
           

介紹完了 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.
Your public key has been saved in ./test.pub.
The key fingerprint is:
SHA256:xxxxx/B17z/xxxxxx [email protected]
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 編寫的 pssh,C++ 編寫的 hss(幫同僚做個廣告)等。

多伺服器檔案合并

前幾天,幫同僚在多個伺服器上查找日志,需要把在多個伺服器上查到的日志都彙總到同一台機器上進行統計分析。我是用 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 平台內建更多功能,實作完整的自動化。

關于本文有什麼疑問可以在下面留言交流,如果您覺得本文對您有幫助,歡迎關注我的 微網誌 或 GitHub 。您也可以在我的 部落格REPO 右上角點選 

Watch

 并選擇 

Releases only

 項來 

訂閱

 我的部落格,有新文章釋出會第一時間通知您。

繼續閱讀