2018년 2월 9일 금요일

NiFi Clustering과 HA(High Availability)


  NiFi Clustering으로 찾아보면 Zero_master Clustering이라는 문구를 가장 쉽게 마주치게 됩니다. 1.0.0 버전에서부터 적용한 방식으로, 간단히 설명하면 ZooKeeper를 활용해서 내부적으로 Cluster Coordinator를 선정하여 NiFi의 flow를 클러스터된 노드들 모두 동일하게 유지합니다.
  Zero-master라 함은, Cluster Coordinator가 떠있는 노드가 SPOF가 되는 것이 아니라 또다시 다른 Cluster Coordinator를 띄울 리더를 선출하기 때문에, 마스터 역할만을 하는 노드가 별도로 존재하지 않기 때문입니다.

  그래서 처음에 쉽게 생각했습니다.
  'NiFi Clustering을 통해서 작업도 쉽게 분배하고, HA도 이룰 수 있겠구나.' 라고...

  결과는 둘 다 아니었습니다?
  HDFS, Hive, Spark 같이 처음부터 distributed 환경에서 쓰도록 만들어진 Application을 너무도 쉽고 당연하게 접할 수 있어서, 막연히 비슷하게 생각했던 것 같습니다.


  먼저, NiFi Clustering.

  3개의 노드를 하나의 클러스터로 구성을 하고, 그 중 하나의 노드의 UI에 접근을 해서 작업을 정의한다고 생각해 봅시다. 간단하게 아래와 같은 flow로 작성을 했습니다.
  ExecuteSQL -> ConvertAvroToORC -> PutHDFS
  그러면, 3개의 NiFi 노드에 동일하게 해당 flow가 생성이 됩니다. 그리고 작업을 전부 시작시켜 봅시다. 첫 번째 Processor(ExecuteSQL)가 폭주(?!)하지 않도록 적당한 schedule을 넣어 실행시켜 보겠습니다. 3개의 노드에서 모두 같은 SQL을 수행을 할 것이고, 그 결과 파일을 각자의 노드로 가져와서 ConvertAvroToORC 작업을 수행한 후에 해당 flowfile의 이름대로 HDFS에 파일을 put 할 것입니다. 즉 같은 파일이 3개가 HDFS에 쌓이게 되겠죠. (?)
  NiFi를 Single 노드로 쓸 때와 같이 작업을 정의하게 되면, 클러스터링된 NiFi는 각각의 노드에서 모두 같은 작업을 수행하게 됩니다. 사실 문서에도 해당 내용을 나와 있습니다. 클러스터링이 된다고 했지, 작업 분배를 자동으로 해 준다고는 안 했으니까요. (작업 분배가 필요하다고도 나와 있습니다.)
  그래서, NiFi 클러스터링에서는 작업 분배가 필요하고, 보통 아래와 같은 추가적인 Data flow 정의가 필요합니다.

  1) Kafka와 같은 queue로부터 작업을 시작 (ex. ConsumeKafka, ConsumeJMS)
  2) Nifi의 remote group이나 HAProxy처럼 Nifi Clustering 앞 단에서 분배해서 시작
  3) Data source를 바꿀 수 없는 경우 (대부분), 해당 데이터를 가져와야 할 processor를 On Primary Node로 설정한 후, 1,2와 같은 방법을 통해 작업을 재분배

  어쨌든 작업을 분배했으니 클러스터링해서 사용하는 의미를 찾을 수 있습니다. 우리는 처리해야 할 data에 비해서 cpu core도 부족하고, NiFi JVM에게 할당한 메모리도 넉넉치 못한 서버라서, 어쩔 수 없으니까요. 하지만 클러스터링이 곧 HA는 아닙니다.


  그러면 이제 HA (High Availability)

  NiFi를 3개의 노드로 클러스터링을 했으니까, 노드 하나가 죽어도 NiFi는 다음 리더를 선출해서 남은 노드들 중에서 Cluster Coordinator(및 Primary Node)를 시키면서 클러스터링을 유지해 나갑니다. NiFi UI를 통해서 현재 클러스터링 상태가 2/3 으로 바뀌는 것을 볼 수 있습니다. 그러니까 HA라고 할 수 있을까요.

  이번에도 이미 안 될 거라고 말했는데요.. NiFi도 클러스터링이 된다고 했지, HA는 된다고 한 적이 없습니다. 아마도 Failover가 안된다는 것이 조금 더 정확한 표현일 지 모르겠습니다.
  되는 것부터 보면, 하나의 노드가 떨어져나간 클러스터링의 상태에서도 Primary node를 zookeeper를 통해 새로 선정하게 될 것입니다. 그러면 위에서 말한 어느 방법이더라도 작업 분배에는 이상이 없습니다.

   문제는 이미 분배된 작업(및 data)인데요. 죽은 노드에서 돌던 작업들은 그 노드의 NiFi 프로세스를 되살리기 전까지는 그냥 사라져버린다고 볼 수 있습니다. 돌고 있는 노드들이 뭔가 조치를 해줄 수도 없고, 분배된 작업만 수행하는 입장에서는 솔직히 무슨 문제가 있는 지도 모릅니다.
  이 점은 정의해 놓은 flow가 복잡하고 오래 걸리는 작업일 수록, 그리고 데이터가 빠지지 않고 다 들어오는 것이 중요할 수록, 그리고 시간 제약이 있는 데이터일수록 문제가 크다는 생각을 합니다.
  어떤 이유에서 죽었는 지는 모르겠지만 해당 노드의 NiFi를 되살리면 NiFi의 ContentRepository, FlowFileRepository가 힘을 발휘해서 이어서 작업을 할 수는 있습니다.
  죽은 노드에서의 작업을 온전히 재수행이라도 시키기 위해서는 해당 노드의 NiFI를 어떻게든 되살리는 것 말고는 답이 없습니다.



  그래서

  ...
  '그래서'라고 부제목을 달고 보니, 답도 없으면서 무얼 위해 이렇게 장황하게 썼는가라는 생각이 드는 순간입니다. 저는 모르겠습니다. 비슷한 고민을 하는 Nifi 개발자가 Confluence에 던진 제안이 있는데 보면 요약해보면, Repository 를 HDFS와 같은 외부의 distributed 환경에 저장을 하고, failover 시 활용할 수 있도록 하면 HA를 할 수 있을 것이라는 내용입니다.(https://cwiki.apache.org/confluence/display/NIFI/High+Availability+Processing)

  NiFi 적용을 고려 중이시라면, 위 내용을 알고 접근하시면 좋을 것 같다는 생각에 글을 남깁니다. 긴 글 읽어주셔서 감사합니다. (사실은 넋두리..)