大型企業(yè)Unix服務(wù)器的自動(dòng)化運(yùn)維
企業(yè)主機(jī)服務(wù)器日常運(yùn)維工作中,經(jīng)常需要登錄并以 root 方式執(zhí)行系統(tǒng)操作,如果在主機(jī)數(shù)量少的情況下,手工方式登錄并執(zhí)行效率尚可,但如果主機(jī)數(shù)量龐大(如筆者運(yùn)維的國外客戶服務(wù)器數(shù)量達(dá) 2000+),依次對(duì)一臺(tái)臺(tái)服務(wù)器進(jìn)行手工操作工作量巨大且出錯(cuò)概率與主機(jī)數(shù)量成線性增大。 本文分析了在大數(shù)量企業(yè)服務(wù)器情況下,利用 shell 管道,Java SSHD 開源包,Expect 腳本三種方式實(shí)現(xiàn)自動(dòng)登錄并執(zhí)行系統(tǒng)運(yùn)維操作,三種方式分別適用于不同的場景,可以滿足絕大多數(shù)企業(yè)主機(jī)服務(wù)器自動(dòng)化運(yùn)維的工作內(nèi)容,大大減輕了系統(tǒng)管理員的工作量,同時(shí)降低了操作失誤的風(fēng)險(xiǎn)。 本文中的三種方式的代碼示例稍作修改,即可直接用于實(shí)際的生產(chǎn)主機(jī)的運(yùn)維工作。
采用自動(dòng)化腳本進(jìn)行企業(yè)服務(wù)器運(yùn)維的原因
傳統(tǒng) Unix 主機(jī)服務(wù)器手工運(yùn)維方式的問題
大型企業(yè)的 IT 基礎(chǔ)設(shè)施中,Unix 主機(jī)服務(wù)器占據(jù)重要的地位。在日常運(yùn)維工作中,經(jīng)常需要登錄并以 root 方式執(zhí)行系統(tǒng)操作。如果在主機(jī)數(shù)量少的情況下,手工方式登錄并執(zhí)行運(yùn)維工作的效率尚可,但如果主機(jī)數(shù)量龐大(如筆者運(yùn)維的國外客戶服務(wù)器數(shù)量達(dá) 2000+),依次一臺(tái)臺(tái)服務(wù)器的登錄和操作工作量巨大,且出錯(cuò)的概率隨服務(wù)器數(shù)量的增加的遞增。
采用自動(dòng)化腳本進(jìn)行運(yùn)維的特點(diǎn)及好處
在大數(shù)量 Unix 企業(yè)服務(wù)器情況下,為了提高運(yùn)維工作效率,減低手工操作而出錯(cuò)的風(fēng)險(xiǎn),采用自動(dòng)化腳本進(jìn)行運(yùn)維是一個(gè)很好的方式。通過向腳本提供主機(jī)列表,用戶名賬戶名密碼等輸入?yún)?shù)方式,讓其自動(dòng)登錄遠(yuǎn)程目標(biāo)主機(jī)并執(zhí)行相應(yīng)的運(yùn)維操作,批量處理所有涉及的企業(yè)服務(wù)器主機(jī)。該方式下系統(tǒng)管理及運(yùn)維人員只需要在自動(dòng)化腳本中提供一次系統(tǒng)運(yùn)維操作的步驟命令,設(shè)定主機(jī)列表及正確的帳號(hào)密碼輸入?yún)?shù),利用 nohup 方式后臺(tái)運(yùn)行該腳本,在完成后監(jiān)控該腳本的輸出日志就可以完成上千臺(tái)服務(wù)器的重復(fù)運(yùn)維工作,并大大減輕了系統(tǒng)管理及運(yùn)維人員的工作量,降低了重復(fù)操作中出錯(cuò)的風(fēng)險(xiǎn)。
本文介紹了利用 shell 管道,Java SSHD 開源包,Expect 腳本三種方式實(shí)現(xiàn)自動(dòng)登錄并執(zhí)行系統(tǒng)運(yùn)維操作的案例。三種方式分別適用于不同的場景,如 shell 管道方式適用于 Telnet/FTP 協(xié)議登錄的普通賬戶,SSHD 開源包適用于 ssh1/ssh2 安全登錄協(xié)議下的登錄運(yùn)維,Expect 腳本適用于 SSHD 協(xié)議且需要 sudo 切換到 root 帳號(hào)權(quán)限的運(yùn)維操作。
自動(dòng)化腳本運(yùn)維的具體實(shí)現(xiàn)
利用 shell 管道進(jìn)行自動(dòng)化運(yùn)維
在企業(yè) Unix 服務(wù)器上如果開放了 telnet 或者 FTP 協(xié)議,通??梢岳?shell 的 EOF 和 << 管道功能將后續(xù)的輸入作為子命令或子 Shell 的輸入,直到遇到 EOF 為止,再返回到主調(diào) Shell,當(dāng) s h e l l 看到 < < 的時(shí)候,它就會(huì)知道下一個(gè)詞是一個(gè)分界符。在該分界符以后的內(nèi)容都被當(dāng)作輸入,直到 shell 又看到該分界符 ( 位于單獨(dú)的一行 )。這個(gè)分界符可以是你所定義的任何字符串
例如:
<
(你需要執(zhí)行的操作內(nèi)容)
EOF
利用該功能,可以將需要 ftp 或者 telnet 登錄的運(yùn)維操作做成自動(dòng)化腳本,將本來需要交互式輸入的帳號(hào)和密碼及登錄后需要的操作指令包在 EOF 和 << 管道符中,以實(shí)現(xiàn)自動(dòng)化 ftp 或者 telnet 到多臺(tái)服務(wù)器并執(zhí)行。
考慮如下場景:一批客戶的 Unix 服務(wù)器主機(jī)要打 patch,需要將 patch 包用 ftp 上載到服務(wù)器指定目錄,服務(wù)器數(shù)量巨大(超過 1000+),單個(gè)手工的上載操作是不現(xiàn)實(shí)的,因此我們使用 shell EOF 和管道功能編寫自動(dòng)化 ftp 腳本
autoftp 腳本示例如下:
清單 1. autoftp.sh 腳本示例
#!/bin/bash # 指定 ftp 服務(wù)器的 i serverip=192.168.1.159 # 指定 ftp 服務(wù)器的 ftp 用戶 ftpuser=ftptest # 指定 ftp 服務(wù)器的 ftp 用戶密碼 ftppwd=123456 # 指定 client 主機(jī)本地下載文件存放的目錄 localdir=/home/xiutuo/ftp # 指定 server 主機(jī)的 ftp 目錄 remotedir=/opt/IBM/DB2/ # 登錄 server 主機(jī)的 ftp -v -n $serverip << EOF > /tmp/autoftp.log.2010.XX.XX 2>&1 set head off set echo off set wrap off # 指定 ftp 用戶和密碼 $ftpuser $ftppwd # 指定 server 主機(jī)的 ftp 目錄和本地目錄 lcd $localdir cd $remotedir bin # 上傳 patch 包文件至 server 主機(jī)的指定目錄 put patchXXX.tar.gz EOF
如上可以看到 FTP 登錄和上傳不再需要手工與每臺(tái) server 交互。讀者可以修改該腳本,讓服務(wù)器主機(jī) IP 或主機(jī)名通過讀配置文件循環(huán)獲得,從而實(shí)現(xiàn)對(duì)多臺(tái)服務(wù)器主機(jī)的操作。也可以修改 ftp 用戶 / 密碼部分代碼,改成讀取輸入?yún)?shù),以增強(qiáng)安全性。
Java SSHD 開源包自動(dòng)化運(yùn)維
上述的 Ftp,telnet 管道方式的運(yùn)維是簡單可行的,但是現(xiàn)在業(yè)界大型的企業(yè)處于安全性的考慮,逐步淘汰此類協(xié)議的登陸方式,而改用基于公鑰體系的 SSHD 登陸協(xié)議。關(guān)于 SSHD 協(xié)議具體內(nèi)容已超出本文涉及的范圍,請(qǐng)各位感興趣的讀者參考 open-ssh 官方網(wǎng)站。
在 SSHD 下的登陸是不允許 shell 管道方式的(如果允許的話意味著 ssh 跟 telnet,ftp 一樣喪失了安全性),在此種情況下如果系統(tǒng)管理員要進(jìn)行自動(dòng)化運(yùn)維操作,可以采用 Java 開源的 SSHD 包來進(jìn)行。
Java ganymed 開源包是成熟的 SSHD 客戶端,采用封裝 socket 編程方式進(jìn)行底層的 ssh 通信協(xié)議,用戶調(diào)用其 API 與自己使用 SSHD 命令行登陸服務(wù)器的步驟和方法都一致,很容易理解和掌握。開源包的很多 Demo 實(shí)例,使即使對(duì) Java 編程不熟悉的系統(tǒng)管理人員,也可以通過簡單的修改 demo 代碼來實(shí)現(xiàn)自身需求的自動(dòng)化運(yùn)維操作。
考慮如下案例:一個(gè)企業(yè)的所有服務(wù)器需要將 /etc/services 文件備份至 /usr/local/etc/ 特定邏輯卷目錄,企業(yè)服務(wù)器都采用 SSH2 安全協(xié)議,不允許 telnet,ftp 登錄。
用 ganymed 的 SSH2 開源包編寫自動(dòng)化登陸腳本,以管理員賬號(hào)和密碼登陸企業(yè) Unix 服務(wù)器,執(zhí)行 cp 操作進(jìn)行備份。
ganymed 的 Java 代碼示例如下:
清單 2. AutoCp.java 類代碼示例
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import ch.ethz.ssh2.Connection; import ch.ethz.ssh2.Session; import ch.ethz.ssh2.StreamGobbler; public class AutoCopy { public static void main(String[] args) { String hostname = "9.212.XXX.XXX; String username = "SolarisAdmin"; String password = "********"; try { /* 創(chuàng)建 SSH2 連接實(shí)例 */ Connection conn = new Connection(hostname); /* 打開主機(jī) ssh 端口連接(默認(rèn) 22) */ conn.connect(); /* 認(rèn)證方式為 user/passwd */ boolean isAuthenticated = conn.authenticateWithPassword(username, password); if (isAuthenticated == false) throw new IOException("Authentication failed."); /* 已連接到遠(yuǎn)程主機(jī),打開 session 會(huì)話 */ Session sess = conn.openSession(); /* 執(zhí)行備份操作 */ sess.execCommand(\ "cp /etc/services /usr/local/etc/services; \ ls -lt /usr/local/etc/|grep -i services >&2"); /* 遠(yuǎn)程主機(jī)輸入輸出流 */ InputStream stdout = new StreamGobbler(sess.getStdout()); InputStream stderr = new StreamGobbler(sess.getStderr()); BufferedReader stdoutReader = new BufferedReader(new InputStreamReader(stdout)); BufferedReader stderrReader = new BufferedReader(new InputStreamReader(stderr)); System.out.println("process result on remote server:"); while (true) { String line = stdoutReader.readLine(); if (line == null) break; System.out.println(line); } System.out.println("Process error from remote server:"); while (true) { String line = stderrReader.readLine(); if (line == null) break; System.out.println(line); } /* 關(guān)閉會(huì)話 */ sess.close(); /* 關(guān)閉連接 */ conn.close(); } catch (IOException e) { e.printStackTrace(System.err); System.exit(2); } } }
以上可以看到,ganymed 的 SSH2 開源包使用簡單,建立 connection 并打開會(huì)話 session 后,都是普通的對(duì) session 的 IO 操作,讀操作則可以獲取服務(wù)器端的輸出,而寫操作則對(duì)應(yīng)對(duì)服務(wù)器端的敲入命令,一目了然,簡便實(shí)用。
熟悉 Java 編程的系統(tǒng)管理員可以很快上手,即使是沒有 Java 基礎(chǔ)的管理員也可簡單的修改示例中的 sess.execCommand("cp /etc/services /usr/local/etc/services; ls -lt /usr/local/etc/|grep -i services >&2"); 行操作代碼來實(shí)現(xiàn)自己的業(yè)務(wù)需求。
具體的 ganymed API 請(qǐng)參考 ganymed project 官方網(wǎng)站。
Expect 腳本自動(dòng)化運(yùn)維
考慮如下案例情況:有一批客戶服務(wù)器需要做一個(gè)變更,將 sudoer 配置文件從 /etc/sudoer 目錄搬移備份到 /usr/local/etc/sudoer 目錄,由于該配置文件比較重要,需要 root 權(quán)限才能執(zhí)行搬移操作,該客戶服務(wù)器也采用 SSHD 協(xié)議,不允許 ftp/telnet 等的登錄。
該情況下上文所提到的第二種方式 Java 開源包已不適用,因在 shell 進(jìn)程下當(dāng)切換用戶尤其是 sudo 到 root 時(shí),會(huì) fork 一個(gè)新的進(jìn)程,sudo 到 root 的會(huì)話在新的進(jìn)程中進(jìn)行,而 Java 開源包是限定在一個(gè)會(huì)話中的,執(zhí)行 sudo 操作后,新的進(jìn)程已經(jīng)脫離了 Java 開源包的控制,這時(shí)候再試圖用 Java 開源來執(zhí)行后續(xù)命令,將會(huì)報(bào)錯(cuò)。
此類需要切換用戶的情況下我們采用 Expect 腳本來進(jìn)行模擬交互。
在這里將 Expect 腳本簡介如下:
Expect 使用 Tcl 作為語言核心。不僅如此,不管程序是交互和還是非交互的,Expect 都能運(yùn)用。這是一個(gè)小語言和 Unix 的其他工具配合起來產(chǎn)生強(qiáng)大功能的經(jīng)典例子。Expect 是一個(gè)控制交互式程序的工具。它解決了上述需要用戶角色轉(zhuǎn)換的問題,用非交互的方式實(shí)現(xiàn)了所有交互式的功能。
Expect 被設(shè)計(jì)成專門針和交互式程序的交互。一個(gè) Expect 程序員可以寫一個(gè)腳本來描述程序和用戶的對(duì)話。接著 Expect 程序可以非交互的運(yùn)行“交互式”的程序。寫交互式程序的腳本和寫非交互式程序的腳本一樣簡單。Expect 還可以用于對(duì)對(duì)話的一部分進(jìn)行自動(dòng)化,因?yàn)槌绦虻目刂瓶梢栽阪I盤和腳本之間進(jìn)行切換。
簡單的說,Expect 腳本是用一種解釋性語言寫的。( 也有 C 和 C++ 的 Expect 庫可供使用,但這超出了本文的范圍 ).Expect 提供了創(chuàng)建交互式進(jìn)程和讀寫它們的輸入和輸出的命令。它是在 Tcl 基礎(chǔ)上創(chuàng)建起來的,并提供了一些 Tcl 所沒有的命令。
編寫 Expect 腳本的基本方式如下:
表 1. Expect 腳本基本使用示例
Expect 命令 | 作用 | 使用示例 |
spawn | 激活一個(gè) Unix 程序來進(jìn)行交互式的運(yùn)行 . | spawn ssh 192.168.1.2 |
send | 向進(jìn)程發(fā)送字符串 | send "sudo -s\r" |
set | 給 Expect 腳本中的變量賦值 | set username “joe” |
expect | 等待進(jìn)程收到的遠(yuǎn)程主機(jī)的輸出,并匹配對(duì)應(yīng)的字符串 , 一旦匹配,執(zhí)行后續(xù)的操作 | expect { "yes/no" send "yes" ;} |
Expect 還能理解一些特殊情況,如超時(shí)和遇到文件尾。 :set timeout 60 ;expect eof
我們以上述的實(shí)例作為例子,來看看 Expert 腳本如何實(shí)現(xiàn)自動(dòng)化登陸并 sudo 到 root,然后搬移文件的功能。
Expect 腳本 autoMove 示例如下:
清單 3. autoMove.sh 腳本示例
#!/usr/bin/expect # 導(dǎo)入 Expect 類庫 set hostname [lindex $argv 0] # 設(shè)置操作的遠(yuǎn)程主機(jī),$argv 類似 Shell 函數(shù)中的接收參數(shù) [lindex $argv 0] # 則表示***個(gè)接收參數(shù) , 例如 expectExample.sh host1 set username [lindex $argv 1] # 同上,第二個(gè)接收參數(shù)為登陸用戶名 set passwd [lindex $argv 2] # 同上,第三個(gè)接收參數(shù)為登陸用戶密碼 set timeout 60 # 設(shè)置等待超時(shí)為 60 秒 spawn ssh $username@$hostname # 使用 spawn 命令來激活 ssh 程序,模擬終端的輸出將能夠被 Expect 所讀取,模擬終端也能從 send 輸入到遠(yuǎn)程主機(jī) expect { "yes/no" {send "yes ";exp_continue} "Password:" {send "$passwd ";} } #Expect 語句等待遠(yuǎn)程主機(jī)的字符串匹配,當(dāng)匹配到了“yes/no” #則執(zhí)行后面的操作 .expect 搜索模式"*password:",其中 * 允許匹配 # 任意輸入,所以對(duì)于避免指定所有細(xì)節(jié)而言是非常有效的。如果遠(yuǎn)程主機(jī)沒有 action, #所以 Expect 檢測到該模式后就繼續(xù)運(yùn)行。 一旦接收到提示后,下一行就就把密碼送給當(dāng)前進(jìn)程。 send "sudo -s\r" expect "Password:" {send "$newpasswd\r"} # 執(zhí)行 sudo 用戶角色轉(zhuǎn)換操作 send "copy /etc/sudoers /usr/local/etcsudoers\r" # 執(zhí)行實(shí)際運(yùn)維操作 send "exit\r " send "exit\r " expect eof {exit 1}
由上我們可以看出 Expect 使用偽終端來和派生的進(jìn)程相聯(lián)系。偽終端提供了終端語義以便程序認(rèn)為他們正在和真正的終端進(jìn)行 I/O 操作。使用 Expect 等待遠(yuǎn)程主機(jī)的響應(yīng)并匹配需要的字符串,當(dāng)匹配到后執(zhí)行 send 操作向遠(yuǎn)程主機(jī)發(fā)送命令,set 操作為賦值,腳本的編寫于通常的 Shell 腳本很類似,相當(dāng)簡潔和實(shí)用。
在 AIX ,Solairs 的 Unix 平臺(tái)環(huán)境下 Expect 是默認(rèn)安裝的,Linux 需要安裝對(duì)應(yīng)的 rpm 包。
總結(jié)和展望
以上分析了大型企業(yè)服務(wù)器的自動(dòng)化腳本運(yùn)維,通過不同的案例分別介紹了 shell 管道,Java SSHD 開源包和 Expect 腳本三種方式的自動(dòng)化運(yùn)維。三種方式針對(duì)不同的業(yè)務(wù)需求及客戶服務(wù)器實(shí)際環(huán)境,有很強(qiáng)的實(shí)用性和操作性??梢詽M足絕大多數(shù)企業(yè)服務(wù)主機(jī)的自動(dòng)化運(yùn)維工作內(nèi)容,三種方式的代碼示例稍作修改,即可直接用于實(shí)際的生產(chǎn)主機(jī)日常運(yùn)維工作中。
如今隨著 IT 運(yùn)維管理工作的復(fù)雜度和難度的大大增加,將純粹的人工操作變?yōu)橐欢ǔ潭鹊淖詣?dòng)化管理是一個(gè)必然趨勢。未來的 IT 自動(dòng)化運(yùn)維必將更加專業(yè)化、標(biāo)準(zhǔn)化和流程化。通過自動(dòng)化運(yùn)維監(jiān)控,系統(tǒng)能及時(shí)發(fā)現(xiàn)故障隱患,主動(dòng)的告訴用戶需要關(guān)注的資源,以達(dá)到防患于未然。通過自動(dòng)化運(yùn)維診斷,能***限度地減少維修時(shí)間,提高服務(wù)質(zhì)量。
原文:http://www.ibm.com/developerworks/cn/aix/library/1107_tangqy_serverautomain/index.html?ca=drs-
【編輯推薦】