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

語言: CN / TW / HK

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

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 檢測到是已認證伺服器後就會跳過公鑰驗證階段。建連過程

關於通訊加密的概念,我在之前的文章也有所介紹,參見:再談加密-RSA非對稱加密的理解和使用。至於 SSH 協議的建連過程,則可以參閱:Protocol Basics: Secure Shell Protocol 。總結起來主要包括以下步驟:

  • 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] [-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 平臺整合更多功能,實現完整的自動化。

以上內容希望幫助到大家,更多PHP大廠PDF面試文件,PHP進階架構視訊資料,PHP精彩好文免費獲取可以關注公眾號:PHP開源社群,或者訪問:

2021金三銀四大廠面試真題集錦,必看!

騰訊一面的Redis秒殺面試題你會麼?

四年精華PHP技術文章整理合集——PHP框架篇

四年精華PHP技術文合集——微服務架構篇

四年精華PHP技術文合集——分散式架構篇

四年精華PHP技術文合集——高併發場景篇

四年精華PHP技術文章整理合集——資料庫篇