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集群的通用体系结构,并 ...
随机推荐
- Redis原理(一)
基础和应用 1.Redis是远程调用技术的首字母缩写. 2.Redis可以用来做什么? Redis可以用来做缓存. 分布式锁 3.Redis的应用举例 记录帖子的点赞数.评论数和点击数.(使用HASH ...
- 二叉树的递归插入【Java实现】
C++中由于有指针的存在,可以让二叉树节点指针的指针作为插入函数的实参,在函数体内通过*操作实现对真实节点指针.节点左孩子指针.节点右孩子指针的改变,这样很容易使用递归将大树问题转化到小树问题.但在J ...
- scrapy-加蘑菇代理
加代理ip 隧道代理 setting中 解开 下载器
- java线程——详解Callable、Future和FutureTask
回顾: 接上篇博客 java线程--三种创建线程的方式,这篇博客主要介绍第三种方式Callable和Future.比较继承Thread类和实现Runnable接口,接口更加灵活,使用更广泛.但这两种方 ...
- (转)linux screen 命令详解
转自:http://www.cnblogs.com/mchina/archive/2013/01/30/2880680.html 一.背景 系统管理员经常需要SSH 或者telent 远程登录到Lin ...
- [React Intl] Format a Date Relative to the Current Date Using react-intl FormattedRelative
Given a date, we’ll use the react-intl FormattedRelative component to render a date in a human reada ...
- 高级Java工程师必备 ----- 深入分析 Java IO (三)
概述 Java IO即Java 输入输出系统.不管我们编写何种应用,都难免和各种输入输出相关的媒介打交道,其实和媒介进行IO的过程是十分复杂的,这要考虑的因素特别多,比如我们要考虑和哪种媒介进行IO( ...
- ArcGIS中ObjectID,FID和OID字段区别
lysc_forever 原文 ArcGIS中ObjectID,FID和OID字段有什么区别 ArcGIS Desktop 独立的表和属性表都有一个ObjectID字段.这个字段中包含一个唯一的,长整 ...
- [慕课笔记]Node入口文件分析和目录初始化
1:我们要在根目录下安装这些模块 2:然后来编写这些入口文件,这几行代码的大概意思是说,我引入一个express的模块,然后生成一个webview 的实例,将这个实例的监听端口设置成3000,然后就可 ...
- 【a101】高精度实数加法
Time Limit: 1 second Memory Limit: 2 MB 问题描述 给出两个高精度正实数(可以含有小数点或没有),最长200位,字符串读入 求它们的和,小数部分末尾的0要舍去. ...