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服务器集群重复批量操作脚本实现的更多相关文章

  1. Linux服务器集群系统(一)--转

    引用地址:http://www.linuxvirtualserver.org/zh/lvs1.html LVS项目介绍 章文嵩 (wensong@linux-vs.org)2002 年 3 月 本文介 ...

  2. Linux服务器集群系统(一)(转)

    add by zhj:虽然是2002年的文章,但读来还是收益良多.在 章文嵩:谈LVS及阿里开源背后的精彩故事 中LVS发起人及主要贡献者谈了LVS的开发过程及阿里开源的一些故事 原文:http:// ...

  3. 【原创】Linux服务器集群通过SSH无密码登录

    SSH 无密码授权访问slave集群机器 1. 安装SSH,所有集群机器,都要安装SSH环境介绍:  Master : CNT06BIG01 192.168.3.61 SLAVE 1: CNT06BI ...

  4. Linux服务器集群系统(LVS)

    from:http://www.linuxvirtualserver.org/zh/lvs1.html#5 本文介绍了Linux服务器集群系统--LVS(Linux Virtual Server)项目 ...

  5. 浅析Linux服务器集群系统技术

    浅析Linux服务器集群系统技术 目录 前言 常用的服务器集群 集群系统的优势 LVS集群的通用体系结构 为什么使用层次的体系结构 为什么是共享存储 可伸缩Web服务 前言 总结两篇技术文章,努力学习 ...

  6. Gravitational Teleport 开源的通过ssh && kubernetes api 管理linux 服务器集群的网关

    Gravitational Teleport 是一个开源的通过ssh && kubernetes api 管理linux 服务器集群的网关 支持以下功能: 基于证书的身份认证 ssh ...

  7. Linux服务器集群系统(一)

    Reference: http://www.linuxvirtualserver.org/zh/lvs1.html LVS项目介绍 章文嵩 (wensong@linux-vs.org)2002 年 3 ...

  8. 官方文档-Linux服务器集群系统(一)

    转载-Linux服务器集群系统(一) LVS项目介绍 章文嵩 (wensong@linux-vs.org)2002 年 3 月 本文介绍了Linux服务器集群系统--LVS(Linux Virtual ...

  9. 转载-lvs官方文档-Linux服务器集群系统(二)

    Linux服务器集群系统(二) LVS集群的体系结构 章文嵩 (wensong@linux-vs.org) 2002 年 4 月 本文主要介绍了LVS集群的体系结构.先给出LVS集群的通用体系结构,并 ...

随机推荐

  1. python 序列排序 排序后返回相应的索引

    https://blog.csdn.net/longwei92/article/details/83098289 https://blog.csdn.net/u013731339/article/de ...

  2. Python 极简教程(十二)逻辑控制语句 if else

    计算机软件之所以能够对不同的情况进行不同的处理,就是我们在编码的时候,通过逻辑控制语句,告诉软件在不同的情况下应该做什么处理. 比如我们在登录的时候,那么当你输入正确的账号密码和错误的账号密码,完全是 ...

  3. BC 52 div2 A Victor and Machine

    简单数学题,把这道题目贴上去的不过为了不想看到这个月写了某个数字篇博客,该数字有点不吉利... 近期没有学习的欲望.. . 集中不了注意力,今天打BC还是做出来一题,尽管涨分了,真心希望能接近cf的水 ...

  4. SafeSEH原理及绕过技术浅析

    SafeSEH原理及绕过技术浅析 作者:magictong 时间:2012年3月16日星期五 摘要:主要介绍SafeSEH的基本原理和SafeSEH的绕过技术,重点在原理介绍. 关键词:SafeSEH ...

  5. 【】queue

    [链接]点击打开链接 [题意] 实话实说,给 OIER 大神们排队这种工作是最让人头疼的事情了.因为同学们都有自尊 心,都不愿意排后面. 现在共有 n 个同学要排成一列,每个同学有两个属性:影响力和承 ...

  6. 判断Bigdecimal类型是否等于0的方法

    1.我之前用来判断Bigdecimal类型是否等于0的方法 b.equals(BigDecimal.ZERO); 用equals方法和BigDecimal.ZERO进行比较. 2.上面方法存在的问题 ...

  7. 10.13 android输入系统_多点触摸驱动理论与框架

    1.多点触摸驱动理论 驱动程序仅上报多个触点的位置就可以,是放大还是缩小由应用程序控制 对于多点触摸驱动在linux系统中有个输入子系统,其已经实现了open/read/write等接口 我们只需要实 ...

  8. bow lsa plsa

    Bag-of-Words (BoW) 模型是NLP和IR领域中的一个基本假设.在这个模型中,一个文档(document)被表示为一组单词(word/term)的无序组合,而忽略了语法或者词序的部分.B ...

  9. 【LeetCode】【C++】Linked list cycle 2

    Given a linked list, return the node where the cycle begins. If there is no cycle, return null. Foll ...

  10. HDU 1425 sort hash+加速输入

    http://acm.hdu.edu.cn/showproblem.php?pid=1425 题目大意: 给你n个整数,请按从大到小的顺序输出其中前m大的数. 其中n和m都是位于[-500000,50 ...