[DEMO]

[동기]

모 앱스토어에서 결제시스템을 운영하면서 크고작은 많은 문제들이 발생을 하였고, 

제니퍼와 같은 기존의 서버 모니터링 툴로는 위험 상황을 감지하기 힘든 상황이 계속해서 발생하게 됩니다.



[운영 장애 사고의 유형]

  • 예측하기 힘든 트래픽
  • 자동화된 툴 혹은 인위적인 서비스 취약점 공격


[요구사항 원칙]

  • 앱스토어를 통해 배포된 앱별로 모니터링이 가능할 것
    • 배포된 앱별 사용자수와 앱의 이벤트 시점에 따라서 트래픽이 급증하는 상황이 발생
  • 특정사용자 혹은 사용자군의 모니터링이 가능할 것  
  • Velocity Check
    • 통상적인 수치를 벗어나는 상황에 대한 인지가 필요 (QA나 혹은 인지할 수 없는 오류 혹은 장애 확인 필요)
    • FDS 시스템과 연동 등을 고려 (특정 API에 대해서 일정 수준이하의 응답 시간 확보)


[고려사항 및 설계]

  • 로그 수집을 통한 실시간 혹은 대량의 데이터에 대한 MR 분석이 가능할 것
    • 별도 plugin 설치등 Live 서비스에 영향을 주지 않고 각 was 인스턴스의 로그만 수집하는 agent만 가동
    • 특정 기능에 대해서 일정수준 실시간 성을 지원하여 허용 시간 범위내에서 데이터 분석이 가능할 것
    • MR을 활용한 대량의 데이터를 분석하여 원사는 데이터를 추출 할 수 있을 것
    • 다양한 데이터 분석 결과에 대응할 수 있는 Chart를 지원할수 있을 것
  • 개발과 운영 비용의 최소화
    • IT회사가 아니기 때문에 장비 지원은 항상 최소를 고려 (ㅠㅠ)
      • 시스템 운영으로 장애 및 피해 예방을 통하여 비용확보 필요
    • 기존 업무를 하면서 남는 시간을 활용하여 개발 진행, 초기 운영까지도.. 개인 업무 Capacity 고려


[개발 stack]

  • Java 8
  • SpringBoot Web (with thymeleaf)
  • d3.js
  • Hbase 1.2


[참고 자료]

  • OpenTSDB
    • Hbase를 활용하여 시계열(time series) 데이터 처리를 위한 sheme 설계에 참고
  • D3.js
    • 다양한 정적 or 동적 차트 처리를 위해 도입



[시스템 구조]
  • LogCrawlAgent
    • 각 Live 서버상의 로그를 실시간으로 전송(http keepalive socket)
    • jar console application
  • TXD (Transaction Deamon)
    • 실시간 모듈 : API, 실시간 처리, UI 제공
    • Batch 모듈 : 앱 전체의 통계 및 Report 추출

[데이터 스키마]

  • 총 6개의 Table로 구성되며, 
    • 실시간 시계열 데이터 처리를 위한 tsTable은 아래와 같이 OpenTSDB 유사 scheme 적용 (아래)
    • 사용자 데이터의 Velocity Check의 응답 속도 보장을 위한 별도의 table에 scheme 적용
  • MR처리 효율상 특정 부분은 bit가 아닌 String, Number 등의 데이터 Type 적용

범주RowKey
구성metricId:3bytebaseTimeStamp:4bytekey1:3bytevalue1:3bytekeyN:3bytevalueN:3byte
각주정의된 metric 시단위까지의 변환값추가정보 key값추가정보 value값추가정보추가정보

범주Column
구성qualifervalue
데이터 구조time offset:4byteString or Long
각주

시단위 미만 시간값

임의 등록 가능



[Major Features]

  • 메인화면
    • 상단 Traffic Chart는 30초 주기로 갱신 (Cubism.js 적용)
    • 트래픽, 결제 상위 앱들의 랭킹을 최근 10분 및 일간 누적 기준으로 제공
    • 고 결제빈도 사용자 군 랭킹 추출

  • 실시간 로그 확인
    • 각 API별 트래픽의 갱신주기를 사용자가 정의할 수 있도록 하여 긴급 상황 대응

  • 트래픽 비교
    • 양일간 특정 트래픽의 추이 비교를 통하여, 감지되지 않은 장애상황 및 프로모션 등 이벤트 등 마케팅 분석에 활용


  • 앱별 트래픽
    • 특정 트래픽을 기준으로 앱별 & 시간대별 순위를 추출하여 차지하는 비중을 확인


  • 상위 결제 유저 
    • 자동화 툴 혹은 악의적(실험적)의도의 사용자 트래픽의 분석 확인

[개발과 운영을 하면서]

  • Hbase는 실시간 및 대량 Batch(MR)의 두마리 토끼를 잡을 수는 있으나,
    • 잘못된 Scheme 적용시 Data 증가에 따라 응답 속도가 기하급수적으로 느려질 수 있음 (당연한 이야기 이겠지만..)
    • Scheme 디자인시 철저히 데이터 실시간 요구사항에 근거해야 하며, 이외의 기능은 MR로 커버 가능
    • MR자체의 성능은 Hadoop의 RawData 직접 처리보다는 느리다 (2,3배 정도)
    • 성능 및 효율을 위해서는 Bit 단위의 data 를 기반으로 하는 scheme 적용이 필요하나,
      • Hadoop Eco 시스템 기반의 쿼리 등의 다양한 툴을 적용하기에 어려움이 있으며,
      • 경우에 따라서는 별도의 공용(범용) Query Lang혹은 API 제공이 필요할 듯 보이며,
      • 개발 자체의 어려움도 수반하니 신중을 기해야 하겠다
  • D3.js는 정말 훌륭하다
  • 부하량은,
    • Agent의 부하는 CPU사용률 1%이내로 Live 시스템에 미치는 영향이 거의 없으나,
    • TXD의 특정 쿼리의 실시간 처리를 위해 CPU사용률이 최대 20%가량 치솟는 경우도 있었음
      • 실시간 통계 처리를 위해서는 Cache등의 적용이 필요할 듯 (사용자가 늘어나면..)
    • Hbase
      • StandAlone 모드로 300tps 수준 로그 적제시에도 CPU 사용률은 최대 3%를 넘지않은 미미한 수준
  • TBD


'Expired > Hadoop Hbase Nutch2' 카테고리의 다른 글

Hbase JSON API 사용하기  (0) 2015.01.04
Nutch Cycle Step  (0) 2015.01.03
하둡 이클립스 플러그인(Hadoop1.1.0 Eclipse Plugin)  (0) 2012.11.17

아직 HBASE에 쓸만한 client tool 이 없는 것 같네요. 

몇몇 오픈소스와 cloudera쪽의 몇개 툴들이 보이긴 합니다만, 무거움은 사람과 시스템을 지치게 만들죠.


찾아보니 HBASE에서 공식적으로 제공되는 API 툴이 있는 것 같습니다.

KVP, Json, XML 등 다양하게 지원하는 듯 하구요, Stargate라는 경량 Servlet container를 Jetty상에서 실행시켜야 사용할 수 있습니다.

 % ./bin/hbase rest start -p <port>  


아래는 실행 화면



자세한 내용은 아래 공식 페이지 참조

 https://wiki.apache.org/hadoop/Hbase/Stargate




InjectorJob

Crawling에 필요한 URL을 crawlDB에 적재하는 작업. 해당 Job을 구동시키기 위해서는 URL 리스트가 제공되어야 하며, 일반적으로 URL Directory를 생성하여 해당정보 파일을 넣어둔다. 다음은 Nutch에서 InjectorJob을 실행시키는 명령어,

#bin/nutch inject crawl/crawldb urls

urls는 crawling 대상 seed URL이 포함된 디렉토리이고, crawldb는 해당 url이 포함된 nutch의 자료구조에 해당된다.


GenerateJob

InjectorJob이 완료되었으며, CrawlDB에 저장된 URL을 fetch (다운로드 및 각종 operation) 해야 한다.

URL에 대해서 fetch작업 이전에 우선적으로 GenerateJob이 실행되어야 하며, 다음은 명령어 이다.

#bin/nutch generate crawl/crawldb crawl/segments

crwaldb는 URL이 생성된 db이며, segment는 crawling시에 필요한 정보를 fetch하는데 필요한 디렉토리이 이다.


FecherJob

GeneratorJob에 의해 생성된 URL을 실질적으로 fetch하는 역할로, GeneratorJob에 의해서 생성되는 input으로 fetch(consume)작업을 수행한다.

#bin/nutch fetch -all

-all 옵션은 Generator에 의해 생성된 모든 URL을 fetch하겠다는 의미이다.


ParserJob

Fetch가 완료된 이후에 URL에 대한 파싱작업을 수행하며, 명령어는 아래와 같다.

#bin/nutch parse -all


DbUpdateJob

일단 ParserJob이 완료되며, FetcherJob에 의해 제공되는 결과를 DB에 Update를 해야한다.이 작업으로 각자의 DB에 가장 최근에 Fetch된 URL의 정보를 update한다. 명령어는 다음과 같다.

#bin/nutch updatedb crawl/crawldb -all

이 작업이 완료되면 DB는 초기 페이지의 update된 정보와 신규 페이지 정보가 DB에 적재된다. 신규 페이지는 Link 정보를 포함된 새롭게 발견된 페이지를 의미한다.


Invertlinks

인덱스를 적용하기 이전에, 모든 링크에 대한 invert가 필요하다.이 작업을 통하여, 특정 페이지에 대해서 anchor text를 인덱스에 적용할 수 있다. 다음은 명령어이다.

#bin/nutch invertlinks crawl/linkdb -dir crawl/segments


Indexing with Apache Solr

크롤링이 완료되며, Nutch에 의해 완료된 URL 데이터에 대해서 Apache Solr로 인덱싱이 가능하다. 인덱싱이 완료되면 특정 URL을 쉽게 검색할 수 있다. 다음은 명령어 이다.

#bin/nutch solrindex http://127.0.0.1:8983/solr/ crawl/crawldb -linkdb crawl/linkdb crawl/segments/*


** 의역 문서 입니다.  (참고 http://www.scala-lang.org/node/107)


Case class는 일반적은 Parameter를 포함하는 생성자를 제공하는 일반적은 클래스와 패턴매칭을 통해 재귀적 객체 추출구성(recursive decomposition)을 제공한다.

아래는 clase class의 예제

abstract class Term
case class Var(name: String) extends Term
case class Fun(arg: String, body: Term) extends Term
case class App(f: Term, v: Term) extends Term


사용상의 편의를 위해서 calse class의 생성시 new 키워드를 사용하지 않아도 된다. 클래스 명을 함수같이 쓰면 된다.

아래의 예제 참고

Fun("x", Fun("y", App(Var("x"), Var("y"))))


생성자의 parameter는 모두 public 변수로 간주되며, 아래와 같이 직접 접근이 가능하다.

val x = Var("x")
Console.println(x.name)

 

모든 case class는 equal method(내부적으로 equality 규칙과 toString method가 재정의 된)가 컴파일러에 의해 자동 생성되어 아래와 같이 사용할 수 있다. 

val x1 = Var("x")
val x2 = Var("x")
val y1 = Var("y")
println("" + x1 + " == " + x2 + " => " + (x1 == x2))
println("" + x1 + " == " + y1 + " => " + (x1 == y1))


위의 실행 결과는 아래

Var(x) == Var(x) => true
Var(x) == Var(y) => false


case class는 아래와 같이 data structure를 구분하는데 사용될 수 있다.

object TermTest extends Application {
  def printTerm(term: Term) {
    term match {
      case Var(n) =>
        print(n)
      case Fun(x, b) =>
        print("^" + x + ".")
        printTerm(b)
      case App(f, v) =>
        Console.print("(")
        printTerm(f)
        print(" ")
        printTerm(v)
        print(")")
    }
  }
  def isIdentityFun(term: Term): Boolean = term match {
    case Fun(x, Var(y)) if x == y => true
    case _ => false
  }
  val id = Fun("x", Var("x"))
  val t = Fun("x", Fun("y", App(Var("x"), Var("y"))))
  printTerm(t)
  println
  println(isIdentityFun(id))
  println(isIdentityFun(t))
}

위의 예제이서, print 함수는 각 matching 상태를 나타내며, match 키워드에 해당 case의 구현체(body)에 matching 되는 형태를 취하고 있다.

isIdentityFun은 주어진 term이 simple identity 조건에 부합하는지를 검사하는 함수이다. 주어진 값이 패턴에 matching된 이후에 if 함수가 실행(evaluate)된다. 성공적으로 매칭된 경우 true를 return 하고 fail인 경우 다음 pattern matching을 시도하게 된다.

실행 결과는 아래와 같다.

^x.^y.(x y)
true
false
x == y는 scala 컴파일러에 의해 구현된 equal 함수에 의해 자동적으로 처리되었음을 확인 할 수 있다.


학창시절 Swing을 정말 열심히 공부했던 기억이 난다.. 그러고 보니 그때가 이미 10년전인가..

Swing은 MVC기반의 Java의 UI Framework으로 기존의 AWT 제약을 탈피하고, 탄생한 Java GUI를 위한 회생의 역작이라고도 할 수 있다..

물론 그 이면에는 MS의 아성을 띄어넘기 위해 PC Client 시장을 장악하기 위한 Sun의 초대회장 Scott Mcnealy의 야심의 결과물인지도 모르겠다..

어찌되었건,

학창시절 정말 열심히 Swing을 공부했다.. Swing은 철저희 MVC의 기본 원칙을 내세우며 완벽에 가까운 객체지향 기반의 API를 제공한다. Swing 한번 정도 해본 사람들이라면 JTable의 악몽을 기억할 것이다. 엑셀같은 간단한 Sheet를 표현하기 위해서 얼마나 많은 Interface와 Class간 의존구조를 알아야 하는지..

그리고 사실상 디자인 패턴이나 중고급이상의 S/W 설계 지식이 없으면 제대로 사용할 수 도 없는, 지금 생각해보면 극악의 어려움을 제공하는 구조임에는 틀림없었으나,

Swing의 사용 경험을 통해 객체지향 설계의 많은 부분을 경험할 수 있었고, 이후에 많은 프로그래밍 설계에 있어서 경험적 지식의 기반이 되었다는 점에서 공부한것에 대해 그리 나쁘지 않았다고 생각된다..


이후, Swing은 거의 사장되고, 아무도 쓰지 않는 상황이 되버렸지만,

어찌보면 Swing을 공부했던 사람들에게 Android는 정말 하늘에서 내려준 선물이 아닐까 생각이 들 정도로, 구조적인 측면 사용성의 측면에서 Swing의 것을 닮아있다..

물론 결국 둘다 GUI 프로그래밍을 위한 것이다 보니 설계의 구조적으로 비슷해 질 수 밖에는 없겠지만..


Android는 Swing 혹은 AWT에서 사용하던 LayoutManager들과 거의 유사한 기능을 제공하며, Swing 코드 작성시 많이 생성되는 반복적이고 Tree 구조의 특성을 xml로 분리하여, 개발 효율을 극대화 시켰다고 봐도 무방할 것 같다.

EventListener를 이용한 이벤트 Handling 방식은 Swing이나 AWT에서 사용했던 방식과 거의 동일하며, 모듈의 Life Cycle 관리나 Paint 메카니즘은등은 Applet의 그것과 매우 유사하다. (이건 뭐 다른 유사 시스템에서 비슷할 것으로 보인다)


Android Platform 초기에는 OSGi 가 탑재될뻔 했던적이 있었던 것으로 생각난다. OSGi 가 탑재되면 개발측면에서 많은 부분 Application 간의 의존성 관리나 App - Service 사용 측면에서 유리한 점이 많아 질 것 같지만, 현 분위기상 쉽지는 않아 보인다.

하둡 MapReduce 개발시, 개발환경을 어떻게 설정해야하는 것이 최선인지는 아직도 고민중입니다.

jar로 배포되어 Hadoop에 의해 로드되어 구동되는 구조적 특성상, 이클립스에 개발환경을 설정해서 뭔가를 해본다는 것이 상당한 제약이 많은 것이 사실이며, 이러한 이유 때문인지는 몰라도 아직 쓸만한 Eclipse Plugin하나 없는 실정입니다. 

Hadoop 정식 배포버전의 contrib 폴더에는 누군가에 의해 개발된 hadoop plugin이 있는데, 어떻게 설치라도 해볼려고 하니 쉽지가 않네요, ant 빌드를 해야하는데 한번에 될리가 없고, 설정상 수정이나 추가가 필요한 부분이 있습니다.

그래서 대략 일주일 삽질끝에 정상 동작하는 이클립스 플러그인으로 빌드하는데 성공하였습니다.

아래의 파일은 이클립스 Indigo(3.7), Hadoop 1.1.0 기준으로 빌드되어 정상동작이 확인되었습니다.


hadoop-eclipse-plugin-1.1.0.jar



타버전의 빌드가 필요하신 분들은 아래 링크를 참고하세요. 

(library 버전이 상위버전인 경우 정상동작 하지 않습니다.)

http://cloudfront.blogspot.kr/2012/07/how-to-run-mapreduce-programs-using.html

http://creatorw.tistory.com/entry/%EC%9D%B4%ED%81%B4%EB%A6%BD%EC%8A%A4%EC%97%90-%ED%95%98%EB%91%A1-%ED%94%8C%EB%9F%AC%EA%B7%B8%EC%9D%B8-%EC%84%A4%EC%B9%98%ED%95%98%EA%B8%B0




'Expired > Hadoop Hbase Nutch2' 카테고리의 다른 글

Hbase를 활용한 로그 모니터링 시스템 개발  (0) 2017.04.15
Hbase JSON API 사용하기  (0) 2015.01.04
Nutch Cycle Step  (0) 2015.01.03

+ Recent posts