2020년 11월 30일 월요일

Apache Hadoop 3.1.4 HA(High Availability) Install Guide with Cloudera guide

과거에 개인적으로 하둡 2~3 버전을 완전분산 모드로 설치를 해보면서 확실히 크게 달라진 점은 없었다. 당시에는 HA구성을 할 필요가 없었는데 이번에 HA 구성을 해보려고 한다.
하지만 프로덕션 환경에서 설치하기위해서 최적의 구성을 생각했어야했는데 개인적으로 생각한 내용과 클라우데라에서 제안한 아키텍처를 보더라도 크게 차이는 없었다.


출처 : 클라우데라 제안 구성

여기서 상황에 맞게 필요한 것만 구성을 하고 결론은 마스터 3대, 배치서버1대, 데이터노드 N대로 구성하고 다음과 같은 룰을 따르고자 한다.

리소스 매니저는 Yarn을 한번 올려보고.. 실제로 사용은 Mesos로 컨트롤 할 것이다. Mesos의 장점이 하둡 클러스터의 구간을 나눠놓고 1번 메소스, 2번 메소스, 3번 메소스 클러스터로 분리운영이 가능한데 이는 서버가 장애가 날때, 혹은 배치들을 분리했을 때 운영상 장점이 있기 때문이다.

대략적인 구조는 이런식으로 가져간다.


마스터1 : 주키퍼 저널노드 네임노드(HA) ZKFC 리소스매니저(HA) 메소스마스터

마스터2 : 주키퍼 저널노드 네임노드(HA) ZKFC 리소스매니저(HA) 메소스마스터

마스터3 : 주키퍼 저널노드 잡히스토리서버 스파크히스토리서버 메소스마스터

슬레이브서버들 : 데이터노드 메소스슬레이브


사실 yarn을 쓸 일은 거의 없긴한데 일단 sqoop이나 다른 프레임워크들과 etl을 하면서 필요할 일이 있으니 넣어두고 ha를 굳이 안해도되는데 간단하니까 해보자. 왠만하면 최소화로 가져가야 서버를 리부팅할때 올려야할 서비스를 빼먹지 않기 때문에 간소화하는 것을 좋아한다.

설치는 자바설치, /etc/hosts, 인증키 배포 등 기본적으로 세팅해놓아야 할 설정들은 모두 되었다고 가정을 한다.


먼저 마스터 3대에 주키퍼를 설치한다. (3.6.2 설치함.)
zoo_sample.cfg를 복사해서 zoo.cfg를 만들고
dataDir=path
server.1=master1:2888:3888
server.2=master2:2888:3888
server.3=master3:2888:3888
적당한 곳으로 세팅한다.
다 되었으면 주키퍼를 띄운다. (./zkServer.sh start)

다음은 바로 하둡이다.
압축을 풀고 
tar -zxvf hadoop-3.1.4.tar.gz
링크를 걸고
ln -s hadoop-3.1.4 hadoop
필요한 환경세팅을 세팅한다.



기존에는 slaves, masters로 나뉘어져있던 파일이 /etc/workers로 통합이 되었고 여기에 datanode가 올라갈 서버들을 적는다.

각각의 서버는 data01, appData01~04로 구성이 되어있고 하둡은 data01에 두고 log나 data를 쓰는 부분은 appData01로 구성한다. 하둡 같은 경우는 os부분만 레이드1(미러링) 하고 나머지는 솔직히 안해도 된다고 본다. 어차피 3복제이고 하둡을 다시 올리는데 큰 리소스가 안들기 때문이다.

그럼 설정파일을 작성하자.
하둡 설정파일들이 default로라도 되어있으면 좋은데 그냥 깡통이라서 사실 하나씩 쓰기가 너무 괴롭다..

hadoop-env.sh에 몇몇 필요한 경로 세팅을 하고 저장한다.
주석을 풀고 커스터마이징해서 작성하면 된다.
HADOOP_CONF_DIR
HADOOP_HEAPSIZE_MAX
HADOOP_WORKERS
HADOOP_LOG_DIR
HADOOP_PID_DIR
HADOOP_SECURE_PID_DIR


다음은 hdfs-site.xml이다.
<configuration>
        <!-- for NameNode -->
        <property>
                <name>dfs.namenode.name.dir</name>
                <value>/appData01/hadoop/hdfs/name,/appData02/hadoop/hdfs/name,/appData03/hadoop/hdfs/name,/appData04/hadoop/hdfs/name</value>
        </property>

        <!-- for DataNode -->
        <property>
                <name>dfs.datanode.data.dir</name>
                <value>/appData01/hadoop/hdfs/data,/appData02/hadoop/hdfs/data,/appData03/hadoop/hdfs/data,/appData04/hadoop/hdfs/data,/appData05/hadoop/hdfs/data</value>
        </property>

        <!-- HA -->
        <property>
                <name>dfs.nameservices</name>
                <value>hadoop-cluster-bdnode</value>
        </property>
        <property>
                <name>dfs.ha.namenodes.hadoop-cluster-bdnode</name>
                <value>namenode1,namenode2</value>
        </property>
        <property>
                <name>dfs.namenode.rpc-address.hadoop-cluster-bdnode.namenode1</name>
        <value>master1:8020</value>
        </property>
            <property>
                    <name>dfs.namenode.rpc-address.hadoop-cluster-bdnode.namenode2</name>
                        <value>master2:8020</value>
            </property>
        <property>
                <name>dfs.namenode.http-address.hadoop-cluster-bdnode.namenode1</name>
                <value>master1:50070</value>
        </property>
        <property>
                <name>dfs.namenode.http-address.hadoop-cluster-bdnode.namenode2</name>
                <value>master2:50070</value>
        </property>
        <property>
                <name>dfs.namenode.shared.edits.dir</name>
                <value>qjournal://master1:8485;master2:8485;master3:8485/hadoop-cluster-bdnode</value>
        </property>
        <property>
                <name>dfs.client.failover.proxy.provider.hadoop-cluster-bdnode</name>
                <value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value>
        </property>
        <property>
                <name>dfs.ha.fencing.methods</name>
                <value>sshfence</value>
        </property>
        <property>
                <name>dfs.ha.fencing.ssh.private-key-files</name>
                <value>/home/scom/.ssh/id_rsa</value>
        </property>
        <property>
                <name>dfs.journalnode.edits.dir</name>
                <value>/appData01/hadoop/hdfs/journalnode</value>
        </property>
        <property>
                <name>dfs.ha.automatic-failover.enabled</name>
                <value>true</value>
        </property>
        <property>
                <name>dfs.blocksize</name>
                <value>268435456</value>
        </property>
        <property>
                <name>dfs.namenode.handler.count</name>
                <value>100</value>
        </property>
        <property>
                <name>dfs.hosts</name>
                <value>/data01/sw/hadoop/etc/hadoop/workers</value>
        </property>
        <property>
                <name>dfs.webhdfs.enabled</name>
                <value>true</value>
        </property>
        <property>
                <name>dfs.namenode.datanode.registration.ip-hostname-check</name>
                <value>false</value>
        </property>

        <property>
                <name>dfs.datanode.du.reserved</name>
                <!-- cluseter variant 90G -->
                <value>96636764160</value>
                <description>Reserved space in bytes per volume. Always leave this much space free for non dfs use.</description>
        </property>
</configuration>


다음은 core-site.xml이다.
<configuration>
        <property>
                <name>fs.defaultFS</name>
                <value>hdfs://hadoop-cluster-bdnode</value>
        </property>
        <property>
                <name>ha.zookeeper.quorum</name>
                <value>master1:2181,master2:2181,master3:2181</value>
        </property>
        <!-- trash -->
        <property>
                <name>fs.trash.interval</name>
                <value>14400</value>
                <description>Number of minutes after which the checkpoint gets deleted. If zero, the trash feature is disabled. </description>
        </property>
        <!-- for HUE-->
        <property>
                <name>hadoop.proxyuser.hue.hosts</name>
                <value>*</value>
        </property>
        <property>
                <name>hadoop.proxyuser.hue.groups</name>
                <value>*</value>
        </property>
</configuration>


다음은 yarn-site.xml 이다.
<configuration>

<!-- Site specific YARN configuration properties -->
        <property>
                <name>yarn.nodemanager.aux-services</name>
                <value>mapreduce_shuffle</value>
        </property>
        <property>
                <name>yarn.nodemanager.aux-services.mapreduce.shuffle.class</name>
                <value>org.apache.hadoop.mapred.ShuffleHandler</value>
        </property>
        <property>
                <name>yarn.nodemanager.local-dirs</name>
                <value>/appData01/hadoop/yarn/nm-local-dir,/appData02/hadoop/yarn/nm-local-dir,/appData03/hadoop/yarn/nm-local-dir,/appData04/hadoop/yarn/nm-local-dir</value>
        </property>
        <property>
                <name>yarn.resourcemanager.address.rm1</name>
                <value>master1:8032</value>
        </property>
        <property>
                <name>yarn.resourcemanager.webapp.address.rm1</name>
                <value>master1:8088</value>
        </property>
        <property>
                <name>yarn.resourcemanager.scheduler.address.rm1</name>
                <value>master1:8030</value>
        </property>
        <property>
                <name>yarn.resourcemanager.resource-tracker.address.rm1</name>
                <value>master1:8031</value>
        </property>
        <property>
                <name>yarn.resourcemanager.admin.address.rm1</name>
                <value>master1:8041</value>
        </property>

        <property>
                <name>yarn.resourcemanager.address.rm2</name>
                <value>master2:8032</value>
        </property>
        <property>
                <name>yarn.resourcemanager.webapp.address.rm2</name>
                <value>master2:8088</value>
        </property>
        <property>
                <name>yarn.resourcemanager.scheduler.address.rm2</name>
                <value>master2:8030</value>
        </property>
        <property>
                <name>yarn.resourcemanager.resource-tracker.address.rm2</name>
                <value>master2:8031</value>
        </property>
        <property>
                <name>yarn.resourcemanager.admin.address.rm2</name>
                <value>master2:8041</value>
        </property>

        <property>
                <name>yarn.resourcemanager.fs.state-store.uri</name>
                <value>/appData01/hadoop/yarn/system/rmstore,/appData02/hadoop/yarn/system/rmstore,/appData03/hadoop/yarn/system/rmstore,/appData04/hadoop/yarn/system/rmstore</value>
        </property>
        <property>
                <name>yarn.resourcemanager.hostname.rm1</name>
                <value>master1</value>
        </property>
        <property>
                <name>yarn.resourcemanager.hostname.rm2</name>
                <value>master2</value>
        </property>

        <!-- configure yarn -->
        <property>
                <name>yarn.nodemanager.resource.cpu-vcores</name>
                <value>10</value>
        </property>
        <property>
                <name>yarn.nodemanager.resource.memory-mb</name>
                <value>40960</value>
        </property>
        <property>
                <name>yarn.scheduler.minimum-allocation-mb</name>
                <value>1024</value>
        </property>
        <property>
                <name>yarn.scheduler.maximum-allocation-mb</name>
                <value>24576</value>
        </property>

        <!-- ResourceManager 시작시 state 복구여부 --> 
        <property> 
                 <name>yarn.resourcemanager.recovery.enabled</name> 
                 <value>true</value> 
        </property> 
        <!-- persistent store로 사용할 class --> 
        <property> 
                 <name>yarn.resourcemanager.store.class</name> 
                 <value>org.apache.hadoop.yarn.server.resourcemanager.recovery.ZKRMStateStore</value> 
        </property> <!-- Zookeeper 서버 리스트 --> 
        <property> 
                 <name>yarn.resourcemanager.zk-address</name> 
                 <value>master1:2181,master2:2181,master3:2181</value> 
        </property>
        <property>
                <name>yarn.resourcemanager.ha.enabled</name>
                <value>true</value>
        </property>

        <!-- ResourceManager가 leader election에 참가할 cluster 이름 지정 -->
        <property>
                <name>yarn.resourcemanager.cluster-id</name>
                <value>rm-cluster</value>
        </property>
        <!-- cluster에서 HA를 구성할 ResourceManager id 지정 -->
        <property>
                <name>yarn.resourcemanager.ha.rm-ids</name>
                <value>rm1,rm2</value>
        </property>
</configuration>


다음은 mapred-site.xml이다
<configuration>
        <property>
                <name>mapreduce.framework.name</name>
                <value>yarn</value>
        </property>

        <!-- mapreduce2 memory setting -->
        <property>
                <name>yarn.app.mapreduce.am.resource.mb</name>
                <value>1536</value>
        </property>
        <property>
                <name>mapreduce.map.memory.mb</name>
                <value>4096</value>
        </property>
        <property>
                <name>mapreduce.reduce.memory.mb</name>
                <value>8192</value>
        </property>

        <!-- mapreduce java memory setting -->
        <property>
                <name>mapreduce.map.java.opts.max.heap</name>
                <value>3276</value>
        </property>
        <property>
                <name>mapreduce.reduce.java.opts.max.heap</name>
                <value>6553</value>
        </property>

                <property>
                <name>yarn.app.mapreduce.am.resource.mb</name>
                <value>2048</value>
        </property>

                <property>
                                 <name>yarn.app.mapreduce.am.env</name>
                                 <value>HADOOP_MAPRED_HOME=/data01/sw/hadoop</value>
                </property>
                <property>
                                <name>mapreduce.map.env</name>
                            <value>HADOOP_MAPRED_HOME=/data01/sw/hadoop</value>
                </property>
                <property>
                                <name>mapreduce.reduce.env</name>
                                <value>HADOOP_MAPRED_HOME=/data01/sw/hadoop</value>
                </property>

</configuration>


여기까지 하면 하둡 파일은 다 작성이 되었고 이 디렉토리를 마스터2~3, 데이터노드들에 복사를 하자.

scp -r hadoop-3.1.4 ip:/data01/sw/ 


네임노드 HA는 주키퍼로 한 것인데 주키퍼는 가벼우면서도 참으로 편리하다.
zkfc 실행 전 포맷 한번 해주고
hdfs zkfc -formatZK

마스터 1~3번기에서 저널노드 실행
현재 : hdfs --daemon start journalnode 
과거 : hadoop-daemon.sh start journalnode

마스터 1번기에서 네임노드 초기화
hdfs namenode -format

마스터 1번기에서 네임노드 실행
현재 : hdfs --daemon start namenode 
과거 : hadoop-daemon.sh start namenode


마스터 1번기에서 zkfc 실행
현재 : hdfs --daemon start zkfc 
과거 : hadoop-daemon.sh start zkfc

이제 마스터 1번기가 active 상태가 되었다.
이제 마스터 2번기를 standby로 올리자.

마스터 2번기에서 스탠바이상태로 세팅
hdfs namenode -bootstrapStanby

마스터 2번기에서 스탠바이 네임노드 실행
hdfs --daemon start namenode 

마스터 2번기에서 zkfc 실행
hdfs --daemon start zkfc

이제 리소스매니저(yarn)을 올린다.
마스터 1번기에서 yarn --daemon start resourcemanager 실행.

마스터 3번기에서 jobhistory server 실행
현재 : mapred --daemon start historyserver
과거 : sbin/mr-jobhistory-daemon.sh start historyserver

마스터 2번기에서도 yarn을 올린다.
yarn --daemon start resourcemanager

마지막으로 데이터노드들에서 노드매니저를 올린다.
yarn --daemon start nodemanager

그림 작업은 끝이났다.
마스터1:50070 혹은 마스터2:50070에 접근을 해보면 active/standby를 확인할 수 있다.
테스트를 해보기 위해서 active namenode를 kill해보면 아직 사용을 안해서 거의 즉시 failover가 되기는 한다. 이제 배치에서도 네임스페이스를 활용하도록 하자!




네임노드 ha와 리소스매니저 ha가 끝이났다.
하둡이 버전이 올라가면서 가장 크게 바뀐 것은 아무래도 명령어가 바뀌고 workers 파일로 바뀌었다는것, 포트가 바뀌었다는 것, 내부적으로는 압축 효율이 바뀌었다는 점 정도인 것 같다. 그리고 ui가 많이 바뀌었다. 적응이 안된다.

만약에 설치를 하다가 제대로 안되면(특히 포맷) 해당 개인이 사용하려고 생성했던 디렉토리를 삭제하고 다시 해보도록 하자. 그 안에 파일들이 이미 만들어져서 사용되고 있기 때문에 안될 가능성이 높다.

아래 명령어로 네임노드 Active/Standby를 확인 가능하다. 한번만 쳐도 알아낼 수 있는 명령어가 있었으면 좋겠다.
hdfs haadmin -getServiceState namenode1
hdfs haadmin -getServiceState namenode2

리소스매니저 Active/Standby 상태 확인은 다음과 같다.
yarn rmadmin -getServiceState rm1
yarn rmadmin -getServiceState rm2

개인적으로 네임노드2대를 VIP로 사용하여 인터페이스를 해야하는 경우가 아니라면 딱히 VIP는 필요없어보이고 어차피 네임스페이스로 접근하여 해결하도록 하자.

댓글 4개:

  1. 안녕하세요

    좋은정보 감사드립니다.

    한가지 궁금한사항이 있는데 혹시 DataNode는 실행을 안하는 이유가 있을까요?

    답글삭제
    답글
    1. 방금확인했습니다~ 데이터노드 실행해야죠 ㅎㅎ 네임노드 올리고 데이터노드 올리면 됩니다

      삭제
  2. 너무 완벽한 튜토리얼 감사합니다
    혹시 종료의 순은 시작순의 역순이 될까요?
    안전하게 종료시 standby active노드의 종료 순을 알고싶습니다

    답글삭제
    답글
    1. 네 역순으로 킬하면 됩니다 데이터노드-마스터순, 노드매니저-리소스매니저 순. 하지만 굳이 순서를 안지킨다고해서 크리티컬한 상황이발생할 확률은 매우 적습니다 운영하다보면 한대씩 어떠한 이유로 내려갈때가 있기도 해요

      삭제

2022년 회고

 올해는 블로그 포스팅을 열심히 못했다. 개인적으로 지금까지 경험했던 내용들을 리마인드하자는 마인드로 한해를 보낸 것 같다.  대부분의 시간을 MLOps pipeline 구축하고 대부분을 최적화 하는데 시간을 많이 할애했다. 결국에는 MLops도 데이...