Hadoop1 시절에는 NameNode가 SPOF(SinglePointOfFailure)였음.

그때도 체크포인팅 주기를 조절하는 등 나름대로의 FailOver 노력이 있었으나,

Hadoop2 부터는 정식으로 고가용성(HighAvailability)을 보장하는 세팅이 가능함.


아래는 QJM(QuorumJournalManager)로 HA를 구성한 HDFS 구성도. (YARN관련은 제외)








각 프로세스별 동작은 아래와 같음.


Zookeeper는 네임서비스별 active/standby NameNode의 정보를 저장.


DFSZKFailoverController는 NameNode를 모니터링 하고 있다가 active NameNode가 죽으면,

standby NameNode를 active로 전환시키고 죽은 active NameNode를 클러스터에서 뽑아내고,

Zookeeper에 정보를 갱신.


JournalNode는 namespace가 변경될 때 발생하는 edit log를 저장함. 

최소 3대 이상 홀수로 실행되어야 하며, (N/2)+1 이상의 JournalNode가 살아있어야 정상동작이 보장됨.


NameNode(active)는 edit log를 JournalNode에 기록. (active만 기록 가능)


NameNode(standby)는 JournalNode에서 edit log를 읽어와 fsImage 갱신.


DataNode는 active/standby NameNode 모두에 Block 정보와 HeartBeat 보냄.






NameNode HA 구성 예제.


기존 구성이 아래와 같다고 가정함. (YARN 관련 프로세스 제외)

server01: NameNode

server02: SecondaryNameNode, DataNode

server03: DataNode

server04: DataNode

server05: DataNode


HA 구성은 아래처럼 할 예정. (YARN 관련 프로세스 제외)

server01: QuorumPeerMain(zookeeper), JournalNode, DFSZKFailoverController, NameNode(active)

server02: QuorumPeerMain(zookeeper), JournalNode, DFSZKFailoverController, NameNode(standby)

server03: QuorumPeerMain(zookeeper), JournalNode, DataNode

server04: DataNode

server05: DataNode


신규설치가 아니라 기존 non-HA NameNode를 HA NameNode로 컨버팅하는 예제임에 주의.

그래봐야 hdfs namenode -format 정도가 대체되는 수준이지만..


또 하나 주의할 점은 Failover 상황시 standby NameNode가 active NameNode로 전환되면서 이 서버 저 서버 집적거려야 하므로,

server02 도 클러스터 내부 서버들을 인증없이 ssh 로 들락거릴 수 있도록 세팅해야 함. (server01은 이미 되어있을테고)


우선 zookeeper 설치.

server01 에서 아래처럼 설치.

// 다운로드

[root@server01]# cd /home

[root@server01]# wget http://mirror.apache-kr.org/zookeeper/zookeeper-3.4.6/zookeeper-3.4.6.tar.gz


// 압축해제, 설정

[root@server01]# tar xvfz zookeeper-3.4.6.tar.gz

[root@server01]# mv zookeeper-3.4.6 zookeeper

[root@server01]# cd zookeeper

[root@server01]# cp conf/zoo_sample.cfg conf/zoo.cfg

[root@server01]# vi conf/zoo.cfg


// 아래처럼 설정하고 저장

;;zoo.cfg

tickTime=2000

initLimit=10

syncLimit=5

dataDir=/home/zookeeper/data

clientPort=2181

maxClientCnxns=0

maxSessionTimeout=180000

server.1=server01:2888:3888

server.2=server02:2888:3888

server.3=server03:2888:3888


// 데이터 디렉토리 생성, myid 세팅

[root@server01]# mkdir -p /home/zookeeper/data

[root@server01]# echo 1 > /home/zookeeper/data/myid


// 여기까지 한 다음 /home/zookeeper를 압축해서 scp 등으로 server02, server03 에 전송

// 그리고 server02, server03 서버에서 /home/zookeeper/data/myid 파일 내용을 각각 2, 3으로 저장

[root@server02] # echo 2 > /home/zookeeper/data/myid

[root@server03] # echo 3 > /home/zookeeper/data/myid


// 이제 server01~03 에서 zookeeper 서버 실행

[root@server01]# /home/zookeeper/bin/zkServer.sh start

[root@server02]# /home/zookeeper/bin/zkServer.sh start

[root@server03]# /home/zookeeper/bin/zkServer.sh start


// jps 실행해서 QuorumPeerMain이 떠있으면 성공.

[root@server01]# jps

25051 QuorumPeerMain


// 서버 상태 확인 (Mode가 leader 아니면 follower로 나올것임)

[root@server01]# /home/zookeeper/bin/zkServer.sh status

JMX enabled by default

Using config: /home/zookeeper/bin/../conf/zoo.cfg

Mode: follower



이제 Hadoop 세팅.


Hadoop은 /home/hadoop 경로에 설치되어 있다고 가정함.


NameNode HA 에서는 SecondaryNameNode란 게 사라지므로,

/home/hadoop/etc/hadoop 경로의 설정파일 중 masters 파일은 이제 쓸모없게 됨.


나머지 설정정보는 그대로 두고 아래 파일들만 설정을 바꿔주면 됨.


core-site.xml

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
    <!-- 기본 파일시스템 명 -->
    <property>
        <name>fs.defaultFS</name>
        <value>hdfs://my-hadoop-cluster</value>
    </property>
    <!-- zookeeper 서버 리스트 -->
    <property>
        <name>ha.zookeeper.quorum</name>
        <value>server01:2181,server02:2181,server03:2181</value>
    </property>
</configuration>




hdfs-site.xml (설정할 게 좀 되므로 HA와 관계없는 설정들은 제외함)

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
    <!-- 저널노드가 edit logs를 저장할 경로 -->
    <property>
        <name>dfs.journalnode.edits.dir</name>
        <value>/home/hadoop/data/dfs/journalnode</value>
    </property>
    <!-- 
        네임서비스 리스트. 콤마(,)로 구분하여 여러개 설정가능. 
        HDFS Federation 등을 위한 부분인데, 일단 core-site.xml에서 fs.defaultFS로 지정한 이름 하나만 지정함.
    -->
    <property>
        <name>dfs.nameservices</name>
        <value>my-hadoop-cluster</value>
    </property>
    <!-- my-hadoop-cluster 네임서비스의 NameNode ID -->
    <property>
        <name>dfs.ha.namenodes.my-hadoop-cluster</name>
        <value>nn1,nn2</value>
    </property>
    <!-- nn1 NameNode의 RPC 포트 -->
    <property>
        <name>dfs.namenode.rpc-address.my-hadoop-cluster.nn1</name>
        <value>server01:8020</value>
    </property>
    <!-- nn2 NameNode의 RPC 포트 -->
    <property>
        <name>dfs.namenode.rpc-address.my-hadoop-cluster.nn2</name>
        <value>server02:8020</value>
    </property>
    <!-- nn1 NameNode의 Web UI 포트 -->
    <property>
        <name>dfs.namenode.http-address.my-hadoop-cluster.nn1</name>
        <value>server01:50070</value>
    </property>
    <!-- nn2 NameNode의 Web UI 포트 -->
    <property>
        <name>dfs.namenode.http-address.my-hadoop-cluster.nn2</name>
        <value>server02:50070</value>
    </property>
    <!-- NameNode가 edit log를 쓰고 읽을 JournalNode URI -->
    <property>
        <name>dfs.namenode.shared.edits.dir</name>
        <value>qjournal://server01:8485;server02:8485;server03:8485/my-hadoop-cluster</value>
    </property>
    <!-- HDFS 클라이언트가 active NameNode에 접근할 때 사용되는 Java class -->
    <property>
        <name>dfs.client.failover.proxy.provider.my-hadoop-cluster</name>
        <value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value>
    </property>
    <!-- Failover 상황에서 기존 active NameNode를 차단할 때 사용할 방법 지정 -->
    <property>
        <name>dfs.ha.fencing.methods</name>
        <value>sshfence</value>
    </property>
    <!-- 
        ha.fencing.method를 sshfence로 지정했을 경우 ssh를 경유해 기존 active NameNode를 죽이는데,
        이때 passphrase를 통과하기 위해 SSH private key file을 지정해야 함.
     -->
    <property>
        <name>dfs.ha.fencing.ssh.private-key-files</name>
        <value>/home/USERNAME/.ssh/id_rsa</value>
    </property>
    <!-- 장애복구를 자동으로 한다고 지정 -->
    <property>
        <name>dfs.ha.automatic-failover.enabled</name>
        <value>true</value>
    </property>
</configuration>



여기까지 설정하고 설정파일 압축해서 서버 전체에 배포.


이제 본격적으로 실행.


// zookeeper 초기화

[root@server01]# /home/hadoop/bin/hdfs zkfc -formatZK


// 초기화 확인을 위해 zookeeper 접속

[root@server01]# /home/zookeeper/bin/zkCli.sh


// /hadoop-ha 아래에 dfs.nameservices 지정한 nameserviceID 노드가 있으면 성공.

ls /hadoop-ha

[my-hadoop-cluster]

// 빠져나오자

[zk: localhost:2181(CONNECTED) 1] quit



// server01~03 에서 JournalNode 실행

[root@server01]# /home/hadoop/sbin/hadoop-daemon.sh start journalnode

[root@server02]# /home/hadoop/sbin/hadoop-daemon.sh start journalnode

[root@server03]# /home/hadoop/sbin/hadoop-daemon.sh start journalnode


// 저널 초기화

[root@server01]# /home/hadoop/bin/hdfs namenode -initializeSharedEdits


// active NameNode 실행

[root@server01]# /home/hadoop/sbin/hadoop-daemon.sh start namenode


// active NameNode용 ZKFC 실행

[root@server01]# /home/hadoop/sbin/hadoop-daemon.sh start zkfc


// 확인

[root@server01]# jps

1096 NameNode

1650 DFSZKFailoverController

1456 JournalNode

25051 QuorumPeerMain


// 전체 DataNode 실행 (server03 ~ 05)

[root@server03]# /home/hadoop/sbin/hadoop-daemon.sh start datanode

[root@server04]# /home/hadoop/sbin/hadoop-daemon.sh start datanode

[root@server05]# /home/hadoop/sbin/hadoop-daemon.sh start datanode


// server02에서 standby NameNode 준비

[root@server02]# /home/hadoop/bin/hdfs namenode -bootstrapStandby


// standby NameNode 실행

[root@server02]# /home/hadoop/sbin/hadoop-daemon.sh start namenode


// standby NameNode용 ZKFC 실행

[root@server02]# /home/hadoop/sbin/hadoop-daemon.sh start zkfc


// 여기까지 하면 NameNode HA 설정완료.


// 이제 나머지 YARN, JobHistoryServer를 실행

[root@server01]# /home/hadoop/sbin/start-yarn.sh

[root@server01]# /home/hadoop/sbin/mr-jobhistory-daemon.sh start historyserver




여기까지 하면 완료.

잘 돌아가는지 확인하고 싶으면 http://server01:50070 혹은 http://server02:50070 으로 접속해도 되고,

아래처럼 haadmin 명령어로 확인도 가능함.

// 각 NameNode 상태확인

[root@server01]# /home/hadoop/bin/hdfs haadmin -getServiceState nn1

active


[root@server01]# /home/hadoop/bin/hdfs haadmin -getServiceState nn2

standby






이제 진짜 Failover가 되는지 테스트.

// NameNode의 active/standby 상태는 위처럼 nn1이 active, nn2가 standby라고 가정.


// active NameNode를 죽여보자.

[root@server01]# jps

1456 JournalNode

25051 QuorumPeerMain

1096 NameNode

1650 DFSZKFailoverController

26058 JobHistoryServer

1911 ResourceManager


[root@server01]# kill -9 1096


// active NameNode가 죽은 후 standby였던 nn2의 상태를 확인해보면 active로 바뀐 걸 확인할 수 있음.

[root@server01]# /home/hadoop/bin/hdfs haadmin -getServiceState nn2

active


// 기타 나머지 dfs 관련 명령어도 아무 문제 없이 잘 되는 것을 확인해 보자.


// 이제 죽었던 namenode를 다시 살리고

[root@server01]# /home/hadoop/sbin/hadoop-daemon.sh start namenode


// 죽었다 살아난 namenode는 이제 standby

[root@server01]# /home/hadoop/bin/hdfs haadmin -getServiceState nn1

standby








ps. 혹시나 삽질할 사람이 있을까 걱정되어 사족을 남기는데, 내 경험에 의하면 Hadoop 1.0.0 에서 2.6.0 으로 업그레이드 할 때 한방에 HA로는 안되었음.

일단 non-HA 로 업그레이드하고 나서 HA 로 컨버팅하는 것을 권장함.

나는 어떻게든 되지 않을까 싶어서 삽질하다가 시간만 옴팡지게 낭비했음.




[참고]

https://hadoop.apache.org/docs/r2.6.0/hadoop-project-dist/hadoop-hdfs/HDFSHighAvailabilityWithQJM.html











Posted by bloodguy
,