kraken-confdb의 내부 구조 학술

이 글은 kraken-confdb의 내부 구조를 다룬다. kraken-confdb를 단순히 사용하는데 관심이 있다면 위키의 http://krakenapps.org/wiki/KrakenConfDB 글을 참고하시기 바란다.

1. 서론

2005년부터 보안 솔루션 개발을 해오면서 가장 큰 고민거리는 설정과 정책을 관리할 때 어떤 접근 방식을 취할 것인가 하는 점이었다. 대부분의 경우 설정과 정책을 관리하는 방식은 관계형 데이터베이스 (RDBMS)나 파일 (XML, JSON 등) 둘 중 하나로 양분된다. 나는 이 두 가지 접근 방식이 모두 불만족스러웠다. 

RDBMS는 멀티-티어 아키텍처를 구현하는 경우 일반적으로 사용되는 접근 방식이다. RDBMS를 사용하면 트랜잭션을 이용하여 원자성 (Atomicity), 일관성 (Consistency), 고립성 (Isolation), 지속성 (Durability)을 쉽게 구현할 수 있으므로, 다수의 프로세스나 스레드가 접근하더라도 별다른 고민할 필요없이 데이터를 처리할 수 있다. 그러나 RDBMS를 사용하려면 반드시 정규화를 해야 하고, 설정/정책이 복잡한 경우 원래 의도한 것보다 몇 배나 더 많은 엔터티가 발생하게 된다. 가령, 하나의 객체가 다수의 객체를 포함하는 경우 일 대 다 관계로서 일반적으로 2개의 테이블을 생성해야 한다. 데이터베이스가 안전하고 편리하기는 하지만 XML의 유연성에 비하면 모델이나 쿼리가 너무 복잡하다는 생각을 하게 된다. 비즈니스 데이터가 아닌 단순한 설정과 정책을 저장하는데 이렇게까지 하고 싶지는 않다. ORM이 많은 수작업을 대신해주지만 복잡도가 줄어드는 것은 아니다.

파일 접근 방식을 선택하는 경우에는 동시에 파일을 읽고 수정하는데 취약하다는 점을 피할 수 없다. 파일을 수정하고 있는데 전원이 나간다거나, 동시에 다른 프로세스가 접근하여 수정하면 파일이 깨질 확률이 높다. 여러분들이 오피스 문서를 작업할 때 주의깊게 보았다면 파일 손상을 막기 위해 반드시 임시 파일을 쓰고 이름을 바꿔치기 하는 것을 보았을 것이다. 그러나 다수의 동시 접근이 있을 때 이렇게 하기에는 곤란하다. 그렇다고 무조건 파일에 잠금을 걸고 읽고 쓰기엔 동시성이 매우 떨어질 것이다.

한편으로, 오랜 기간 개발하면서 대부분의 관리/운영 담당자가 인프라에서 핵심적인 네트워크 및 서버의 설정 실수를 두려워하며 주기적으로 설정 백업을 수행한다는 사실을 알게 되었다. 만약 우리가 개발할 때 일상적으로 사용하는 버전 컨트롤 시스템을 설정 관리하는데도 사용할 수 있다면 과거 설정 이력을 쉽게 조회할 수 있을 뿐만 아니라 설정을 실수하더라도 장애 발생 원인을 쉽게 파악하고 과거의 특정 시점으로 되돌릴 수 있게 될 것이다. 또한 HA 시스템에서 설정을 동기화 할 때에도 전체 설정을 전송하여 복제하는 대신, 설정 변경사항만을 전달하여 복제할 수 있다.

confdb는 RDBMS와 파일 양쪽 접근 방식의 장점만을 취하고 설정에 형상 관리를 도입함으로써 위에서 언급한 문제들을 해결한다.

2. confdb 파일 포맷

confdb는 mercurial의 설계를 참고하였다. mercurial의 내부에 대하여 궁금하다면 Towards a Better SCM: Revlog and Mercurial 논문을 참고하기 바란다. confdb는 로그 파일과 데이터 파일의 쌍으로 구성된 RevLog를 사용한다. 로그 파일은 오퍼레이션의 유형 (문서 생성, 문서 갱신, 문서 삭제, 컬렉션 생성, 컬렉션 삭제 중 하나)과 리비전, 문서의 ID와 데이터 파일에 기록된 위치 (파일 오프셋), 문서의 길이를 담은 34바이트짜리 레코드의 집합이다.

하나의 로그 레코드 레이아웃은 다음과 같다:
  • 리비전 (8바이트)
  • 이전 리비전 (8바이트) - 충돌 탐지 및 이후 브랜치 확장 대비
  • 오퍼레이션 유형 (1바이트)
  • 0 패딩 (1바이트)
  • 문서 ID (4바이트)
  • 문서 파일 오프셋 (8바이트)
  • 문서 길이 (4바이트)
데이터 파일 레이아웃은 다음과 같다:
  • 문서 길이 (4바이트)
  • 옵션 (4바이트) - 향후 압축, 무결성 검증 등의 확장을 대비한 것
  • 문서 본문 (가변 길이)
이렇게 로그 파일과 데이터 파일이 한 쌍으로 RevLog를 구성하는데, 이를 이용하여 컬렉션, 매니페스트, 변경로그를 구성한다. 컬렉션 RevLog는 단일 컬렉션에 다수의 문서들을 보관한다. 매니페스트는 ID, 다수의 CollectionEntry와 다수의 ConfigEntry로 구성되는데, 이를 통해 컬렉션의 메타데이터와 특정한 리비전의 문서에 대한 포인터를 관리한다. 마지막으로 변경로그는 리비전, 커밋 일시, 커미터, 커밋 로그, 매니페스트 ID, 그리고 해당 커밋에서 변경된 모든 컬렉션/문서에 대한 기록을 포함한다.

즉, 변경로그는 매니페스트의 ID를 참조하고, 매니페스트는 각 문서의 리비전을 참조하기 때문에, 특정한 리비전에서의 문서 집합을 바로 얻어낼 수 있다. 또한 전체적으로 RevLog의 자기복제적인 구조를 갖추고 있으므로 짧은 시간 안에 구현할 수 있다.

3. 동시성 제어

설정의 변경은 자주 일어나지 않지만, 읽기는 매우 빈번하게 발생한다. confdb는 파일 끝에만 데이터가 추가되는 append only 모델을 갖추고 있다. 쓰기가 발생할 때 컬렉션 데이터를 먼저 쓰고, 매니페스트, 변경로그 순으로 쓰기가 발생하기 때문에, 읽기 수행 시 별도로 잠금을 걸지 않더라도 불완전한 읽기를 방지할 수 있다. 쓰기를 수행할 때는 쓰기의 시작부터 끝까지 데이터베이스에 전역적인 파일잠금을 건다. 이를 통해 다른 프로세스가 동시에 쓰는 일이 없도록 방지한다.

4. 충돌의 탐지

일반적으로 뷰와 모델의 간극으로 인해 동시에 읽고 쓰기를 수행할 때, 동일한 리비전에 대해 다른 사람이 이미 수정한 데이터를 덮어쓰는 경우가 발생할 수 있다. confdb는 변경을 수행할 때 넘겨받은 이전 Config 개체의 리비전을 탐색하여 충돌이 발생했는지 탐지하고 예외를 발생시킬 수 있다. 이는 낙관적인 잠금 시나리오 구현에 유용하게 사용될 수 있다. 물론 충돌과 관계없이 덮어쓸 수 있도록 충돌 무시 옵션도 제공한다.

5. 트랜잭션의 구현

컬렉션 데이터와 매니페스트를 쓰더라도 마지막 순간에 변경로그를 기록하지 않으면 읽을 방법이 없어지기 때문에 트랜잭션 역시 간편하게 구현된다. 트랜잭션을 커밋하면 변경로그를 쓰고, 도중에 예외가 발생하거나 롤백을 호출하는 경우 변경로그를 쓰지 않는다.

6. 플래시백(회상)과 롤백

특정 시점의 변경로그를 선택하면, 변경로그에 연결된 매니페스트와, 매니페스트에 연결된 문서 리비전들을 통해 과거 시점의 문서 내용들을 들여다 볼 수 있다. 플래시백 모드에서는 수정이 불가능하다. 롤백은 변경로그를 새로 쓰면서 과거의 매니페스트 ID를 쓰는 방법으로 간단히 구현된다. 롤백하면 최신의 리비전이 과거의 매니페스트를 참조하는 것이므로 이후 수정도 가능하다.

7. 문서 인코딩과 리플렉션의 활용

문서는 RevLog 파일에서 바이트 배열로 저장되는데, confdb는 자바 객체 그래프를 크라켄 코덱을 이용하여 직렬화하고 저장한다. 크라켄 코덱은 ASN.1 DER과 유사한 인코딩 규칙으로 구성되어 있어서, 바이트 배열로 직렬화 된 상태에서도 자바 원시 타입을 보존할 수 있고 읽을 때도 원본 자바 객체 그래프를 그대로 추출할 수 있다. 크라켄 코덱은 위키 페이지 http://krakenapps.org/wiki/KrakenCodec를 참고하기 바란다. 

크라켄 코덱이 자바 원시 타입과 기본적인 컬렉션 타입 (Map, List, Array 등)만을 지원하지만, confdb는 임의의 사용자 정의 타입 또한 리플렉션을 이용하여 원시 타입과 컬렉션 타입의 조합으로 자동 변환할 수 있는 기능을 갖추고 있다. 따라서 사용자는 confdb를 객체를 쉽게 저장하고 그대로 꺼낼 수 있는 객체 지향 데이터베이스로 사용할 수 있다.

8. 결론

kraken-confdb 라이브러리는 설정 형상 관리(configuration version control)를 지원하는 내장 가능한 경량 객체 지향 데이터베이스 (embeddable leightweight object-oriented database) 이다. 형상 관리는 코드 뿐 아니라 설정을 관리하는데도 유용한 개념이다. 무거운 데이터베이스를 사용하는 대신 confdb를 사용하면 객체 그래프를 쉽게 영속적으로 저장하고 원하는 시점에 따라 조회할 수 있다.

궁금한 점이 있다면 언제든지 메일(xeraph@nchovy.com)로 연락하시기 바란다.

충격과 공포의 URL.equals() 코드

http://docs.oracle.com/javase/1.5.0/docs/api/java/net/URL.html#equals(java.lang.Object)

Compares this URL for equality with another object.
If the given object is not a URL then this method immediately returns false.

Two URL objects are equal if they have the same protocol, reference equivalent hosts, have the same port number on the host, and the same file and fragment of the file.

Two hosts are considered equivalent if both host names can be resolved into the same IP addresses; else if either host name can't be resolved, the host names must be equal without regard to case; or both host names equal to null.

Since hosts comparison requires name resolution, this operation is a blocking operation.

Note: The defined behavior for equals is known to be inconsistent with virtual hosting in HTTP.

아침에 http://staging.krakenapps.org/와 http://download.krakenapps.org/ 가 equal로 나와서 코드가 망하는 바람에 찾아봤는데 아 이런 충격과 공포의 그지 깽깽이 썬 놈들아.... 시키지도 않은 IP 매칭하지마..

ps. 코드 다 고쳐야 됨 (...)

[Future ASF 2011] WeGuardia KRadar 발표자료 NCHOVY


2011년 회고

폭풍 같았던 2011년이 저물어간다.. 한달간 탁묘를 하다보니 마치 고양이 블로그처럼 변해버린듯 (..)

큰 사건들을 시간대 위주로 정리하면 아래와 같다.

- 퓨쳐시스템 프로젝트 시작
- SMD 프로젝트 드랍
- NCHOVY 인터넷 스톰 센터 XMLRPC 오픈API 공개 
- 퓨쳐시스템이 엔초비 인수
- 뇌수막염으로 졸업 실패
- 반딧불 1명 외 전원 퇴사
- SSLVPN 개발 완료 후 CC인증 시작
- IPS+DLP 정부과제 완료 (여름에 현장평가 후 12월 8일 최종 PT 진행)
- 로그DB 완성 (현재 논문 작성중)
- 차세대 SIEM 개발 진행 중
- NCHOVY 인터넷 스톰 센터의 스톰캐스트 개편 진행 중

작년에 무리하게 달려서 누적된 피로도 심각한데, 올해 인수합병으로 두 조직을 합치게 되면서 많은 잡음과 균열이 생기면서 엔초비가 거의 붕괴할 뻔 했다. 한 달간 병원 신세를 진 것도 그렇고 아무래도 졸업을 목표로 학교에 매달리다보니 상반기는 엉망진창으로 돌아가면서 제대로 진행된 일이 거의 없었다. 그동안 연관되어 있던 회사들에게 소스 인계해주고 정리하는 것도 상당한 에너지와 시간을 소모했고, 정부과제도 잡일이 많아서 등에 지고 있던 짐을 정리하는데만도 상반기가 다 날라갔다. 우리의 프레임웍과 반딧불에서 그동안 써오던 개발툴들을 통합하거나 빌드시스템 셋업, 업무적인 역할분담 정리, 통합된 조직에서의 자기 위치에 대한 고민과 갈등 등등 완전 혼돈의 카오스-_- 무수히 많은 이슈들이 쏟아졌다.

결국 자기는 더 이상 필요없을 것 같다고 그만두거나, 개발하고 싶어서 왔는데 기획시킨다고 그만두거나, 작년에 냈던 대기업에 붙었다고 가거나 공부한다고 가거나 기타 등등의 이유로 줄줄이 빠져나가고.. 나도 황천길 가다 겨우 돌아오고.. 초반에 이사님이 뭐든지 다 할 수 있을 것 같던 자신감(?)으로 무리하게 잡았던 플랜이 아작나면서 조직에서의 신뢰가 바닥을 기고.. 그렇게 되면서 여름 이후에는 분위기가 매우 좋지 않게 돌아갔다. 언제 터질지 모르는 폭탄 하나 끼고 사는 기분으로 매일 불안하기 짝이 없었지.. 이 때는 다들 출근하는 것 자체가 괴로운 상황이 계속되었다. 그러다가 SSLVPN 데모하기 직전에 결국 폭탄 터져서 갈라서고 이것도 안 해, 저것도 안 해 하다가 결국 퇴사하셨다.

이 때가 참 불안하고 난감한 시점이었는데.. 남 부장님이 splunk를 끼워서 팔자고 데모를 부르시는 바람에 나도 우연히 다이나믹 대시보드가 최신 splunk 버전에 올라가있는 것과 사람들 반응을 보고는 저걸 당장 해야겠다는 생각을 했다. 오랜만에 splunk를 다시 보다보니 어쩐지 맵리듀스 구조도 어떻게 구현해야 할지 자연스럽게 생각이 이어졌고, SSLVPN을 진행하면서 크라켄 로그DB에 실시간 압축도 넣고 시간정렬 문제도 해결하고 계속 refine 해왔기 때문에, 타이밍이 잘 맞아서 한 번에 다이나믹 대시보드를 올릴 수 있었던 것 같다. 게다가 별 생각없이 mercurial 논문을 보다가 아이디어를 얻어서 버전 컨트롤이 지원되는 설정DB까지 한 큐에 다 맞출 수 있었다.

아무튼 이제서야 퓨쳐가 크라켄을 충분히 인식하게 되었고.. 내가 생각하는 그림들이 몇 년 삽질 끝에 다 구현되었으니, 내년에는 본격적으로 오픈소스 버전 UI 개발과 클라우드를 진행할 수 있을 것 같다.. 올해는 BMT도 깨지고 우울했는데 내년에는 누가 들이대더라도 퓨쳐를 이기기 쉽지 않을 것이다..

일련의 사건들로부터 얻은 교훈은 아래와 같다:

- 죽는거 별거 아니다(?) 순식간에 훅 갈 수 있다.
- 어차피 금방 죽을거라면 하고 싶은 일하면서 살자
- 개발 집중력은 체력에서 나온다
- 개발 능력보다 비전 설정, 시장 흐름 읽기, 적절한 조직 내 커뮤니케이션과 일정 조율이 더 중요하다
- 내가 여기저기 잇는 잡노가다 하는 동안 집중이 필요한 고난도 개발업무는 통짜로 떠넘기는게 효율적이다
  (mindori가 크라켄 PCAP과 L7 스택을 만들고 mitz가 로그DB를 만든 것처럼)
- 당연한 얘기지만 기술보다 가치에 집중해야 사람들이 알아먹고 인정해준다
- 완벽을 목표로 개발만 하고 끊어치지 못하면 팔 방법이 없어진다. 적당히 앞설 때 잘 봐서 끊고 팔아야 한다
- 데모 전날에는 수정하지 말자
- 적게 약속하고 많이 주자
- 호환성 깨면 지옥간다
- 앞으로는 스펙라이팅에 집중하자 (확실한 패키지 버전관리 및 기술문서 포함)

이하는 올해 기록(메일, 미투밴드, 블로그 등)을 정리하면서 나온 내용들로 채워질 예정임.

2011 목표 대비 평가

1. BSD는 무슨.. 망할거야 아마..
2. B+tree를 못한건 아쉽지만 나머지는 전부 만족스러움.. B+Tree 안 쓰고도 훨씬 명쾌하고 크래시에 안전하고 속도도 빠른 방식으로 정렬 문제를 해결했고 실시간 압축 후 랜덤 액세스도 역시 구현되었음. 게다가 허구헌날 깨지는 OSGi Preference 서비스 때문에 고민이 많았는데 이것도 confdb 개발로 해결됨
3. PPTP/L2TP는 전부 정리했고, IPSEC은 8con이 정리한 것으로 더 봐야함
4. 크라켄 RPC는 비동기 API가 추가되었고 세션별 순서보장 추가되어서 호환성을 또 깨먹었지만 안정적으로 동작하게 됨. 맵리듀스로 200만건 순서대로 다 맞아서 분산 쿼리됨..
5. 안드로이드 크라켄 코어를 돌려보긴 했으나 본격적으로 시작하진 못함
6. 2012 목표가 됨 -_-;;



1 2 3 4 5 6 7 8 9 10 다음