커피닉스, 시스템 엔지니어의 쉼터
  여러 서버에 일괄 명령을 위한 expect 스크립트 작성일 : 2007/08/06 22:58
 
  • 글쓴이 : 좋은진호 ( http://coffeenix.net/ )
  • 조회수 : 21613
     
    제  목 : 여러 서버에 일괄 명령을 위한 expect 스크립트
    작성자 : 좋은진호(truefeel, http://coffeenix.net/ )
    작성일 : 2007.5
    정리일 : 2007.8.6(월)

    여러대의 서버가 있고, 이 서버에 일괄적으로 동일한 명령을 줘야할 상황이 있다고 하자. 이런 상황에서 각 서버에 일일히 접속한 다음에, 명령을 내리고, 로그 아웃한다는 것은 손발이 고생하는 작업이 아닌가. 이러한 무모한(?) 작업은 다음과 같은 문제가 있다.

    - 너무나도 지루한 작업이다.
    - 시간이 너무 오래 걸린다. (짧은 시간에 반드시 모든 서버에 적용해하는 상황이면 더더욱 문제다.)
    - 깜빡하고 작업하지 않은 서버가 생길 수 있다.
    - 실행해야할 명령이 많을 때, 명령중 하나를 빼먹은 서버가 생길 수 있다.

    1. 자동 접속 후 명령을 실행하는 expect 스크립트

    이러한 문제를 expect 스크립트로 해결해보자. 다음은 지정한 서버에 자동으로 접속해서 명령을 실행하는 expect 스크립트이다.

     
    #!/usr/bin/expect --
    #
    # 서버접속 -> root로 명령 실행후 종료
    #
    # 2006.11.22
    # Made by 좋은진호(truefeel, http://coffeenix.net/ )

    set timeout   7

    set server      [lindex $argv 0]
    set cmd         [lindex $argv 1]
    set logout_flg  [lindex $argv 2]

    set sleeptime   0.3
    set sleeptime2   1

    # default
    set pass        "일반유저_PW_입력하세요"
    set pass_root   "root_PW_입력하세요"

    # server 1 (기본 root PW가 다른 서버들)
    if { $server == "truefeel1" || $server == "truefeel2" || $server == "truefeel3"  } {
       set pass_root   "root_PW_입력하세요"
    }

    # server 2 (기본 root PW가 다른 서버들)
    if { $server == "w1" || $server == "w2" } {
       set pass_root   "root_PW_입력하세요"
    }

    # server 3 (기본 사용자 PW가 다른 서버들)
    if { $server == "coffeenix1" || $server == "coffeenix2" } {
       set pass        "일반유저_PW_입력하세요"
    }


    # Mesg
    set login_msg  "assword:"
    set prompt_msg "truefeel"

    # ------------------------------------------
    # option 확인
    if { $cmd == "" } {
       send "Usage : ./cmd.exp <Server> <Command> \[logout flag\] \n\n"
       send "        logout flag = 1    : 명령 실행 후에 자동으로 로그아웃\n"
       send "        logout flag 미지정 : 명령만 실행하고, 로그아웃은 하지 않음\n"
       exit
    }

    # ------------------------------------------
    spawn ssh $server

    # login
    expect $login_msg
    sleep $sleeptime
    send "$pass\r"

    sleep $sleeptime
    send "\r"

    # su
    expect $prompt_msg
    sleep $sleeptime
    send "su -\r"

    expect $login_msg
    send "$pass_root\r"

    #
    sleep $sleeptime
    send "$cmd\r"
    sleep $sleeptime2

    # logout
    if { $logout_flg == "1" } {
       send "exit\r"
       sleep $sleeptime
       send "exit\r"
       sleep $sleeptime
    }

    interact
     

    * Download : http://coffeenix.net/truefeel/files/expect/cmd.exp.txt

    위 스크립트는 Linux, FreeBSD에서 모두 사용이 가능하며, 사용 방법은 다음과 같다.

     # ./cmd.exp <접속할 서버> <명령어 나열> [자동_로그아웃_여부=1]
     


    스크립트는 너무나 간단하다. 스크립트를 크게 나눠보면, 첫번째 변수설정 부분, 2번째 옵션 체크 부분, 3번째 서버에 접속해서 명령을 내리는 부분으로 나뉜다.

    2군데 정도만 살펴보자.

     
         17 # default
         18 set pass        "일반유저_PW_입력하세요"
         19 set pass_root   "root_PW_입력하세요"
         20
         21 # server 1 (기본 root PW가 다른 서버들)
         22 if { $server == "truefeel1" || $server == "truefeel2" || $server == "truefeel3"  } {
         23         set pass_root   "root_PW_입력하세요"
         24 }
     


    17라인부터는 접속할 사용자의 PW와 root PW를 적는다. 서버에 따라 PW가 다르면 if 문을 통해서 별도로 지정할 수도 있다. 그 아래에 expect, sleep, send 가 반복적으로 쓰인 부분은 서버에 접속해서 su 명령으로 root로 변경하고, 지정한 명령을 실행하는 과정이다.

     
         74 # logout
         75 if { $logout_flg == "1" } {
         76         send "exit\r"
         77         sleep $sleeptime
         78         send "exit\r"
         79         sleep $sleeptime
         80 }
     


    이 부분은 명령 실행 후에 사용자가 원하면, 자동으로 로그아웃을 한다. exit가 2번 쓰인 것은 한번은 root -> 일반사용자로 전환을, 그 다음은 logout을 위한 것이다.

    간단한 사용 예제다. 첫번째줄은 t1.coffeenix.net 서버에 접속한 후 리부팅을 하고, 두번째줄은 ls와 'cat /etc/fstab' 명령을 내린 후 자동으로 로그 아웃(3번째 옵션으로  '1'을 지정했음)을 한다.

     
    사용예)
    ./cmd.exp t1.coffeenix.net "ls;pwd;shutdown -r now"
    ./cmd.exp t1.coffeenix.net "ls;cat /etc/fstab" 1
     


    2. 여러 서버에 접속하기 위한 shell script

    앞에 소개한 expect 스크립트는 지정한 1대의 서버에 접속하여 명령을 실행한다. 처음에 우리가 원하던 것은 여러 대의 서버에 접속하는 것이었다. 여러 대의 접속은 shell script로 처리했다.

     #!/bin/sh
    #

    # 접속할 server를 공백으로 구분하여 나열
    SERVER="$SERVER server1 server2 server3 server4 server5 server6"
    SERVER="$SERVER www1 www2 www3 www4 www5"

    # cmd.exp 경로
    EXP="$HOME/bin/cmd.exp"
    # 실행할 명령어를 ; 로 구분하여 나열 (예. ls;cd /etc )
    CMD=""
    # 명령 실행 후 자동 로그아웃하려면 1을 입력
    OPT=""

    # -------------------------------
    for s in $SERVER
    do
            case "$s" in
            *)
                    $EXP ${s} "$CMD" $OPT
                    ;;
            esac

            sleep 0.5
    done
     


    이 때 /etc/resolv.conf 의 search 옵션에는 서버의 도메인명을 적어주는게 편하다. 이를 테면 search coffeenix.net 로 설정해두면,  ssh server1.coffeenix.net 으로 하지 않고 ssh server1 명령으로만으로도 접속할 수 있게 된다.

    3. 관련 expect 스크립트

    * expect 프로그래밍 예 (글 좋은진호)
      http://coffeenix.net/board_view.php?bd_code=112
                                                                                                            
    * expect 프로그래밍 예 두번째 (글 좋은진호)
      http://coffeenix.net/board_view.php?bd_code=115

    * 스위치장비 접속/명령용 expect 스크립트 (2006.1, 글 좋은진호)
      http://coffeenix.net/board_view.php?bd_code=1327



    커피닉스, 시스템 엔지니어의 쉼터 / URL : http://coffeenix.net/board_view.php?bd_code=1519