본문 바로가기
DATA SCIENCE/HADOOP ECOSYSTEMS

Hadoop 3 완전 분산 모드 설치 - 2 (Hadoop 설치 & MapReduce)

by 나른한 사람 2021. 10. 31.

목표

VM 환경에서 하둡을 완전 분산 모드로 설치하고 Map-Reduce를 실행해보자.


준비

- Java 8
- SSH
- Hadoop-3.3.1


기본설치

- 필요한 패키지 정보 업데이트

~$ sudo apt update
~$ sudo apt upgrade -y

 

- Java 8 설치

~$ sudo apt install openjdk-8-jdk -y

# 버전 확인
~$ java -version
openjdk version "1.8.0_292"
OpenJDK Runtime Environment (build 1.8.0_292-8u292-b10-0ubuntu1~20.04-b10)
OpenJDK 64-Bit Server VM (build 25.292-b10, mixed mode)

 

- SSH 설치

~$ sudo apt install ssh -y
~$ ssh localhost

# yes 입력 시 다음부터 비밀번호 계속 입력할 필요 없음
? yes
password: 로그인 시 입력했던 비밀번호

# exit를 통해 ssh 연결 끊음
~$ exit

 

- Hadoop 다운로드

~$ wget https://archive.apache.org/dist/hadoop/common/hadoop-3.3.1/hadoop-3.3.1.tar.gz

# 압축 해제
~$ tar -xzf hadoop-3.3.1.tar.gz

# 폴더명 변경
~$ mv hadoop-3.3.1 hadoop

 

- ~/.bashrc 수정

~$ nano ~/.bashrc

# java jdk 설치 경로
export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64
# hadoop 설치 경로
export HADOOP_HOME=/home/hadoop/hadoop
# PATH 설정을 통해 해당 경로로 이동하거나 경로를 입력하지 않아도 실행 가능
export PATH=$PATH:$JAVA_HOME/bin:$HADOOP_HOME/bin:$HADOOP_HOME/sbin:
# 이후 python으로 mapreduce 진행할 때 hadoop streaming 사용
# 경로는 find /home/hadoop -name hadoop-streaming* 으로 찾을 수 있음
export HADOOP_STREAMING=$HADOOP_HOME/share/hadoop/tools/lib/hadoop-streaming-3.3.1.jar

# [Ctrl + X] + Enter 로 저장하고 나옴.

source ~/.bashrc

 

 


추가 설정

Hadoop은 Master - Slave 구조로 이루어져 있고, 통신을 하기 위해서는 IP 주소가 필요하다.
하지만 IP는 255^4 개로 제한되어 있어서 사용하지 않는 IP는 회수하고 이후에 다시 할당받게 되는데, 이 과정에서 IP가 변하게 된다. 매번 번거로운 작업을 하지 않기 위해서 VM 내에서 서로 통신을 할 수 있도록 IP를 고정시키도록 한다.

참고: 네트워크 가상화 모드 (https://technote.kr/213)

Slave 생성(복제) 및 IP 고정

  1. 먼저 실행된 VM을 종료한다.
  2. VirtualBox 도구 우측의 메뉴에서 네트워크 클릭.
  3. VirtualBox Host-Only Ethernet Adapter가 만들어져 있으면 클릭해서 속성을 수정함.
    - 어댑터 탭 -> 수동으로 어댑터 설정(M)
    - DHCP 서버 탭 -> 서버 사용함(E) 체크
  4. 복제할 VM을 우클릭 하고 복제를 클릭, 
  5. 적절한 이름을 입력하고 [MAC 주소 정책(P) -> 모든 네트워크 어댑터의 새 MAC 주소 생성] 설정 후 [다음(N)]. [완전한 복제(F)] 선택 후 [복제].
  6. (4~5 를 한번 더 반복함).
  7. VM-master부터 모든 slave에 대해 진행함.
    7.1. VM 우클릭 - 설정
    7.2. 네트워크 - 어댑터1(NAT) - 어댑터2
    - [네트워크 어댑터 사용하기(E) - 다음에 연결됨(A)(호스트 전용 어댑터) - 위에서 생성한 어댑터 선택 - 무작위 모드(P)(모두 허용) - 확인] 
    - 그리고 여기서 가장 아래에 MAC 주소를 기억해둔다.
  8. 각각의 VM을 모두 실행시키고 터미널 실행[Ctrl + Alt + T] 
    ~$ ip addr
    # 아마도 3번까지의 주소가 나올텐데, 
    # 여기서 link/ether 옆에 아까 확인한 MAC 주소와 같은 주소가 적혀있는 부분의 IP 주소를 확인하면 된다.
    
    ex)
     
    3: enp0s8: < ...
    	link/ether xx:xx:xx:xx:xx:xx brd ff:...
        inet 192.168.x.x ...
        ...
    # inet 부분이 IP 주소이다.​

hostname, /etc/hosts 수정

매번 IP 주소를 사용하기 번거롭기 때문에 이를 hostname으로 사용할 수 있도록 수정한다.

- hostname 변경

# VM - master
~$ hostnamectl set-hostname master
~$ sudo reboot

# VM - slave1
~$ hostnamectl set-hostname slave1
~$ sudo reboot

# VM - slave2
~$ hostnamectl set-hostname slave2
~$ sudo reboot

 

- /etc/hosts 수정

hadoop@master:~$ sudo nano /etc/hosts

# ** 모든 node에서 수정 ** #
# x.x.x.x localhost
# x.x.x.x hadoop-VirtualBox
# 아래에
# 123.123.123.3	master
# 123.123.123.4 slave1
# 123.123.123.5 slave2
# 와 같이 추가하고 [Ctrl + x] - y - 엔터

# 이제 IP 주소가 아닌 hostname으로 접속 가능.
hadoop@master:~$ ssh slave1
> yes
> 비밀번호

# 사용자가 slave1로 바뀐것을 확인할 수 있음.
hadoop@slave1:~$ exit

# 다시 접속해도 비밀번호를 입력해야 함.
hadoop@master:~$ ssh slave1
> 비밀번호

 

암호 없이 접속하기

- 암호가 없는 ssh 접속을 위해서 master의 공개키를 생성해서 이를 각 slave의 인증된 키로 보내주어야함.

hadoop@master:~$ ssh-keygen -t rsa -f ~/.ssh/id_rsa
# 엔터

# authorized_keys에 공개키 추가
hadoop@master:~$ cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys

# 권한 변경
hadoop@master:~$ chmod 0600 ~/.ssh/authorized_keys

# slave로 키 복사
hadoop@master:~$ ssh-copy-id -i /home/hadoop/.ssh/id_rsa.pub slave1
hadoop@master:~$ ssh-copy-id -i /home/hadoop/.ssh/id_rsa.pub slave2
# 계속 연결 할 것인지 물어보면 > yes
# password 입력

hadoop@master:~$ ssh slave1

hadoop@slave1:~$ exit

하둡 세팅

- 세팅해야 할 파일들이 있는 위치로 이동
- master 에서 실행

cd $HADOOP_HOME/etc/hadoop
# or
# cd /home/hadoop/hadoop/etc/hadoop

 

- core-site.xml

# nano or vi or vim 아무거나 사용가능

vim core-site.xml
# <configuration> 안에 입력
<property>
	<name>fs.defaultFS</name>
    <value>hdfs://master:9000</value>
</property>

 

- hdfs-site.xml

vim hdfs-site.xml

# <configuration> 안에 입력
# namenode 데이터 경로 설정
<property>
	<name>dfs.namenode.name.dir</name>
    <value>/home/hadoop/hadoop/data/namenode</value>
</property>

# datanode 데이터 경로 설정
<property>
	<name>dfs.datanode.data.dir</name>
    <value>/home/hadoop/hadoop/data/datanode</value>
</property>

# data 복제 갯수 설정 (장애 대응)
<property>
	<name>dfs.replication</name>
    <value>1</value>
</property>

# secondary namenode 설정
<property>
	<name>dfs.namenode.secondary.http-address</name>
    <value>slave1:50090</value>
</property>

 

- yarn-site.xml

vim yarn-site.xml

# <configuration> 안에 입력
<property>
	<name>yarn.resourcemanager.hostname</name>
    <value>master</value>
</property>

# log 출력
<property>
	<name>yarn.log-aggregation-enables</name>
    <value>true</value>
</property>

 

- mapred-site.xml

vim mapred-site.xml

# <configuration> 안에 입력
<property>
	<name>mapreduce.framework.name</name>
    <value>yarn</value>
</property>
<property>
	<name>yarn.app.mapreduce.am.env</name>
	<value>HADOOP_MAPRED_HOME=$HADOOP_HOME</value>
</property>
<property>
	<name>mapreduce.map.env</name>
	<value>HADOOP_MAPRED_HOME=$HADOOP_HOME</value>
</property>
<property>
	<name>mapreduce.reduce.env</name>
	<value>HADOOP_MAPRED_HOME=$HADOOP_HOME</value>
</property>

 

- hadoop-env.sh

vim hadoop-env.sh

# / 누르고 export JAVA_HOME 검색, #을 지우고 아래로 수정
export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64

 

- workers

vim workers

# 모두 지우고
slave1
slave2
# 입력 후 저장

 

- slave에 복사

scp $HADOOP_HOME/etc/hadoop/* slave1:$HADOOP_HOME/etc/hadoop/
scp $HADOOP_HOME/etc/hadoop/* slave2:$HADOOP_HOME/etc/hadoop/

 


하둡 실행

# namenode foramt
hdfs namenode -format

# 하둡 실행 
start-dfs.sh
start-yarn.sh

hadoop@master:~/hadoop$ jps
5752 ResourceManager
5449 NameNode
6031 Jps

# slave1에는 세컨더리 네임노드가 있음
hadoop@slave1:~/hadoop$ jps
3360 Jps
3234 NodeManager
2931 DataNode
3059 SecondaryNameNode

hadoop@slave2:~/hadoop$ jps
2880 Jps
2757 NodeManager
2533 DataNode

 

- hdfs 저장소

hadoop@master:~/hadoop$ hdfs dfs -ls 
ls: '.': No such file or directory

# 디렉토리가 없으므로 /user, /user/wordcount directory 생성
hadoop@master:~/hadoop$ hdfs dfs -mkdir -p /user/wordcount

# README.txt를 hdfs /user/wordcount에 저장
hadoop@master:~/hadoop$ hdfs dfs -put $HADOOP_HOME/README.txt /user/wordcount
hadoop@master:~/hadoop$ hdfs dfs -ls /user/wordcount
Found 1 items
~ ~ ~

Map Reduce 예제 실행해보기

Map-Reduce는 mapper와 reducer로 이루어져 있고, 어떤 파일을 열고 파싱을 통해 key-value값을 reducer로 전달, reducer에서는 key-value 값을 일정한 규칙에 따라 정리함.

cd $HADOOP_HOME
find . -name *mapreduce-example*
~
./share/hadoop/mapreduce/hadoop-mapreduce-examples-3.3.1.jar
~
~
# hadoop jar $HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.3.1.jar
# 를 입력하면 사용할 수 있는 예제들이 나옴.

hadoop jar $HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.3.1.jar \
wordcount \
/user/wordcount/README.txt \
/user/wordcount/example0

...

# 결과 확인
hdfs dfs -text /user/wordcount/example0/*

Mapper / Reducer 구현 - Python

sudo apt-get install python-is-python3

- mapper.py

#!/usr/bin/env python

import sys

value = 1
for line in sys.stdin:
	line = line.strip()
    words = line.split()
    for word in words:
    	print(f'{word}\t{value}')

 

- reducer.py

#!/usr/bin/env python

import sys

word_last = None
word_count = 0

for line in sys.stdin:
	line = line.strip()
    key, value = line.split('\t', 1)
    value = int(value)
    
    if word_last == key:
    	word_count += value
	else:
    	if word_last:
        	print(f'{word_last}\t{word_count}')
		word_last = key
        word_count = value

if word_last == key:
	print(f'{word_last}\t{word_count}')

 

- 실행

hadoop jar $HADOOP_STREAMING \
-input /user/wordcount/README.txt \
-output /user/wordcount/example00 \
-file $HADOOP_HOME/code/mapper.py -mapper mapper.py \
-file $HADOOP_HOME/code/reducer.py -reducer reducer.py

hdfs dfs -text /user/wordcount/example00/*

# mapreduce-example과 같은 결과

댓글