빅데이터

[세번째 이야기] Hadoop Cluster 설치하기

CyberI 2017. 6. 14. 20:52

Hadoop의 HA

Hadoop 1.x에서 HDFS와 MapReduce를 통해 대량의 데이터를 분산 병렬처리 하면서 아쉬움이 있었다면 하나의 Name Node가 전체 클러스터를 관리하면서 SPOF(Single Point Of Failure)가 존재하며, 최대 4,000노드와 4만 Task까지만 확장이 된다는 것이었습니다.
* Hadoop 1.x에 Secondary NameNode가 존재했지만 주기적으로 NameNode의 파일 시스템 이미지를 갱신하는 역할(체크포인트)로 NameNode의 백업 역할이 아니었습니다.

이후에 Hadoop 2.x 부터 NameNode 이중화(Active-Standby), Zookeeper를 이용한 ResourceManager 이중화를 지원하면서, 최대 10,000노드와 YARN기반의 분산처리 환경을 제공하고 있습니다.

 

NameNode의 이중화를 위해 두 가지 방법을 제공합니다.

1) Journal Node를 이용하는 방법

2) NFS를 이용한 Shared Storage를 이용하는 방법

 

Journal Node를 이용하는 방법

JournalNode는 NameNode의 edits 정보를 저장하고 공유하는 기능을 수행하는 노드로 3대의 분산 환경에서 각 노드들은 Ring topology로 구성하며 JournalNode들도 fault tolerance 기능을 지원하고 있습니다.

(출처: https://hadoopabcd.wordpress.com/2015/02/19/hdfs-cluster-high-availability/)

 

JournalNode의 failover 절차는 다음과 같습니다.

1.     Active NameNode는 edit log 처리용 epoch number를 할당 받는다. 이 번호는 unique하게 증가하는 번호로 새로 할당 받은 번호는 이전 번호보다 항상 크다.

2.     Active NameNode는 파일 시스템 변경 시 JournalNode로 변경 사항을 전송한다. 전송 시 epoch number를 같이 전송한다.

3.     JournalNode는 자신이 가지고 있는 epoch number 보다 큰 번호가 오면 자신의 번호를 새로운 번호로 갱신하고 해당 요청을 처리한다.

4.     JournalNode는 자신이 가지고 있는 번호보다 작은 epoch number를 받으면 해당 요청은 처리하지 않는다.

-       이런 요청은 주로 SplitBrain 상황에서 발생하게 된다.

-       기존 NameNode가 정상적으로 Standby로 변하지 않았고, 이 NameNode가 정상적으로 fencing 되지 않은 상태이다.

5.     Standby NameNode는 주기적(1분)으로 JournalNode로 부터 이전에 받은 edit log의 Transaction id(txid) 이후의 정보를 받아 메모리의 파일 시스템 구조에 반영

6.     Active NameNode 장애 발생 시 Standby NameNode는 마지막 받은 txid 이후의 모든 정보를 받아 메모리 구성에 반영 후 Active NameNode로 상태 변환

7.     새로 Active NameNode가 되면 1번 항목을 처리한다.

  

Shared Storage를 이용하는 방법

Active NameNode와 Standby NameNode 사이에 fsimage와 edits log 파일을 공유할 수 있는 저장소를 구성하는 방식으로, 만약, Active NameNode와 Standby NameNode에서 각각 데이터를 수정하면 crash 되게 됩니다. 보통 Network 문제나 CPU 과부하 상황에서 많이 발생해서 failover 처리에 한계가 있기 때문에 잘 사용하지 않는 방법입니다.

 

(출처https://hadoopabcd.wordpress.com/2015/02/19/hdfs-cluster-high-availability/)

  

Hadoop의 HA 구성하기

Hadoop을 설치하면서 Active NameNode에서 장애발생시 Automatic Failover를 위해 Zookeeper를 통한 모니터링을 설정합니다.

 

Hadoop HA의 구성 모습으로 2대의 물리 서버에서 Docker의 가상화 Container를 활용해서 총 6대 서버에 분산환경으로 설치하려고 합니다.

 

1. 물리 서버 환경

OS : CentOS 7.2 (64-bit)

KERNEL : 3.10.0

 

2. 가상화 서버 설치 정보

machine IP address Host name
Active NameNode 202.xxx.xxx.51 nn01
Standby NameNode 202.xxx.xxx.60 nn02
DataNode #1 202.xxx.xxx.52 dn01
DataNode #2 202.xxx.xxx.39 dn02
DataNode #3 202.xxx.xxx.61 dn03
DataNode #4 202.xxx.xxx.62 dn04

 

 

 

3. Setting Up and Configuring

- Docker의 Container를 실행할 때 Container 포트와 Host 포트를 매핑하기 위해 –p 옵션을 사용해서 실행합니다.

Command: docker run -ti --name nn01 -p 51218:51218 -p 53070:53070 -p 53470:53470 -p 53100:53100 -p 53105:53105 -p 53090:53090 -p 53091:53091 -p 55077:55077 -p 55080:55080 -p 55081:55081 docker.io/centos

위 명령어를 통해 docker.io/centos image로 nn01 이름을 지정한 container를 실행합니다.

 

- Download the zookeeper binary file

Command: wget http://apache.mirror.cdnetworks.com/zookeeper/zookeeper-3.4.9/zookeeper-3.4.9.tar.gz

 

 

 - Untar the zookeeper tar file

Command: tar -xvf zookeeper-3.4.9.tar.gz

 

- move the zookeeper directory

Command: mv zookeeper-3.4.9 /usr/local/ && ln -s /usr/local/zookeeper-3.4.9 /usr/local/zookeeper

 

- Download the Hadoop binary file

Command: wget http://archive.apache.org/dist/hadoop/core/hadoop-2.7.3/hadoop-2.7.3.tar.gz

 

- Untar the Hadoop tar file

Command: tar –xvf hadoop-2.7.3.tar.gz

 

- move the hadoop directory

Command: mv hadoop-2.7.3 /usr/local/ && ln -s /usr/local/hadoop-2.7.3 /usr/local/hadoop

 

- edit ~/.bashrc

Command: vi ~/.bashrc

[edit]

export JAVA_HOME=/usr/java/default

export JAVA_LIBRARY_PATH=$HADOOP_HOME/lib/native:$JAVA_LIBRARY_PATH

 

export HADOOP_HOME=/usr/local/hadoop

export HADOOP_CLASSPATH=${JAVA_HOME}/lib/tools.jar

export HADOOP_PREFIX=$HADOOP_HOME

export HADOOP_COMMON_HOME=$HADOOP_PREFIX

export HADOOP_COMMON_LIB_NATIVE_DIR=$HADOOP_PREFIX/lib/native

export HADOOP_CONF_DIR=$HADOOP_PREFIX/etc/hadoop

export HADOOP_HDFS_HOME=$HADOOP_PREFIX

export HADOOP_MAPRED_HOME=$HADOOP_PREFIX

export YARN_HOME=$HADOOP_PREFIX

 

export ZOOKEEPER_HOME=/usr/local/zookeeper

export ZOOKEEPER_PREFIX=$ZOOKEEPER_HOME

export ZOO_LOG_DIR=$ZOOKEEPER_HOME/logs

 

export PATH=$PATH:$JAVA_HOME/bin:$HADOOP_HOME/bin:$HADOOP_HOME/sbin:$ZOOKEEPER_HOME/bin

 

 

- Generate the SSH rsa key

분산환경으로 설치하는 경우 각 Hadoop 데몬들의 SSH(Secure Shell) 로그인을 수행하기 때문에 공개키 인증방식(Public Key Authentication)을 사용하여 비밀번호 입력 없이 서버간 통신이 가능하게 됩니다.

Command: ssh-keygen -t rsa

ssh-keygen 명령 실패로 yum을 통해 openssh를 설치 후 명령어를 재실행 한다.

 

설치 중 rsa key의 저장경로와 암호를 입력 프롬프트에 대해 enter를 눌러 그냥 넘어간다.

 

key 생성이 완료되면 key가 저장된 기본 경로(.ssh)에 대해 Permission 700, .ssh 하위 디렉토리에 Permission 600을 정의한다.

Command: chmod 700 /root/.ssh

Command: chmod 600 *

 

- copy the id_rsa.put using cat command

Command: cat ~/.ssh/id_rsa.pub >> ~/.ssh/autohrized_keys

 

ssh key 생성 후 sshd를 재실행한다.

Command: systemctl restart sshd

*  현재 실행환경은 Docker Container로 가상화를 통해 설치하고 있다. Docker Container에서 별도의 설정이 없는 상태에서는 systemctl 명령어 사용이 불가능해서 /sbin/sshd를 직접 실행한다.

/sbin/sshd를 실행할 때 위와 같은 에러 로그가 발생한다. ssh-keygen 명령어를 통해 해당 경로에 key를 생성한다.

 

- Generate the SSH rsa key

Command: ssh-keygen -q -N "" -t rsa -f /etc/ssh/ssh_host_rsa_key

 

- Generate the SSH dsa key

Command: ssh-keygen -q -N "" -t dsa -f /etc/ssh/ssh_host_dsa_key

 

- Generate the SSH ecdsa key

Command: ssh-keygen -q -N "" -t ecdsa -f /etc/ssh/ssh_host_ecdsa_key

 

- Generate non-existent host keys for all key types

Command: ssh-keygen -A

 

- Restart the sshd

Command: /sbin/sshd

 

Hadoop 설치를 위한 사전작업이 끝났습니다. 지금부턴 Hadoop config 파일 설정을 시작합니다.

 

- Edit the core-site.xml

Command: vi /usr/local/hadoop/etc/hadoop/core-site.xml

[edit]

<configuration>

    <property>

        <name>fs.defaultFS</name>

        <value>hdfs://hadoop</value>

        <description>Use HDFS as file storage engine</description>

    </property>

    <property>

        <name>fs.hdfs.impl</name>

        <value>org.apache.hadoop.hdfs.DistributedFileSystem</value>

    </property>

    <property>

        <name>dfs.journalnode.edits.dir</name>

        <value>/repository/dfs/journal</value>

    </property>

    <property>

        <name>io.file.buffer.size</name>

        <value>131072</value>

    </property>

    <property>

        <name>hadoop.tmp.dir</name>

        <value>file:/repository/tmp</value>

        <description>A base for other temporary directories.</description>

    </property>

</configuration>

 

dfs.journalnode.edits.dir 설정 값을 위해 /repository 디렉토리를 생성한다.

 

- Edit the hdfs-site.xml

Command: vi /usr/local/hadoop/etc/hadoop/hdfs-site.xml

[edit]

<configuration>

    <property>

        <name>dfs.namenode.rpc-bind-host</name>

        <value>0.0.0.0</value>

    </property>

    <property>

        <name>dfs.namenode.servicerpc-bind-host</name>

        <value>0.0.0.0</value>

    </property>

    <property>

        <name>dfs.namenode.http-bind-host</name>

        <value>0.0.0.0</value>

    </property>

    <property>

        <name>dfs.namenode.https-bind-host</name>

        <value>0.0.0.0</value>

    </property>

    <property>

        <name>dfs.namenode.name.dir</name>

        <value>file:/repository/dfs/name</value>

    </property>

    <property>

        <name>dfs.datanode.data.dir</name>

        <value>file:/repository/dfs/data</value>

    </property>

    <property>

        <name>dfs.replication</name>

        <value>3</value>

    </property>

    <property>

        <name>dfs.permissions</name>

        <value>false</value>

    </property>

    <property>

        <name>dfs.nameservices</name>

        <value>hadoop</value>

    </property>

 

    <!-- HA configuration -->

    <!-- only a maximum of two NameNodes may be configured per nameservice -->

    <property>

        <name>dfs.ha.namenodes.hadoop</name>

        <value>nn1,nn2</value>

    </property>

    <property>

        <name>dfs.namenode.rpc-address.hadoop.nn1</name>

        <value>nn01:52020</value>

    </property>

    <property>

        <name>dfs.namenode.rpc-address.hadoop.nn2</name>

        <value>nn02:52020</value>

    </property>

    <property>

        <name>dfs.namenode.http-address.hadoop.nn1</name>

        <value>nn01:53070</value>

    </property>

    <property>

        <name>dfs.namenode.http-address.hadoop.nn2</name>

        <value>nn02:53070</value>

    </property>

    <property>

        <name>dfs.namenode.https-address.hadoop.nn1</name>

        <value>nn01:53470</value>

    </property>

    <property>

        <name>dfs.namenode.https-address.hadoop.nn2</name>

        <value>nn02:53470</value>

    </property>

    <property>

        <name>dfs.namenode.shared.edits.dir</name>

        <value>qjournal://nn01:52485;nn02:52485;dn01:52485/hadoop</value>

    </property>

    <property>

        <name>dfs.client.failover.proxy.provider.hadoop</name>

        <value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value>

    </property>

 

    <!-- Automatic failover configuration -->

    <property>

        <name>dfs.ha.automatic-failover.enabled</name>

        <value>true</value>

    </property>

    <property>

        <name>ha.zookeeper.quorum</name>

        <value>nn01:51218,nn02:51218,dn01:51218</value>

    </property>

 

    <!-- Fencing configuration -->

    <property>

        <name>dfs.ha.fencing.methods</name>

        <value>shell(/bin/true)</value>

    </property>

    <property>

        <name>dfs.datanode.max.xcievers</name>

        <value>4096</value>

    </property>

    <property>

        <name>dfs.support.append</name>

        <value>true</value>

    </property>

 

    <!-- Storage for edits' files -->

    <property>

        <name>dfs.namenode.max.extra.edits.segments.retained</name>

        <value>1000000</value>

    </property>

    <property>

        <name>dfs.webhdfs.enabled</name>

        <value>true</value>

    </property>

    <property>

        <name>dfs.namenode.backup.address</name>

        <value>0.0.0.0:53100</value>

    </property>

    <property>

        <name>dfs.namenode.backup.http-address</name>

        <value>0.0.0.0:53105</value>

    </property>

    <property>

        <name>dfs.namenode.secondary.http-address</name>

        <value>0.0.0.0:53090</value>

    </property>

    <property>

        <name>dfs.namenode.secondary.https-address</name>

        <value>0.0.0.0:53091</value>

    </property>

    <property>

        <name>dfs.datanode.address</name>

        <value>0.0.0.0:53010</value>

    </property>

    <property>

        <name>dfs.datanode.ipc.address</name>

        <value>0.0.0.0:53020</value>

    </property>

    <property>

        <name>dfs.datanode.http.address</name>

        <value>0.0.0.0:53075</value>

    </property>

    <property>

        <name>dfs.datanode.https.address</name>

        <value>0.0.0.0:53475</value>

    </property>

    <property>

        <name>dfs.journalnode.rpc-address</name>

        <value>0.0.0.0:52485</value>

    </property>

    <property>

        <name>dfs.journalnode.http-address</name>

        <value>0.0.0.0:52480</value>

    </property>

    <property>

        <name>dfs.journalnode.https-address</name>

        <value>0.0.0.0:52481</value>

    </property>

</configuration>

nameservice 값을 기준으로 이중화 구성을 위한 설정정보이다.

namenode 이중화 구성으로 nn1, nn2 이름으로 정의하고 각각에 대해 포트정보를 설정한다. journalnode는 nn01, nn02, dn01에 설치된다.

 

zookeeper를 통한 Automatic failover를 위한 설정정보로 zookeeper quorum을 통해 NameNode의 장애를 감지하고 Standby NameNode가 Active로 전환된다.

 

- Change the Zookeeper conf directory

Command: cd /usr/local/zookeeper/conf 

 

- Edit the Zookeeper conf file

Command: cd /usr/local/zookeeper/conf

zookeeper의 환경파일은 sample 파일을 복사해서 생성하도록 되어있다.

[edit]

dataDir=/usr/local/zookeeper/data

clientPort=51218

환경파일 최하단에 zookeeper가 실행될 서버와 port 번호를 입력한다.

server.1=nn01:51288:51388

server.2=nn02:51288:51388

server.3=dn01:51288:51388

server.# 는 zookeeper id 파일을 생성 후 입력 값으로 해당 서버에 id를 구분해서 입력한다.

zookeeper 간 사용하는 Default port는 2888, 3888로 각각 zookeeper간 접속 port, 리더 선출을 위한 port 이다. 이번 설정에서는 2888->5128, 3888->51388로 변경해서 구성한다.

 

- Deploy docker image

Command: docker commit 44f475c05a9d docker.io/cyberi/hdfs:17.06.14.01

docker image를 통한 배포를 위해 현재까지 작업된 image에 대해 commit을 실행하고, 새로 만들어진 이미지로 Container를 실행한다.

 

- Create the myid file inside the zookeeper directory

Command: echo "1" > $ZOOKEEPER_PREFIX/data/myid

명령어를 실행하기 전에 반드시 $ZOOKEEPER_PREFIX/data, $ZOOKEEPER_PREFIX/logs directory를 생성하고 실행해야 한다.

nn02, dn01 서버에서 각각 “2”, “3” 내용으로 myid 파일을 생성한다.

 

 

4. Start the Hadoop Cluster

- Start the Journalnode

Command: hadoop-daemon.sh start journalnode

jps 명령으로 JournalNode 프로세스가 실행됨을 확인할 수 있다. 동일한 명령어로 nn02, dn01 서버에서 JournalNode를 실행한다.

 

- Format the Active NameNode

Command: hdfs namenode -format

 

 

실행 로그를 통해 정상완료 되었음을 확인할 수 있다.

 

- Start the Active NameNode

Command: hadoop-daemon.sh start namenode

 

- Copy the HDFS Meta data from Active NameNode to Standby NameNode

Command: hdfs namenode -bootstrapStandby

 

 

실행 로그를 통해 정상완료 되었음을 확인할 수 있다.

 

- Start the Standby NameNode

Command: hadoop-daemon.sh start namenode

 

- Start the Zookeeper service all nodes

Command: zkServer.sh start

jps 명령어를 통해 QuorumPeerMain 프로세스가 정상 가동되었음을 확인할 수 있다. nn02, dn01 서버에서도 동일한 명령어로 Zookeeper를 실행한다.

 

- Start the DataNode

Command: hadoop-daemon.sh start datanode

jps 명령어를 통해 DataNode 프로세스가 정상 가동되었음을 확인할 수 있다. 나머지 DataNode 서버에서도 동일한 명령어로 실행한다.

 

- Start the Zookeeper fail over controller in Active NameNode and Standby NameNode

failover controller를 실행하기 전 Active NameNode에서 fail over controler를 초기화(format)를 실행 한다.

Command: hdfs zkfc -formatZK

프로세스 실행 로그를 통해 정상완료 되었음을 확인할 수 있다.

 

- Start the ZKFC in Active NameNode

Command: hdfs zkfc -formatZK

jps 명령어를 통해 DFSZKFailoverController가 정상가동 되었음을 확인할 수 있다. 동일한 명령어로 nn02서버에서 실행한다.

 

5. Check the status of NameNode

- Check the status using terminal

Command: hdfs haadmin -getServiceState nn1

 

- Check the status using terminal

Command: http://202.xxx.xxx.51:53070

http://nn01:53070 접속화면을 통해 nn01 서버의 NameNode가 Active 상태로 가동 중임을 확인할 수 있다.

http://nn02:53070 접속화면을 통해 nn02 서버의 NameNode가 Standby 상태로 가동 중임을 확인할 수 있다.

 

접속화면에서 DataNode에 대해 정상가동 중임을 확인할 수 있다.

  

- Check the automatic fail over

Zookeeper에 의한 Automatic fail over 기능의 정상여부를 확인하기 위해 Active NameNode를 종료 후 Standby NameNode가 Active로 승격되는지 확인한다.

 

nn01 서버에서 Active로 가동 중인 NameNode를 강제 종료한다.

 

이후 nn02 서버의 접속화면에서 Standby 모드에서 Active 모드로 전환됨을 확인할 수 있다.

 

nn02 서버에서 zkfc 프로세스의 로그내용에서 기존 Active NameNode와의 접속오류를 감지하고 Standby NameNode를 Active로 전환되었음을 확인할 수 있다.

 

 

6. 참고사항

HDFS의 환경설정이 끝나고 JournalNode를 실행하면서 오류가 발생했다.

[오류내용]

[root@88f31c6d3118 sbin]# hadoop-daemon.sh start journalnode

starting journalnode, logging to /usr/local/hadoop/logs/hadoop--journalnode-88f31c6d3118.out

Error: Could not find or load main class org.apache.hadoop.hdfs.qjournal.server.JournalNode

[원인]

장애원인을 확인한 결과 이유를 알 수 없었지만, /usr/local/hadoop/share/ 디렉토리에 /hadoop 경로가 사라져서 프로세스 실행에 실패하였다.

[해결]

해당 경로로 /hadoop 디렉토리를 옮겨주고 재실행 결과 정상가동 되었다.

 

시리즈 내용은 아래 링크를 통해 확인해주세요.

 

[첫번째 이야기] 빅데이터란?

[두번째 이야기] 데이터 분산처리 시스템, Hadoop

[네번째 이야기] Hadoop 장애 Test