標籤:blog http io ar os 使用 sp for strong
http://blog.csdn.net/flyinmind/article/details/8074863
在伺服器叢集的維護中,經常會遇到同樣的操作重複執行很多遍的情況,“登入伺服器->做操作->退出”,繼續下一個伺服器。簡單枯燥、容易出錯、並且毫無成就感。
我在做push產品的過程中,見到有同事在這個簡單重複的工作中,經常犯一些低級錯誤,心灰意冷。所以我花了一點時間將能自動化的過程全部自動化,操作人員只需做兩件事:
1、記錄所有伺服器的IP、SSH連接埠、使用者名稱、密碼、登入提示符、主路徑,記為:xx.srv檔案;
每行一個伺服器,以逗號分隔,比如:"192.168.9.1:22,opush,opush,>,/home/push/8080"
2、記錄每個伺服器上要做的重複操作,記為:yy.cmd檔案。
每行一個命令,[]括起的部分表示在本地執行的,<>括起的是最開始執行一次,{}括起的部分表示在所有伺服器都操作完之後,最後在本地執行一次的命令,比如:
[scp {USER}@{HOST}:/home/opush/logs/* ./rec]
ps aux|grep {USER}|grep catalina|grep startup|awk ‘{print \\\$2}‘ | xargs kill -9
cd /home/{USER}
rm -rf ./logs/*
./bin/startup.sh
{tar cfz logs.tar.gz ./rec/*}
上面例子中,先拷貝tomcat的日誌到本地的rec目錄,然後登陸到伺服器上關閉tomcat、刪除日誌、啟動tomcat,所有伺服器都做一遍之後,退回到本機打包壓縮記錄檔。
上面例子中出現了{USER}、{HOST},這個類似於“宏”,在執行時會被替換為相應值,看當前在叢集中的哪個伺服器上執行,比如在192.168.9.1上執行,使用者名稱是opush,則這裡就會被替換成他們,還有:{PORT}、{PASSWORD}、{PATH}、{NO}幾個宏,可以在命令中使用。
還有一點需要注意,<>,[],{}只能括住一行,不能多行,如果有多個命令,比如多行,並且每行一對括弧。
最後一點,這兩個檔案如果行首是"#",則表示注釋。
本工具的目錄結構如下:
/--
|--conf/
|--exec
做好後,將這個兩個檔案放到conf目錄下,xx.srv檔案可以只需一份,在不同的操作中重複使用;每一類操作記錄一個yy.cmd檔案(比如上面的操作可以命名為getlogs_reset.cmd),在以後操作時只需運行:
./exec xx.srv yy.cmd
OK,所有操作都自動完成。
指令碼的基本原理是使用awk讀取設定檔,使用expect指令碼來完成自動的互動,所以在你的跳板機上(只需跳板機安裝就可以了)必須要有awk、expect;這個指令碼用的是bash,所以也需要它,如果沒有,可能要對exec做一點改動,比如適應ksh、csh等。如果你用的是suse,安裝包中已內建了expect,使用yast安裝就可以了,其他的linux也應該可以安裝。
下面是指令碼的源碼,供參考,如果你做了什麼改進,或發現了問題,歡迎發給我,一起來改進它。本來想起一個開源項目,因為這個太小了,也沒有必要做的太通用,所以就放在這裡,供大家參考吧。希望能將你從重複枯草的鍵盤運動中解放出來:)
#!/bin/bash
translateServers() {
awk ‘
BEGIN {
FS=","
serverNum = 0
}
function trim(str) {
gsub(/^\s*/, "", str)
gsub(/\s*$/, "", str)
return str
}
{
prompt = ">"
port = 22
host = ""
user = ""
password = ""
path = ""
line = trim($0)
pos = index(line, "#")
if (pos != 1) {
if (NF >= 3) {
server = $1
user = $2
password = $3
pos = index(server, ":")
if ( pos > 1 ) {
split(server, arr, ":")
host = arr[1]
port = arr[2]
} else {
host = server
}
if (NF > 3) {
prompt = $4
}
if (NF > 4) {
path = $5
}
print "host_" NR "=\"" host "\""
print "port_" NR "=" port
print "user_" NR "=\"" user "\""
print "password_" NR "=\"" password "\""
print "prompt_" NR "=\"" prompt "\""
print "path_" NR "=\"" path "\""
serverNum = serverNum + 1
}
}
}
END {
print "SERVER_NUM=" serverNum
}
‘ $1
}
translateCommands() {
awk ‘
BEGIN {
commandNum = 0
remote_commands = ""
foreType = 0
FS=","
}
function trim(str) {
gsub(/^\s*/, "", str);
gsub(/\s*$/, "", str);
return str
}
function print_remote() {
if (remote_commands != "" && foreType == 0) {
print "cmd_type" commandNum "=0"
print "cmd_line" commandNum "=\"" remote_commands "\""
}
remote_commands = ""
}
function print_local(line, end, type) {
print_remote()
commandNum = commandNum + 1
len = length(line)
last = substr(line, len, 1)
if (last == end) {
command = substr(line, 2, len - 2)
} else {
command = substr(line, 2)
}
print "cmd_type" commandNum "=" type
print "cmd_line" commandNum "=\"" command "\""
}
{
type = 0
gsub(/\"/, "\\\\\\\"")
#gsub(/\$/, "\\\\\\\$")
line = trim($0)
header = substr(line, 1, 1)
if (header != "#" && length(line) > 1) {
if (header == "[") {
type = 1
print_local(line, "]", type)
} else if (header == "{") {
type = 2
print_local(line, "}", type)
} else if (header == "<") {
type = 3
print_local(line, ">", type)
} else {
if (remote_commands == "") {
commandNum = commandNum + 1
}
type = 0
remote_commands = remote_commands"\\r"line
}
foreType = type
}
}
END {
print_remote()
print "COMMAND_NUM=" commandNum
}
‘ $1
}
executeRemote() {
HOST="$1"
PORT="$2"
USER="$3"
PASSWORDD="$4"
PROMPT="$5"
CMDS="$6"
remote_commands="
puts \"login server, wait ${PROMPT}...\\n\";
spawn ssh -p ${PORT} ${USER}@${HOST};
set timeout 15;
set doItAgain 1;
while { \${doItAgain} } {
expect {
\"*continue connecting*\" {
send \"yes\\r\";
}
\"*assword*\" {
send \"${PASSWORD}\\r\";
}
\"*${USER}*${PROMPT}\" {
puts \"login ${HOST} successfully : )\";
set doItAgain 0;
}
\"*#\" {
puts \"login ${HOST} successfully : )\";
set doItAgain 0;
}
timeout break;
}
}
if { \$doItAgain == 0 } {
set CMDS [split \"${CMDS}\" \"\\r\"];
foreach CMD \${CMDS} {
send \"\${CMD}\\r\";
expect \"*${USER}*${PROMPT}\";
}
send \"exit\\r\";
expect eof;
} else {
puts \"fail to login\";
}
"
expect -c "$remote_commands"
}
scpFile() {
SCPCMD=$1
PASSWORD=$2
scp_commands="
puts \"spawn ${SCPCMD}\\n\";
spawn ${SCPCMD};
set doItAgain 1;
while { \$doItAgain } {
expect {
\"*continue connecting*\" {
send \"yes\\r\";
}
\"*assword:*\" {
send \"${PASSWORD}\\r\";
}
eof {
set doItAgain 0;
}
}
}
"
expect -c "$scp_commands"
}
runCommand() {
N=$1
HOST=$(getCfgItem "host_${N}")
PORT=$(getCfgItem "port_${N}")
USER=$(getCfgItem "user_${N}")
PASSWORD=$(getCfgItem "password_${N}")
PMPT=$(getCfgItem "prompt_${N}")
MAINPATH=$(getCfgItem "path_${N}")
for((k = 1; k <= COMMAND_NUM; k++)); do
TYPE=$(getCfgItem "cmd_type${k}")
CMD=$(getCfgItem "cmd_line${k}")
CMD=${CMD//\{HOST\}/$HOST}
CMD=${CMD//\{PORT\}/$PORT}
CMD=${CMD//\{USER\}/$USER}
CMD=${CMD//\{PATH\}/$MAINPATH}
CMD=${CMD//\{PASSWORD\}/$PASSWORD}
CMD=${CMD//\{NO\}/$N}
if [ $TYPE = 1 ]; then
echo "execute \"${CMD}\""
if [[ $CMD =~ "^scp.*$" ]]; then
scpFile "${CMD}" "${PASSWORD}"
else
eval "${CMD}"
fi
elif [ $TYPE = 0 ]; then
executeRemote "$HOST" "$PORT" "$USER" "$PASSWORD" "$PMPT" "$CMD"
fi
done
}
## only local command, and run at the end
runCommandOnce() {
EXPECTED_TYPE=$1
for((i = 1; i <= COMMAND_NUM; i++)); do
TYPE=$(getCfgItem "cmd_type${i}")
if [ "$TYPE" -eq "$EXPECTED_TYPE" ]; then
echo "execute \"${CMD}\""
CMD=$(getCfgItem "cmd_line${i}")
eval "${CMD}"
fi
done
}
if [ $# -lt 1 ]; then
echo "Usage: exec server_list_file command_list_file"
exit
fi
server_file="./conf/$1"
command_file="./conf/$2"
temp_file="./$2.cmd"
dos2unix ${server_file}
dos2unix ${command_file}
translateServers ${server_file} > ${temp_file}
translateCommands ${command_file} >> ${temp_file}
echo -e "getCfgItem() {\nif [ -n \\$\${1} ]; then\n eval echo \\$\${1}\nelse\n echo \"\"\nfi\n}" >> ${temp_file}
source ${temp_file}
runCommandOnce 3
echo "Start to execute command on all ${SERVER_NUM} servers"
for(( i = 1; i <= SERVER_NUM; i++)); do
runCommand $i
done
runCommandOnce 2
echo "Execute commands end"
rm ${temp_file}
linux伺服器叢集重複大量操作指令碼實現