gruter 님 hadoop 장애 복구

일요일 오전 PC를 켜자 마자 gruter님이 갑자기 메신저를 연결하였습니다. Namenode가 죽어서 올라오지 않는다. 라는 메세지와 함께요...
다음은 Namenode 장애 해결한 과정입니다.

1. Namenode log 확인
일단 Namenode를 로그를 보니 다음과 같은 로그가 있습니다.
WARN org.apache.hadoop.dfs.FSNamesystem: java.io.IOException: No space left on device
Namenode에 새로운 파일이 추가되는 경우 파일정보는 메모리에 저장되지만 shutdown된 후 다시 정보를 가져오기 위해서는 Namenode의 로컬 디스크에 파일로 저장합니다.

2. 디스크 파티션 용량 확인
df -k
conf에 설정된 "dfs.name.dir" 디렉토리 할당된 파티션의 용량을 보니 remain이 0이네요.

3. Namenode restart
Namelog에 다음과 같은 에러 로그가 나타나면서 namenode kill 됨

2008-08-17 08:54:13,204 ERROR org.apache.hadoop.dfs.NameNode: java.io.EOFException
        at java.io.DataInputStream.readFully(DataInputStream.java:180)
        at org.apache.hadoop.io.UTF8.readFields(UTF8.java:106)
        at org.apache.hadoop.io.ArrayWritable.readFields(ArrayWritable.java:90)
        at org.apache.hadoop.dfs.FSEditLog.loadFSEdits(FSEditLog.java:433)
        at org.apache.hadoop.dfs.FSImage.loadFSEdits(FSImage.java:759)
        at org.apache.hadoop.dfs.FSImage.loadFSImage(FSImage.java:639)
        at org.apache.hadoop.dfs.FSImage.recoverTransitionRead(FSImage.java:222)
        at org.apache.hadoop.dfs.FSDirectory.loadFSImage(FSDirectory.java:79)
        at org.apache.hadoop.dfs.FSNamesystem.initialize(FSNamesystem.java:254)
        at org.apache.hadoop.dfs.FSNamesystem.<init>(FSNamesystem.java:235)
        at org.apache.hadoop.dfs.NameNode.initialize(NameNode.java:131)
        at org.apache.hadoop.dfs.NameNode.<init>(NameNode.java:176)
        at org.apache.hadoop.dfs.NameNode.<init>(NameNode.java:162)
        at org.apache.hadoop.dfs.NameNode.createNameNode(NameNode.java:846)
        at org.apache.hadoop.dfs.NameNode.main(NameNode.java:855)

원인 분석 결과
"filesystem/name/current"에 있는 edits.new 파일에 마지막 레코드가 일부 byte만 쓰여지고 나머지 byte는 용량부족으로 저장되지 못한 상태에서 Namenode는 이 데이터를 읽다보니 EOFException이 발생 하였습니다.

4. Secondary Namenode에 백업본 확인
불행하게도 Hadoop Q&A에 있는 Secondary Namenode의 접속 오류 문제로 인해 백업을 못받고 있었습니다.

5. Rsync 버전 확인
다행히 "filesystem/name" 디렉토리를 rsync하고 있었는데 파일 확인해본 결과 원본과 동일하게 sync되어 역시 사용할 수 없었습니다.

6. edits 파일과 edits.new파일 시간 확인
두개의 파일 시간이 2시간 정도만 있어서 일단 2시간 동안의 파일 변경 사항은 포기하기로 했습니다.

7. edits.new rename
edits.new 파일을 edits.new_bak로 rename 한 후 Namenode를 시작하였습니다.
정상적으로 NameNode는 시작되었습니다. 전체적인 파일 볼륨에는 큰 문제가 없었습니다. 물론 2시간 사이에 입력된 파일은 유실 되었습니다.

8. 디스크 용량 확보
디스크를 확인해보니 hadoop/logs 디렉토리가 대부분의 용량을 다 차지하고 있었습니다.(9 GB 이상) 일단 불필요한 과거 로그를 삭제하였습니다.
로그가 많이 쌓인 원인은 사용자가 만든 프로그램에서 반복문을 돌면서 계속 파일을 오픈하는 프로그램이 있었는데 여기서 잘못된 파일 디렉토리를 지정하였습니다.
Namenode에는 다음과 같은 로그가 ms 단위로 한줄씩 작성되고 있었습니다.

2008-08-18 01:31:06,476 INFO org.apache.hadoop.ipc.Server: IPC Server handler 1 on 9000, call open(/file/aaa, 0, 1342177280) from xxx.xxx.xxx.xx:3952: error: java.io.IOException: Cannot open filename /file/aaa
java.io.IOException: Cannot open filename /file/aaa
        at org.apache.hadoop.dfs.NameNode.open(NameNode.java:243)
        at sun.reflect.GeneratedMethodAccessor11.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at org.apache.hadoop.ipc.RPC$Server.call(RPC.java:409)
        at org.apache.hadoop.ipc.Server$Handler.run(Server.java:899)

이런 메세지가 나타나지 않도록 하기 위해서는 Namenode의 로그레벨을 WARN으로 올리는 방법이 있지만 INFO를 못보기 때문에 일단은 기존대로 한 다음에 로그 디렉토리를 주기적으로 지원주는 방법으로 가기로 했습니다.

9. fsck로 확인
bin/hadoop fsck /

이상 장애 긴급 장애 복구 상황이었습니다.
수 TB이상 유지하고 있는 클러스터라 함부로 다루는 것이 조심스러웠습니다. 이런 장애복구는 처음이라서 공유하는 차원에서 정리해봤습니다.
크리에이티브 커먼즈 라이센스
Creative Commons License

Posted by 김형준


생각날 때 적어 놓아야지 안그러면 잊어버려서요...

    public void configure(JobConf job) {
      String taskId = job.get("mapred.task.id");
    }

크리에이티브 커먼즈 라이센스
Creative Commons License

Posted by 김형준


Hadoop 0.17 유감

Hadoop이 0.17로 버전업 되면서 몇가지 하위 버전과의 호환성에서 문제가 발생하고 있습니다.

가장 큰 문제는 FileSystem에서 listPath() 메소드 자체가 없어져서 기존 코드는 컴파일 에러가 발생하고 있습니다. listStatus()로 변경해 주어야 하고 return 타입도 Path[]가 아닌 FileStatus[] 입니다. 하위 버전 호환을 위해서라도 반드시 남겨 놓아야 하지 않았나 생각합니다. 단순히 해당 메소드를 사용하는 몇군데만 수정하면 되지 않냐하겠지만 HBase 등을 사용하게 되면 복잡한 문제가 발생합니다.
예를 들어 HBase는 현재 0.17 기준으로 만들어 지고 있습니다. Hadoop이 0.16에서 운영되고 있는 경우라면 HBase를 위해서 어쩔수 없이 0.17로 업그레이드를 해야 합니다. 이런 T.T

또 다른 문제는 타입 체크를 엄격하게 적용했다는 것입니다. map, reduce, RecordReader 등의 클래스에서 제네릭을 이용하여 타입을 엄격하게 처리하고 있습니다. 0.16대에서 map(WritableComparable key, Writable value ...) 이런 코드를 모두

map(WritableComparable<Text> key, Writable<Text> value ...) 의 형태로 변경해야 합니다.
크리에이티브 커먼즈 라이센스
Creative Commons License

Posted by 김형준


Yahoo에서 얼마전에 BOSS라고 하는 검색 API를 오픈하면서 일일 query를 무제한으로 하였습니다.
사용자 삽입 이미지
http://developer.yahoo.com/search/boss/

이 BOSS API를 Google App Engine에 탑재하여 서비스를 제공하는 시도가 나타나고 있습니다.

http://www.4hoursearch.com

http://bossy.appspot.com

국내 개발자들도 국내 서비스만 생각하지 말고 Google App Engine, FaceBook, Salesforce.com 등에서 제공하는 API나 플랫폼을 이용하여 세계 유저를 대상으로 하는 서비스를 고민해야 합니다. 저는 벌써 머리가 굳어서 아이디어가 없네요.. ㅋㅋ
크리에이티브 커먼즈 라이센스
Creative Commons License

Posted by 김형준


올 11월에 있는 Aapche Con에 Hadoop Camp가 추가 되었다고 합니다.

A special added feature of ApacheCon is Camp Hadoop, (November 6-7). Camp sessions will focus on Apache Hadoop, the platform being utilized by organizations to deliver petabyte scale computing and storage on commodity hardware, and by academic and industrial research groups to teach data parallel computing. Hadoop developers and users will review new projects and case studies of Hadoop applications while participating in interactive discussions on the platform's future.

http://www.marketwatch.com/news/story/apachecon-us-ofbiz-symposium-bring/story.aspx?guid=%7B5DFE1132-38AB-468C-BBC0-9199B31E9D82%7D&dist=hppr

영어만 되면 질러보고 싶은데 가봐야 알아들을 수가 있어야죠...
크리에이티브 커먼즈 라이센스
Creative Commons License

Posted by 김형준


Yahoo SearchMonkey

Yahoo 블로그에서 SearchMonkey라는 검색 관련 새로운 서비스를 봤습니다.

지금까지의 검색결과는 검색엔진이 크롤해온 페이지를 인덱싱하여 검색엔진에서 제공하는 포맷(제목, 요약정보, URL 등)으로만 검색결과를 보여주었는데 SearchMonkey의 경우 사이트 운영자가 검색 결과에 나타나는 내용을 설정할 수 있도록 해주는 기술 같습니다.

사용자 삽입 이미지
위의 그림에서 영화의 평점, 상영시간 등에 대한 정보는 Yahoo에서 설정한 것이 아니라 해당 영화를 제공하는 정보를 이용해서 보여주고 있습니다.

사용자 삽입 이미지
자세한 내용은 http://developer.yahoo.com/searchmonkey/ 참고하세요.

국내의 서비스들은 왜 이런 시도와 아이디어를 내지 못하는지 안타깝네요.
크리에이티브 커먼즈 라이센스
Creative Commons License

Posted by 김형준


어제 TV 토크쇼를 보는 중에 DJ DOC의 이하늘이 나와서 "과거에는 가진것이 없어서 잘못되더라도 포기해야 할 것도 없었기 때문에 사회를 비판하는 가사, 심의에 위반되는 가사도 만들어 낼 수 있었다. 지금은 10년만에 신용불량자에서 회복도 되고 자유로운 삶을 위해서는 포기해야 할 것이 점점 늘어 나는것 같다" 라는 멘트를 하더군요.

버려야 새로운 것을 얻을 수 있다는 단순한 진리이지만 버리기만 하고 새로운 것을 얻을 수 없으면 어쩌나 하는 마음에 버리지 못하고 있습니다.
크리에이티브 커먼즈 라이센스
Creative Commons License

Posted by 김형준


facebook에서도 자체적으로 만든 bigtable 스타일의 데이터 저장소를 오픈했습니다.

http://www.techcrunchit.com/2008/07/14/facebook-release-data-storage-cluster-code-as-open-source/

http://code.google.com/p/the-cassandra-project

http://www.slideshare.net/jhammerb/data-presentations-cassandra-sigmod/

데이터 모델은 Bigtable과 비슷하고 파일시스템은 로컬 파일시스템을 이용하고 복제본을 DHT 기반으로 구성하는 것 같은데 자세한 것은 한번 돌여봐야 할 것 같네요.
크리에이티브 커먼즈 라이센스
Creative Commons License

Posted by 김형준


Bigtable이 단순히 데이터를 저장하는 데이터베이스의 역할만 수행하는 경우라면 MySQL 클러스터 기능과 같은 기존의 데이터베이스 기능을 적절하게 활용하면 된다.
Bigtable과 같은 데이터 저장소의 가치를 이해하기 위해서는 데이터 모델에 대한 이해와 적절하게 분산되어 있는 분산의 구조, 컬럼 기반 저장 방식 등에 대해 제대로 이해하고 있어야 한다.

Bigtable이 sparse matrix 저장소에 적합한 구조라는 것은 이미 알려진 사실이다. sparse matrix는 10억*10억 matrix에 대부분의 값은 0이고 하나의 row에 수십 ~ 수천 정도만 값이 있는 matrix를 말한다. 하나의 웹 페이지내에 있는 anchor나 term 들이 이런 경우이다. 전체 웹 페이지는 수십억 내지 수백억이고 이들이 포함하고 있는 anchor 값을 이용하여 matrix를 만들면 다음과 같은 sparse matrix가 된다.

사용자 삽입 이미지

기존 관계형 데이터베이스에서는 row-column이 만나는 cell에 값을 하나만 저장할 수 있다. 따라서 이런 sparse matrix를 저장하기 위해서는 다음과 같은 테이블 구성이 되어야 한다.

사용자 삽입 이미지

이런 경우 데이터가 많은 경우 분산된 데이터베이스 인스턴스에서 관리되어야 하고 T_URL과 T_ANCHOR에 대한 외래키 관계도 구성하기 어렵다.
Bigtable을 이용할 경우에는 다음과 같이 저장할 수 있다.

사용자 삽입 이미지

하나의 cell에 n개의 데이터를 저장할 수 있기 때문에 특정 웹 페이지에 잇는 anchor 정보를 하나의 컬럼에 모두 저장할 수 있다.
다음은 Map&Reduce와 함께 matrix 계산에 Bigtable이 어떻게 활용되는지 살펴본다.
matrix 곱하기의 경우 O(n*n)의 복잡도를 가지기 때문에 n이 증가할 수록 속도는 급격하게 증가하게 된다.  matrix 계산에서 병렬처리는 많은 연구가 이루어 졌고 다양한 방법이 있지만 여기서 사용한 방법은 다음과 같다.

사용자 삽입 이미지
위에서 보는 것 처럼 C11 계산과 C12계산은 병렬로 계산이 가능하다. 자세한 내용은 다음 사이트에 잘 나와 있다.
http://carbon.cudenver.edu/csprojects/CSC5809S01/Simd/parmult.html

이런 계산식에서 문제는 Matrix-B의 데이터가 많은 경우라면 문제가 되지만 Matrix-B가 sparse한 경우라면 O(n*m)이 아니라 O(n * avg(count(b에서 0이 아닌 값의 갯수)) 의 복잡도가 된다. 그리고 이것을 분산병렬 처리할 경우 병렬처리 만큼 속도가 향상된다.

Bigtable과 같은 저장소와 Map&Reduce를 이용하여 위의 matrix 연산을 처리하는 방법은 다음과 같다.
1. 두개의 matrix를 테이블에 저장한다.
사용자 삽입 이미지

2. Map Task를 구성하기 위해 Tablet 단위로 job을 split한다.
3. InputFormat에서는 자신이 처리할 Tablet의 scanner를 오픈하여 값을 하나씩 읽으면서 map()으로 전달한다.
사용자 삽입 이미지
4. map() 에서는 전달받은 값의 row, column 정보를 이용하여 Matrix-B의 값을 조회한 다음 계산 결과를 출력한다.
사용자 삽입 이미지
5. reduce() 에서는 계산 결과를 받아 sum한 다음 결과 테이블에 저장한다.
사용자 삽입 이미지

위의 코드는 Hadoop Map&Reduce를 기준으로 하였으며 정식 코드가 아니라 pseudo 코드 수준이다.
위의 코드에서 보는 것처럼 코드는 아주 간단하다. 여기서 성능에 영향을 미치는 부분은 4번 map 처리에서 scannerB에서 데이터를 가져오는 부분이 된다. Matrix-B에서 row 전체를 가져오는 것이기 때문에 row의 데이터가 많은 경우 속도가 급격하게 저하된다. matrix는 sparse 하다라고 가정했기 때문에 수십 ~ 수백 정도의 데이터만 저장되어 있어 수 ms 내에 데이터 조회가 가능하다. Bigtable의 경우 sequential read의 경우 초당 수천개 데이터를 처리할 수 있다. HBase의 경우 현재는 수백 내지 천개 정도 처리하는 수준이다.
Matrix-B에서 하나의 row를 조회하는데 평군 5ms 정도 소요된다고 했을 때 전체 처리 시간은 다음과 같다.
사용자 삽입 이미지
100개의 서버와 한 서버에 6개의 map task를 동시에 수행할 경우 map task 수행되는데 소요되는 시간은 2.3 시간 정도이다. Map&Reduce 작업 처리 과정과 Bigtable의 데이터 조회시간은 데이터가 증가할수록 급격하게 증가하지 않고 선형적으로 증가하고, 장비 대수가 늘어날 수록 처리 속도는 선형적으로 감소하기 때문에 더 많은 장비를 투입하면 더 짮은 시간에도 처리가 가능할 것이다.
물론 이 수치는 이론적인 수치이며 HBase를 이용하여 테스트를 수행해 봐야 정확한 처리시간을 알 수 있을 것이다.
페이지랭크 계산을 할 경우에도 이런 방법을 적용할 수 있다. 다음과 같은 페이지 관계가 있다.
사용자 삽입 이미지
 Bigtable에는 다음과 같이 저장할 수 있다.

사용자 삽입 이미지

여기서 계산은 다음과 같이 처리한다.
사용자 삽입 이미지
1. InputFormat는 rowkey와 rank 데이터를 scan하여 map() 으로 전달한다.
2. map()에서는 rowkey에 해당하는 outlink 값을 조회하여 계산 값을 reduce로 보낸다.
3. reduce에서는 row, column을 키로 하여 sum한 다음 rank 필드에 다시 저장한다.
4. 1 ~3번 과정을 수렴할때까지(50번 정도) 반복한다.

* HBase와 Bigtable, Map&Reduce 등의 자료를 참고로 하여 작성된 내용이기 때문에 틀린 부분이 있을 수 있습니다. 틀린 부분이나 다른 의견은 댓글 달아 주시면 많은 도움이 될 것 같습니다.
크리에이티브 커먼즈 라이센스
Creative Commons License

Posted by 김형준


hadoop io.sort.mb 설정 관련

hadoop에서 MapTask에서 map function의 결과를 reduce로 보내기 위해서는 map function에서 collector.collect()를 호출한다. 하지만 collect() 메소드를 호출하는 즉시 파일시스템으로 저장하지 않는다. 메모리 버퍼에 계속 쌓아 놓은 다음 일정 사이즈가 되면 메모리에 있는 내용을 key를 이용하여 정렬한 다음에 Combiner가 지정되어 있으면 combiner로 보내고 그렇지 않으면 파일시스템으로 저장한다.
환경 변수 중 "io.sort.mb" 값이 메모리 버퍼의 크기를 설정하는 값이다. 기본은 100MB로 되어 있다. 따라서 MapTask에서 필요한 메모리를 계산하기 위해서는 io.sort.mb 값까지 같이 고려해야 한다. 예를 들어 하나의 map task에서 약 200MB 정도의 메모리를 사용할 것으로 예상되는 경우라면 mapred.child.java.opts에서는 300MB 이상으로 설정해야 한다.

<property>
  <name>mapred.child.java.opts</name>
  <value>-Xmx300m</value>
</property>


여기서 고려해야 될 사항이 하나 더 있다. http://www.jaso.co.kr/252 에서 처럼 자바의 경우 실제 메모리 사용량보다 더 많은 메모리를 사용하고 있기 때문이다. 자바에서 메모리 사용량을 정확하게 계산하는 것은 어렵다.
Hadoop의 경우 io.sort.mb 값과 메모리 사용량을 비교해야 하는데 이 부분을 어떻게 처리하는지 참고하면 메모리 사용량을 찾을 것 같아서 소스를 좀 더 찾아 보았는데 0.16 버전과 0.17 버전에서 다르게 처리하고 있는 것을 알 수 있었다.
0.16에서는 BasicTypeSorterBase 클래스의 getMemoryUtilized() 메소드에 구현되어 있다.

"(startOffsets.length) * BUFFERED_KEY_VAL_OVERHEAD +
              maxKeyLength + maxValLength"

이처럼 구현되어 있는데 startOffsets.length는 저장된 값의 갯수이고 maxKeyLength 는 저장된 key의 max 길이 값, maxValLength는 저장된 value의 max 길이 값이다. BUFFERED_KEY_VAL_OVERHEAD 는 다음과 같은 주석이 달려 있다.

  //the overhead of the arrays in memory
  //12 => 4 for keyoffsets, 4 for keylengths, 4 for valueLengths, and
  //4 for indices into startOffsets array in the
  //pointers array (ignored the partpointers list itself)
  static private final int BUFFERED_KEY_VAL_OVERHEAD = 16;

하나의 key, value 값을 저장하기 위해 16byte의 오버헤드가 더 추가 된다는 것을 알 수 있다.

0.17에서는 몇가지 옵션 값이 더 추가 되었다. "io.sort.record.percent",  "io.sort.spill.percent" 값이다.
io.sort.record.percent는 버퍼에 저장된 레코드의 갯수가 일정 수준에 도달하면 flush() 시키기 위한 값이다. io.sort.mb * 1024 * 1024 * io.sort.record.percent / 4 보다 갯수가 많으면 flush() 한다.
io.sort.spill.percent는 io.sort.mb 값에 100% 도달했을 때 저장하는 것이 아니라 버퍼의 메모리 용량이  io.sort.spill.percent 에 도달하면 flush() 하게 된다.

Map Task 수행 중 가끔 OutOfMemory 가 발생하는 경우가 있는데 이때에는 mapred.child.java.opts 값을 무작정 늘리기 보다는 위의 값들에 대한 처리도 적절하게 해야 한다.

크리에이티브 커먼즈 라이센스
Creative Commons License

Posted by 김형준


« Previous : 1 : ... 2 : 3 : 4 : 5 : 6 : 7 : 8 : 9 : 10 : ... 13 : Next »