linux服务器集群重复批量操作脚本实现
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服务器集群重复批量操作脚本实现的更多相关文章
- Linux服务器集群系统(一)--转
引用地址:http://www.linuxvirtualserver.org/zh/lvs1.html LVS项目介绍 章文嵩 (wensong@linux-vs.org)2002 年 3 月 本文介 ...
- Linux服务器集群系统(一)(转)
add by zhj:虽然是2002年的文章,但读来还是收益良多.在 章文嵩:谈LVS及阿里开源背后的精彩故事 中LVS发起人及主要贡献者谈了LVS的开发过程及阿里开源的一些故事 原文:http:// ...
- 【原创】Linux服务器集群通过SSH无密码登录
SSH 无密码授权访问slave集群机器 1. 安装SSH,所有集群机器,都要安装SSH环境介绍: Master : CNT06BIG01 192.168.3.61 SLAVE 1: CNT06BI ...
- Linux服务器集群系统(LVS)
from:http://www.linuxvirtualserver.org/zh/lvs1.html#5 本文介绍了Linux服务器集群系统--LVS(Linux Virtual Server)项目 ...
- 浅析Linux服务器集群系统技术
浅析Linux服务器集群系统技术 目录 前言 常用的服务器集群 集群系统的优势 LVS集群的通用体系结构 为什么使用层次的体系结构 为什么是共享存储 可伸缩Web服务 前言 总结两篇技术文章,努力学习 ...
- Gravitational Teleport 开源的通过ssh && kubernetes api 管理linux 服务器集群的网关
Gravitational Teleport 是一个开源的通过ssh && kubernetes api 管理linux 服务器集群的网关 支持以下功能: 基于证书的身份认证 ssh ...
- Linux服务器集群系统(一)
Reference: http://www.linuxvirtualserver.org/zh/lvs1.html LVS项目介绍 章文嵩 (wensong@linux-vs.org)2002 年 3 ...
- 官方文档-Linux服务器集群系统(一)
转载-Linux服务器集群系统(一) LVS项目介绍 章文嵩 (wensong@linux-vs.org)2002 年 3 月 本文介绍了Linux服务器集群系统--LVS(Linux Virtual ...
- 转载-lvs官方文档-Linux服务器集群系统(二)
Linux服务器集群系统(二) LVS集群的体系结构 章文嵩 (wensong@linux-vs.org) 2002 年 4 月 本文主要介绍了LVS集群的体系结构.先给出LVS集群的通用体系结构,并 ...
随机推荐
- net基础题
1. 简述 private. protected. public. internal 修饰符的访问权限. 答 . private : 私有成员, 在类的内部才可以访问. protected : 保 ...
- jemter--录制的脚本设置循环次数不起作用
以下是比较jmeter线程组中设置循环次数和循环控制器中设置循环次数的区别 1.jmeter生成的脚本没有step1(循环控制器)控制器,故循环在线程组中设置 2.badboy录制的脚本有setp ...
- windows 控制台下 无法获取完整的回车键值
问题描述: 收集的网友分析: http://bbs.csdn.net/topics/370084904 因为C语言和UNIX的开发者是同事…… C语言里统一用的\n表示另起一行.微软的DOS受到了当时 ...
- thinkphp事务不能回滚的问题(因为助手函数)
thinkphp事务不能回滚的问题(因为助手函数) 一.总结 二.thinkphp 5 事务不能回滚 Db::startTrans(); try{ db('address')->where([' ...
- apache与IIS共用80端口冲突解决方法
如果同一台电脑安装了apache和iis,会提示80端口冲突,如何解决apache与iis 80端口冲突的问题呢,并且同时使用apache和iis 将apache设为使用80端口,IIS使用其它端口, ...
- ASI使用
一.ASI类库集成: .添加源代码文件 ASIAuthenticationDialog.h ASIAuthenticationDialog.m ASICacheDelegate.h ASIDataCo ...
- [WASM] Compile C Code into WebAssembly
We use the C language instead of pure WAST to create a square root function using WASM Fiddle (https ...
- ANSCII码和BCD码互转
bool AtoBCD(unsigned char* Asc,unsigned char* BCD,int len) { int i; unsigned char ch; //高位 unsigned ...
- 【Codeforces Round #439 (Div. 2) A】The Artful Expedient
[链接] 链接 [题意] [题解] 暴力 [错的次数] 在这里输入错的次数 [反思] 在这里输入反思 [代码] #include <bits/stdc++.h> using namespa ...
- php 小程序获取渠道二维码 保存
function ppost($url,$arr){ $post_data = json_encode($arr); $url=$url; $ch = curl_init(); curl_setopt ...