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集群的通用体系结构,并 ...
随机推荐
- solr/lucence和关系数据库的混合使用
我们知道solr提供了一个DIHandler,提供将关系数据库中的数据导成索引,然后使用solr查询. 对于一个大表中关联数个小表的查询,这非常耗费时间. 我的思路是: 1. 将一个大表做成索引,使用 ...
- LED恒流设计
- 【LCS】POJ1458Common Subsequence
题目链接:http://poj.org/problem?id=1458 这是一道最长公共子序列的模板题: #include<iostream> #include<string> ...
- 开发板 视频04_05 ubuntu的联网及基本设置
4g内存 如果电脑有两g,只能给1.5g 处理器可以根据实际选 usb3.0 或者 2.0 联网模式:: 桥接模式 启动式连接,,,,网是不固定的 仅主机模式,主机和虚拟机在一个网络 第三种联网,自定 ...
- LA 3882 - And Then There Was One(约瑟夫 递归)
看题传送门 题目大意: N个数排成一圈,第一次删除m,以后每k个数删除一次,求最后一被删除的数. 如果这题用链表或者数组模拟整个过程的话,时间复杂度都将高达O(nk),而n<=10000,k&l ...
- html的meta标签的charset应该用UTF-8还是utf-8?
之前我也纠结过写html的时候是用<meta charset="UTF-8"/> 或者是 <meta charset="utf-8"/> ...
- 在此页上的 ActiveX 控件和本页上的其它部份的交互可能不安全
版权声明:转载时请以超链接形式标明文章原始出处和作者信息http://xqy266.blogbus.com/logs/66258230.html 在EOS6的项目中,如果采用VC++开发的Active ...
- 【u237】分数化小数
Time Limit: 1 second Memory Limit: 128 MB [问题描述] 写一个程序,输入一个形如N/D的分数(N是分子,D是分母),输出它的小数形式.如果小数有循环节的话,把 ...
- (二)SSO之CAS框架单点退出,自己定义退出界面.
用CAS的退出,仅仅能使用它自己的那个退出界面,假设有这种要求, 要求退出后自己主动跳转到登录界面, 该怎样做呢? 以下这篇文章实现了退出后能够自己定义跳转界面. 用了CAS,发现退出真是个麻烦事,退 ...
- js进阶解决浏览器缓存不能自动更新的问题(在ajax的url上带上一个参数,可以是日期,或者是随机数)(随机数Math.random)(取得日期的毫秒数:new Date().getTime();)
js进阶解决浏览器缓存不能自动更新的问题(在ajax的url上带上一个参数,可以是日期,或者是随机数)(随机数Math.random)(取得日期的毫秒数:new Date().getTime();) ...