[Hadoop] Hadoop 설치 및 WordCount 예제 실습

16 minute read

Hadoop

Hadoop = HDFS + MapReduce

Mode 3가지

HDFS 설치방식

  • Stand alone (독립실행모드) : 기본 실행모드. 분산저장 안함. 코딩은 가능.
  • Pseudo-distributed (가상분산모드) : 하나의 컴퓨터에 설치
  • Fully distributed (완전분산모드) : 여러 대의 컴퓨터에 설치

순서

  1. VirtualBox 설치
  2. Ubuntu 14.04.2 설치 // 이후에는 VirtualBox 내의 Ubuntu 에서 진행
  3. JDK 설치
  4. 하둡 다운로드
  5. 하둡 Stand-alone 모드 구성
  6. 하둡 가상 분산 모드 구성
  7. 이클립스 다운로드, 설정
  8. WordCount 예제 코딩
  9. 테스트

Ubuntu에 JDK 8 설치


$ sudo add-apt-repository ppa:webupd8team/java
$ sudo apt-get update
$ sudo apt-get install oracle-java8-installer

하둡 다운로드 후 압축 해제

URL : https://archive.apache.org/dist/hadoop/common/hadoop-1.2.1/hadoop-1.2.1.tar.gz 에서 다운로드

아래 명령어로 압축해제 및 설정.

참고 : 앞으로 나오는 /home/유저명/ 에서 “유저명” 부분은 “여러분 자신의 유저명"으로 수정하여 입력하면 된다.

vim 설정

$ vi .vimrc

# 아래 내용 입력 후 저장
filetype plugin on
syntax on

set number
set paste

set ruler
set laststatus=2

$ cat .vimrc     # 확인
$ cd Downloads
$ cp hadoop-1.2.1.tar.gz ~       # ~ : home folder
$ cd ~
$ tar zxvf hadoop-1.2.1.tar.gz
$ sudo apt-get install vim

$ vim .bashrc # 맨 끝에 아래 3줄 추가

export JAVA_HOME=/usr/lib/jvm/java-8-oracle
export HADOOP_INSTALL=/home/유저명/hadoop-1.2.1
export PATH=PATH:HADOOP_INSTALL/bin

$ source .bashrc # 변경 내용 적용
$ hadoop # 메시지가 정상 출력되면 OK

하둡 Stand-alone 모드 구성 및 예제 테스트

아래 명령어로 테스트.

폴더명 중복 안되게 해야 한다.

$ cd /home/유저명/hadoop-1.2.1
$ mkdir input
$ cp README.txt input
$ hadoop jar hadoop-examples-1.2.1.jar wordcount input output
# cat output/part-r-00000 # 결과 확인
  • jar : 파일을 실행하는 명령어

  • hadoop-examples-1.2.1.jar 파일이 하둡 예제파일

  • $ hadoop jar hadoop-examples-1.2.1.jar wordcount input output : hadoop / 파일실행 / 파일명 / 할일 /입력폴더 / 출력폴더

    # 출력 결과
    
    18/02/24 14:10:18 INFO util.NativeCodeLoader: Loaded the native-hadoop library
    18/02/24 14:10:18 INFO input.FileInputFormat: Total input paths to process : 1
    18/02/24 14:10:18 WARN snappy.LoadSnappy: Snappy native library not loaded
    18/02/24 14:10:18 INFO mapred.JobClient: Running job: job_local1707454656_0001
    18/02/24 14:10:19 INFO mapred.LocalJobRunner: Waiting for map tasks
    18/02/24 14:10:19 INFO mapred.LocalJobRunner: Starting task: attempt_local1707454656_0001_m_000000_0
    18/02/24 14:10:19 INFO util.ProcessTree: setsid exited with exit code 0
    18/02/24 14:10:19 INFO mapred.Task:  Using ResourceCalculatorPlugin : org.apache.hadoop.util.LinuxResourceCalculatorPlugin@415eb82d
    18/02/24 14:10:19 INFO mapred.MapTask: Processing split: file:/home/jane/hadoop-1.2.1/input:0+1366
    18/02/24 14:10:19 INFO mapred.MapTask: io.sort.mb = 100
    18/02/24 14:10:19 INFO mapred.MapTask: data buffer = 79691776/99614720
    18/02/24 14:10:19 INFO mapred.MapTask: record buffer = 262144/327680
    18/02/24 14:10:19 INFO mapred.MapTask: Starting flush of map output
    18/02/24 14:10:19 INFO mapred.MapTask: Finished spill 0
    18/02/24 14:10:19 INFO mapred.Task: Task:attempt_local1707454656_0001_m_000000_0 is done. And is in the process of commiting
    18/02/24 14:10:19 INFO mapred.LocalJobRunner: 
    18/02/24 14:10:19 INFO mapred.Task: Task 'attempt_local1707454656_0001_m_000000_0' done.
    18/02/24 14:10:19 INFO mapred.LocalJobRunner: Finishing task: attempt_local1707454656_0001_m_000000_0
    18/02/24 14:10:19 INFO mapred.LocalJobRunner: Map task executor complete.
    18/02/24 14:10:19 INFO mapred.Task:  Using ResourceCalculatorPlugin : org.apache.hadoop.util.LinuxResourceCalculatorPlugin@2f017655
    18/02/24 14:10:19 INFO mapred.LocalJobRunner: 
    18/02/24 14:10:19 INFO mapred.Merger: Merging 1 sorted segments
    18/02/24 14:10:19 INFO mapred.Merger: Down to the last merge-pass, with 1 segments left of total size: 1832 bytes
    18/02/24 14:10:19 INFO mapred.LocalJobRunner: 
    18/02/24 14:10:19 INFO mapred.Task: Task:attempt_local1707454656_0001_r_000000_0 is done. And is in the process of commiting
    18/02/24 14:10:19 INFO mapred.LocalJobRunner: 
    18/02/24 14:10:19 INFO mapred.Task: Task attempt_local1707454656_0001_r_000000_0 is allowed to commit now
    18/02/24 14:10:19 INFO output.FileOutputCommitter: Saved output of task 'attempt_local1707454656_0001_r_000000_0' to output
    18/02/24 14:10:19 INFO mapred.LocalJobRunner: reduce > reduce
    18/02/24 14:10:19 INFO mapred.Task: Task 'attempt_local1707454656_0001_r_000000_0' done.
    18/02/24 14:10:19 INFO mapred.JobClient:  map 100% reduce 100%
    18/02/24 14:10:19 INFO mapred.JobClient: Job complete: job_local1707454656_0001
    18/02/24 14:10:19 INFO mapred.JobClient: Counters: 20
    18/02/24 14:10:19 INFO mapred.JobClient:   Map-Reduce Framework
    18/02/24 14:10:19 INFO mapred.JobClient:     Spilled Records=262
    18/02/24 14:10:19 INFO mapred.JobClient:     Map output materialized bytes=1836
    18/02/24 14:10:19 INFO mapred.JobClient:     Reduce input records=131
    18/02/24 14:10:19 INFO mapred.JobClient:     Virtual memory (bytes) snapshot=0
    18/02/24 14:10:19 INFO mapred.JobClient:     Map input records=31
    18/02/24 14:10:19 INFO mapred.JobClient:     SPLIT_RAW_BYTES=99
    18/02/24 14:10:19 INFO mapred.JobClient:     Map output bytes=2055
    18/02/24 14:10:19 INFO mapred.JobClient:     Reduce shuffle bytes=0
    18/02/24 14:10:19 INFO mapred.JobClient:     Physical memory (bytes) snapshot=0
    18/02/24 14:10:19 INFO mapred.JobClient:     Reduce input groups=131
    18/02/24 14:10:19 INFO mapred.JobClient:     Combine output records=131
    18/02/24 14:10:19 INFO mapred.JobClient:     Reduce output records=131
    18/02/24 14:10:19 INFO mapred.JobClient:     Map output records=179
    18/02/24 14:10:19 INFO mapred.JobClient:     Combine input records=179
    18/02/24 14:10:19 INFO mapred.JobClient:     CPU time spent (ms)=0
    18/02/24 14:10:19 INFO mapred.JobClient:     Total committed heap usage (bytes)=357564416
    18/02/24 14:10:19 INFO mapred.JobClient:   File Input Format Counters 
    18/02/24 14:10:19 INFO mapred.JobClient:     Bytes Read=1366
    18/02/24 14:10:19 INFO mapred.JobClient:   FileSystemCounters
    18/02/24 14:10:19 INFO mapred.JobClient:     FILE_BYTES_WRITTEN=395714
    18/02/24 14:10:19 INFO mapred.JobClient:     FILE_BYTES_READ=290330
    18/02/24 14:10:19 INFO mapred.JobClient:   File Output Format Counters 
    18/02/24 14:10:19 INFO mapred.JobClient:     Bytes Written=1326
    

하둡 가상분산 모드 구성

5개의 프로세스 : Name node, Secondary Namenode, Data node, Job tracker, Tast tracker

아래 순서로 네개 파일 수정해 준다.

  • hadoop-env.sh : 환경변수 설정

  • core-site.xml : HDFS와 MapReduce에서 공통적으로 사용할 환경정보 설정

  • hdfs-site.xml : HDFS에서 사용할 환경정보 설정

  • mapred-site.xml : MapReduce에서 사용할 환경정보 설정

hadoop-env.sh 수정

JAVA_HOME 파라미터를 실제 JDK가 설치된 경로로 수정하는 작업

$ vim /home/유저명/hadoop-1.2.1/conf/hadoop-env.sh # 맨 끝에 3줄 추가

export JAVA_HOME=/usr/lib/jvm/java-8-oracle
export HADOOP_HOME=/home/유저명/hadoop-1.2.1
export HADOOP_HOME_WARN_SUPPRESS="TRUE"  # Warning: $HADOOP_HOME is deprecated.

보안 인증 관련 명령 실행

# 컴퓨터끼리 연결해서 접속할 수 있게 ssh 설정

$ sudo apt-get install openssh-server
$ sudo /etc/init.d/ssh restart # ssh 재실행
$ netstat -ntl # 0:::22 있으면 OK


# 접속할 때마다 비밀번호 묻지 않게 public key 공유(리눅스의 기능)

$ ssh-keygen -t rsa # 엔터 3번
$ cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
$ ssh localhost # 처음 한번만 yes, 두번째 접속시 부터는 안 물어봄.
$ exit # exit 로 꼭 나와야 함

core-site.xml (하둡의 핵심 설정 파일) 편집

$ vim /home/유저명/hadoop-1.2.1/conf/core-site.xml

<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<!-- Put site-specific property overrides in this file. -->
<configuration>
    <property>
        <name>fs.default.name</name>
        <value>hdfs://localhost:9000</value>
    </property>
    <property>
        <name>hadoop.tmp.dir</name>
        <value>/home/유저명/hadoop-1.2.1/hadoop-${user.name}</value>
    </property>
</configuration>
  • fs.default.name : HDFS의 기본 이름을 의미. URI 형태로 사용.
  • hadoop.tmp.dir : 하둡에서 발생하는 임시 데이터를 저장하기 위한 공간

hdfs-site.xml (하둡 분산 파일 관련 설정) 편집

$ vim /home/유저명/hadoop-1.2.1/conf/hdfs-site.xml

<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<!-- Put site-specific property overrides in this file. -->
<configuration>
    <property>
        <name>dfs.name.dir</name>
        <value>/home/유저명/hadoop-1.2.1/dfs/name</value>
    </property>
    <property>
        <name>dfs.name.edits.dir</name>
        <value>${dfs.name.dir}</value>
    </property>
    <property>
        <name>dfs.data.dir</name>
        <value>/home/유저명/hadoop-1.2.1/dfs/data</value>
    </property>
</configuration>
  • dfs.replication : HDFS의 저장될 데이터의 복제본 갯수를 의미. 가상분산모드 : 1 완전분산모드 : 3

  • dfs.http.address : 네임노드용 웹서버의 주소값. 기본값 : 0.0.0.0:50070

  • dfs.secondary.http.address : 보조네임노드용 웹서버 주소값. 기본값 : 0.0.0.0:50090

mapred-site.xml (하둡 맵리듀스 관련 설정) 편집

$ vim /home/유저명/hadoop-1.2.1/conf/mapred-site.xml

<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<!-- Put site-specific property overrides in this file. -->
<configuration>
    <property>
        <name>mapred.job.tracker</name>
        <value>localhost:9001</value>
    </property>
    <property>
        <name>mapred.local.dir</name>
        <value>${hadoop.tmp.dir}/mapred/local</value>
    </property>
    <property>
        <name>mapred.system.dir</name>
        <value>${hadoop.tmp.dir}/mapred/system</value>
    </property>
</configuration>
  • mapred.job.tracker : JobTracker 데몬의 주소를 의미. 데이터 노드에서 이 주소로 맵리듀스 작업을 요청.

namenode 포맷 ( namenode 는 처음에 꼭 한번 만 포맷해야 한다.)

$ hadoop namenode -format

하둡 시작 및 확인

$ cd /home/유저명/hadoop-1.2.1
$ bin/start-all.sh

하둡 프로세스 확인

$ jps # 5개의 프로세스 출력되면 OK : namenode, secondarynamenode, datanode, jobtracker, tasktracker

# 브라우저에서 접속해서 확인

URL : http://localhost:50070/dfshealth.jsp   // HDFS 확인
URL : http://localhost:50030/jobtracker.jsp // MapReduce 확인

이클립스 다운로드, 설정

이클립스 다운로드

URL : http://www.eclipse.org/downloads/ 로 접속

Eclipse IDE for Java Developers 를 다운로드

압축해제

$ cd Downloads
$ tar zxvf eclipse-java-luna-SR2-linux-gtk-x86_64.tar.gz

이클립스를 사용하여 Maven 프로젝트 생성

Maven에 대한 이해

https://www.slideshare.net/sunnykwak90/ss-43767933

이클립스 실행

파일 (탐색기)를 이용해서 eclipse 폴더의 eclipse 파일을 더블 클릭해서 실행

Maven Project 생성

File -> New -> Project -> Maven -> Maven Project 선택

-> [Next] -> [Next] -> [Next] ->

Group Id : kr.co.mycompant.hd

Artifact Id : wcount

-> [Finish]

pom.xml 수정해서 하둡 jar 파일 설정

pom.xml 을 열어서 사이에 아래 내용을 입력

의미 : 하둡 1.2.1 관련된 jar 파일들을 자동으로 다운로드해서 환경 설정

<dependency>
  <groupId>org.apache.hadoop</groupId>
  <artifactId>hadoop-core</artifactId>
  <version>1.2.1</version>
</dependency>

Hadoop_pom_xml

WordCount 예제 코딩

WordCount 클래스 생성

Hadoop_class

sr/main/java 밑의 kr.co.mycompany.hd.wcount 에서

우클릭해서 -> New -> Class : WordCount -> [Finish]

아래 소스를 입력

 1package kr.co.mycompany.hd.wcount;
 2import java.io.IOException;
 3import java.util.StringTokenizer;
 4import org.apache.hadoop.conf.Configuration;
 5import org.apache.hadoop.fs.Path;
 6import org.apache.hadoop.io.LongWritable;
 7import org.apache.hadoop.io.Text;
 8import org.apache.hadoop.mapreduce.Job;
 9import org.apache.hadoop.mapreduce.Mapper;
10import org.apache.hadoop.mapreduce.Reducer;
11import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
12import org.apache.hadoop.mapreduce.lib.input.TextInputFormat;
13import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
14import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;
15
16public class WordCount {
17  public static class MyMapper
18    extends Mapper<LongWritable, Text, Text, LongWritable> {
19    private final static LongWritable one = new LongWritable(1);
20    private Text word = new Text();
21    @Override
22    public void map(LongWritable key, Text value, Context context)
23        throws IOException, InterruptedException {
24      String line = value.toString();
25      StringTokenizer tokenizer =
26        new StringTokenizer(line, "\t\r\n\f|,.()<> ");
27      while(tokenizer.hasMoreTokens()) {
28        word.set(tokenizer.nextToken().toLowerCase());
29        context.write(word, one);
30      }
31    }// map
32  }// MyMapper
33  public static class MyReducer
34    extends Reducer<Text, LongWritable, Text, LongWritable> {
35    private LongWritable sumWritable = new LongWritable();
36    @Override
37    protected void reduce(Text key, Iterable<LongWritable> values,
38        Context context)
39        throws IOException, InterruptedException {
40      long sum = 0;
41      for(LongWritable val : values) {
42        sum += val.get();
43      }
44      sumWritable.set(sum);
45      context.write(key, sumWritable);
46    }// reduce
47  }// MyReducer
48  public static void main(String[] args) throws Exception {
49    Configuration conf = new Configuration();
50    Job job = new Job(conf, "WordCount");
51
52    job.setJarByClass(WordCount.class);
53    job.setMapperClass(MyMapper.class);
54    job.setReducerClass(MyReducer.class);
55
56    job.setOutputKeyClass(Text.class);
57    job.setOutputValueClass(LongWritable.class);
58
59    job.setInputFormatClass(TextInputFormat.class);
60    job.setOutputFormatClass(TextOutputFormat.class);
61
62    FileInputFormat.addInputPath(job, new Path(args[0]));
63    FileOutputFormat.setOutputPath(job, new Path(args[1]));
64
65    job.waitForCompletion(true);
66  }// main
67}// end
코드 설명

WordcountExample

 1package kr.co.mycompany.hd.wcount;
 2
 3public class WordCount {
 4  public static class MyMapper
 5    extends Mapper<LongWritable, Text, Text, LongWritable> {
 6    // Mapper를 상속받음 <KEYIN, VALUEIN, KEYOUT, VALUEOUT>
 7    
 8    private final static LongWritable one = new LongWritable(1);
 9    // final로 정의(상수). 공유하기 위해 static. 1을 담음. 어떤 단어가 한 번 나올 때 1이 들어가는 용도
10    
11    private Text word = new Text();
12    // 단어 담는 용도. 이 위치에서 생성하는 게 성능 향상에 도움이 됨. 
13    // Map에 생성되면 갯수만큼 호출되므로 매번 객체생성. Map에서는 객체 생성 안함. Map 들어가기 전에 생성.
14    
15    @Override  
16    public void map(LongWritable key, Text value, Context context)
17    // Text value : 텍스트가 한 줄씩 들어옴
18    // Context : 나와 하둡의 연결고리
19    
20        throws IOException, InterruptedException {
21      String line = value.toString();
22      // String으로 바꿔야 자바에서 자료 관리가 가능하므로 변경
23      // toStirng : String으로 바꿔줌
24      
25      StringTokenizer tokenizer = new StringTokenizer(line, "\t\r\n\f|,.()<> ");
26      // StringTokenizer<String, 구분자>, 구분자 맨 뒤에 공백 있음.
27      // Dear Bear River
28      
29      while(tokenizer.hasMoreTokens()) {
30        word.set(tokenizer.nextToken().toLowerCase());
31        context.write(word, one);
32      // hasMoreTokens() : 반복문 돌면서 'hello hadoop world'
33      // -> 3개로 끊어짐. 'hello', 'hadoo', 'world'
34          
35      // word.set : 하나씩 담음
36      // nextToken : 다음 토큰 가져옴
37      // toLowerCase : 대소문자 구분 안하기 위해 모두 소문자로 변경
38      
39      // context.write(word, one) : 단어 기록. (ex) 'hello'가 1번 나왔다 : ('hello', 1)
40          // 첫번째 루프 : Deer, 1 / 두번째 루프 : Bear, 1 / 세번째 루프 : River 1
41      }
42    }// map
43  }// MyMapper
44  
45  
46  // Reducer
47  public static class MyReducer
48    extends Reducer<Text, LongWritable, Text, LongWritable> {
49    // Reducer <KEYIN, VALUEIN, KEYOUT, VALUEOUT>  
50      
51    private LongWritable sumWritable = new LongWritable();
52    
53    @Override
54    protected void reduce(Text key, Iterable<LongWritable> values,
55        Context context)
56    // Iterable : 반복자
57        
58        throws IOException, InterruptedException {
59      long sum = 0;
60      for(LongWritable val : values) {
61        sum += val.get();
62      }
63      // 반복문 돌면서 LongWritable로 하나씩 가져온다.
64      // 배열로 두개가 들어온다고 생각(그림의 Shuffling 부분 참고). (ex) Bear[1, 1]
65      
66      sumWritable.set(sum);
67      // 더해 줌
68        
69      context.write(key, sumWritable);       
70    }// reduce
71  }// MyReducer
72    
73 
74  // Main method
75  public static void main(String[] args) throws Exception {
76    Configuration conf = new Configuration();
77    // Configuration : 환경설정
78   
79    Job job = new Job(conf, "WordCount");
80    // Job : MapReduce의 Job
81
82    job.setJarByClass(WordCount.class);
83    job.setMapperClass(MyMapper.class);
84    job.setReducerClass(MyReducer.class);
85
86    job.setOutputKeyClass(Text.class);
87    job.setOutputValueClass(LongWritable.class);
88
89    job.setInputFormatClass(TextInputFormat.class);
90    job.setOutputFormatClass(TextOutputFormat.class);
91
92    FileInputFormat.addInputPath(job, new Path(args[0]));  // 입력폴더
93    FileOutputFormat.setOutputPath(job, new Path(args[1]));  // 출력폴더
94
95    job.waitForCompletion(true);
96  }
97}// end

예제를 Maven Install 로 ~.jar 파일로 패키징한다.

프로젝트 우클릭 -> Run As -> 8 Maven install

target 폴더에 wcount-0.0.1-SNAPSHOT.jar 이 생성된다.

하둡 가상 분산 모드에서 생성된 jar 을 사용하여 WordCount 실행, 테스트

  • wcount-0.0.1-SNAPSHOT.jar 파일을 /home/유저명/hadoop-1.2.1 폴더에 복사한다.
  • hadoop fs 명령을 사용하여 테스트할 파일을 복사한다.
$ hadoop fs -put READEME.txt .

hadoop jar 명령을 사용하여 예제를 실행/테스트한다.

$ hadoop jar wcount-0.0.1-SNAPSHOT.jar kr.co.mycompany.hd.wcount.WordCount README.txt output1

결과가 output1 폴더에 part-r-00000 이라는 파일로 저장된다.

hadoop fs -cat 명령어를 통해서 학인 할 수 있다.

$ hadoop fs -cat output1/part-r-00000

wc : wordcount 명령어

단어수 31, 줄 179, 크기 1366

linux

[참고문헌]

https://blog.naver.com/sungback/220381870733

https://www.slideshare.net/sunnykwak90/ss-43767933

http://naver.me/IDMfcGl3

https://hadoop.apache.org/docs/stable/hadoop-mapreduce-client/hadoop-mapreduce-client-core/MapReduceTutorial.html