[발 번역] Quorum-based Journaling in CDH4.1

해당 글은 Cloudera의  http://blog.cloudera.com/blog/2012/10/quorum-based-journaling-in-cdh4-1/ 를 발번역 한 것입니다. 기존의 HDFS HA를 위해서 사용하던 NFS를 이용한 방법에 Cloudera에서 의문을 품고, 이를 JournalingNode를 이용한 방법으로 개선을 시도했습니다. 왜, 이런 작업을 했고, 어떤 효과가 있을지에 대해서 알아보도록 하겠습니다.

모 고수님들의 이야기를 들어보면, JournalingNode 가 도입되도, 대용량 파일을 올리는 경우가 대부분이므로 크게 성능에는 문제가 없을꺼라고 하시고, 실제로 소규모에서 돌려부신 분의 얘기도, MR(task)에서 MR I/O가 많을 때 부하가 조금 발생하고,  파일 저장시에는 큰 문제가 없다고 하시네요. 다만, 실험군에 따라서 다를테니, 꼭 테스트 해보시고 적용해보시기 바랍니다.(참고로 이 글 보다 중간에 링크된 디자인 문서를 이해하시는게 훨씬 중요합니다.)

 

Quorum-based Journaling in CDH4.1

몇 주전에, Cloudera 에서는 Apache Hadoop을 포함한 클라우데라의 최신 배포판인, CDH4.1 을 출시했습니다. 이 것은 HDFS NameNode의 HA 를 위한 외부 소프트웨어나 특별한 장치에 의존하지 않는 첫번째 Standalone 버전의 출시입니다.( 역자 주: 이전에는 NFS Shared 장치가 HA를 위해서 필요했습니다. ), 이번 글은 개발자 관점에서 해당 새 기능의 내부 동작을 설명합니다. 만약, 해당 기능의 운영과 설정에 대한 정보가 필요하다면 CDH4 High Availability Guide 를 참고하시기 바랍니다.

Background

HDFS 프로젝트가 시작된 이래로, HDFS는 아주 간단한 아키텍처를 유지해왔습니다. 파일시스템 메타데이터를 저장하는 NameNode라고 불리는 마스터 데몬과, 파일시스템 데이터를 저장하는 DataNode라고 불리는 슬레이브 데몬이 있습니다. NameNode는 매우 안정적이고 효율적이며, 간단한 아키텍처는 HDFS가 중요한 서비스 데이터를 수천대의 서버에서 수년 동안 페타 바이트까지 저장하도록 해줍니다. 그러나, 그 동안 NameNode는 HDFS의 SPOF 입니다. 2월에 있었던 CDH4의 첫번째 베타 릴리즈 이후로, 백업 기능을 제공하는 자동 hot failover를 제공하는 Standby NameNode를 소개했었습니다. HA NameNode  디자인에 대한 자세한 토론은 동료인 Aron Myers의 이전 글을 참고 하시기 바랍니다.

Limitations of NameNode HA in Previous Versions

3월에 올렸던 블로그에서 설명한 것처럼, NameNode HA는 특별한 공유 스토리지에 의존합니다. Active NameNode에서 HDFS edit log 쓰고, 동시에 Standby NameNode에서 해당 edit log를 읽어갈 수 있는 를 저장 공간이 필요합니다. 게다가, 이 공유 스토리지는 그 자체로 HA해야 합니다. 공유 스토리지에 접근이 되지 않으면, Active NameNode는 더 이상 namespace의 변경 사항 저장을 계속할 수 없을 것입니다.

CDH4.1 의 HDFS에서는 NFS mount 형태로 제공되는 공유 스토리지가 필요했습니다. 일반적으로 Enterprise 등급의 NAS 장비입니다.(역자주: 이 Enterprise 등급의 NAS는 자체적인 HA를 제공해주지만, 가격은 후덜덜합니다.) 몇몇 조직에서는 이런 구조가 그들의 운영 환경에 적합하고, 실제로 실 서비스 환경에서 HA NameNode를 운영중인 몇몇 고객이 있습니다. 그러나, 다른 고객들과 커뮤니티 멤버들은 NFS 에 기반한 스토리지에서 다음과 같은 제약 사항을 발견했습니다.

  • Custom hardware – NAS 장비를 사용하면, 매우 비싸질 수 있습니다. 게다가, fencing 설정을 위해서 원격 설정가능한 파워 분배 장치(PDU) 나 다른 특별한 장치가 필요할 것입니다. 추가적인 장비 비용이 필요하고, 운영 비용도 들 것입니다. 많은 조직이 데이터센터내에 NAS 장비나 다른 특별한 하드웨어를 추가하지 않기를 원합니다.(역자 주: fencing이라는 것은 현재 동작중인 클러스터에서 특정 멤버를 클러스터에서 제거하는 것을 말합니다.)
  • Complex deployment – HDFS가 설치된 이후에도, 관리자는 NFS를 마운트하고, 해당장비에 맞는 fencing 스크립트를 설정하고, 그외의 여러 작업을 수행해야 합니다. 이것은 HA 배포를 어렵게 하고 때로는 잘못설정해서 이용할 수 없는 경우도 생깁니다.
  • Poor NFS client implementations – 많은 Linux 버전에서 NFS client를 구현은 버그도 많고, 설정하기 어렵습니다. 예를 들어, 관리자가 mount 옵션을 잘못 설정하기 쉽고, 이렇게 되면 NameNode는 장애 시나리오에서 복구가 되지 않을 것입니다.
  • External dependencies – 스토리지를 위해서 NAS 장비에 대한 의존은 하나 이상의 인프라스트럭처에 대해서 모니터링과 유지보수를 필요로 합니다. 최소한, NAS를 사용하는 것은 추가적인 경고나 모니터링 수치를 설정해야 하고, 몇몇 조직에서는 하둡 운영 조직이외에도 다른 스토리지 관련 직에 대한 내부 의존성이 생깁니다.(역자 주: 예를 들어서, NAS를 관리하는 조직이 따로 있다면, 해당 하둡 운영팀에서 NAS 문제가 발생했을 때는 어떻게 손을 쓸 수 가 없습니다.)

Removing These Limitations

위의 제약사항으로 부터, 여러가지 옵션을 평가하고, 실제 가능한 교체를 위해서 다음과 같은 요구사항 리스트를 만들었습니다.

  • No requirement for special hardware – 하둡과 마찬가지로, 오직 Commodity 하드웨어에만 의존해야 합니다. 특히, 실제 장비들은 현재 클러스터의 일부분이어야 합니다.
  • No requirement for custom fencing configuration -STONITH 와 같은 fencing 방법은 특별한 장비를 필요로 합니다. 오직 소프트웨어적인 방법에만 의존해야 합니다.
  • No SPOFs – 최종 목표가 HA이기 때문에, 다른 컴포넌트에 HA 요구사항을 집어넣고 싶지 않습니다.
 SPOF와 특별한 하드웨어를 피하겠다는 주어진 요구사항으로 인해서, 어떤 디자인이라도 여러대의 commodity 장비 위에 여러개의 replica 를 저장하는 방식을 사용해야 한다는 것을 알게 되었습니다. 여기다가 다음과 같은 요구사항이 추가되었습니다.
  • Configurable for any number of failures – 한 종류의 장애에 대해서만 견고한 시스템을 디자인하는 것 보다는, 메타데이터를 복제하는 추가 노드를 이용해서, 요구하는 복구 레벨에 따라서 처리할 수 있도록 유연성이 필요합니다.
  • One slow replica should not affect latency – 메타데이터의 Write Path는 NameNode의 성능을 결정하는 중요한 컴포넌트이기 때문에, latency가 낮게 유지된다는 것에 대한 확신이 필요했습니다. 여러개의 리플리카를 가지고 있다면, 그 중의 하나가 장애가 나거나 느리더라도 시스템의 latency에는 영향을 주지 않는다는 확신이 필요합니다..
  • Adding journal replicas should not negatively impact latency – 만약 관리자가 여러 장애가 동시에 일어나는 것에 대비하기 위해서 추가적인 리플리카를 설정하는 것을 허용한다면, 이것이 성능에 부정적인 영향을 미쳐서는 안됩니다.
 하둡을 좀 더 쉽게 배포하고, 운영할 수 있는데 초점을 맞추고 있는 회사로써, 다음과 같은 운영측면의 요구사항도 고려해야 했습니다.

  • Consistency with other Hadoop components– 새롭게 소개되는 모든 컴포넌트는 기존에 존재하던 것들과 유사한 동작을 하도록 디자인되어야 했습니다. 예를 들어, XML 기반의 config을 사용하고 log4j를 logging에 사용하고, 같은 metric framework를 사용하는 것입니다.
  • Operations-focused metrics – NameNode 운영의 중요한 부분이기 때문에, metrics를 보여주는 것에 높은 우선순위를 두었습니다. 새로운 시스템은 오래 동작하는 실 클러스터로 동작한다면, 모든 중요한 metric를 보여주는 것과 실제로 이용할 수 없는 어떤 문제에 대해서도 일찍 경고를 주는 것이 필요했습니다.
  • Security – CDH는 데이터에 대한 암호화와 Kerberos를 통한 강한 인증 기능등의 종합적인 보안을 제공합니다. 모든 새로운 컴포넌트는 스택의 나머지 부분과 같은 수준을 유지하도록 디자인되었습니다. 암호화가 필요한 고객에게, 데이터를 암호화하는 것 만큼 메타데이터를 암호화 하는 것도 중요합니다.

QuorumJournalManager

클라우데라에서의  고객들과 커뮤니티와의 내부적인 토론 후에, QuorumJournalManager 라고 불리는 시스템을 디자인했습니다. 이 시스템은 다음과 같은 간단한 아이디어에 기반합니다. HDFS edit log들을 NFS 같이 한 지점에 저장하는 것보다는 여러 군데에 저장하자는 것입니다. 그리고 분산 프로토콜을 이용해서 여러 곳의 데이터가 올바르게 동기화 되는 것을 보장한다는 것입니다. 해당 시스템에서 원격 저장소는 JournalNode 라고 불리는 새로운 형태의 HDFS 데몬입니다. NameNode는 JournalNode 들에 edit log를 저장하는 클라이언트로써 동작합니다. 그리고 해당 노드들의 과반수가 복제에 성공했을 때 해당 변경 사항을 커밋하게 됩니다.

유사하게 NameNode가 Standby 상태에 있을 때, 네임스페이스에 대한 hot backup을 유지하기 위해서 edits 를 읽을 필요가 있습니다. 이때 JournalNode에 저장된 다른 아무 replica에서 읽을 수 있습니다.

Distributed Commit Protocol

위의 그림은 간단합니다. NameNode는 세 노드 각각에 edits를 저장하고, 과반수가 복제되었을 때, 성공하게 됩니다. 그러나, 다음과 같은 재미있는 질문이 생길 수 있습니다.

  • 만약 edits에 대한 배치작업이 하나의 서버에는 보냈지만, 다른 서버들에 보내지 않았을 때, 그리고 그때 NameNode 가 장애가 나면 어떻게 될까?
  • “Spit Brain” 시나리오에서는 무슨 일이 벌어질까? 양쪽에 모두 쓰려고 할까?
  • 시작시에 불일치로 부터 어떻게 복구하게 될까? edits 가 메모리에 있는 상황에서 몇몇 노드가 장애가 나면 어떻게 될까?

해당 질문들에 대한 자세한 설명은 해당 블로그 글에 담기에는 힘들지만, 짧게 말하자면 해당 시스템은 Paxos라는 잘 알려진 프로토콜의 구현체에 의존하고 있습니다. 해당 프로토콜은 클러스터의 여러개의 노드들끼리 데이터가 일치하다는 것을 확신할 수 있는 정확한 방법을 제공합니다. 이 시스템에서, Mutli-Paxos 의 구현체를 edits 의 각각의 작업을 커밋하기 위해서 사용하고 있습니다. 추가적으로 복구를 위해서 failover 후에 standby NameNode들이 지연된 edit를 지우기 위해서도 사용됩니다. 해당 내용에 대한 자세한 내용과 알고리즘은  HDFS-3077 design documentThe 를 참고하시기 바랍니다.

Fencing and Epoch Numbers

이 글의 앞에서 소개된 시스템의 주요 요구사항 중에 하나는, 특별한 장비나 소프트웨어를 이용한 어떠한 fencing 이든지 회피하는 것입니다. Fencing은 failover 후에 새로운 Active NameNode가 이전 Active 노드가 더 이상 시스템의 메타데이터에 변경을 가하지 않는 것을 확신하는 것입니다. 다른 말로, fencing은 “Split Brain Syndrome”의 치료제입니다. “Split Brain Syndrome”은 두 개의 노드가 Active NameNode라고 생각하고, 네임스페이스에서 서로 다른 변경을 통해 충돌을 만드는 잠재적인 시나리오를 말합니다. 그럼 QuorumJournalManager는 fencing을 어떻게 구현했을까요?

QuorumJournalManager 에서 fencing을 위한 핵심은 Epoch Number 라는 개념입니다. NameNode가 active가 될 때마다, 처음에 Epoch Number를 생성하는 것이 요구됩니다. 이것은 계속 증가하는 정수형태로 할당될 때 마다 유일한 값이 생성됩니다.ㅣ 최초의 Active NameNode는 네임스페이스를 초기화한 다음에 epoch number 1로 시작하게 됩니다. 그리고 failover 나 재시작하게 되면 증가된 epoch number를 가지게 됩니다. 즉, epoch number는 두 개의 NameNode 사이에 순서를 정해주게 됩니다. 더 높은 epoch number를 가진 NameNode가 “더 최신” 이 되고, 다른 NameNode 들은 더 이전 epoch number 값을 가지게 됩니다. NameNode는 epoch number 를 한번 주어진 epoch number를 두 번 이상 만들지 않는, 항상 유일한 값을 만들어내는 간단한 알고리즘을 이용합니다. 해당 알고리즘의 자세한 내용은 위에서 언급한 디자인 문서를 참고하시기 바랍니다.

두 대의 NameNode 가 주어져 있고, 두 대 모두 자기가 Active 라고 생각하는 상황에서, 각각 자신의 유일한 epoch number를 가지고 있을대, 어떻게 Split Brain Syndrome을 회피할 수 있을까요? 그 대답은 놀랍게도 매우 간단합니다. 그리고매우 우아합니다. 하나의 NameNode 가 하나의 JournalNode로 어떠 메시지(또는 RPC 호출할때)를 보낼 때, 요청의 일부로 epoch number가 포함되어 있습니다.  JounalNode 가 해당 메시지를 받으면, 로컬에 저장된 promised epoch 과 epoch number를 비교하게 됩니다. 만약 요청이 더 최신의 epoch number를 가지고 있으면, 새로운 epoch number를 로컬에 promised epoch이라고 불리는 값으로 저장하게 됩니다. 만약 더 이전의 epoch number가 요청에 포함되어 있으면,  해당 요청을 거부하게 됩니다. split-brain을 피하기 위한 간단한 정책은 다음과 같습니다.

  • 어떤 NameNode라고 edits를 성공적으로 쓰기 위해서는 과반수의 노드들이 쓰기에 성공해야 한다. 이것은 과반수 이상의 노드들이 해당 epoch number를 최신이라고 인정해야 한다는 뜻입니다.
  • 새로운 NameNode가 active 가 되었을 때, 이전의 다른 NameNode의 epoch number 보다 더 높아야 합니다. 이것은 JournalNode 의 epoch number를 증가하게 함으로써, 모든 JournalNode를 호출하는 것을 간단하게 만들어줍니다. 이것이 과반수 이상으로 성공하면, 그것은 새로운  epoch 으로 인정받게 됩니다.
  • 두 개 모두 과반수 이상의 노드로 부터 인정받아야 하기 때문에, 이것은 이전 NameNode 가 active 라고 생각함에도 불구하고, 더 이상 과반수로 부터 성공적인 쓰기를 요청할 수 없다는 것을 의미합니다. 그로 인해 네임스페이스 변경이 일어나는 것을 방지합니다.

Testing

Paxos 가 논문에는 간단하고, 정확성을 보장하지만, 올바르게 구현하는 것은 어렵기로 악명높습니다. 그러므로 이 시스템을 개발하는 동안, 절반 이상의 시간을, 테스팅하고 검증하는데 소모했습니다. 특별히 몇 가지 테크닉이 중요하다는 것을 발견했습니다.

  • MiniCluster testing – 초기부터, 같은 JVM위에서 여러 개의 JournalNode를 실행하는MiniJournalcluster 라고 불리는 작은 클래스를 작성했습니다. 이것으로 분산 시나리오에 대한 테스트를 JUnit 기능 테스트 케이스 내에서 자동화 할 수 있었습니다.
  • Mock/spy testing – Mockito를 이용해서 같은 JVM내의 QuorumJournalManager Client 와 JournalNode 에 Spy를 삽입하는 많은 수의 unit tests를 작성했습니다. 예를 들어, MOckito Spy는 매칭되는 특정 구문에서 쉽게 IOException을 던지는 것을 지시할 수 있습니다. 디자인 토론 중에 많은 수의 다른 장애 상황과 시나리오를 확인하는 deterministic tests를 위해서 이것을 사용하였습니다.
  • Randomized fault testing – 여러가지 다른 장애 시나리오를 위해, 손으로 수십가지 테스트를 작성할 수 있었지만, 이 테스트들은 우리가 쉽게 생각할 수 있는 것들로 제한이 됩니다. 분산 시스템을 구축했던 경험에 따르면, 쉽게 인식하지 못하는 경우가 훨씬 더 염려스럽습니다. 그래서 deterministic seeds에 기반한 랜덤 장애 테스트를 도입했습니다. 주어진 랜덤 seed 에 의해서, 완전히 결정된 프로토콜에 따른 일련의 장해를 일으키는 테스트 케이스가 수행됩니다. 예를 들어, 주어진 seed 는 장애를 만들기 위해, 두번째, 세번째, 8번째, 그리고 45번째 RPC 를 NameNode 에서 두번째 JournalNode로 전달합니다. 해당 테스트는 장애가 주입되는 동안에 수백개의 Failover를 시물레이션 하고, 동시에 커밋된 트랜잭션이 사라지지 않았는지 검증합니다.

추가적으로, 위에서 수행되는 테스트들 외에도, MapReduce 클러스터에서 돌아가는 랜덤 장애 테스트 셋도 작성했습니다. 해당 테스트들은 몇분 동안 랜덤 장애 테스트를 수행하는 Hadoop Streaming 작업입니다. 이 때, 결과물은 해당 테스트 로그로 HDFS에 저장됩니다. 입력으로는 random seed를 가지고 있는 5000 줄 짜리 파일이 들어갑니다. NLineInputFormat을 이용해서, 각각의 seed를 개별적인 태스크에 전달합니다. 그 결과, 큰 클러스터에서 동시에 쉽게 5000개의 인스턴스가 수행될 수 있습니다.  해당 잡이 끝나면, 두 번째 스트리밍 잡이 해당 결과에 대해서 테스트 실패나 기대하지 않은 메시지(AssertionError, NullPointerException)들을 찾기 위해 grep을 수행합니다.

해당 테스트 셋을 이용해서, 수백만개의 failover 시나리오를 테스트 할 수 있었고 여러가지 버그를 발견할 수 있엇습니다. 해당 테스트는 사실상 매우 종합적이었고, Hadoop에서 내부적으로 사용하는 Jetty의 새로운 버그 두개를 발견할 수 있었습니다. Jetty의 버그를 수정한 것 역시 CDH4.1 에 추가되었습니다.

Summary

이 글에서 설명한 내용은 CDH4.1 에서 사용이 가능합니다. 그리고 많이 고생한 Cloudera Manager Team 에 CM4.1 에서는 몇번의 마우스 클릭만으로 배포하고 모니터링 하는 것이 매우 쉽습니다. 다른 HDFS 개발 제품처럼, Apache Software Foundation Repositories 에 올라가 있습니다. 그리고 ASF Jira  HDFS-3077 에서 확인할 수 있습니다. 새로운 코드는 Apache trunk에 머지되었고, 가까운 후에 Apache HDFS release에 포함될 것입니다.

Acknowledgements

해당 프로젝트에 공헌한 분들의 호의에 감사합니다.

  • Aaron T. Myers and Eli Collins for code reviews and contributions around security, configuration, and docs
  • Sanjay Radia, Suresh Srinivas, Aaron Myers, Eli Collins, Henry Robinson, Patrick Hunt, Ivan Kelly, Andrew Purtell, Flavio Junqueira, Ben Reed, Nicholas Sze, Bikas Saha, and Chao Shi for design discussions
  • Brandon Li and Hari Mankude for their work on the HDFS-3092 branch which formed some of the early building blocks for the JournalNode
  • Stephen Chu and Andrew Purtell for their help with cluster testing
  • Vinithra Varadharajan, Chris Leroy, and the Cloudera Manager team for help with integration testing, metrics and configuration