Category Archives: Shell Scripting

Linux #23 : IO Monitoring with Cacti #2

IO Monitoring with Cacti #1에서 Rsync를 이용해서 Cacti Server에 Data를 전송한다고 하였는데 아래와 같이 rsync를 설정하였습니다.

$ more /etc/rsyncd.conf
..
[iomon]
path = [File Location Directory]/iomon
uid = root
gid = root
read only = false
hosts allow = [Allowed IP Addresses]
hosts deny =
..

Monitoring Target 서버에서 온 Data file은 지난번에 설명했던 것 처럼 아래와 같은 형식이 었고,

$ more iostat_[IP_Address]_emcpowerd
device:emcpowerd tps:48 brs:289 bws:101 br:17368 bw:6096 rrqms:0 wrqms:0 rs:36 ws:12 rsecs:289 wsecs:101 rkbs:8 wkbs:71555 avgrqsz:2 avgqusz:20 await:100 svctm:100 util:100
$ more iostat_[IP_Address]_emcpowerh
device:emcpowerh tps:0 brs:0 bws:0 br:0 bw:0 rrqms:0 wrqms:0 rs:0 ws:0 rsecs:0 wsecs:0 rkbs:0 wkbs:63182 avgrqsz:0 avgqusz:0 await:100 svctm:100 util:100

이런 형식의 Data file은 Cacti에서 간단한 설정으로 값을 수집할 수 있습니다. 여러 서버를 처리해야 함으로 아래와 같이 $1, $2 변수를 Cacti에서 index 값으로 입력 할 수 있게 하였습니다.

$1 => IP Address
$2 => Device Name


$ more mcd.sh
#!/bin/sh
DATA=`cat /[iomon location]/iomon/iostat_$1_$2`
echo $DATA

이제 나머지 부분은 Cacti에서 만들어진 Graph, Data Template과 Collection Methods의 Data Input Methods 기능을 이용하면 됩니다.

Linux #22 : IO Monitoring with Cacti #1

iostat command를 통해서 IO Status Data를 수집하는데 Cacti로 Graph를 그리기 위해서 아래와 같이 매일 1분단위로 Data를 수집합니다.
60 (Sec) / 1441 (1일). Crontab에 등록하여 그날 그날의 Data가 저장되도록 rotate를 합니다.

#!/bin/sh
# ================================================================
# -process
# Gathering IOstat data per day
# -process content
# Gathering IOstat data per day
# -how to use
# Gathering_iostat_per_day.sh
# in crontab (everyday 23:59)
# 59 23 * * * [File Location Directory]/Gathering_iostat_per_day.sh
#
# -NOTE
#
# -created date
# 2011/08/17 Jeff Kim
#
# ================================================================
BASE=[File Location Directory]
##################################################################

iostat -xt 60 1441 > $BASE/iostat_`date +%Y%m%d -d +1day` &
iostat -dt 60 1441 > BASE/iostat_tps_`date +%Y%m%d -d +1day` &

iostat에서 측정가능한 Parameter에 대한 iostat key를 만들었는데 아래와 같습니다. 개별 Parameter의 Data 추출시 이용하게 됩니다.
뒤에 나오게 되는 Script를 보시면 이 Key가 Cacti에서 Index로 사용됩니다.

$ more iostat_key.infodevice:
tps:
brs:
bws:
br:
bw:
rrqms:
wrqms:
rs:
ws:
rsecs:
wsecs:
rkbs:
wkbs:
avgrqsz:
avgqusz:
await:
svctm:
util:

또, 개별 Device에 대한 측정값이므로 device Key를 만들었고 아래와 같습니다. EMC Storage를 사용하고 있어서 emcpower[X]라는 device name이고 device에 따라 name이 다르므로 수정해야 합니다.

$ more device.infoemcpowerb
emcpowerd

예로 iostat -xt 를 찍어보면 아래와 같이 출력됩니다.

$ iostat -xt
Linux [Kernel Version] ([HostName]) 12/14/2011 _x86_64_ (8 CPU)

12/14/2011 11:26:15 AM
avg-cpu: %user %nice %system %iowait %steal %idle
1.66 0.00 0.50 0.94 0.00 96.90

Device: rrqm/s wrqm/s r/s w/s rsec/s wsec/s avgrq-sz avgqu-sz await svctm %util
sda 0.06 1.90 13.29 1.16 187.92 24.52 14.71 0.11 7.49 1.90 2.74
cciss/c0d0 0.00 12.62 1.53 2.93 12.25 124.46 30.66 0.03 7.25 0.32 0.14
cciss/c0d1 0.00 42.27 44.05 1.87 352.42 353.05 15.36 1.17 25.50 0.08 0.38
emcpowerb 0.00 0.00 0.00 0.00 0.00 0.00 7.99 0.00 3.46 1.09 0.00
emcpowerd 0.00 0.00 26.70 6.14 374.29 49.09 12.89 0.52 8.66 29.06 95.42

Data가 수집되고 있고(위 script 내용대로라면 iostat_20111214, iostat_tps_20111214), device key(device.info)와 iostat key(iostat_key.info)가 존재한다면 아래의 script를 통해 필요한 값을 추출하고 이를 Cacti에서 Graph로 그릴 수 있도록 처리 가능합니다. Crontab에 등록하여 Data file이 갱신될 수 있도록 하고 수집 cycle은 1분으로 되어 있는데 조정하시면 됩니다.

$ more Monitoring_iostat_to_cacti.sh#!/bin/sh
# ================================================================
# -process
# Monitoring IOstat data to cacti
# -process content
# Monitoring IOstat data to cacti
# -how to use
# [File Location Directory]/Monitoring_iostat_to_cacti.sh bond0
# in crontab (every min)
# * * * * * [File Location Directory]/Monitoring_iostat_to_cacti.sh bond0
# -NOTE
#
# -created date
# 2011/08/19 Jeff Kim
#
# ================================================================
BASE=[File Location Directory]
DEVICES=`cat $BASE/device.info`
DATE=`date +%Y%m%d`
KEYS=`cat $BASE/iostat_key.info`
###################################################################

rm -f $BASE/tmp*
for device in $DEVICES
do
TMP1=`cat $BASE/iostat_tps_$DATE | grep $device | cat -n | /usr/bin/perl -ane 's/\s+/,/g;print "$_\n";' | tail -n 1 | tr ',' '\n'`
TMP2=`cat $BASE/iostat_$DATE | grep $device | cat -n | /usr/bin/perl -ane 's/\s+/,/g;print "$_\n";' | tail -n 1 | tr ',' '\n'`
num=0
for value in $TMP1
do
if [ $num -ge 1 ];then
echo $value >> $BASE/tmp_iostat_value
fi
num=`expr $num + 1`
done
num=0
for value in $TMP2
do
if [ $num -ge 2 ];then
echo $value >> $BASE/tmp_iostat_value
fi
num=`expr $num + 1`
done

num=1
for key in $KEYS
do
echo $key`cat $BASE/tmp_iostat_value | head -n $num | tail -n1 | awk -F"." '{print $1};'` >> $BASE/tmp_iostat_$device
num=`expr $num + 1`
done

cat $BASE/tmp_iostat_$device | tr '\n' ' ' > $BASE/iostat_`/sbin/ifconfig $1 | grep "inet addr:" | awk -F" " '{print $2};' | awk -F":" '{print $2};'`_$device
rm -f $BASE/tmp*
rsync -arv $BASE/iostat_`/sbin/ifconfig $1 | grep "inet addr:" | awk -F" " '{print $2};' | awk -F":" '{print $2};'`_$device [Cacti_Server_HostName]::[Rsync Community Name]
done

Script를 보면 rsync로 Cacti Server에 Data를 전송하고 있는데 Cacti Server에서 Data File을 체크하는게 SNMP EXEC등으로 받아가는 것 보다 간편해서 그렇게 하고 있습니다. 그럼, 어떤 file들을 보내게 되는냐 하면

$ more iostat_[IP_Address]_emcpowerd
device:emcpowerd tps:48 brs:289 bws:101 br:17368 bw:6096 rrqms:0 wrqms:0 rs:36 ws:12 rsecs:289 wsecs:101 rkbs:8 wkbs:71555 avgrqsz:2 avgqusz:20 await:100 svctm:100 util:100
$ more iostat_[IP_Address]_emcpowerh
device:emcpowerh tps:0 brs:0 bws:0 br:0 bw:0 rrqms:0 wrqms:0 rs:0 ws:0 rsecs:0 wsecs:0 rkbs:0 wkbs:63182 avgrqsz:0 avgqusz:0 await:100 svctm:100 util:100

이 Data file은 crontab에 설정된 내용대로 매분(혹은 매 5분) 단위로 갱신되고 Local 및 Cacti server에 전송이 되겠죠. Cacti Server에서 이 Data값을 이용해서 Graph를 그리면 됩니다. 나머지 부분은 Linux #23 : IO Monitoring with Cacti #2 에서 설명하도록 하겠습니다.

Linux #4 : Basic of Shell & Shell Scripting #1

Shell은 Interface의 한 부분으로 Command Base의 User Interface라고 할 수 있습니다. 그 이유는 Keyboard를 통해 입력하여 Enter Key를 누르는 순간 동작 및 결과 값을 보여주기 때문이죠. 이는 Unix(Linux)의 기초라고도 할 수 있습니다.

Shell을 이용함에 있어 중요한 것 중 하나가 Stream인데, 이는 여러 Command를 조합하여 사용할 수 있는 걸 말하죠.  Input -> Command -> Command -> Output과 같은 식으로 결과를 조작 할 수 있게 됩니다.

$ cat file1 file2 | grep "hello world" | sort -r | less
여기서는 cat, grep, sort, less라는 command가 사용되었죠.

위에서 command는 Input과 Output을 가지게 되는데 이걸 Standard Input, Standard Output으로 표현 합니다. 여기서 Standard가 붙은 이유는 변경 가능하기 때문이죠. 이 변경을 Redirect라고 합니다.

$ cat < file1 | grep "hello world" | sort -r > result_file
두 기호 "<"과 ">"로 Redirect를 행합니다. Standard Input은 file1, Standard Output은 result_file이 됩니다.

그럼, 일단 변수에 대해서 정의를 해보면, Shell 변수는 미리 선언 할 필요가 없고 처음 사용될 때 만들어지며, 대소문자를 구별하며 기본적으로 문자열로 저장하게 된다. 만약 계산이 필요하다면 수치로 변환되어 계산되고 이후 문자열로 저장되고, “$“을 붙여서 사용한다. 단, 변수에 값을 대응 할 시에는 “$“을 사용하지 않습니다.

변수를 미리 선언하는 경우 ex) float a, b;
Shell 변수의 경우(값 대입시) ex) HOME="Hello World"
Shell 변수의 경우(사용시) ex) echo $HOME

변수는 환경 변수, 인자 변수, 일반 변수로 구분된다.
환경 변수의 경우는 Shell 기동 후 기본적으로 Setting 되어 있는 변수입니다.

$0 : 실행 된 Shell Script Name
$# : Scripts에 넘겨진 인자의 갯수
$$ : Shell Script의 Process ID

이고, 일반 변수를 export를 사용하여 환경 변수로 처리할 수 있습니다.

export HOME

인자 변수의 경우는 Shell Script에 인자를 넘겨 줄 때 그 인자들에 대한 정보를 가지는 변수입니다.

$1 ~ $nnn : 넘겨진 인자들
$* : 스크립트에 전달된 인자들을 모아놓은 문자열
[email protected] : $*과 동일함

그리고, 마지막으로 일반 변수인데 특별한 제약이 없고 대소문자 만 구별하면 됩니다.

#!/bin/sh
echo "Test Script #1 : $0"
echo "Process ID : $$"
echo "Argument Count : $#"
echo "Argument List(*) : $*"
echo "Argument List(@) : [email protected]"
echo "Argument #1 : $1"
echo "Argument #2 : $2"

위 Script를 아래와 같이 실행하면

$./Test_Script_#1 1_hello 2_world
Test Script #1 : ./Test_Script_#1
Process ID : 12342
Argument Count : 2
Argument List(*) :  1_hello 2_world
Argument List(@) : 1_hello 2_world
Argument #1 : 1_hello
Argument #2 : 2_world

의 결과값이 나오겠죠.

간단하게 순서대로 명령어만 실행되는 Script라면 Script라고 하긴 좀 그렇고, 조건문들을 다루어 봐야 합니다.  조건문은 Programing을 하셨다면 아시다시피 if, for, while, case 크게 다르진 않습니다. 한가지 재미있는 건 조건문을 끝낼 때 if는 fi, case는 esac같이 거꾸로 쓰는게 있다는 거 for나 while의 경우는 do로 시작해 done이죠.

if 구문 : 조건에서 이게 없으면 시체나 다름 없죠. 삶은 case와 같이 여러 갈래가 있기도 하지만, Yes or No 만큼 중요한 건 없습니다.(거의 일상 생활이나 다름 없죠)

CHK_EXT3=`cat /etc/fstab | grep -c "ext3"`
if [ $CHK_EXT -eq 0 ];then
echo "There is no ext3 filesystem."
else
echo `cat /etc/fstab | grep "ext3"`
fi

여기서 “`(Back quote)“가 사용되고 있는데, “`[command line]`” Command Line을 실행한 결과 값을 변환 해 주는 겁니다. 그리고, 여기선 /etc/fstab에 ext3 filesystem이 있으면 grep으로 count하여 없으면 There is no ext3 filesystem 있으면 해당 line을 보여주는 겁니다. 또, 수식에 따른 조건 연산자(산술 연산자) -eq를 사용하였는데 각 연산자를 설명하면 아래와 같습니다.

-lt : 보다 작다 (Lesser Than)
-le : 이하 (Less or Eaqual)
-eq : 같다 (Eaqual)
-ge : 이상 (Greater or Eaqual)
-gt  : 보다 크다 (Greater Than)
-ne : 같지 않다 (Not Eaqual)

위에서는 A가 아니면 B(if ~ else문)인데, 아래와 같이 elif(if~elif문)를 쓰는 경우 조건을 더욱 확장 가능합니다.

# Source function library
if [ -f /etc/rc.d/init.d/functions ]; then
. /etc/rc.d/init.d/functions
elif [ -f /etc/init.d/functions ]; then
. /etc/init.d/functions
elif [ -f /etc/rc.d/functions ]; then
. /etc/rc.d/functions
fi

여기선 if와 함께 file의 속성확인에 사용되는 연산자가 사용 되었는데, File 조건 연산자는 아래와 같습니다.

-d FILE : Directory로서 존재(Directory)
-e FILE : File이 존재(Exist)하나 Directory일 수도 있음
-f FILE : File로서 존재(File)
-r FILE : Readable file임
-s FILE : File is not empty (Size가 있음)
-w FILE : Writable file이고, 또한 Directory안을 확인 가능
-x FILE : 실행가능(eXecute)
-O FILE : 소유권이 있음(Owner)
-G FILE : 그룹에 포함되어 있음(Group)

추가로 File의 시각확인을 위한

FILE1 -nt FILE2 : FILE1이 FILE2보다 새거임 (Newer Than)
FILE1 -ot FILE2 : FILE1이 FILE2보다 오래됨 (Older Than)

추가로

if [ \( -d FILE1 \) -a \( -x FILE2 \) ];then
-a : &&과 같이 둘 다 성립할 때만 조건 만족
if [ \( -d FILE1 \) -o \( -x FILE2 \) ];then
-o : ||과 같이 둘 중에 하나만 성립해도 조건 만족

마지막으로 문자열 비교 연산자를 확인 해 보면,

[ String ] : String이 NULL이 아니면 조건 만족
[ String1 = String2 ] : 두 문자열이 같으면 조건 만족
[ String1 != String2 ] : 두 문자열이 다르면 조건 만족
[ -n String1 ] : String1이 NULL이 아니면 조건 만족
[ -z String2 ] : String1이 NULL이면 조건 만족

아래 예제를 참고 해 보세요.

CONTINUE="$INIT_CONTINUE"
if [ -z "$CONTINUE" ];then
cat <<EOF

$DATE_TIME [INFO] Continue....
EOF
CONTINUE=`Prompt "[INFO] Press ('Any Key' for Continue): "`
fi