[ZooKeeper] (1) ZNode - ZooKeeper가 data를 저장하는 방법.

이미지
지난번 글 에서 ZooKeeper 는 일종의 파일시스템을 제공해주어 이를 이용하여 semaphore나 mutex를 만들어 사용할 수 있다고 말했다. 이때 ZooKeeper가 제공해주는 파일시스템에 저장되는 파일 하나하나를 znode라고 부른다. 이번에는 znode에 대해서 자세히 설명해보도록 하겠다. ZNode의 특징 hierarchy znode는 unix-like 시스템에서 쓰이는 file system처럼 node 간에 hierarchy namespace를 가지고, 이를 /(slash)를 이용하여 구분한다. https://zookeeper.apache.org/doc/r3.3.2/zookeeperOver.html 일반적인 file system과 다른 부분이 있다. ZooKeeper는 file과 directory의 구분이 없이 znode라는 것 하나만을 제공한다. 즉, directory에도 내용을 적을 수 있는, directory와 file 간의 구분이 없는 file system이라는 것이다. 이는 znode의 큰 특징 중 하나이다. namespace hierarchy를 가지기 때문에 관련 있는 일들을 눈에 보이는 하나의 묶음으로 관리할 수 있으면서, directory가 내용을 가질 수 있게 함으로써(혹은 file 간에 hierarchy를 가진다고 하기도 한다. 원하는 표현으로 말하면 된다.) redundunt한 file을 생성해야 하는 것을 막을 수 있다. size 제한 ZooKeeper는 모든 data를 메모리에 저장한다. data를 메모리에 저장하기 때문에 jvm의 heap memory를 모든 znode를 올릴 수 있는 충분한 크기로 만들어야 한다. 심지어 The disk is death to ZooKeeper. 라고 말하면서, JVM이 heap memory를 swap 하여 하드에 저장하는 것을 피하도록 설정하는 것을 강요(?)하고 있다. data를 저장하는 보통의 파일 시스템이나 DBMS같은 경우 모든 data가 메모리에

[ZooKeeper] (0) zookeepr는 무엇인가?

보통 분산 시스템을 구현할 때, 모든 시스템이 완전히 독립적으로 돌아가는 시스템이 아니라면, 시스템 간의 락, 설정 공유, 리더 선출, atomic 한 연산 등을 구현하는 것이 필요하지만, 분산환경에서 이를 구현하는 것은 매우 어려운 일이다. 위의 기능들을 구현하기 어렵기 때문에 보통은 apache에서 제작한 zookeeper 라는 시스템을 이용하여 분산 시스템 간의 동기화된 작업을 구현한다. zookeeper는 위의 기능들을 직접적으로 제공하지는 않지만 이런 일들을 하기 쉽게 해주는 환경을 제공한다. zookeepr가 제공해주는 환경이라는 것은 일종의 공유 가능한 file system을 제공해준다. 그러면 사용자가 file을 이용해서 semaphore 나 mutex 를 구현하듯이 zookeeper를 이용해서 semaphore나 mutex등을 구현하여 사용하면 된다. zookeeper는 분산환경에서의 다음과 같은 특징을 보장해준다. ZooKeeper의 특징 Atomicity zookeeper에서 data의 저장은 원자성 을 가진다. 즉, node를 만들건 node에 data를 update하든 해당 request는 완벽하게 처리되거나 처리되지 않거나 하지 그 중간의 어중간한 상태는 존재하지 않는다. Consistency 분산환경에서, 특히나 data를 copy하여 여러 서버에 저장하면서 strong consistency 를 보장하는 것은 매우 어려운 일다. 그래서 zookeeper에서는 아래의 2가지 consistency를 보장한다. 첫 번째는 sequential consistency 다. 즉, 모든 요청은 들어온 순서대로 처리되고, 모든 서버가 요청을 같은 순서로 처리하는 것을 보장하는 것이다. 두 번째는 eventual consistency 다. strong consistency와 달리, 모든 요청에 대해 모든 서버가 완벽히 같은 순간에 같은 값을 갖지는 않지만 결국에는 같은 값을 가질 것을 보장하는 것이다. 즉, 어떤 서버에서는

config_script 재작성

이미지
이 컴퓨터 저 컴퓨터 옮겨가면서 작업할 때, 컴퓨터별로 환경을 통일하는 작업은 귀찮은 일이다. 그래서 나는 이런 스크립트 를 만들어서 써왔다. 아직 사용하는데 큰 문제는 없었지만, 그때그때 필요한 기능들을 넣다 보니 스크립트가 난잡해지고, 내가 다른 프로젝트에서 작업할 때 자주 사용하지 않는 bash를 이용해서 만들다 보니 수정이 필요해서 코드를 다시 볼 때, 익숙하지 않아서 실수하는 문제가 생겼다. 그래서 이번 연휴를 맞아서 스크립트를 완전히 새로 작성하였다. 이번에 작업에서 초점을 맞춘 부분은 크게 2가지였다. 첫 번째는 시간이 지나도 알아보기 쉽도록 익숙한 언어로 작업할 것, 두 번째는 수정이 필요하거나 새로운 일이 추가될 때 확장하기 쉽도록 하는 것이었다. 첫 번째 요구사항을 맞추기 위하여 작업은 python을 이용하였다. 어차피 하는 대부분 작업이 조건에 맞추어 파일을 옮기고 command를 실행시키는 일들뿐이고 복잡한 일은 없어서 언어는 무엇을 사용해도 상관없어 보였기에 그중에 가장 익숙한 python을 이용했다. 두 번째 요구사항을 맞추기 위하여 기존의 스크립트를 분석해 보니 대부분의 일이 본 작업에 앞서 사전작업을 한다. 작업해야 할 파일 리스트에서 하나씩 꺼내서 source가 있는지 파악하여 에러처리를 하고 destination에 이미 파일이 존재하는지 확인하여 에러처리를 하고 source를 destination으로 복사/링크 등을 한다. 본 작업이 끝난 뒤 마무리 작업을 한다. 의 순서로 진행되었다. 그래서 Config라는 abstract class를 만들고, 각각의 단계에 맞춰서 pre() source_exists() resolve_conflict() do() post() 의 5개의 abstract method를 만들어 원하는 작업마다 Config를 상속받는 class를 만들어 상황에 맞게 위의 method들을 override 하는 방식으로 작업을 진행하였다. 또한, source_exist

왜 Triple buffering을 사용하는가

이미지
지난번 글 에서 double buffering 과 VSync 에 관하여 설명하였다. 이번 글에서는 VSync가 가지는 문제와 그것을 어떻게 triple buffering 이 해결하는지를 적어보도록 하겠다. 우선 다음 문제를 풀어보자. vertical interval이 16.6ms마다 있는 display를 사용하고 double buffering을 사용하는 드라이버에서 VSync를 사용한다고 했을 때 back buffer에서 scene 하나를 완성하는 데 걸리는 시간이 16ms인 프로그램을 작성하면 이 프로그램은 몇 fps가 나올 것인가? 매 frame마다 하나의 scene을 16ms 동안 그리고, 남은 0.6ms는 VSync를 기다리기 때문에 16.6ms마다 한 frame을 그려서 60fps가 나온다. VSync를 기다리는 0.6ms가 아쉽기는 하지만 어차피 60fps 이상은 사람 눈으로 잘 구분이 안 되니 큰 손해를 보는 것은 아니다. 문제는 아래의 상황이다. vertical interval이 16.6ms마다 있는 display를 사용하고 double buffering을 사용하는 드라이버에서 VSync를 사용한다고 했을 때 back buffer에서 scene 하나를 완성하는 데 걸리는 시간이 홀수 번째 scene은 15ms, 짝수 번째 scene은 17ms가 걸리도록 프로그램을 작성하면 이 프로그램은 몇 fps가 나올 것인가? 매 프레임 back buffer에 그리는 데 걸리는 평균 시간은 첫 번째 경우와 같다. 그렇다면 이 케이스에서도 60 fps가 나올까? 아니다. 홀수 번째 frame에서는 15ms 동안 그림을 그리고 1.6ms 동안 기다리지만, 짝수 번째 frame에서는 back buffer에 그림을 그리는데 17ms가 걸렸기 때문에 첫 번째 vertical interval 때 swap buffer를 하지 못하고, 그 다음 vertical interval까지 기다려야 한다. 즉, 그림이 완성된 뒤 16.2ms만큼 더 기다려야

copy와 flip - Double buffering의 2가지 기법

이미지
Buffer는 그림을 그리기 위해 메모리에 잡아놓은 영역을 의미한다. 그래픽 드라이버가 화면을 갱신해야 할 때 이 영역에서 그림을 읽어 화면을 그린다. 과거의 graphic card(혹은 요새 나오는 저 사양의 embedded 용 graphic card)에서는 single buffering이라는 기법을 이용한다. single buffering이란, 말 그대로 한 개의 버퍼만을 사용하는 방식이다. 완성된 그림을 buffer에 그리고, 화면에 그림을 그려야 할 때마다 이 buffer에서 화면으로 그림을 가져가는 것이다. 가장 기본적인 구현이지만, 이런 방식은 buffer에서 화면으로 가져가는 동안 buffer를 업데이트하지 못한다는 단점이 있다. 이런 단점을 해결하기 위하여 double buffering 이라는 기법을 사용한다. Double buffering Double buffering에서는 front buffer와 back buffer라고 불리는 2개의 buffer를 사용한다. back buffer에 그림을 그리고 한 프레임이 완성되면 back buffer와 front buffer를 바꾼다. (이를 swap buffer라고 한다.) 화면에 그림이 필요할 때는 언제나 front buffer에서 화면을 가져간다. 화면이 읽어들이는 buffer는 언제나 front buffer이므로 화면을 업데이트할 필요가 있으면 언제나 back buffer에 그리면 된다. 이때 back buffer에서 front buffer로 옮기는 방식에는 2가지 방법이 있다. Copy 첫 번째 방법은 copy라고 부르는 방법이다. 이 방법은 front buffer는 그래픽 메모리에 만든다. back buffer는 그래픽 메모리나 시스템 메모리 어디에 만들어도 상관없지만, 보통 copy를 사용하는 경우는 그래픽 메모리를 아끼기 위한 경우가 많아서 back buffer는 시스템 메모리에 잡는다. 프로그램이 그림을 그리라고 하면 이를 back buffer에 그리고, swap buff

Fluentd - Pluggable log collector

이미지
지난번에 소개했던 글 에서 여러 가지 log aggregator들을 소개했었다. 이번에는 그중에서도 특별히 마음에 들었던 fluentd를 더 자세히 소개해 보도록 하겠다. Semi-structured log https://blog.treasure-data.com/post/13047440992/fluentd-the-missing-log-collector-software 우선 fluentd의 가장 큰 특징은 log를 time/tag/record형식 의 semi-structured 형식으로 저장한다는 것이다. 시간은 event가 발생한 시간으로 event를 fluentd로 넘겨줄 때 시간을 같이 넘겨주지 않으면, fluentd에서 받은 시간을 기록하게 된다. tag는 이벤트를 만들 때 넘기게 되어 있는데, fluentd에서 사용하는 값이다. 이에 대해서는 config를 어떻게 하는지 설명하면서 설명하도록 하겠다. record는 사용자가 저장하려고 했던 값들로 json 형식의 key/value pair로 저장된다. semi-structured라고 해도 record가 json 형식으로 저장되기 때문에 원하는 형식대로 저장할 수 있다. Use case fluentd는 config파일을 바꾸는 것만으로도 여러 머신들 간의 설정을 쉽게 바꿀 수 있다. https://blog.treasure-data.com/post/16034997056/enabling-facebooks-log-infrastructure-with-fluentd 위의 그림은 가장 기본적인 형태로 frontend에 붙어 있는 fluentd에서 보내는 이벤트를 중개 서버(?)에 해당하는 fluentd에서 한번 수집하여 최종 저장소에 보내는 형태이다. 위의 그림은 특별히 fluentd의 성능을 고려하여 하나의 중개 서버가 너무 무리하는 일 없도록 여러 개의 중개 서버에 나누어서 보내는 방식이다. https://docs.fluentd.org/articles/high-availabi

Log Aggregator 비교 - Scribe, Flume, Fluentd, logstash

이미지
https://docs.google.com/presentation/d/12A5RlMCVDN6tA_zUnLrZ0TN5v08gz2GhlRdzxy3T3mU/edit?usp=sharing 회사에서 최근에 Log aggregator system으로 무엇을 사용해야 할지 조사해본 자료다. 우선 log aggregator가 무엇인지 한 문장으로 설명하면, 여러 머신에서 쌓인 로그들을 한 번에 분석할 수 있도록 수집하여 주는 시스템을 말한다. 요새는 특히나 클라우드 시스템이 유행하면서 같은 일을 하는 시스템임에도 다른 머신에서 돌아가는 일이 많아지면서 필요성이 크게 증가하였다. 이번 조사로 알게 된 것들을 적어보도록 하겠다. Scribe 우선 scribe 는 Facebook에서 제작하고 사용하던 log aggregator system이다. scribe: https://www.cnblogs.com/brucewoo/archive/2011/12/13/2285482.html 다른 로그 수집 시스템들을 보면 알겠지만, Scribe는 다른 시스템보다 간단한 구조로 되어 있다. Scribe는 일종의 message queue와 message queue에 쌓인 message를 DB에 저장해 주거나, DB가 실패하였으면 local에 저장하였다가 DB가 복구되었을 때 다시 DB에 저장해 주는 것만을 책임진다. 다시 말하면, message queue에 실제로 메시지를 보내는 부분은 사용자가 직접 작성하여야 한다는 것이다. 흔히들 말하는 scribe의 장점은 성능이다. C++로 만든 만큼 다른 시스템들의 3~5배 정도의 성능을 보여준다는 것이다. 하지만 실제 scribe 사용자들은 무엇보다도 Facebook이 실제로 사용하였던 솔루션인 만큼 성능과 안정성에서 신뢰도가 있다는 것을 장점으로 뽑는다. 하지만 나는 scribe를 사용하는 것을 추천하지는 않는다. 일단 가장 큰 문제는 더이상 Facebook이 Scribe를 사용하지 않는다는 것이다. Facebook은 이미 Ja

이 블로그의 인기 게시물

USB 2.0 케이블의 내부 구조

[C++] enum class - 안전하고 쓰기 쉬운 enum

Log Aggregator 비교 - Scribe, Flume, Fluentd, logstash

[Web] SpeechSynthesis - TTS API

[Python] cache 데코레이터로 최적화하기