SSH::Batch,在公有云中使用 ssh 工具箱
人吶就都不知道,自己就不可以預料。一個人的命運啊,當然要靠自我奮斗,但是也要考慮到歷史的行程,我絕對不知道,我作為一個服務端開發者怎么開始研究運維去了,所以 ECS 醬同我講話,說「大家都決定了,你來負責運維」,我說另請高明吧。我實在我也不是謙虛,我一個服務端開發者怎么就搞運維了呢?但是呢,ECS 醬講「大家已經研究決定了」,所以后來我就念了兩首詩,叫「茍利集群生死以,豈因禍福避趨之」,所以我就開始運維。
就像之前的博文中講的那樣,我買了兩臺阿里云的 ECS,一臺在香港,一臺在新加坡。由于總所周知的網絡原因,從大陸 ping 這兩臺服務器的 RTT 一直都在兩三百毫秒,之前只有一臺位于香港的 ECS 的時候,我 ssh 上去部署一些服務,碰上網絡抖動的時候都能卡出翔,敲擊一個按鍵之后許久才出現在屏幕上。
如今我有了兩臺服務器,如果還像之前那樣直接用 ssh 去維護的話,簡直就是不敢想象的事情。且不說一樣的配置文件我要修改兩遍,僅僅想象一下剛剛在 A 機器卡成翔的情況下完成維護,又要去 B 機器上再次被卡成翔,就會讓我懷疑人生。不要問為什么卡成翔了還不用mosh,我也不知道😂
其實說起來,雖然過去一年多,我做的是服務端開發,但是也涉足一些簡單的運維工作。應用服務器從我剛入職時候的幾臺擴容到幾十臺到現在的一百多臺,一次又一次的自主發布,偶爾的手動批量重啟、下線服務器,捕獲線程快照、內存快照、大批量處理應用日志,經歷過虛擬機宕機、物理機宕機,不勝枚舉……當 AppOps 的日子,其實就是不那么規范的 DevOps 的日子。
集團的對內運維水平還處在 IaaS 的時代,這也給了我們開發者接觸運維的機會。如果哪天對內的運維達到了 PaaS 的級別,開發者們也許就接觸不到這些東西了。PaaS 似乎有點遙遠,目前來說比較現實的是 CaaS,Containers-as-a-Service,要是能做到這個,開發者估計也沒啥機會接觸運維了。
在工作中,當我需要批量地在集群中執行命令時,我會使用一個叫 pgm 的內部腳本。這個腳本是 Python 寫的,基于 pssh,用起來很不錯,能夠并發地在集群中執行命令。這個命令應該是我到目前為止會用的唯一一個內部腳本,其他的像開源的 tsar 反而不會用。
離開了公司的環境,我就沒有 pgm 用了。昨天我嘗試尋找一個能夠在集群中批量執行 ssh 命令的工具,這樣我能夠比較輕松地管理我的 ECS 們。那時候我還不知道 pgm 是基于 pssh 實現的。隨便 Google 了一下 「ssh batch」,就找到一個 Github repo,agentzh/sshbatch。
進去看了一下 README,這是一個用 Perl 實現的工具箱,4 個命令分別實現如下功能:
- fornodes 計算機器列表
- atnodes 在指定機器集上執行命令
- tonodes 把文件或目錄上傳到指定機器集
- key2nodes 把公鑰上傳到指定機器集
看起來很厲害的樣子,不過 agentzh 是誰?點開主頁一看,我當時就跪了,有眼不識泰山,這不是傳說中的春哥「章亦春」么!幾個月前孤陋寡聞的我是不知道春哥的存在的,直到我出差去北京參加 Velocity,在大會上見識了王院生對的 OpenResty 的簡介[1],當時就驚為天人。后來通過各種渠道加深了對 Nginx 和 OpenResty 的學習和了解,更是對春哥頂禮膜拜。
sshbatch 的文檔寫的很詳細,從安裝到使用面面俱到,因此我這里就不再贅述,雖然文檔用英文寫的。
這里主要介紹一下 sshbatch 中讓我感覺驚艷的地方。
首先是機器列表的管理方式。之前用 pgm 的時候,一個應用分組的機器放在一個文件里面,在執行批處理的時候指定存放機器列表的文件。fornodes 則是把機器列表看做是一個個的集合,集合與集合之間可以做交并補等運算,通過集合運算得到不同的機器列表。這靈活性簡直不能更贊。
其次是批量推送文件的 tonodes。之前用 pgm 只能批量執行命令,我在內網一直沒有找到科學的批量向服務器推送文件的腳本。tonodes 很好地滿足了我的需求。
于是我用 tonodes 和 atnodes 把我兩臺 ECS 上的 Nginx 配置文件重新維護了一遍,之前是直接登錄服務器修改的,如今變成本地使用一個 git repo 去維護這些配置文件,修改完成后批量推送并重啟 Nginx。
事情并沒有想象中的一帆風順。
由于服務器位于公有云,出于安全考慮,我禁止了 root 登錄,禁止了密碼登錄,只允許公鑰登錄。于是我沒法直接把 nginx.conf 放到 /etc/nginx/ 中。因為我懶,不想在啟動 nginx 的時候指定配置文件,于是只好把 nginx.conf 放到 /tmp/,然后再把它移動到 /etc/nginx/ 并重啟。
- tonodes ./nginx/nginx.conf '{ecs}:/tmp/'
- atnodes 'sudo mv /tmp/nginx.conf /etc/nginx/ && sudo nginx -t && sudo service nginx restart' '{ecs}' -w
根據文檔中的描述,atnodes 加了 -w 參數,會要求用戶輸入密碼,作為登錄密碼和 sudo 密碼。抱著試一試的心態執行了一下,果然跪了。
- ➜ sshbatch git:(master) ✗ atnodes 'sudo mv /tmp/nginx.conf /etc/nginx/ && sudo nginx -t && sudo service nginx restart' '{ecs}' -w
- Password:
- Permission denied (publickey).
- ===================== server ip =====================
- ERROR: unable to establish master SSH connection: bad password or master process exited unexpectedly
唉,我都把密碼登錄禁用了,這里還強行要密碼登錄,不跪才怪了。從文檔中發現似乎把 -w 替換為 -tty 也可以實現遠程執行 sudo 命令,趕緊試試。結果發現,用了 tty 倒是能輸入密碼執行 sudo 了,但是,每臺機器都得輸入一次密碼,這是什么鬼!
目前我只有兩臺機器,輸密碼就忍了,假如哪天我有 10 臺機器了,光輸密碼就得累死。
其實這個問題在企業環境甚至私有云環境應該都不是問題。哪個運維會閑著蛋疼把服務器禁止密碼登錄啊!反正機器都在局域網,IP 不暴露在公網就是相對安全的,只要守護好邊界出口就好。所以說在內網批量執行 sudo 命令的時候,直接用 -w 參數就好了。
問題來了,就要解決問題。最直接暴力的方案是,把我的賬號設置為 sudo 免密碼模式,很黃很暴力,我并不喜歡。第二種方法,就是修改 atnodes,支持 -w 參數輸入的密碼僅作為 sudo 密碼,不作為登錄密碼。
于是我 fork 了代碼,拉到本地做了些修改。雖然是完全沒用過的 Perl,但還是分分鐘就改好了~通過增加參數 -W 來表達「passowrd for sudo only」的含義。
隨便執行一個 sudo 命令看看效果。
- ➜ sshbatch git:(master) ✗ atnodes 'sudo ls' '{ecs}' -W
- Password:
- ===================== server ip =====================
- sudo: no tty present and no askpass program specified
- Remote command returns status code 1.
居然,出錯了……根據報錯信息,給之前的命令追加一個 -tty 參數,于是我終于能在服務器上使用 sudo 了!不幸的是,開啟 tty 之后,批處理就沒法并發執行了,只能按順序一個一個來。不過想想也是,開啟 tty 之后一般是要做一些交互操作的,而標準輸入流就只有一個,所以只好一個一個來了。
- ➜ sshbatch git:(master) ✗ atnodes 'sudo ls /etc/nginx/sites-enabled/' '{ecs}' -W -tty -q
- Password:
- ===================== server ip =====================
- [sudo] password for admin:
- blog.jamespan.me blog.xuminzheng.com default hatta wekan
- ===================== server ip =====================
- [sudo] password for admin:
- blog.jamespan.me default
我把我的修改補充測試之后提交了 PR,希望能被春哥接收😇經過春哥一番悉心教導,經歷 7 次修改,終于被合并到了主干~
然后我又嘗試了一下 pssh,似乎它沒法很好地應對類似于我的機器這種禁止密碼登陸之后還要執行 sudo 命令的場景。所以說啊,企業上云還不是把內部應用換個地方部署那么簡單,對企業的技術水平還是很有挑戰的。上云不保證系統質量會因此變好,穩定性因此而提高,甚至因為基礎設施變化太大,本來部署在小型機上的現在只能部署在虛擬機上而導致應用幾乎殘廢也不是沒有。
最后的最后,今天我發現了一個叫 Ansible 的運維工具,感覺有點強大,而且對系統毫無入侵,正在看文檔學習中。
Update:
春哥 Review 代碼后,我按照他的意見把 -so 修改成了 -W。