Category Archives: Development

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 #19 : Installation Apache + PHP + Subversion

PHP개발을 위해 Apache와 Subversion을 구축 할 때 사용했던 App와 설치 옵션 들입니다.

SQLite
Link : http://www.sqlite.org/
Current Source version : 3.6.15

$ ./configure
$ make;make install

Linux(RHEL)에 설치된 Version을 사용해도 되고 Source Version을 별도로 설치해서 사용해도 됩니다.

BDB (Berkeley DB)
Link : http://www.oracle.com/technetwork/database/berkeleydb/downloads/index.html
Current Source version : 4.7.25

$ cd $BASE/build_unix
$ ../dist/configure --prefix=/usr/local
$ make install

Apr과 Apr-util은 다른 이것 저것들을 해보기 위해서 설치 했습니다. 개인적으로 App들을 개별적인 Directory에 관리하는 걸 선호해서 prefix를 일일이 설정 해 주기도 합니다.

Apr
Link : http://apr.apache.org/
Current Source version : 1.3.5

$ ./configure --prefix=/usr/local/apr

Apr-util
Link : http://apr.apache.org/
Current Source version : 1.3.7

$ ./configure --prefix=/usr/local/apr --with-berkeley-db=/usr/local/lib --with-apr=/usr/local/apr --with-ldap

Apache
Link : http://httpd.apache.org/
Current Source version : 2.2.11

$ ./configure --prefix=/usr/local/apache2 --enable-proxy --enable-proxy-http --enable-proxy-balancer --enable-dav --enable-rewrite --enable-deflate --enable-headers --enable-logio --enable-expires --enable-so --with-berkeley-db=/usr --with-expat=builtin --with-ldap --enable-authnz-ldap=shared --enable-ldap=shared --with-ssl=/usr --enable-ssl --with-apr=/usr/local/apr --with-apr-util=/usr/local/apr

Subversion
Link : http://subversion.tigris.org/
Current Source version : 1.6.3

$ ./configure --with-apxs=/usr/local/apache2/bin/apxs --with-apr=/usr/local/apr --with-apr-util=/usr/local/apr --with-berkeley-db=db.h:/usr/local/include:/usr/local/lib:db-4.7 --with-sqlite=/usr/local/sqlite --with-neon=/usr/local --with-ssl

PHP
Link : http://www.php.net/
Current Source version : 5.2.8

$ ./configure --prefix=/usr/local/php --with-apxs2=/usr/local/apache2/bin/apxs --with-mysql=/usr/local//mysql --with-gd --with-pear --with-ldap --enable-sysvsem --enable-sysvshm --enable-sysvmsg --with-gettext --with-curl -enable-gd-native-ttf --with-pic --enable-wddx --with-kerberos --with-mcrypt --enable-mbstring --enable-sockets --with-freetype-dir --with-jpeg-dir --with-snmp --enable-ucd-snmp-hack --enable-pcntl --with-zlib

혹 compile이 안되고 아래와 같은 Error가 있을 경우, libtool-ltdl을 설치합니다.

/usr/bin/ld: cannot find -lltdl
collect2: ld returned 1 exit status
$ yum install libtool-ltdl.x86_64 libtool.x86_64 or libtool-ltdl.i386 libtoolxi386

그럼, 이제 PHP와 Subversion을 사용하기 위한 Apache를 설정합니다.

PHP 관련 설정

$ vi /usr/local/apache2/conf/httpd.conf
..
# PHP
Include conf/extra/httpd-php.conf
..
$ vi /usr/local/apache2/conf/extra/https-php.conf
#
# PHP is an HTML-embedded scripting language which attempts to make it
# easy for developers to write dynamically generated webpages.
#
LoadModule php5_module modules/libphp5.so
#
# Cause the PHP interpreter to handle files with a .php extension.
#
AddHandler php5-script .php
AddType text/html .php
#
# Add index.php to the list of files that will be served as directory
# indexes.
#
DirectoryIndex index.php
#
# Uncomment the following line to allow PHP to pretty-print .phps
# files as PHP source code:
#
#AddType application/x-httpd-php-source .phps

Subversion관련 설정

$ vi /usr/local/apache2/conf/httpd.conf
..
# SVN
Include conf/extra/httpd-svn.conf
..
$ vi /usr/local/apache2/conf/extra/https-svn.conf
DAV svn
SVNPath /home/www/svn/project1
# Apache의 authentication을 사용할 경우
AuthType Basic
AuthName "Secure Area"
AuthUserFile /usr/local/apache2/.htaccess
Require valid-user jeff

.htaccess File을 생성할 필요가 있으므로 아래와 같이..

$ /usr/local/apache2/bin/htpasswd -c /usr/local/apache2/.htaccess jeff
New password:

SVN과 관련 된 Repository를 생성 한다. 위 설정대로라면

$ svnadmin create /home/www/svn/project1

이후 WebDAV를 경유해서 Repository에 접근 할 시 아래와 같은 에러가 발생 한다면


Could not open the requested SVN filesystem

$ tail -f /usr/local/apache2/log/error_log
(20014)Internal error: SQLite compiled for 3.6.15, but running with 3.3.7
Could not fetch resource information. [500, #0]
Could not open the requested SVN filesystem [500, #200030]
Could not open the requested SVN filesystem [500, #200030]

이는 PHP를 먼저 설치하고 Subversion을 설치 할 경우 아래와 같은 Error가 나는 경우가 있는데, 이는 /usr/local/apache2/modules/mod_dav_svn.so이 SQLite의 3.6.15가 설치되었음에도 불구하고 3.3.7을 보고 있는 경우로 PHP Module인 libphp5.so가 입력되어 있으면 libphp5.so로 인해 Subversion을 도입시에 같은 오래된 library를 link해 버리는 문제가 있습니다.
따라서 PHP Module을 comment 처리 한 후에 Subversion을 재설치하면 해결이 됩니다.

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