zoukankan      html  css  js  c++  java
  • 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() { if [ -n \$${1} ]; then eval echo \$${1} else echo "" fi }" >> ${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}

  • 相关阅读:
    Vue框架构造
    JavaScript-改变this指向
    前端发展史
    python篇第10天【For 循环语句】
    python篇第10天【While 循环语句】
    python篇第8天【运算符】
    python篇第6天【数据类型】
    python篇第5天【变量】
    Python篇函数总结【输出函数】
    python篇第3天【编码规范】
  • 原文地址:https://www.cnblogs.com/fvsfvs123/p/4126850.html
Copyright © 2011-2022 走看看