平常工做中,咱們常須要同時在多臺服務器上執行一樣的命令,如對比日誌、檢查服務等。這就須要咱們有服務器批量操做的能力。html
兩年前寫過一篇文章,shell實現SSH自動登錄 使用 shell 的 expect
命令進行 ssh 登錄,這種方式的靈活性確實很是高,但實現起來比較麻煩,並且單進程阻塞的特性也是它的硬傷,若是使用它進行批量操做,就須要啓動多個 expect 進程,涉及到各個進程和主進程的雙向通訊,處理起來很是麻煩。python
不過咱們能夠借用 ssh 公鑰登錄
的能力,方便地實如今多個服務器上批量執行命令。git
轉載隨意,文章會持續修訂,請註明來源地址:https://zhenbianshu.github.io 。github
說公鑰登錄以前,先來講一下 SSH 協議。算法
SSH 是一種網絡協議,咱們常說的 ssh 通常指其實現,即 OpenSSH,在 shell 中,也就是 ssh 命令。shell
Secure Shell(安全外殼協議,簡稱SSH)是一種加密的網絡傳輸協議,可在不安全的網絡中爲網絡服務提供安全的傳輸環境。 SSH經過在網絡中創建安全隧道來實現SSH客戶端與服務器之間的鏈接。安全
SSH 的原理跟 HTTPS 差很少,都是基於 TCP 和 非對稱加密進行的應用層協議。它跟 HTTPS 的不一樣之處在於 HTTPS 經過 數字證書
和 數字證書認證中心
來防止中間人攻擊,而 ssh 服務器的公鑰沒有人公證,只能經過其公鑰指紋來人工肯定其身份。服務器
以下圖所示,咱們第一次使用 ssh 登錄某臺服務器時, ssh 會提示咱們驗證服務器的公鑰指紋。網絡
當咱們驗證此公鑰指紋是咱們要登錄的服務器後,服務器的公鑰會被添加到 ~/.ssh/known_hosts
裏,再登錄時,ssh 檢測到是已認證服務器後就會跳過公鑰驗證階段。運維
關於通訊加密的概念,我在以前的文章也有所介紹,參見:再談加密-RSA非對稱加密的理解和使用。至於 SSH 協議的建連過程,則能夠參閱:Protocol Basics: Secure Shell Protocol 。
總結起來主要包括如下步驟:
我使用 tcpdump + wireshark 抓包並查看了一下其 SSH 的建連過程,以下圖所示:
不得再也不次感嘆 tcpdump + wireshark 是學習網絡協議的真神器。
做爲工具, 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 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 編寫的 pssh,C++ 編寫的 hss(幫同事作個廣告)等。
前幾天,幫同事在多個服務器上查找日誌,須要把在多個服務器上查到的日誌都彙總到同一臺機器上進行統計分析。我是用 pssh 登錄的多個服務器,因爲日誌量太大,查出來的結果輸出到終端上再複製有些不現實,而使用重定向,結果又會重定向到各自的服務器。
這時候可使用 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 的 -k
選項,可讓 nc 服務端在文件傳輸結束後保持鏈接不關閉。這樣,咱們使用 nc -k -4l port > result.log
啓動一個 nc 服務端,再使用 grep xxx info.log | nc ip port
便可實現結果數據的合併。
本文介紹的各個工具仍是屬於開發的小打小鬧,瞭解多一些工具老是好的。若是作運維工做的話,仍是須要依賴 OPS 平臺集成更多功能,實現完整的自動化。
關於本文有什麼疑問能夠在下面留言交流,若是您以爲本文對您有幫助,歡迎關注個人 微博 或 GitHub 。您也能夠在個人 博客REPO 右上角點擊 Watch
並選擇 Releases only
項來 訂閱
個人博客,有新文章發佈會第一時間通知您。