본문 바로가기

기타

[linux] 여러 서버에 일괄 명령을 위한 expect 스크립트

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

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

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 명령으로만으로도 접속할 수 있게 된다.