본문 바로가기

카테고리 없음

Elasticsearch-dsl, Django 삽질 복기(1)

로그 분석 기능 개발 중.

향후 통합 로그 관리 기능을 염두에 두고 Elastic Stack 으로 밑그림을 그리고 시작했는데 당장의 요건은 장애 발생 후 로그 파일을 토대로 원인 추적, 분석이 핵심이다보니 편리한 하향식 검색 기능이 중요한 상황이다.
Kibana 만으로 화면 구성하는 것은 한계가 있다고 판단하고 Elastic Search(이하 ES) API 이용한 화면 개발 쪽으로 가닥 잡고 레퍼런스 검색.


ES 잘 모르는데, Django 개발 안해봤는데, Vue.js 써본 적 없는데 ... 그래도 이 글이 제일 깔끔해보이기에 따라 가보기로 했다.

우선 글 대로 따라하기. 안 된다.
documnets.py 의 @talks.doc_type 부분에서 문제 발생했는데 ES 7.* 대에서는 Index 에서 doc_type 속성이 deprecated 된 탓(참고 문서는 ES 6.6.1 환경에서 작성)
맞는지는 모르겠지만 @registry.register_document 로 바꾸고 나니 동작한다.

따라하기로 짠 Djanog 앱 소스 복사해서 우리 데이터에 맞추어 model, search, template 수정. 안 된다. 당연하게도.
에러 기록해두지 않아 정확하지는 않지만 id 의 type 문제 때문에 search.py 의 TalkDocument.search().query(query) 에서 문제 생겼던 것으로 어렴풋이 기억난다.
일단 어찌되나 보자 싶어 과격하게 elasticsearch-dsl 소스 까서 id 타입 체크하는 부분 막아보니 에러는 안 나는데 결과값이 아무 것도 안나온다. 에러 조차 안나니 추적이 더 안된다 ㅜㅠ
Logstash 에서 데이터 파싱할 떄 ID 필드 추가하고 int 타입의 일련값을 생성해서 이 필드값을 _id 대신 사용하려고 시도, 자동 생성되는 인덱스 대신 사용자 정의해서 하는 방식 시도(예전 버전에서는 _id 컬럼 수정하는게 가능했으나 지금은 deprecated 된 상황이라 함) 등등 해봤다. 안된다 ㅜㅠ. 이 후 생쇼하면서 하루 날린거는 패스.

처음부터 찬찬히 다시 생각해보았다. 각 기술 요소 지식이 부족하니 추적은 어려웠지만 원인은 간단했다.

레퍼런스의 동작 구조는 대충 이해한 바로는 다음과 같다.
1. 로우 데이터를 DB 에 넣고 그걸 ES에 벌크 인덱싱한다. 
2. 구현한 search 함수는 ES 에서 Document 의 ID 필드값을 구한 뒤 DB 테이블에서 그 ID 값 가진 레코드를 queryset 형태로 리턴.

일단 이 구조는 우리거랑 안 맞는다. 로그 데이터를 직접 Logstash 태워 ES 에 직접 인덱싱한다. DB 는 사용하지 않는다.
이 상태에서 레퍼런스 소스 구조 그대로 사용하니 ES 인덱싱하면서 자동 생성된 문자열 타입의 _id 로 DB 의 int 타입의 테이블 pk 와 매핑하려고 하니 당연히 에러가 날 수 밖에.

Django 의 ORM 은 AutoGenerated 된 int 타입 pk 값을 기본으로 상정하는걸까? 잘 모르겠다. 그런데 더 들어갈 여유는 없다.

int 타입 문제 해결하려고 삽질한거는 그냥 뻘짓이었다. 그 문제 해결한들 테이블에 보여줄 데이터 없는데 뭔 소용이란 말인가!

원인 간단하니 해법도 명료하다. elasticsearch dsl API 이용해서 그냥 search 함수 따로 구현하면 되지 않나?!

참조 문서의 search.py 내용 싹지우고 동작 확인할 수 있을 정도의 수준으로 뚝딱뚝딱 고쳐봤다.

def search(phrase): 
  q = Q("match", message=phrase) 
  s = Search(using=client, index="eep-log") 
  return s.query(q) 


된다. 그런데, 뭔가 부족하다.
시간 순서가 뒤죽 박죽이다. 데이터가 10개 씩 밖에 안나온다.

과제 시작할 때 ES 책 빨리 훑었었는데 그 기억 되살려보면 정렬 문제는 ES 의 shard, segment 구조에서는 당연한 문제일 듯 싶다. 찾아보니 인덱싱할 때 다음과 같은 설정하면 될 듯 한데 일단은 코딩 차원에서 해결해보기로 했다.

"settings" : { 
  "index" : { 
    "sort.field" : "date",  
    "sort.order" : "desc"  
  } 
} 



데이터가 10개 씩 밖에 안 나오는 문제는 성능 때문에 기본 설정이 그렇다하고 paging 형식으로 쓸 수 있게 해놓았다. 이것도 무식하게 코드로 해결해보기로 했다. 

10개 문제와 정렬 문제 해결 위해 고친게 이런 식.

def search(phrase): 
  q = Q("match", message=phrase) 
  s = Search(using=client, index="eep-log") 
  s = s.query(q) 
  s = s.query(q).sort("@timestamp") 
  total = s.count() 
     
  response = s[0:total] 
  return response 


된다. 조금 나아졌다.

아 그런데 큰 놈이 나왔다. 로그 데이터에 포함된 json 값들이 검색이 안된다.
이 문제는 내일 정리해보련다.