본문 바로가기

HaDoop...?

HADOOP 프로그래밍!(2)

저번 게시물에서 xml 을 대충(?) 살펴보았다.

이번에는 본격적으로 build.xml을 만들어 보겠다!


build.xml에서 여러 가지 일을 수행한다. 다운도 받고 뭐도 하고 뭐도 하고.. 그 중 필요한 것만.. 실은 아는 것만 코드로 만들 것이다. 우리가 원하는 hdfs.jar 을 만들기 위해 필요한 과정을 살펴보겠다.


1. 초기화 ( 디렉터리 생성 )
2. classpath 설정

3. java 컴파일

4. .jar 만들기

이 정도다.


우선 큰 틀을 만들자

vim build.xml

<?xml version="1.0"?>

<project name="hadoop" default="compile" basedir=".">

</project>



만들었으면 :w 저장한 번 하고!

굳이 설명할 필요가 없겠지만 간단하게 보면 프로젝트 이름은 hadoop이고 ant 명령을 실행했을 때 기본적으로 실행되는 target name은 "compile" 이라는 target이 실행된다. 그리고 기본 디렉터리 위치는 "." 현 위치라는 뜻이다.

그리고 필요한 변수선언(?)을 하면 되는데 나중에 필요할 때마다 추가하겠다.

그럼 시작을 위한 compile target을 만들어보자


<project> </project> 안에 

<target name="compile" depends="compile-core-classes,compile-hdfs-classes" description="Compile core only">

</target>



을 추가한다. 그러면 순서는 

ant라는 명령어가 들어오면 기본 target인 compile이라는 target이 실행되고 compile은 compile-core-classes, compile-hdfs-classes라는 target을 실행한다.

실제로 compile target은 아무 행동도 안 하고 두 개의 compile classes를 불러주는 행동만 한다.

그러면 compile-core-classes를 만들어보자


compile target이 끝나는 지점 (</target>) 밑에다가 추가한다.

<target name="compile-core-classes" depends="init">

  <echo message="Compile core classes ..."/>

  <taskdef classname="org.apache.jasper.JspC" name="jsp-compile">

    <classpath refid="test.classpath"/>

  </taskdef>

  <javac

  encoding="${build.encoding}"

  srcdir="${core.src.dir}"

  includes="org/apache/hadoop/**/*.java"

  destdir="${build.classes}"

  debug="${javac.debug}"

  optimize="${javac.optimize}"

  target="${javac.version}"

  source="${javac.version}"

  deprecation="${javac.deprecation}">

  <compilerarg line="${javac.args} ${javac.args.warnings}"/>

  <classpath refid="classpath"/>

  </javac>

  <copy todir="${build.classes}">

    <fileset dir="${core.src.dir}" includes="**/*.properties"/>

    <fileset dir="${core.src.dir}" includes="core-default.xml"/>

  </copy>

  <echo message="OK!"/>

</target>



이 부분은... 딱 놓고 말해서 이놈이 core에 있는 java 파일들을 컴파일하는구나~ 라고만 알고 있다! 
막 모르는 변수(?)들이 있다 ${ ~~ }라는 놈들이다. 맨 위에 property로 추가하자. (<project name="hadoop ~> 밑에)

<property name="build.encoding" value="ISO-8859-1"/>
<property name="PROJECT" value="${basedir}"/>
<property name="src.dir" value="${PROJECT}/src"/>
<property name="core.src.dir" value="${src.dir}/core"/>
<property name="build.dir" value="${PROJECT}/build"/>
<property name="build.classes" value="${build.dir}/classes"/>
<property name="javac.debug" value="on"/>  
<property name="javac.optimize" value="on"/>
<property name="javac.deprecation" value="off"/>
<property name="javac.version" value="1.6"/>
<property name="javac.args" value=""/>
<property name="javac.args.warnings" value="-Xlint:unchecked"/>


추가하였다.
일단 컴파일을 하겠지만 그 전에 class 파일을 담고 있을 디렉터리가 필요하다. 그 과정은 target init에서 한다.
다시 compile-core-classes target 밑에(</target>) 추가한다.

<target name="init">
  <echo message="init... start"/>
  <mkdir dir="${build.dir}"/>
  <mkdir dir="${build.src}"/>
  <mkdir dir="${build.classes}"/>
  <mkdir dir="${build.webapps}/hdfs/WEB-INF"/>
  <mkdir dir="${build.webapps}/datanode/WEB-INF"/>
  <echo message="init... end"/>
</target>


막 모르는 변수(?)들이 있다. 위에 property로 추가하자.

<property name="build.src" value="${build.dir}/src"/>
<property name="build.webapps" value="${build.dir}/webapps"/>


지금까지 만든 build.xml의 실행을 예상해보면
1. init
2. compile-core-classes
까지 실행될 것이다. 다음으로는 compile-hdfs-classes target을 만들어보자.

init target 밑에 

<target name="compile-hdfs-classes" depends="compile-core-classes">
  <echo message="Compile hdfs classes ..."/>
  <jsp-compile
  uriroot="${src.webapps}/hdfs"
  outputdir="${build.src}"
  package="org.apache.hadoop.hdfs.server.namenode"
  webxml="${build.webapps}/hdfs/WEB-INF/web.xml">
  </jsp-compile>
  <jsp-compile
  uriroot="${src.webapps}/datanode"
  outputdir="${build.src}"
  package="org.apache.hadoop.hdfs.server.datanode"
  webxml="${build.webapps}/datanode/WEB-INF/web.xml">
  </jsp-compile>
  <javac
  encoding="${build.encoding}"
  srcdir="${hdfs.src.dir};${build.src}"
  includes="org/apache/hadoop/**/*.java"
  destdir="${build.classes}"
  debug="${javac.debug}"
  optimize="${javac.optimize}"
  target="${javac.version}"
  source="${javac.version}"
  deprecation="${javac.deprecation}">
  <compilerarg line="${javac.args} ${javac.args.warnings}"/>
  <classpath refid="classpath"/>
  </javac>
  <copy todir="${build.classes}">
    <fileset dir="${hdfs.src.dir}" includes="**/*.properties"/>
    <fileset dir="${hdfs.src.dir}" includes="hdfs-default.xml"/>
  </copy>
  <echo message="OK!"/>
</target>


을 추가한다. 모르는 변수(?)들이 있다. 위에 property로 추가하자.

<property name="src.webapps" value="${basedir}/src/webapps"/>
<property name="hdfs.src.dir" value="${src.dir}/hdfs"/>


끝! 이 아니다! classpath를 설정해야 한다. 아무 대다 해도 상관없지만 깔끔해 보이기 위해 property 아래에다가 추가한다.

<path id="project.classpath">
  <fileset dir="${PROJECT}/lib" includes="**/*.jar" />
</path>
<path id="classpath">
  <pathelement location="${build.classes}"/>
  <fileset dir="${lib.dir}">
    <include name="**/*.jar"/>
    <exclude name="**/excluded/"/>
  </fileset>
  <pathelement location="${conf.dir}"/>
</path>
<path id="test.classpath">
  <pathelement location="${test.build.extraconf}"/>
  <pathelement location="${test.build.classes}"/>
  <pathelement location="$[test.src.dir}"/>
  <pathelement location="${build.dir}"/>
  <fileset dir="${test.lib.dir}">
    <include name="**/*.jar"/>
    <exclude name="**/excluded"/>
  </fileset>
  <path refid="classpath"/>
</path>


변수(?)도 추가하고!

<property name="lib.dir" value="${basedir}/lib"/>
<property name="test.build.dir" value="${build.dir}/test"/>
<property name="test.build.extraconf" value="${test.build.dir}/extraconf"/>
<property name="test.build.classes" value="${test.build.dir}/classes"/>
<property name="test.src.dir" value="${basedir}/src/test"/>
<property name="test.lib.dir" value="${basedir}/src/test/lib"/>
<property name="conf.dir" value="${basedir}/conf"/>

그다음 ! JAR 파일을 만들기 위한 target을 맨 마지막에 추가한다. (</project> 위에)

<target name="jar" depends="compile" description="Make hadoop.jar">
  <tar compression="gzip" destfile="${build.classes}/bin.tgz">
    <tarfileset dir="bin" mode="755"/>
 </tar>
  <jar jarfile="${build.dir}/${final.name}-core.jar"
  basedir="${build.classes}">
    <manifest>
      <section name="org/apache/hadoop">
        <attribute name="Implementation-Title" value="Hadoop"/>
        <attribute name="Implementation-Version" value="${version}"/>
        <attribute name="Implementation-Vendor" value="Apache"/>
      </section>
    </manifest>
    <fileset file="${conf.dir}/commons-logging.properties"/>
    <fileset file="${conf.dir}/log4j.properties"/>
    <fileset file="${conf.dir}/hadoop-metrics.properties"/>
    <zipfileset dir="${build.webapps}" prefix="webapps"/>
  </jar>
</target>


변수(?)도 추가하고!
<property name="name" value="hadoop"/>
<property name="version" value="0.20.3-dev"/>
<property name="final.name" value="${name}-${version}"/>


build.xml에서 해줄 수 있는 건 모두 끝났다. 이제 :wq로 vim을 종료한 후 ant jar을 해보자!

오류가 빡! 날 것이다! 어떤 오류냐면 compile 중 어떤 class를 찾을 수 없다고 나올 것이다.
이것은 우리가 전 포스터에서 hdfs와 mapreduce를 나누는 작업을 했는데. 그 작업은 프로그램 실행 시 필요한 class만 추가한 것이고 컴파일에 필요한 class는 추가한 것이 아니다. 고로 compile에 필요한 class들을 가져와야 된다.

간단하다. 윈도우 탐색기에다가 해당 class 명을 검색한다. 그러면 그 class 이름의 .java 파일이 있을 것이다. 그 파일을 실제 하둡 디렉터리 안의 위치와 같게 복사, 붙여 넣기를 한다. 그렇게 하나씩 오류가 나는 class들을 추가하다 보면 오류 없이 BUILD SUCCESSFUL이라는 말과 build 디렉터리 안에 hadoop-{version}-dev-core.jar 라는 파일이 생겨있을 것이다.
hadoop의 설정인 conf 디렉터리를 옮겨와 싱글모드로 설정하고 bin 디렉터리도 옮겨와 hdfs를 실행시켜보자
(싱글모드 설정 방법 : http://gh0stsp1der.tistory.com/45)

bin/start-dfs.sh

명령이 실행되면 logs 디렉터리도 생성되고 jps 명령을 치면 namenode, datanode, secondarynamenode 가 실행되어 있을 것이다. 이렇게 hdfs와 mapreduce를 분리하여 컴파일해보았다.


XML을 이렇게 대충 필요한 것만 뽑아서 만들어 보았다.
물론 test version이니까 이렇게 대충해도 되지만. 나중에 수정하여 배포용으로 compile 할 때는 XML을 공부하고 좀 더 깔끔하게 만들어야겠다.