참고로 위의 모식도는 하둡의 쉘 실행모습입니다. 하마도 하둡과 매우 비슷합니다.

$ bin/start-bspd.sh

위와 같이 start-bspd.sh 로 부터 시작합니다. 마스터 노드에서 실행합니다.

bin=`dirname "$0"`

bin=`cd "$bin"; pwd`       //  bin 디렉토리의 경로를 얻는다.


. "$bin"/hama-config.sh  // hama-config.sh 를 내부적으로 실행한다.


"$bin"/hama-daemons.sh --config "${HAMA_CONF_DIR}" start zookeeper   // 주키퍼 부터 시작하고

"$bin"/hama-daemon.sh --config $HAMA_CONF_DIR start bspmaster   // bspmaster 실행

"$bin"/hama-daemons.sh --config $HAMA_CONF_DIR start groom      // groom 실행 


bin/hama-config.sh

hama-config.sh 안에서는 conf/hama-env.sh 를 실행하며, groomserver 를 export

conf/hama-env.sh

hama-env.sh 에서는 아래와 같이 JAVA_HOME 등을 설정합니다.

# The java implementation to use.  Required.
export JAVA_HOME=/usr/lib/jvm/java-7-oracle

# Where log files are stored.  $HAMA_HOME/logs by default.
# export HAMA_LOG_DIR=${HAMA_HOME}/logs

# The maximum amount of heap to use, in MB. Default is 1000.
# export HAMA_HEAPSIZE=1000

# Tell Hama whether it should manage it's own instance of Zookeeper or not.
export HAMA_MANAGES_ZK=true 


hama-daemon.sh 을 통해 bspmaster 를  실행합니다. 

#!/usr/bin/env bash

usage="Usage: hama-daemon.sh [--config <conf-dir>] [--hosts hostlistfile] (start|stop) <hama-command> <args...>"


# if no args specified, show usage

if [ $# -le 1 ]; then

  echo $usage

  exit 1

fi


bin=`dirname "$0"`

bin=`cd "$bin"; pwd`


. "$bin"/hama-config.sh


# get arguments

startStop=$1

shift

command=$1

shift


hama_rotate_log ()

{

    log=$1;

    num=5;

    if [ -n "$2" ]; then

num=$2

    fi

    if [ -f "$log" ]; then # rotate logs

while [ $num -gt 1 ]; do

   prev=`expr $num - 1`       

   [ -f "$log.$prev" ] && mv "$log.$prev" "$log.$num"

   num=$prev

done

mv "$log" "$log.$num";

    fi

}


if [ -f "${HAMA_CONF_DIR}/hama-env.sh" ]; then

  . "${HAMA_CONF_DIR}/hama-env.sh"

fi


# get log directory

if [ "$HAMA_LOG_DIR" = "" ]; then

  export HAMA_LOG_DIR="$HAMA_HOME/logs"

fi

mkdir -p "$HAMA_LOG_DIR"


if [ "$HAMA_PID_DIR" = "" ]; then

  HAMA_PID_DIR=/tmp

fi


if [ "$HAMA_IDENT_STRING" = "" ]; then

  export HAMA_IDENT_STRING="$USER"

fi


# some variables

export HAMA_LOGFILE=hama-$HAMA_IDENT_STRING-$command-$HOSTNAME.log

export HAMA_ROOT_LOGGER="INFO,DRFA"

log=$HAMA_LOG_DIR/hama-$HAMA_IDENT_STRING-$command-$HOSTNAME.out

pid=$HAMA_PID_DIR/hama-$HAMA_IDENT_STRING-$command.pid


# Set default scheduling priority

if [ "$HAMA_NICENESS" = "" ]; then

    export HAMA_NICENESS=0

fi


case $startStop in

  (start)

    mkdir -p "$HAMA_PID_DIR"


    if [ -f $pid ]; then

      if kill -0 `cat $pid` > /dev/null 2>&1; then

        echo $command running as process `cat $pid`.  Stop it first.

        exit 1

      fi

    fi


    if [ "$HAMA_MASTER" != "" ]; then

      echo rsync from $HAMA_MASTER

      rsync -a -e ssh --delete --exclude=.svn --exclude='logs/*' --exclude='contrib/hod/logs/*' $HAMA_MASTER/ "$HAMA_HOME"

    fi


    hama_rotate_log $log

    echo starting $command, logging to $log

    cd "$HAMA_HOME"

    nohup nice -n $HAMA_NICENESS "$HAMA_HOME"/bin/hama --config $HAMA_CONF_DIR $command "$@" > "$log" 2>&1 < /dev/null &

    echo $! > $pid

    sleep 1; head "$log"

    ;;

          

  (stop)


    if [ -f $pid ]; then

      if kill -0 `cat $pid` > /dev/null 2>&1; then

        echo stopping $command

        kill `cat $pid`

      else

        echo no $command to stop

      fi

    else

      echo no $command to stop

    fi

    ;;


  (*)

    echo $usage

    exit 1

    ;;


esac


--------------------------------------------------------------------------------------------------------

- 역 따옴표는 따옴표나 작은 따옴표와는 완전히 다른 용도로 쓰인다. 공백 문자를 포함하는 데에는 쓰이지 않는다.  글의 앞쪽에서 다음 라인을 사용했던 것을 기억해 보라:

x=$(expr $x + 1)

이미 당신이 알듯이, 명령 expr $x + 1 결과가 변수 x 들어가게 된다.  따옴표를 사용한 다음의 명령도  같은 결과를 낸다:

x=`expr $x + 1`

어느 것을 사용해야 하는가 하는 문제는 순전히 당신에게 달려 있다. 당신이 좋은 쪽을 선택하라.  따옴표가 $(...) 보다 자주 사용되는 것을   있을 것이다. 하지만, $(...) 보다 읽기 쉽다. 다음과 같은 경우엔 특히  그렇다:

$!/bin/bash
echo "I am `whoami`"

--------------------------------------------------------------------------------------------------------

-nohup : 로그아웃 하여 터미널을 빠져나가도 실행중인 프로그램이 종료되지 않고 계속 수행될수 있게 하는 명령

프로그램을 데몬처럼 실행하고 싶을 때는 아래와 같이 하면 됩니다. 

" 명령어 & "
여기서 '&'는 백그라운드로 실행하라는 뜻입니다.
그런데, 이 방법은 실행한 사용자가 로그아웃하면 프로그램도 함께 종료됩니다.
이럴때는 nohup 이란 명령어를 사용하면 됩니다.

 

nohup(노헙) 정의
리눅스, 유닉스에서 쉘스크립트파일(*.sh)을 데몬형태로 실행시키는 프로그램

 

nohup 주의사항
nohup으로 실행할 쉘스크립트파일(*.sh)은 현재 퍼미션이 755이상 상태여야 한다.

chmod 755 shell.sh

--------------------------------------------------------------------------------------------------------

rsync 동기화 툴 

# rsync options source destination


로컬 컴퓨터에 있는 moniwiki 디렉토리를 원격 컴퓨터로 복사하는 예제다.

# rsync -avz moniwiki/ yundream@192.168.56.101:/home/yundream/backups


원격 컴퓨터의 내용을  로컬로 복사 

# rsync -avzh yundream@192.168.56.101:/home/yundream/backups ./


Rsync는 -e 옵션을 이용해서 ssh, rsh와 같은 remote shell 프로그램을 선택할 수 있다. 보통 안전한 통신을 보장해주는 ssh

를 이용한다. -e 옵션이 없다면 ssh를 사용한다.


data 디렉토리를 복사에서 제외한다.
# rsync -avz  --exclude 'data' yundream@192.168.56.101:/home/yundream/backups ./


별표(*)도 사용할 수 있다.
# rsync -avz  --exclude '*.bak' yundream@192.168.56.101:/home/yundream/backups ./

rsync는 파일이나 디렉토리가 없으면 새로 만든다. 그러나 파일이나 디렉토리가 존재할 경우 삭제하고 싶을 때도 있을 거다.

이럴때 
--delete 옵션을 사용한다.

로컬 서버에 "text.txt"파일이 있으면 삭제한 후 복사하는 예제다.
# touch test.txt
# rsync -avz --delete yundream@192.168.56.101:/home/yundream/backups ./





최종적으로 hama 를 실행합니다. 


bin=`dirname "$0"`

bin=`cd "$bin"; pwd`


. "$bin"/hama-config.sh


cygwin=false

case "`uname`" in

CYGWIN*) cygwin=true;;

esac


# if no args specified, show usage

if [ $# = 0 ]; then

  echo "Usage: hama [--config confdir] COMMAND"

  echo "where COMMAND is one of:"

  echo "  bspmaster            run the BSP Master node"

  echo "  groom                run the Groom node"

  echo "  zookeeper            run a Zookeeper server"

  echo "  job                  manipulate BSP jobs"

  echo "  jar <jar>            run a jar file"

  echo "  pipes               run a pipe job"

  echo "  seqdumper            run SequenceFileDumper"

  echo " or"

  echo "  CLASSNAME            run the class named CLASSNAME"

  echo "Most commands print help when invoked w/o parameters."

  exit 1

fi


# get arguments

COMMAND=$1

shift


if [ -f "${HAMA_CONF_DIR}/hama-env.sh" ]; then

  . "${HAMA_CONF_DIR}/hama-env.sh"

fi


# some Java parameters

if [ "$JAVA_HOME" != "" ]; then

  #echo "run java in $JAVA_HOME"

  JAVA_HOME=$JAVA_HOME

fi

  

if [ "$JAVA_HOME" = "" ]; then

  echo "Error: JAVA_HOME is not set."

  exit 1

fi


JAVA=$JAVA_HOME/bin/java

JAVA_HEAP_MAX=-Xmx1000m


# check envvars which might override default args

if [ "$HAMA_HEAPSIZE" != "" ]; then

  #echo "run with heapsize $HAMA_HEAPSIZE"

  JAVA_HEAP_MAX="-Xmx""$HAMA_HEAPSIZE""m"

  #echo $JAVA_HEAP_MAX

fi


# CLASSPATH initially contains $HAMA_CONF_DIR

CLASSPATH="${HAMA_CONF_DIR}"

CLASSPATH=${CLASSPATH}:$JAVA_HOME/lib/tools.jar


# for developers, add Hama classes to CLASSPATH

if [ -d "$HAMA_HOME/core/target/classes" ]; then

  CLASSPATH=${CLASSPATH}:$HAMA_HOME/core/target/classes

fi

if [ -d "$HAMA_HOME/core/target/test-classes/classes" ]; then

  CLASSPATH=${CLASSPATH}:$HAMA_HOME/core/target/test-classes

fi


# for developers, add Commons classes to CLASSPATH

if [ -d "$HAMA_HOME/commons/target/classes" ]; then

  CLASSPATH=${CLASSPATH}:$HAMA_HOME/commons/target/classes

fi

if [ -d "$HAMA_HOME/commons/target/test-classes/classes" ]; then

  CLASSPATH=${CLASSPATH}:$HAMA_HOME/commons/target/test-classes

fi


# for developers, add Graph classes to CLASSPATH

if [ -d "$HAMA_HOME/graph/target/classes" ]; then

  CLASSPATH=${CLASSPATH}:$HAMA_HOME/graph/target/classes

fi

if [ -d "$HAMA_HOME/graph/target/test-classes/classes" ]; then

  CLASSPATH=${CLASSPATH}:$HAMA_HOME/graph/target/test-classes

fi


# for developers, add ML classes to CLASSPATH

if [ -d "$HAMA_HOME/ml/target/classes" ]; then

  CLASSPATH=${CLASSPATH}:$HAMA_HOME/ml/target/classes

fi

if [ -d "$HAMA_HOME/ml/target/test-classes/classes" ]; then

  CLASSPATH=${CLASSPATH}:$HAMA_HOME/ml/target/test-classes

fi


# so that filenames w/ spaces are handled correctly in loops below

IFS=


# for releases, add core hama jar to CLASSPATH

for f in $HAMA_HOME/hama-**.jar; do

  CLASSPATH=${CLASSPATH}:$f;

done


# add libs to CLASSPATH

for f in $HAMA_HOME/lib/*.jar; do

  CLASSPATH=${CLASSPATH}:$f;

done


# add user-specified CLASSPATH last

if [ "$HAMA_CLASSPATH" != "" ]; then

  CLASSPATH=${CLASSPATH}:${HAMA_CLASSPATH}

fi


# default log directory & file

if [ "$HAMA_LOG_DIR" = "" ]; then

  HAMA_LOG_DIR="$HAMA_HOME/logs"

fi

if [ "$HAMA_LOGFILE" = "" ]; then

  HAMA_LOGFILE='hama.log'

fi


# default policy file for service-level authorization

if [ "$HAMA_POLICYFILE" = "" ]; then

  HAMA_POLICYFILE="hama-policy.xml"

fi


# restore ordinary behaviour

unset IFS


# figure out which class to run

if [ "$COMMAND" = "bspmaster" ] ; then

  CLASS='org.apache.hama.BSPMasterRunner'

  BSP_OPTS="$BSP_OPTS $BSP_BSPMASTER_OPTS"

elif [ "$COMMAND" = "groom" ] ; then

  CLASS='org.apache.hama.GroomServerRunner'

  BSP_OPTS="$BSP_OPTS $BSP_GROOMSERVER_OPTS"

elif [ "$COMMAND" = "zookeeper" ] ; then

  CLASS='org.apache.hama.ZooKeeperRunner'

elif [ "$COMMAND" = "job" ] ; then

  CLASS='org.apache.hama.bsp.BSPJobClient'

elif [ "$COMMAND" = "pipes" ] ; then

  CLASS='org.apache.hama.pipes.Submitter'

elif [ "$COMMAND" = "seqdumper" ] ; then

  CLASS='org.apache.hama.pipes.util.SequenceFileDumper'

elif [ "$COMMAND" = "jar" ] ; then

  CLASS=org.apache.hama.util.RunJar

  BSP_OPTS="$BSP_OPTS"

else

  CLASS=$COMMAND

fi


# cygwin path translation

if $cygwin; then

  CLASSPATH=`cygpath -p -w "$CLASSPATH"`

  HAMA_HOME=`cygpath -w "$HAMA_HOME"`

  HAMA_LOG_DIR=`cygpath -w "$HAMA_LOG_DIR"`

  TOOL_PATH=`cygpath -p -w "$TOOL_PATH"`

fi


# cygwin path translation

if $cygwin; then

  JAVA_LIBRARY_PATH=`cygpath -p "$JAVA_LIBRARY_PATH"`

fi


HAMA_OPTS="$HAMA_OPTS -Dhama.log.dir=$HAMA_LOG_DIR"

HAMA_OPTS="$HAMA_OPTS -Dhama.log.file=$HAMA_LOGFILE"

HAMA_OPTS="$HAMA_OPTS -Dhama.home.dir=$HAMA_HOME"

HAMA_OPTS="$HAMA_OPTS -Dhama.id.str=$HAMA_IDENT_STRING"

HAMA_OPTS="$HAMA_OPTS -Dhama.root.logger=${HAMA_ROOT_LOGGER:-INFO,console}"

if [ "x$JAVA_LIBRARY_PATH" != "x" ]; then

  HAMA_OPTS="$HAMA_OPTS -Djava.library.path=$JAVA_LIBRARY_PATH"

fi  

HAMA_OPTS="$HAMA_OPTS -Dhama.policy.file=$HAMA_POLICYFILE"

# run it

exec "$JAVA" $JAVA_HEAP_MAX $HAMA_OPTS -classpath "$CLASSPATH" $CLASS "$@"




- 변수를 참조할 때는 보통 큰 따옴표(" ")로 묶어 주는게 좋습니다. 이렇게 하면 $, `(backquote), \(이스케이프)를 제외한 모든 특수 문자들을 보존해 줍니다. 

작은 따옴표(' ')도 큰 따옴표와 비슷하게 동작하지만 $의 특별한 의미를 꺼 버려서 변수 참조가 일어나지 않게 합니다. 작은 따옴표안의 '을 제외한 모든 특수 문자들은 단순히 문자 그대로 해석됩니다. 작은 따옴표("완전한 쿼우팅")를 큰 따옴표("부분 인용")보다 좀 더 엄격한 방법이라고 생각하면 됩니다.

"$@" 는  이쉘을 호출할때 사용한 모든 arg 를 그대로 써줍니다. ("$1" 은 첫번째 인자만)

JAVA 에 -server 옵션이 없는 이유는 코어가 2개이상 메모리가 1기가 이상이면 자동적으로
  VM -s erver 이 적용될것이기때문입니다.

if [ -e $filename ] ; then    // 파일이 존재하는지 , -f  일반파일인지 , -d 디렉토리인

IFS(Internal File Seperator)

IFS는 명령어의 옵션을 설정하거나 프로그램의 아귀먼트를 지정할때 명령어와 옵션, 프로그램과 아귀먼트를 구분해주는 일종의 시스템 구분 인자를 말한다. 

#!/bin/bash var="'(]\\{}\$\"" echo $var # '(]\{}$" echo "$var" # '(]\{}$" 차이가 없죠? echo IFS='\' echo $var # '(] {}$" \ 가 빈 칸으로 바뀌었네요. echo "$var" # '(]\{}$" # S.C. 제공 exit 0



hama-daemons.sh 를 통해 groom 들을 실행합니다. 

usage="Usage: hama-daemons.sh [--config confdir] [--hosts hostlistfile] [start|stop] command args..."


# if no args specified, show usage

if [ $# -le 1 ]; then

  echo $usage

  exit 1

fi


bin=`dirname "$0"`

bin=`cd "$bin"; pwd`


. $bin/hama-config.sh


remote_cmd="cd ${HAMA_HOME}; $bin/hama-daemon.sh --config ${HAMA_CONF_DIR} $@"

args="--config ${HAMA_CONF_DIR} $remote_cmd"

command=$2


case $command in

  (zookeeper)

    exec "$bin/zookeepers.sh" $args

    ;;

  (*)

    exec "$bin/grooms.sh" $args

    ;;

esac


grooms.sh 실행 

#!/usr/bin/env bash


usage="Usage: grooms.sh [--config confdir] command..."

# if no args specified, show usage
if [ $# -le 0 ]; then
  echo $usage
  exit 1
fi

bin=`dirname "$0"`
bin=`cd "$bin"; pwd`

. "$bin"/hama-config.sh

# If the groomservers file is specified in the command line,
# then it takes precedence over the definition in 
# hama-env.sh. Save it here.
HOSTLIST=$HAMA_GROOMS

if [ -f "${HAMA_CONF_DIR}/hama-env.sh" ]; then
  . "${HAMA_CONF_DIR}/hama-env.sh"
fi

if [ "$HOSTLIST" = "" ]; then
  if [ "$HAMA_GROOMS" = "" ]; then
    export HOSTLIST="${HAMA_CONF_DIR}/groomservers"
  else
    export HOSTLIST="${HAMA_GROOMS}"
  fi
fi

for groom in `cat "$HOSTLIST"|sed  "s/#.*$//;/^$/d"`; do
 ssh $HAMA_SSH_OPTS $groom $"${@// /\\ }" $groom \
   2>&1 | sed "s/^/$groom: /" &
 if [ "$HAMA_GROOM_SLEEP" != "" ]; then
   sleep $HAMA_GROOM_SLEEP
 fi
done

wait


SED는 스트림 에디터입니다. 스트림 에디터는 입력 스트림(파일이나 파이프를 통한 입력)에 대해서 단순한 텍스트 교체가 필요할 때 쓰입니다.

1. 주소 지정

주소 지정이란 편집하고자 하는 행을 선택하는 것을 말한다. 이 주소는 숫자나 정규표현식 또는 둘의 조합으로 표현한다. 주소를 지정하지 않은 경우 입력 파일 전체 행에 대하여 작업을 진행한다.

sed ‘1,3p’ datafile

: datafile 1행에서 3행까지 출력한다. p는 행을 출력하라는 명령어이다.


sed -n ‘/[Ll]ove/p’ datafile

: datafile에서 Love love를 포함하는 행만 출력한다.


레퍼런스:http://hyunkie.tistory.com/51



'HAMA' 카테고리의 다른 글

HAMA 시작하기  (0) 2015.09.30
[하마 인사이드] 1. HAMA with K-Means  (0) 2015.05.05

http://blog.udanax.org/2013/04/blog-post_29.html  펌 


미리 준비해야할 것들

  • Hadoop 1.0의 HDFS 
    • Hama 0.6 버전까지는 Hadoop 1.0과 CDH3만을 지원하고 있습니다.
  • Sun/Oracle 배포 Java
  • SSH
  • Hama 최신버전을 다운로드 합니다.
이 글에서는 준비가 다 완료되었다고 가정하고 바로 하마 설치 들어가봅시다.

분산 모드로 설치하기

Hama 최신버전 타르볼을 압축해제한 후, 편집해야할 파일은 conf 디렉토리 밑에 groomservers, hama-env.sh, 그리고 hama-site.xml 세 개 입니다.

1) 먼저 hama-env.sh 파일을 까서, 아래와 같이 JDK가 설치된 경로와 zookeeper 실행여부를 선택합니다 (주키퍼를 설치해놓지 않았으면 true로 하고, 기존 설치된 주키퍼를 쓰려면 false로 합니다).
# The java implementation to use.  Required.
export JAVA_HOME=/usr/lib/jvm/java-7-oracle

...

# Tell Hama whether it should manage it's own instance of Zookeeper or not.
export HAMA_MANAGES_ZK=true 
2) 이제 hama-site.xml 을 까서 다음과 같이 기본적으로 설정되어야할 properties를 작성해줍니다. 항목별로 설명하면 bsp 프레임워크의 마스터 서버의 호스트명과 포트번호, HDFS 파일시스템의 네임노드 호스트명과 포트번호, 주키퍼 호스트명, 그리고 인풋 데이터 파티셔닝을 실시간 처리하겠다는 설정 값들입니다.
  <property>
    <name>bsp.master.address</name>
    <value>server01.udanax.org:40000</value>
  </property>

  <property>
    <name>fs.default.name</name>
    <value>hdfs://server01.udanax.org:9000/</value>
  </property>

  <property>
    <name>hama.zookeeper.quorum</name>
    <value>server01.udanax.org</value>
  </property>
3) 여기까지 되었으면 이제 groomservers 파일을 열어서, 슬레이브로 동작할 서버들의 호스트명을 다음과 같이 나열해줍니다:
server02.udanax.org
server03.udanax.org
server04.udanax.org
...
설정이 완료되면, sever01.udanax.org 서버가 마스터로 나머지 02~04번까지의 서버가 슬레이브로 동작하게 됩니다.

데몬 실행 및 페이지랭크 실행해보기

설정이 끝났으면, 다음과 같이 start-bspd.sh 구동 스크립트로 데몬을 실행합니다.
$ bin/start-bspd.sh
$ tail -f logs/hama-edward-bspmaster-udanax.org.log
2013-04-29 15:46:10,198 INFO org.apache.hadoop.ipc.Server: IPC Server Responder: starting
2013-04-29 15:46:10,198 INFO org.apache.hama.bsp.BSPMaster: Starting RUNNING
2013-04-29 15:46:21,165 INFO org.apache.hama.bsp.BSPMaster: groomd_server02.udanax.org_50000 is added.
로그를 보면 슬레이브들이 마스터서버에 추가되는 것을 볼 수 있습니다. 데몬이 잘 올라왔다면 이제 예제를 실행해볼 수 있습니다. 다음 명령어는 Pi 계산하는 예제입니다.
$ bin/hama jar hama-examples-0.6.1.jar pi
13/04/29 15:50:15 INFO mortbay.log: Logging to org.slf4j.impl.Log4jLoggerAdapter(org.mortbay.log) via org.mortbay.log.Slf4jLog
13/04/29 15:50:16 INFO bsp.BSPJobClient: Running job: job_201304291546_0001
13/04/29 15:50:19 INFO bsp.BSPJobClient: Current supersteps number: 0
13/04/29 15:50:22 INFO bsp.BSPJobClient: Current supersteps number: 1
13/04/29 15:50:22 INFO bsp.BSPJobClient: The total number of supersteps: 1
13/04/29 15:50:22 INFO bsp.BSPJobClient: Counters: 6
13/04/29 15:50:22 INFO bsp.BSPJobClient:   org.apache.hama.bsp.JobInProgress$JobCounter
13/04/29 15:50:22 INFO bsp.BSPJobClient:     SUPERSTEPS=1
13/04/29 15:50:22 INFO bsp.BSPJobClient:     LAUNCHED_TASKS=3
13/04/29 15:50:22 INFO bsp.BSPJobClient:   org.apache.hama.bsp.BSPPeerImpl$PeerCounter
13/04/29 15:50:22 INFO bsp.BSPJobClient:     SUPERSTEP_SUM=3
13/04/29 15:50:22 INFO bsp.BSPJobClient:     TIME_IN_SYNC_MS=220
13/04/29 15:50:22 INFO bsp.BSPJobClient:     TOTAL_MESSAGES_SENT=3
13/04/29 15:50:22 INFO bsp.BSPJobClient:     TOTAL_MESSAGES_RECEIVED=3
Estimated value of PI is 3.1424
Job Finished in 6.432 seconds
정상적으로 설치가 잘 되었다면 위와 같이 Pi 계산 결과값이 나오는것을 볼 수 있습니다. 성공하셨나요? :D 그럼 다음으로 이제 좀 더 현실적인 예제 PageRank를 계산하는 예제를 실행하려면, 먼저 generator 커맨드로 100개의 vertices와 1,000개의 edge를 갖는 랜덤 그래프 데이터를 HDFS 상에 생성합니다.
$ bin/hama jar hama-examples-0.6.1.jar gen symmetric 100 10 randomgraph 2
13/04/29 15:54:45 INFO mortbay.log: Logging to org.slf4j.impl.Log4jLoggerAdapter(org.mortbay.log) via org.mortbay.log.Slf4jLog
13/04/29 15:54:46 INFO bsp.BSPJobClient: Running job: job_201304291546_0002
13/04/29 15:54:49 INFO bsp.BSPJobClient: Current supersteps number: 0
13/04/29 15:54:52 INFO bsp.BSPJobClient: Current supersteps number: 1
13/04/29 15:54:52 INFO bsp.BSPJobClient: The total number of supersteps: 1
13/04/29 15:54:52 INFO bsp.BSPJobClient: Counters: 6
13/04/29 15:54:52 INFO bsp.BSPJobClient:   org.apache.hama.bsp.JobInProgress$JobCounter
13/04/29 15:54:52 INFO bsp.BSPJobClient:     SUPERSTEPS=1
13/04/29 15:54:52 INFO bsp.BSPJobClient:     LAUNCHED_TASKS=2
13/04/29 15:54:52 INFO bsp.BSPJobClient:   org.apache.hama.bsp.BSPPeerImpl$PeerCounter
13/04/29 15:54:52 INFO bsp.BSPJobClient:     SUPERSTEP_SUM=2
13/04/29 15:54:52 INFO bsp.BSPJobClient:     TIME_IN_SYNC_MS=121
13/04/29 15:54:52 INFO bsp.BSPJobClient:     TOTAL_MESSAGES_SENT=516
13/04/29 15:54:52 INFO bsp.BSPJobClient:     TOTAL_MESSAGES_RECEIVED=516
Job Finished in 6.279 seconds
그 다음 페이지랭크를 실행하면 끗~
$ bin/hama jar hama-examples-0.6.1.jar pagerank randomgraph pagerankresult 4


추가) 

테스트를 위한 구성으로 3대중 1대를 NameNode, DataNode 를
나머지 2대에 DataNode를 실행해서 HDFS를 올려놓습니다.
그 다음에 3대중 1대에 BSPMaster, GroomServer, ZooKeeper 를 나머지 2대에 GroomServer를 실행합니다. 즉,

서버1 - NameNode, DataNode, BSPMaster, GroomServer, Zookeeper
서버2 - DataNode, GroomServer
서버3 - DataNode, GroomServer

요렇게 띄워서 클러스터를 구성하면 됩니다. 설치 경로와 설정 파일은 서버 모두 동일하게 설정하셔야합니다.


'HAMA' 카테고리의 다른 글

HAMA 쉘 분석  (0) 2015.09.30
[하마 인사이드] 1. HAMA with K-Means  (0) 2015.05.05


순서

1)    HAMA with K-Means

2)    HAMA  BSP Inside  &  Zookeeper


하마(Apache Hama) 는 빅데이터 분석(머신러닝) 을 위한 솔루션을 제공합니다. 
흔히 알고있는 하둡(Hadoop) 의 맵리듀스 또한 빅데이터 분석 하는데 사용되는데 맵리듀스의 특성상
속도 및 구현에 있어서 굉장히 비효율적인 모습을 보여주는 부분이 있는데, 그런 가려운 부분을 화끈하게
해결
해줍니다.  

하마는 기본적으로  데이터 저장은  하둡 HDFS 를  사용하며  Zookeeper 를 이용하여 분산된 락을 구현하며  
BSP / 그래프 구조를 통하여 각종 알고리즘을 구현할수있게 합니다. 

- 하둡의 맵리듀스 구조 ( 데이타 - 매핑 - 리듀싱 - 결과 저장)

맵 노드간에 혹은 리듀스 노드간에 정보 교환하지 않습니다. 오로지 맵 -> 리듀스 

 - 하마 BSP 구조 ( 데이터 - 로컬 계산 - 노드 상호간 통신 -  블럭 ) 

위의 그림에 보다시피,하둡은 Iterative 작업을 수행하기에 좋지 않습니다. 반면 하마는 그것에 최적화 되어있습니

                        - K-meas 알고리즘 적용시 머하웃( 하둡 맵리듀스 기반) 과의 속도 차이입니다.

위의 표에 보다시피 머신러닝을 할때 ,  엄청난 속도차이를 볼수있습니다. 하둡 맵리듀스 (머하웃) 와는 비교가 안되게 빠릅니다. 자신이 활용하고자 하는 목표 ( 현재 에코 시스템, 계산복잡도로 인한 속도, 러닝커브, 라이브러리 활용성) 에 따라서 선택해야함.  요즘은 딥러닝이 뜨고 있는데  딥러닝을 위해서도 충분히 활용가능하며 BSP + GPGPU 를 적극활용하면 실시간성에 더욱 가까히 갈수있겠습니다. 


K-Means 알고리즘 이란 ? 

머신러닝의 큰 바운더리로  1. 추천시스템  2. 분류  3. 군집을 들수있는데 이 알고리즘은 군집분석을 할때  활용되는 기본 알고리즘 입니다. 군집이란 , 쉽게 말해 도서관에서 여러분이 어떤 기준에 따라서 책을 그룹핑할때 하는 행동 . 그때 책을 군집화 한다라고 말할수있습니다. 군집할때는 어떤 데이터간의 유사성을 정량적으로 측정하여 수치화하는게 핵심이며, 수치화가 된후 K-Means 알고리즘으로 자동 그루핑 할수있습니다. K-Means 는 말 그래도 K 개로 그루핑하라~ 와 같은데 각 군집의 평균데이터를 구해주며 평균데이터와 비교하여 , 개개의 요소들을 특성을 규정할수있습니다.  굉장히 클리어한 알고리즘이며 간단한데에 비해 많은곳에서 주요 알고리즘으로 사용됩니다.

수행과정을  보면 

1) 임의의 K개의 군집수를 결정하고, 각 군집에 초기치 또는 군집 중심을 1개씩 할당하여 위치 설정한다.
2) 각각의 데이터에 대해 K개의 위치까지의 거리를 구하고 가장 가까운 군집에 소속시킨다.(유클리드 거리를 이용)
3) 군집으로 나뉘어진 데이터를 기준으로 새로운 군집 중앙의 위치를 최소가 되도록 재설정한다.
4) 새롭게 구한 군집 중앙의 위치가 기존과 동일하면 알고리즘 종료하고 다르면 두 번째부터 재수행한다.

이 과정을 통하여 K개의 군집으로 데이터를 구분하고 K값에 따라 clustering에 많은 영향을 받는다


[출처] K-means 알고리즘|작성자 푸키

Apache Hama 로 구현한 K-Means  

하마를 통한 K-Means 알고리즘이 하나의 컴퓨터에서 알고리즘을 구현한것과 다른 점은 오직 여러대의 컴퓨터간에 통신하면서 수행된다는 점입니다. 스텝마다 각자의 컴퓨터에서 계산을 수행한후에 다른 컴퓨터로 결과를 서로 넘겨서 재 조정되면서 알고리즘이 진행됩니다.

아래는 X,Y 좌표에서 가까운것 끼리 군집을 만들기위한 소스와 설명입니다.
먼저 소스 부터 보고 
한단계식 이미지를 통해 확인해가며 학습해보겠습니다.

1. Mean 포인트 ( 그룹의 중간 포인트, 그룹별 하나씩 있음) 

2. 일반 포인트  ( 모든  그룹으로 나누어질 값들  예) 책종류, 영화 장르구분, 다음 아고라 댓글  정치성 분류 )

아래 코드는 하마가 설치된 모든 컴퓨터에서 실행된다.

public void bsp(final BSPPeerProtocol bspPeer) throws IOException,KeeperException, InterruptedException {

	if (isMaster(bspPeer)) {    
                //  1)  초기화 ( 랜덤포인트들 생성 ,  Mean 포인트 생성) , 마스터노드에서만 실행
		masterInitialize(bspPeer); 

	}

	while (true) {    //  K-Means 알고리즘 시작 	

		bspPeer.sync();    // 2) 일단 모든 컴퓨터가 여기까지 실행될때까지 모두 대기!!!! 	
		boolean converged = processMessages(bspPeer);  // 3)다른노드로부터 온 데이터 업데이트 					

		if (converged) {    // 4) 원하는 만큼 중간값이 정해졌으면 해당 노드 (군집) 종료!!
			break;
		}	

		assignmentStep(bspPeer);  // 5) 포인트들을 Mean 포인트에 재 할당	
		// 6)  Mean 값 과 재할당된 포인트들 사이의  유클리드 거리 평균에 따라 위치 조정 
               updateStep(bspPeer);

	} 			

	double wcss = wcss(bspPeer);
	LOG.info("My WCSS is " + wcss);
	writeFinalOutput(bspPeer);	// 7) 결정된 Mean 값을 출력함 ( 하둡 HDFS 나 Hbase 에 )

}


private boolean processMessages(BSPPeerProtocol bspPeer) throws IOException {
			
	boolean converged = true;			
	BSPMessage msg;

	while ((msg = bspPeer.getCurrentMessage()) != null) {
		if (isPointMessage(msg)) {    
			addPoints(msg);    // 8) 다른 노드로부터 온 메세지가 포인트이면 삽입
		} else if (isMeanMessage(bspPeer, msg)) {
			converged = updateMeanMap(msg) && converged; // 9) 다른 노드로부터 온 메세지가 Mean 포인트일때
		} else {
			throw new RuntimeException("Unknown msg tag: " + new String(msg.getTag()));
		}
	}
			
	LOG.info("New Mean Map = " + peerMeanMap);
	return converged;
}



// 10) Mean 포인트들과 포인트들을 계산하여 , 각각의 Mean 포인트에서 가까운 포인트들끼리 그룹핑한다.
private int assignmentStep(final BSPPeerProtocol bspPeer) throws IOException {

	final Map<String, List<Point3D>> peerNewPoints = new HashMap<String, List<Point3D>>		

	for (String peer : peerMeanMap.keySet()) {
		peerNewPoints.put(peer, new ArrayList<Point3D>());
	}

	int changeCount = 0;			

	for (Iterator<Point3D> pointItr = points.iterator(); pointItr.hasNext();) {

		final Point3D obs = pointItr.next();	
		double min = Double.MAX_VALUE;
		String minPeer = null;		
		for (Map.Entry<String, Point3D> peer : peerMeanMap.entrySet()) {

			double distance = obs.distance(peer.getValue());
			if (distance < min) {
				min = distance;
				minPeer = peer.getKey();
			}
		}
		
		if (minPeer.equals(bspPeer.getPeerName())) {
			//I don't send updates for points I already own
			continue;

		}		

		//Remove the point from my collection as I no longer own it.
		pointItr.remove();
		changeCount += 1;
		peerNewPoints.get(minPeer).add(obs);	

	}

	//Notify other clusters of new points	
	for (Map.Entry<String, List<Point3D>> peerPoints : peerNewPoints.entrySet()) {

		if (peerPoints.getValue().size() == 0) {
			continue;
		}	

		LOG.info("Send " + peerPoints.getValue().size() + " to " + peerPoints.getKey());
		bspPeer.send(peerPoints.getKey(), 
		pointToByteMessage(new PointMessage(POINT_MSG_TAG, peerPoints.getValue())));
	}
	return changeCount;

}


// 10) 새로 그룹핑된 포인트들의 중간값을 계산하여 새로운 Mean 포인트를 계산한후에 브로드캐스트.
private void updateStep(BSPPeerProtocol bspPeer) throws IOException {			

		LOG.info("My point count is now: " + points.size());

		if (0 == points.size()) {
			//Catch initial case where we have no points, and thus can't change our mean.
			return;
		}
		broadcastMyMean(bspPeer, calculateCenter(points));
	}

private void broadcastMyMean(BSPPeerProtocol bspPeer, Point3D mean) throws IOException {

	final BSPMessage msg = pointToByteMessage(new PointMessage(bspPeer.getPeerName(), mean));

	for (String peer : bspPeer.getAllPeerNames()) {
		bspPeer.send(peer, msg);    // 11) 각각의 노드로 재 군집된 포인트들을 보내줍니다.
	}

}

private Point3D calculateCenter(List<Point3D> points) {  // 12) 포인트들의 센터를 다시 정합니다.
	double x = 0;
	double y = 0;
	double z = 0;
	
	for (Point3D p : points) {
		x += p.x / points.size();
		y += p.y / points.size();
		z += p.z / points.size();

	}

	return new Point3D(x, y, z);
}

자 다음은 위의 소스에서 일어나는 일을 이해하기 쉽게 그림으로 그려보았습니다.  감에 의지하여 그린것이라 거리차등이 정확하지 않습니다. :-)   시작해보지요.  

아래 그림은 소스에서 1) 번에 해당합니다. 
빨강색  / 연두색 / 노란색  포인트들이 초기 Mean (군집) 포인트 입니다. 3개의 초기점이 점점 포인트들 사이에 3개의 중간지점으로 이동될 것입니다. ( 당연히 회색 포인트들은 이동하지 않음) 


그림에서 0~5번까지의 순서에 의해 알고리즘이 진행됩니다.

소스에서 5) 6) 번에 해당합니다.  서버1에서 계산을 한후에  서버2 / 서버 3 으로  각 서버(군집) 에 해당하는 포인트들을 보내주며 

Mean 포인트들은 모든 노드가 공유합니다. (각 노드는 노드별로 자신의 포인트 들을 가지고 있게 됩니다. 즉 같은 군집들끼리 ) 

- 빨강색 / 연두색 / 노란색 점들 간의 그룹들이 설정되었습니다. (점선테두리) 

- 빨강색 그룹의 포인트들은 서버2로 이동될것이고, 노란색 그룹의 포인트들은 서버3으로 이동됩니다.

- 연두색 Mean 포인트는 위치가 재조정되며  서버2,서버3 에 조정된 위치를 알려줍니다.



자 한스텝이 진행되었습니다. 모든 노드는 각각의 노드들이 한 스텝을 완성할때까지 블럭됩니다.
모두 진행된후에는 모두 블럭을 풀고  받은 데이터로 자신의 Mean 값과 포인트들을 업데이트 합니다.

-  이동되어진 포인트들의 모습입니다.


다시 반복합니다.

- 빨강색 / 연두색 / 노란색 점들 간의 그룹들이 설정되었습니다. (점선테두리) 

- 빨강색 / 노란색 Mean 포인트들이 자신의 그룹 포인트들읜 중간값으로 이동합니다.

- 이동된 Mean 포인트들의 위치를  서로 공유합니다.


- 빨강색 과 노란색의 Mean포인트 위치가 그룹들의 중간으로 이동하였습니다.


- 빨강색 / 노란색의 Mean 포인트의 위치가 이동하고 나니,  어떤 포인트들은 연두색에 더 가까워 졌습니다.

- 연두색에 더 가까워진 포인트들은 서버1으로 보내줍니다. 


- 연두색으로 넘어간 포인트의 모습입니다.


자 먼가 그룹이 이루어져있지요? 

이렇게  블럭 - 재 그룹핑 - Mean 포인트 위치조정 -  각각의 노드로 브로드캐스트 을 반복해서 적당하게 그룹핑되면 종료합니다.

하둡 맵리듀스 기반의  K-means 알고리즘을 구현해놓은 머하웃 라이브러리를 잠깐 공부해보았는데, 머신러닝은 이런 알고리즘 구현이 어려운게 아니라,  머신러닝 학습을 시키기위해 데이터를 가공하거나,  어떤것이 군집되어야하는지 결정하는데 더 어려운거 같았습니다.  이상 K-Means 알고리즘을 살펴보았으며  저 또한  빅데이터/머신러닝에 대한 전문가가 아니기때문에  부족한 부분이 많았을거라고 생각됩니다만 이 글을 통해서 머신러닝에 대한 흥미를 일깨우는데 일조했다면 그것으로 소기의 목적을 이루었다고 생각합니다.  


https://github.com/willmore/hama-kmeans   ( 위에 예제에 사용된 소스) 

http://en.wikipedia.org/wiki/K-means_clustering

http://www.slideshare.net/udanax/k-means-29238792

https://hama.apache.org/

'HAMA' 카테고리의 다른 글

HAMA 쉘 분석  (0) 2015.09.30
HAMA 시작하기  (0) 2015.09.30

+ Recent posts