해당글은 Naver Japan의 기술 블로그에 있는 다음 글(http://tech.naver.jp/blog/?p=1420 ) 발번역 하였습니다.
개인적으로 Line의 이런 구조에 대해서 미리 얘기를 들었긴 했지만, 이렇게 멋진 글로 나올줄은 몰랐네요.
그냥 처음부터 한글로도 적어주시지. 글 쓰신분이 일본분이시라, 영어로만 적으셨나 봅니다. T.T
그리고, Appendix 부분은 대부분 설정내용이라 번역을 하지않고 삭제하였습니다.
원본을 참고하시기 바랍니다.
안녕하세요. 저는 Shunsuke Nakamura(@sunsuk7tp) 입니다.
불과 반년 전에 저는 도쿄텍에서 컴퓨터 사이언스 석사학위를 따고 NHN Japan의 Line 서버 팀으로 입사하였습니다. 저의 목표는 분산 처리와 스토리지 시스템에 대해서 잘 파악하고, 차세대 아키텍처를 개발하는 것이었습니다.
Line 서버 팀에서, Line의 주소록, 그룹, 메시지를 저장하는 스토리지 시스템을 개발하고 관리하는 역할을
맡았습니다. 이제, LINE의 스토리지 스택을 간단히 소개할려고 합니다.
LINE Beginning with Redis [2011.6 ~]
최초에는 LINE의 메인 스토리지에 Redis를 적용했습니다.
LINE은 총 2011년에 백만명의 유저가 사용할 정도로 스케일 되어야 하고, 빠르게 메시지를 주고 받는 메신저를 목표로 했습니다. Redis는 인메모리 데이터 스토어로 그 작업에 적합했습니다.
Redis는 정기적으로 디스크에 스냅샷을 남기는 것도 가능했습니다. 그리고 sync/async 한 master/slave replication도 제공했습니다. 우리는 Redis가 인메모리 데이터 스토어기 때문에, 확장성과 이용성에 문제가 있을 수도 있지만, 최고의 선택이라고 생각했습니다.
그래서 클라이언트 쪽에서 샤딩하기로 하고, 3대의 노드로 구성된 하나의 Redis 클러스터를 LINE의 스토리지 시스템으로 사용하기 시작했습니다.(역자주: 클러스터가 하나면 뭐 -_- 샤딩이고 뭐시기고 없습니다만…)
서비스의 규모가 커지면서, 더 많은 노드가 필요하게 되고, 클라이언트 쪽 샤딩은 효과적인 서비스 확장을
방해했습니다. 원래 Redis는 서버에서의 샤딩을 지원하지 않기 때문에, 우리는 redis 클러스터를 관리할 수
있는 클러스 터 매니저를 개발했습니다. 우리의 샤드된 Redis 클러스터는 클러스터 매니저 데몬과
Zookeeper Quorum 서버로 관리됩니다.
클러스터 매니저는 다음과 같은 특성이 있습니다.
- Zookeeper에 의해서 샤딩을 관리(Consistent Hashing이나 이와 비슷한 다른 알고리즘)
- 마스터/슬레이브 간의 장애 발견과 자동/수동의 FailOver
- 최소의 장애시간안에 확장되어야 한다.(10초 이내)
현재, 샤딩된 여러개의 Redis 클러스터는 수백대의 서버로 구성되어 있다.
샤딩된 Reids 클러스터와 관리 툴
Tolerance Unpredictable Scaling [2011.10 ~]
그러나, 2011년 10월 근처 부터 크게 상황이 바뀌면서 LINE은 세계 여러군데서 엄청난 성장을 경험하게
되었습니다. 그리고 운영비용 역시 증가했습니다. 증가하는 비용의 주된 원인은 Redis 클러스터의 용량
때문이었습니다. 예측 불가능한 정도로 확장이 커지면서, Redis 클러스터를 안정적으로 운영하는 것은
더욱 더 힘들어졌습니다.
인메모리 데이터 저장소의 특성상 다른 퍼시스턴스 스토리지보다 더 많은 서버가 필요하기 때문입니다.
(역자 주: 디스크를 여러 개 추가하면 하드 디스크 용량을 늘리기 싶지만, 메모리를 많이 설치하기는 힘듭니다. 몇백 GB정도도 설치할 수 있지만 이러면 비용이 장난 아닙니다.)
안정적으로 가용성을 가지기 위해서, 스냅샷과 전체에 대한 리플리케이션 기능이 적절히 메모리를 관리하기 위해서 필요합니다. Redis VM(Virtual Memory) 기능이 도움이 될 수도 있지만, VM의 사용은 성능을 엄청나게 떨어뜨립니다. (역자주: VM을 쓰면 넘는 부분을 디스크에 저장하므로 결국 Swap 쓰는 것과 별반 차이가 없습니다. 이렇게 스왑을 쓸거면 차라리 MMAP을 사용해서 OS에게 적절히 맡기는 것이 최근에는 더 좋은 성능을 내는 방법입니다.)
이런 이유로, 우리는 종종 Scale out 해야할 시기를 잘못 판단하고,
성능이상의 과부하와 맞닥트리게 됩니다.
결국 더 큰 확장성과 높은 가용성을 가지는 시스템으로 이전하는 것이 큰 문제가 되었습니다.
결국, LINE의 목표는 수천만명에서 수억 명의 유저 규모의 스케일을 가지는 것으로 변경되었습니다.
이것은 어떻게 해당 문제와 씨름했는지를 보여줍니다.
Data Scalability
처음에 우리는 각 데이터베이스의 규모를 분석했습니다.
(n: # of Users)
(t: Lifetime of LINE System)
- O(1)
- 전송 큐의 메시지
- 잡 큐의 비동기 작업
- O(n)
- 유저 프로필
- 주소록 / 그룹
- 해당데이터는 O(n^2) 로 증가한다. 그러나 유저간 연결된 수에 따라 제한된다.(= O (n * CONSTANT_SIZE))
- O(n*t)
- Inbox의 메시지들
- 유저 프로필 / 그룹 / 주소록의 데이터 변경
LINE 스토리지에 저장되는 레코드 수는 기하급수적으로 증가했었습니다.
가까운 미래에는 한달에 수백억개의 레코드를 다루게 될것 같습니다.
Data Requirement
두번째로, 각각의 사용 시나리오에 따른 데이터 요구사항을 정리했습니다.
- O(1)
- 가용성
- Workload: 빠른 읽기와 쓰기 작업
- O(n)
- 가용성, 확장성
- Workload: 빠른 랜덤 읽기
- O(n*t)
- 확장성, 대용량 볼륨 (하루에 수억 개의 작은 로우가 생기지만 대부분은 자주 접근하는 데이터가 아님)
- Workload: 빠른 연속적인 쓰기(append-only) 와 최근 데이터에 대한 빠른 읽기
Choosing Storage
각 스토리지에 대한 위의 요구 사항에 따라, 결국, 적합한 스토리지를 결정했습니다.
기준중의 하나로 각각의 스토리지의 특성을 설정하고, 어떤 스토리작 LINE app의 워크로드에 가장
적합한가를 결정했습니다. 몇개의 후보군들에 대해서 YCSB 나 직접 벤치마크를 수행했습니다.
그 결과, 메시지 타임 라인처럼 급격하게 증가하는 패턴을 보이는 데이터를 저장하기 위해서 hbase를
메인 스토리지로 사용하기로 결정했습니다. hbase의 특성은 메시지 타임라인에 적합합니다.
메시지 타임라인은 최신 데이터에 집중되고, 최근에 삽입된 데이터가 상위가 배치됩니다.
- O(1)
- Redis가 최선의 선택입니다.
- O(n), O(n*t)
- 몇가지 대안이 있습니다.
- HBase
- 장점:
- 요구사항에 가장 부합함
- 관리가 쉬움 ( 분산 파일 시스템위에 스토리지가 구축되어 있고, 서버마다 데이터가 ad hoc 하게 파티션되어있음)
- 단점:
- 랜덤 읽기와 삭제가 느림.
- 좀 낮은 가용성 (SPOF 도 약간 존재.)
- 장점:
- Cassandra (
내가 가장 좋아하는 NoSQL)- 장점:
- 최근 데이터를 다루는데 적합함
- 높은 가용성 (비중앙집중적 아키텍처(P2P), rack/DC 를 고려한 리플리케이션)
- 단점:
- 낮은 일관성으로 인한 운영 비용 증가
- 카운터 증가가 좀 느림.
- 장점:
- MongoDB
- 장점:
- Auto sharding, auto failover
- 많은 명령어 (그러나 LINE 스토리지에는 이런게 필요없음)
- 단점:
- 타임라인의 워크로드에 부적합함 (B-tree indexing)
- 디스크와 네트웍 사용이 비효과적임
- 장점:
요약하면, LINE 스토리지 레이어는 현재 다음과 같다.
- Standalone Redis: 비동기 작업과 메시지 큐잉용도
- Redis 큐와 큐 디스패처는 같은 서버에서 함께 동작합니다.(역자 주: 아마도 이미지에서 처럼 이런 구조가 된 것은, 이 단계에서 실패하면 그냥 전달 실패가 되는 그 영역일꺼 같습니다. 그리고 큐에 일단 넣어놓고 비동기적으로 메인 저장소로 전달되지 않을까 하는 추측을 해봅니다.)
- 샤딩된 Redis: 데이터의 캐시를 위한 앞단으로 사용됨 O(n*t) 그리고 메인 저장소로 사용 O(n)
- 백업용 MySQL: 2차 스토리지 (백업과 통계용)
- HBase: 메인 스토리지 O(n*t)
- 각 클러스터 마다 수백 테라 규모의 데이터가 핸들링 되고 각각의 클러스터는 100대 규모에서 1000대 규모로 구성됩니다.
LINE의 메인 스토리지는 600대 정도로 구축되어 있으며, 매달 계속 늘어나고 있습니다.
LINE Storage Stack
Data Migration from Redis to HBase
우리는 점진적으로 수십테라의 데이터를 Redis 클러스터로 부터 Hbase 클러스터로 옮겼습니다.
3단계로 데이터를 옮겼는데,
- Redis 와 HBase 에 모두 써서 Redis에서만 읽음( 역자 주: 이것은 대부분 대용량 데이터를 옮기거나, 이전할 때, 필수적으로 하는 방식입니다. 처음부터 바로 바꾸는 건 거의 불가능 하시다고 보시면 됩니다. 최초에는 데이터를 비교해서 동일한 상태가 계속 유지되면, 읽기를 Hbase로 옮기고, 같이 운영하다가 그것도 안정적이다 싶으면 Redis를 제거할 수 있을껍니다.)
- 백엔드에서 이전 스크립트 실행 (순차적으로 Redis로 부터 HBase로 데이터를 이전합니다.)
- Redis (w/ TTL) 와 HBase (w/o TTL) 에 모두 쓰고, 양쪽에서 모두 읽음 (Redis 가 캐시서버를 대체)
최근 데이터를 옛날 데이터가 덮어씌우는 것을 피하기 위해서 이전되는 데이터는 append-only로만 저장되고, 다른 데이터의 일관성은 Hbase column의 timestamp를 이용해서 유지합니다.
HBase and HDFS
많은 HBase 클러스터가 HDFS 위에서 잘 돌아가고 있습니다.
우리는 메시지, 주소록등의 각각의 데이터베이스를 위해서 HBase 클러스터를 만들었고, 각각의 클러스터는
각 데이터베이스의 워크로드에 따라서 튜닝되었습니다. 각 HBase클러스터는 100대의 서버로 구성된
하나의 HDFS 클러스터를 공유하고 있고, 각각의 서버는 32GB의 메모리와 1.5TB의 하드 디스크를
가지고 있습니다. (역자 주: 아마도 Redis로 있던걸 HBase로 가서 디스크를 그렇게 많이 사용하지 않는듯 합니다.)
각 RegionServer는 10GB보다 큰 50개의 작은 Region을 가지고 있습니다.
Big-Table 형태의 아키텍처에서 Read 성능은 major compaction에 영향을 받습니다.
그래서 각각의 region의 크기를 너무 크지 않게 유지하는 것이, 특히 피크 타임에 더더욱, 지속적인 major compaction을 막을 수 있습니다. 피크 타임이 아닌동안에, 운영자들이 수동으로 로드밸런싱을 수행하는 동안에, 큰 region들을 주기적인 cron 잡을 통해서 자동적으로 작은 region으로 나누어줍니다.
물론, HBase는 자동으로 데이터를 나눠주는 기능과 로드 밸런싱 기능이 있습니다만, 서비스 요구사항면에서 수동으로 하는 것이 더 났다고 판단했습니다. 이런 서비스의 성장때문에, 확장성은 가장 중요한 이슈중의 하나입니다. 우리는 클러스터 마다 수백대의 서버가 있고, 각 메시지는 TTL 을 가지고 있으며, TTL 단위로 멀티 클러스터로 파티션 되어집니다. 그렇게 진행되는 동안에, 메시지의 TTL이 경과된 구 서버에서는 모든 데이터가 사라지고 새로운 클러스터로 사용되어집니다. (역자 주: 멀티 클러스터로 파티션 되니, 이렇게 특정 클러스터의 내용을 전부 지우더라도 문제가 되지 않을듯 합니다.)
Current and future challenges [2012]
HBase로 이동한 이래로, LINE 스토리지는 더더욱 안정적이 되었습니다.
아직도 스토리지 때문에 가끔씩 장애가 나긴 하지만, 각각의 HBase 클러스터는 현재 새해 피크 타임 정도의
리퀘스트를 여러 번 처리했습니다. 우리는 가용성을 위해 다음과 같은 HBase와 Redis 클러스터 이슈를
남겨두고 있습니다.
- 안정적인 설정과 SPOF를 가지지 않고 랙/데이터센터에 적합한 FailOver
- 우리는 Full Replication, HDFS 클러스터 간의 SSTable 이나 Block level Replication 등의 다양한 리플리케이션 레이어를 시험했습니다.
- 클러스터 간 장애에 대한 대응 (Redis cluster, HBase, and multi-HBase cluster)
HA-NameNode
다들 알고 있듯이 HDFS에서는 NameNode가 SPOF 입니다. NameNode 프로세스가 좀처럼 죽지 않는다고
하지만,(Experience at Yahoo!) 디스크나 네트워크 장애로 인해서 발생하는 하다른 소프트웨어나 하드웨어 장애가 문제를 일으킬 수 있습니다. 즉, 높은 가용성을 확보하기 위해서는 NameNode Failover 방안이 필요합니다. 몇개의 HA-NameNode 설정이 있습니다.
- HDFS NameNode를 위한 고 가용성 프레임워크(HDFS-1623)
- Backup NameNode (0.21)
- Avatar NameNode (Facebook)
- HA NameNode using Linux HA
- Active/passive 설정을 2대의 NameNode에 배포하는 방법 (cloudera)
우리는 Linux HA를 이용해서 NameNode의 HA를 설정하는 방법을 선택했고,
Linux HA를 위한 각각의 컴포넌트 들은 다음과 같습니다.
- DRBD: Disk mirroring
- Heartbeat / (Corosync): Network fail-detector
- Pacemaker: Failover definition
- service: NameNode, Secondary NameNode, VIP
HA NameNode using Linux HA
DRBD (Distributed Replicated Block Device) 는 block level replication 을 제공합니다.
특별히 네트웍으로 연결된 RAID 드라이브라고 보면됩니다.
Heartbeat monitors가 서버 간의 네트웍 연결 상태를 모니터링 하고,
만약 Heartbeat가 하드웨어나 서비스의 이상을 감지하면, primary/secondary 를 바꿔줍니다.
그리고 각 서비스의 데몬은 pacemaker 에 정의되어 있습니다.
Conclusion
우리는 LINE의 성장에 따라 다양한 확장성과 가용성 문제를 경험했습니다.
앞으로 LINE 스토리지와 전략은 계속 변경될 것이고, 더 큰 확장과 다양한 장애를 경험하게 될 것입니다.
LINE의 미래 서장과 함께 우리 역시 성장하기를 바랍니다.
Appendix: How to setup HA-NameNode using Linux-HA
In the rest of this entry, I will introduce how to build HA-NameNode using two CentOS 5.4 servers and Linux-HA. These servers are to assume the following environment.
- Hosts:
- NAMENODE01: 192.168.32.1 (bonding)
- NAMENODE02: 192.168.32.2 (bonding)
- OS: CentOS 5.4
- DRBD (v8.0.16):
- conf file: ${DEPLOY_HOME}/ha/drbd.conf
- resource name: drbd01
- mount disk: /dev/sda3
- mount device: /dev/drbd0
- mount directory: /data/namenode
- Heartbeat (v3.0.3):
- conf file: ${DEPLOY_HOME}/ha/haresources, authkeys
- Pacemaker (v1.0.12)
- service daemons
- VIP: 192.168.32.3
- Hadoop NameNode, SecondaryNameNode (v1.0.2, the latest edition now)
Configuration
Configure drbd and heartbeat settings in your deploy home direcoty, ${DEPLOY_HOME}.
- drbd.conf
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | global { usage-count no; } resource drbd01 { protocol C; syncer { rate 100M; } startup { wfc-timeout 0; degr-wfc-timeout 120; } on NAMENODE01 { device /dev/drbd0 ; disk /dev/sda3 ; address 192.168.32.1:7791; meta-disk internal; } on NAMENODE02 { device /dev/drbd0 ; disk /dev/sda3 ; address 192.168.32.2:7791; meta-disk internal; } } |
- ha.conf
1 2 3 4 5 6 7 8 9 10 | debugfile ${HOME} /logs/ha/ha-debug logfile ${HOME} /logs/ha/ha-log logfacility local0 pacemaker on keepalive 1 deadtime 5 initdead 60 udpport 694 auto_failback off node NAMENODE01 NAMENODE02 |
haresources(Can skip this step when using pacemaker)
1 2 3 | # <primary hostname> <vip> <drbd> <local fs path> <running daemon name> NAMENODE01 IPaddr::192.168.32.3 drbddisk::drbd0 Filesystem:: /dev/drbd0 :: /data/namenode ::ext3::defaults hadoop-1.0.2-namenode {code} |
- authkeys
1 2 | auth 1 1 sha1 hadoop-namenode-cluster |
Installation of Linux-HA
Pacemaker and Heartbeat3.0 packages are not included in the default base and updates repositories in CetOS5. Before installation, you first need to add the Cluster Labs repo:
1 | wget -O /etc/yum .repos.d /clusterlabs .repo http: //clusterlabs .org /rpm/epel-5/clusterlabs .repo |
Then run the following script:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | yum install -y drbd kmod-drbd heartbeat pacemaker # logs mkdir -p ${HOME} /logs/ha mkdir -p ${HOME} /data/pids/hadoop # drbd cd ${DRBD_HOME} ln -sf ${DEPLOY_HOME} /drbd/drbd .conf drbd.conf echo "/dev/drbd0 /data/namenode ext3 defaults,noauto 0 0" >> /etc/fstab yes | drbdadm create-md drbd01 # heartbeat cd ${HA_HOME} ln -sf ${DEPLOY_HOME} /ha/ha .cf ha.cf ln -sf ${DEPLOY_HOME} /ha/haresources haresources cp ${DEPLOY_HOME} /ha/authkeys authkeys chmod 600 authkeys chown -R www.www ${HOME} /logs chown -R www.www ${HOME} /data chown -R www.www /data/namenode chkconfig -add heartbeat chkconfig hearbeat on |
DRBD Initialization and Running heartbeat
- Run drbd service @ primary and secondary
- Initialize drbd and format NameNode@primary
- Run heartbeat @ primary and secondary
1 | # service drbd start |
1 2 3 4 5 6 | # drbdadm -- --overwrite-data-of-peer primary drbd01 # mkfs.ext3 /dev/drbd0 # mount /dev/drbd0 $ hadoop namenode - format # umount /dev/drbd0 # service drbd stop |
1 | # service heartbeat start |
Daemonize hadoop processes (Apache Hadoop)
When using Apache Hadoop, you need to daemonize each node such as NameNode, SecondaryNameNode in order for heartbeat process to kick them. The follow script, “hadoop-1.0.2-namenode” is an example for NameNode daemon.
- /usr/lib/ocf/resource.d/nhnjp/hadoop-1.0.2-namenode
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | #!/bin/sh BASENAME=$( basename $0) HADOOP_RELEASE=$( echo $BASENAME | awk '{n = split($0, a, "-"); s=a[1]; s = a[1]; for(i = 2; i < n; ++i) s = s "-" a[i]; print s}' ) SVNAME=$( echo $BASENAME | awk '{n = split($0, a, "-"); print a[n]}' ) DAEMON_CMD= /usr/local/ ${HADOOP_RELEASE} /bin/hadoop-daemon .sh [ -f $DAEMON_CMD ] || exit -1 RETVAL=0 case "$1" in start) start ;; stop) stop ;; restart) stop sleep 2 start ;; *) echo "Usage: ${HADOOP_RELEASE}-${SVNAME} {start|stop|restart}" exit 1 ;; esac exit $RETVAL |
Second, place a script for pacemaker to kick this daemon services. There are pacemaker scripts under /usr/lib/ocf/resource.d/ .
- /usr/lib/ocf/resource.d/nhnjp/Hadoop
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 | #!/bin/bash # # Resource script for Hadoop service # # Description: Manages Hadoop service as an OCF resource in # an High Availability setup. # # # usage: $0 {start|stop|status|monitor|validate-all|meta-data} # # The "start" arg starts Hadoop service. # # The "stop" arg stops it. # # OCF parameters: # OCF_RESKEY_hadoopversion # OCF_RESKEY_hadoopsvname # # Note:This RA uses 'jps' command to identify Hadoop process ########################################################################## # Initialization: : ${OCF_FUNCTIONS_DIR=${OCF_ROOT} /lib/heartbeat } . ${OCF_FUNCTIONS_DIR} /ocf-shellfuncs USAGE= "Usage: $0 {start|stop|status|monitor|validate-all|meta-data}" ; ########################################################################## usage() { echo $USAGE >&2 } meta_data() { cat <<END <?xml version= "1.0" ?> <!DOCTYPE resource-agent SYSTEM "ra-api-1.dtd" > <resource-agent name= "Hadoop" > <version>1.0< /version > <longdesc lang= "en" > This script manages Hadoop service. < /longdesc > <shortdesc lang= "en" >Manages an Hadoop service.< /shortdesc > <parameters> <parameter name= "hadoopversion" > <longdesc lang= "en" > Hadoop version identifier: hadoop-[version] For example, "1.0.2" or "0.20.2-cdh3u3" < /longdesc > <shortdesc lang= "en" >hadoop version string< /shortdesc > <content type = "string" default= "1.0.2" /> < /parameter > <parameter name= "hadoopsvname" > <longdesc lang= "en" > Hadoop service name. One of namenode|secondarynamenode|datanode|jobtracker|tasktracker < /longdesc > <shortdesc lang= "en" >hadoop service name< /shortdesc > <content type = "string" default= "none" /> < /parameter > < /parameters > <actions> <action name= "start" timeout= "20s" /> <action name= "stop" timeout= "20s" /> <action name= "monitor" depth= "0" timeout= "10s" interval= "5s" /> <action name= "validate-all" timeout= "5s" /> <action name= "meta-data" timeout= "5s" /> < /actions > < /resource-agent > END exit $OCF_SUCCESS } HADOOP_VERSION= "hadoop-${OCF_RESKEY_hadoopversion}" HADOOP_HOME= "/usr/local/${HADOOP_VERSION}" [ -f "${HADOOP_HOME}/conf/hadoop-env.sh" ] && . "${HADOOP_HOME}/conf/hadoop-env.sh" HADOOP_SERVICE_NAME= "${OCF_RESKEY_hadoopsvname}" HADOOP_PID_FILE= "${HADOOP_PID_DIR}/hadoop-www-${HADOOP_SERVICE_NAME}.pid" trace() { ocf_log $@ timestamp=$( date "+%Y-%m-%d %H:%M:%S" ) echo "$timestamp ${HADOOP_VERSION}-${HADOOP_SERVICE_NAME} $@" >> /dev/null } Hadoop_status() { trace "Hadoop_status()" if [ -n "${HADOOP_PID_FILE}" -a -f "${HADOOP_PID_FILE}" ]; then # Hadoop is probably running HADOOP_PID=` cat "${HADOOP_PID_FILE}" ` if [ -n "$HADOOP_PID" ]; then if ps f -p $HADOOP_PID | grep -qwi "${HADOOP_SERVICE_NAME}" ; then trace info "Hadoop ${HADOOP_SERVICE_NAME} running" return $OCF_SUCCESS else trace info "Hadoop ${HADOOP_SERVICE_NAME} is not running but pid file exists" return $OCF_NOT_RUNNING fi else trace err "PID file empty!" return $OCF_ERR_GENERIC fi fi # Hadoop is not running trace info "Hadoop ${HADOOP_SERVICE_NAME} is not running" return $OCF_NOT_RUNNING } Hadoop_start() { trace "Hadoop_start()" # if Hadoop is running return success Hadoop_status retVal=$? if [ $retVal - eq $OCF_SUCCESS ]; then exit $OCF_SUCCESS elif [ $retVal - ne $OCF_NOT_RUNNING ]; then trace err "Error. Unknown status." exit $OCF_ERR_GENERIC fi service ${HADOOP_VERSION}-${HADOOP_SERVICE_NAME} start if [ $? - ne 0 ]; then trace err "Error. Hadoop ${HADOOP_SERVICE_NAME} returned error $?." exit $OCF_ERR_GENERIC fi trace info "Started Hadoop ${HADOOP_SERVICE_NAME}." exit $OCF_SUCCESS } Hadoop_stop() { trace "Hadoop_stop()" if Hadoop_status ; then HADOOP_PID=` cat "${HADOOP_PID_FILE}" ` if [ -n "$HADOOP_PID" ] ; then kill $HADOOP_PID if [ $? - ne 0 ]; then kill -s KILL $HADOOP_PID if [ $? - ne 0 ]; then trace err "Error. Could not stop Hadoop ${HADOOP_SERVICE_NAME}." return $OCF_ERR_GENERIC fi fi rm -f "${HADOOP_PID_FILE}" 2> /dev/null fi fi trace info "Stopped Hadoop ${HADOOP_SERVICE_NAME}." exit $OCF_SUCCESS } Hadoop_monitor() { trace "Hadoop_monitor()" Hadoop_status } Hadoop_validate_all() { trace "Hadoop_validate_all()" if [ ! -n ${OCF_RESKEY_hadoopversion} ] || [ "${OCF_RESKEY_hadoopversion}" == "none" ]; then trace err "Invalid hadoop version: ${OCF_RESKEY_hadoopversion}" exit $OCF_ERR_ARGS fi if [ ! -n ${OCF_RESKEY_hadoopsvname} ] || [ "${OCF_RESKEY_hadoopsvname}" == "none" ]; then trace err "Invalid hadoop service name: ${OCF_RESKEY_hadoopsvname}" exit $OCF_ERR_ARGS fi HADOOP_INIT_SCRIPT= /etc/init .d/${HADOOP_VERSION}-${HADOOP_SERVICE_NAME} if [ ! -d "${HADOOP_HOME}" ] || [ ! -x ${HADOOP_INIT_SCRIPT} ]; then trace err "Cannot find ${HADOOP_VERSION}-${HADOOP_SERVICE_NAME}" exit $OCF_ERR_ARGS fi if [ ! -L ${HADOOP_HOME} /conf ] || [ ! -f "$(readlink ${HADOOP_HOME}/conf)/hadoop-env.sh" ]; then trace err "${HADOOP_VERSION} isn't configured yet" exit $OCF_ERR_ARGS fi # TODO: do more strict checking return $OCF_SUCCESS } if [ $ # -ne 1 ]; then usage exit $OCF_ERR_ARGS fi case $1 in start) Hadoop_start ;; stop) Hadoop_stop ;; status) Hadoop_status ;; monitor) Hadoop_monitor ;; validate-all) Hadoop_validate_all ;; meta-data) meta_data ;; usage) usage exit $OCF_SUCCESS ;; *) usage exit $OCF_ERR_UNIMPLEMENTED ;; esac |
Pacemaker settings
First, using the crm_mon command, verify whether the heartbeat process is running.
1 2 3 4 5 6 7 8 9 10 | # crm_mon Last updated: Thu Mar 29 17:32:36 2012 Stack: Heartbeat Current DC: NAMENODE01 (bc16bea6-bed0-4b22-be37-d1d9d4c4c213)-partition with quorum Version: 1.0.12 2 Nodes configured, unknown expected votes 0 Resources configured. ============ Online: [ NAMENODE01 NAMENODE02 ] |
After verifying the process is running, connect to pacemaker using the crm command and configure its resource settings. (This step is needed instead of haresource setting)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | crm(live) # configure INFO: building help index crm(live)configure # show node $ id = "bc16bea6-bed0-4b22-be37-d1d9d4c4c213" NAMENODE01 node $ id = "25884ee1-3ce4-40c1-bdc9-c2ddc9185771" NAMENODE02 property $ id = "cib-bootstrap-options" \ dc -version= "1.0.12" \ cluster-infrastructure= "Heartbeat" # if this cluster is composed of two NameNode, the following setting is need. crm(live)configure # property $id="cib-bootstrap-options" no-quorum-policy="ignore" # vip setting crm(live)configure # primitive ip_namenode ocf:heartbeat:IPaddr \ params ip= "192.168.32.3" # drbd setting crm(live)configure # primitive drbd_namenode ocf:heartbeat:drbd \ params drbd_resource= "drbd01" \ op start interval= "0s" timeout= "10s" on-fail= "restart" \ op stop interval= "0s" timeout= "60s" on-fail= "block" # drbd master/slave setting crm(live)configure # ms ms_drbd_namenode drbd_namenode meta master-max="1" \ master-node-max= "1" clone-max= "2" clone-node-max= "1" notify= "true" # fs mount setting crm(live)configure # primitive fs_namenode ocf:heartbeat:Filesystem \ params device= "/dev/drbd0" directory= "/data/namenode" fstype= "ext3" # service daemon setting primitive namenode ocf:nhnjp:Hadoop \ params hadoopversion= "1.0.2" hadoopsvname= "namenode" \ op monitor interval= "5s" timeout= "60s" on-fail= "standby" primitive secondarynamenode ocf:nhnjp:Hadoop \ params hadoopversion= "1.0.2" hadoopsvname= "secondarynamenode" \ op monitor interval= "30s" timeout= "60s" on-fail= "restart" |
Here, ocf:${GROUP}/${SERVICE} path corresponds with /usr/lib/ocf/resource.d/${GROUP}/${SERVICE}. So you should place your original service script there. Also lsb:${SERVICE} path corresponds with /etc/init.d/${SERVICE}.
Finnaly, you can confirm pacemaker’s settings using the show command.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | crm(live)configure # show node $ id = "bc16bea6-bed0-4b22-be37-d1d9d4c4c213" NAMENODE01 node $ id = "25884ee1-3ce4-40c1-bdc9-c2ddc9185771" NAMENODE02 primitive drbd_namenode ocf:heartbeat:drbd \ params drbd_resource= "drbd01" \ op start interval= "0s" timeout= "10s" on-fail= "restart" \ op stop interval= "0s" timeout= "60s" on-fail= "block" primitive fs_namenode ocf:heartbeat:Filesystem \ params device= "/dev/drbd0" directory= "/data/namenode" fstype= "ext3" primitive ip_namenode ocf:heartbeat:IPaddr \ params ip= "192.168.32.3" primitive namenode ocf:nhnjp:Hadoop \ params hadoopversion= "1.0.2" hadoopsvname= "namenode" \ meta target-role= "Started" \ op monitor interval= "5s" timeout= "60s" on-fail= "standby" primitive secondarynamenode ocf:nhnjp:Hadoop \ params hadoopversion= "1.0.2" hadoopsvname= "secondarynamenode" \ meta target-role= "Started" \ op monitor interval= "30s" timeout= "60s" on-fail= "restart" group namenode-group fs_namenode ip_namenode namenode secondarynamenode ms ms_drbd_namenode drbd_namenode \ meta master-max= "1" master-node-max= "1" clone-max= "2" \ clone-node-max= "1" notify= "true" globally-unique= "false" colocation namenode-group_on_drbd inf: namenode-group ms_drbd_namenode:Master order namenode_after_drbd inf: ms_drbd_namenode:promote namenode-group:start property $ id = "cib-bootstrap-options" \ dc -version= "1.0.12" \ cluster-infrastructure= "Heartbeat" \ no-quorum-policy= "ignore" \ stonith-enabled= "false" |
Once you’ve confirmed the configuration is correct, commit it using the commit command.
1 | crm(live)configure # commit |
Once you’ve run the commit command, heartbeat kicks each service following pacemaker’s rules.
You can monitor dead or alive using the crm_mon command.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | $crm_mon -A ============ Last updated: Tue Apr 10 12:40:11 2012 Stack: Heartbeat Current DC: NAMENODE01 (bc16bea6-bed0-4b22-be37-d1d9d4c4c213)-partition with quorum Version: 1.0.12 2 Nodes configured, unknown expected votes 2 Resources configured. ============ Online: [ NAMENODE01 NAMENODE02 ] Master /Slave Set: ms_drbd_namenode Masters: [ NAMENODE01 ] Slaves: [ NAMENODE02 ] Resource Group: namenode-group fs_namenode (ocf::heartbeat:Filesystem): Started NAMENODE01 ip_namenode (ocf::heartbeat:IPaddr): Started NAMENODE01 namenode (ocf::nhnjp:Hadoop): Started NAMENODE01 secondarynamenode (ocf::nhnjp:Hadoop): Started NAMENODE01 Node Attributes: * Node NAMENODE01: + master-drbd_namenode:0 : 75 * Node NAMENODE02: + master-drbd_namenode:1 : 75 |
Finally, you should test the various failover tests.
For example, kill each service daemon and cause pseudo-network failures using iptables.
Reference documents
'IT_Server > etc.(related Server)' 카테고리의 다른 글
[펌] 크로스 도메인 (0) | 2014.04.18 |
---|---|
[펌] 대용량 시스템 레퍼런스 디자인 (0) | 2013.07.13 |
[펌] 잘 죽지 않는 게임 서버를 설계 해보자 - 서버 HA (High Availability) (0) | 2013.06.24 |
[펌] DNS라우팅을 이용한 부하분산 (0) | 2013.04.05 |
[펌] DNS 캐시 디버깅하기 (0) | 2010.07.15 |