반응형

주키퍼는 분산 어플리케이션을 위한 오픈소스 분산 코디네이션 서비스이다. 주요기능은 단순한 세트로 제공한다. 분산 어플리케이션은 동기화, 설정 관리, 그룹핑 및 Naming를 위한 고수준의 서비스를 구현할 수 있다. 프로그래밍하기 쉽게 설게 되었으며, 데이터 모델은 file system의 디렉토리 트리 구조와 유사해서 친숙할 것이다. 또한 Java나 C 모두 바인딩이 쉽다. 


코디네이션 서비스는 정확성을 가지긴 매우 어렵다. 특히 race condition과 데드락 같은 에러가 발생하기 쉽다. 주키퍼의 목적은 코디네이션 서비스를 구현해야하는 분산 어플리케이션을 오류로부터 도와주는 것이다.


Design Goals


주키퍼는 단순하다. 주키퍼는 분산 프로세스들의 코디네이션을 허용한다. 공유되는 계층 네임스페이스는 단순 파일시스템처럼 구성한다. 네임스페이스는 znode라고 불리는  data 등록자들로 구성된다. 주키퍼의 용어는 파일과 디렉토리와 비슷하다. 일반적인 파일시스템과 다르게 Zookeeper 데이터는 메모리에 유지되어 고성능에 대기시간의 거의 없는 서비스가 가능하다. 주키퍼는 고성능, 신뢰성, 정확한 순서 처리에 장점이 있다. 주키퍼의 성능은 대용량 분산 시스템에서도 사용할 수 있다는 것을 의미한다. Single point of failure에도 신뢰성을 보장하며 엄격한 순서처리는 정교한 동기화처리 구현에 사용될 수 있다는 것을 의미한다.


주키퍼는 복제한다. 코디네이션을 원하는 분산 프로세스 처럼 주키퍼는 앙상블이라고 불리우는 호스트의 집합에 자기 자신을 복제할려고 한다.



주키퍼 서비스를 구성하는 서버는 서로 알아야 한다. 그들은 메모리에 상태 이미지를 유지하며, 트랜잭션 로그를 따라 퍼시스턴트 스토어에 스냅샷을 저장한다. 과반수의 서버가 유효하면 주키퍼 서비스는 유지된다. 클라이언트는 하나의 주키퍼 서버에 접속한다. 클라이언트는 TCP 연결을 유지하면서 요청을 전송하고 응답을 받고 watch event를 받고 heart beat를 전송한다. 만약 서버로의 TCP 연결이 끊기면 클라이언트는 다른 서버로 연결될 것이다.


주키퍼는 순차적이다. 주키퍼는 모든 주키퍼 트랜잭션을 순차적으로 반영한다. 이러한 기능은 synchronization primitive와 같은 고수준의 추상화 구현을 위해 이용될 수 있다.


주키퍼는 빠르다. 특히 읽기에 특화된 부하에 빠르다. 주키퍼 어플리케이션은 수천대의 머신에서 수행되었을 때 read와 write의 비율이 10:1일 때 최대 성능을 낼 수 있다.


Data model and the hierarchical namespace


주키퍼에서 제공되는 네임스페이스는 일반적인 파일 시스템과 매우 유사하다. 이름의 path element의 순서는 /로 구분된다. 주키퍼의 모든 노드는 패스로 식별할 수 있다.




Nodes and ephemeral nodes


표준 파일 시스템과는 다르게 주키퍼 네임스페이스의 각 노드는 자식뿐만 아니라 연관된 데이터를 가질 수 있다. 파일시스템에서 디렉토리가 파일을 가지고 있는 것과 비슷하다. 주키퍼는 코디네이션 데이터를 저장하도록 설계되었다. (상태 정보, 설정, 위치 정보 등 각 노드의 데이터는 일반적으로 바이트에서 킬로바이트 범위로 작다.) 주키퍼 데이터 노드를 znode라고 한다.


Znode는 캐쉬 검증 및 코디네이션 업데이트를 위해 데이터 변경, ACL 변경, 타임스탬프 등의 버젼 넘버 통계 구조를 유지한다.  Znode의 데이터가 변경될 떄 마다 버젼 넘버는 증가한다. 예를 들어 클라이언트에서 데이터를 조회할 때 마다 데이터의 버젼 정보 또한 전달 받게 된다. 


각 Znode에 저장된 데이터를 읽고 쓰는 것은 원자적이다. Znode와 연관된 모든 데이터를 읽고 모든 데이터를 변경할 수 있다. 각 노드는 접근 제어를 위한 ACL 정보를 가진다.


주키퍼는 ephemeral node를 가질 수 있다. 이러한 znode는 노드를 생성한 세션이 존대할 동안만 znode가 유효하다. 세션이 종료되면 znode는 지워진다. Ephemeral 노드는 구현시 유용하게 이용된다.

Conditional updates and watches


주키퍼는 watches 개념을 지원한다. 클라이언트는 znode에 watch를 설정할 수 있다. Watch는 znode가 변경될 떄 트리거되고 삭제될 것이다. Watch가 트리거되면 클라인언트는 znode가 변경된 사항을 전달받을 수 있다. 그리고 만약 클라이언트와 주키퍼 서버간의 연결이 끊어졌다면 클라이언트는 로컬 통지를 받게 될 것이다.

Guarantees


주키퍼는 매우 빠르고, 매우 심플하다. 하지만 동기화와 관련된 더욱 복잡한 서비스 구축을 기반으로 하기에 다음과 같은 사항을 보장한다.


  • Sequential Consistency - 클라이언트로부터 변경은 전송된 순서에 따라 적용된다.

  • Atomicity - 업데이트는 성공이 아니면 실패이다. 부분적인 결과는 없다.

  • Single System Image - 클라이언트는 연결된 서버와 상관없이 동일한 뷰를 제공받는다.

  • Reliability - 한번 업데이트가 적용되면 클라이언트에 의해 다른 변경이 일어나기전까지 유지된다.

  • Timeliness - 시스템의 클라이언트 뷰는 특정시간에 최신상태를 보장한다.


Simple API


주키퍼는 매우 간단한 프로그래밍 인터페이스를 제공한다.


create - 트리의 특정 위치에 노드를 생성

delete - 노드를 삭제

exists - 위치에 노드가 존재하는지 테스트

get data - 노드로 부터 데이터를 읽음

set data - 노드에 데이터를 씀

get children - 자식노드 리스트를 조회

sync - 전파된 데이터를 기다림

Implementation


주키퍼 서비스의 하이레벨 컴포넌트는 아래와 같다.  Reqeust processor가 예외가 발생하면 각 서버는 각 컴포넌트의 복제본을 복제하여 주키퍼 서비스를 만든다.

복제된 데이터베이스는 모든 데이터 트리를 포함한 메모리상의 데이터베이스이다. 변경사항은 복구를 위해 디스크에 로깅된다.  그리고 메모리상의 데이터베이스에 적용되기 전에 디스크에 직렬화하여 기록된다. 


모든 주키퍼 서버는 클라이언트에 서비스 된다. 클라이언트는 하나의 서버에 접속하고 요청을 전송한다. 읽기 요청은 각 서버 데이터 베이스의 로컬 복제본을 통해 서비스 된다. 서비스의 상태를 변경하는 요청은 약속된 프로토콜로 처리된다.


클라이언트로 부터 모든 쓰기 요청은 약속된 프로토콜의 부분으로서 leader라고 불리는 하나의 서버로 전송된다. 나머지 주키퍼 서버는 follower라고 불리우며, 리더로 부터 메시지를 제공받고, 메시지 전송을 담당한다. 메시징 레이어는 리더가 죽을 경우 교체와  리더와 팔로워간 동기화를 담당한다.


주키퍼는 개별적인 원자적인 메시지 프로토콜을 사용한다. 메시징 레이어는 atomic하기 때문에 주키퍼는 로컬 복제본이 분리되지 않는 것을 보장한다. 리더가 쓰기 요청을 전송받으면 쓰기가 적용될 시스템의 상태를 계산하고 새로운 상태의 캡쳐를 트랜잭션으로 전송한다.


Uses


주키퍼의 프로그래밍 인터페이스는 단순하다. 하지만 당신은 주키퍼를 기반으로 고수준의 순차 작업을 구현할 수 있다. (synchronizations primitives, group membership, ownership)


Performance


주키퍼는 고성능을 목적으로 설계되었다. 이유는 주키퍼의 개발팀은 야후이다. 




주키퍼 읽기-쓰기 비율의 변화에 따른 처리량이다. 주키퍼의 처리량 그래프는 주키퍼 3.2를 듀얼 2Ghz Xeon 그리고 두개의 SATA 15K RPM drives에서 수행했을때의 결과이다. 하나의 드리이브는 전용 주키퍼 로그 장치로 사용된다. 스냅샷은 OS 드라이브에 의해 쓰여진 것이고. 쓰기와 읽이 요청은 !Kb이다. 서버는 주키퍼 앙상블의 사이즈를 의미하고 서버의 숫자는 서비스를 구성하는 숫자이다. 대략 30개의 서버가 클라인트를 시뮬레이션하기 위해 사용되었다. 

Reliability


주키퍼 서비스의 실패와 복구 시스템 소요시간을 보여주기 위한 내용이다.  주키퍼 서비스는 7대의 머신으로 구성하였다.


이 그래프의 중요한 점은 첫째 만약 팔로워 실패 및 복구가 빠르다면 주키퍼는 실패에도 불구하고 고성능처리량을 유지할 수 있다는 것이다. 하지만 더 중요한 것은 리더 선출 알고리즘이 시스템 복구를 충분히 빠르게 수행한다는 것이다. 주키퍼는 새로운 리더를 선출하는데 200ms 이하를 소요한다. 세번째로 팔로워가 복구되면 주키퍼는 다시 처리량을 증가시킬 수 있다는 것이다.

The ZooKeeper Project


주키퍼는 많은 산업의 어플리케이션에서 성공적으로 사용되고 있다. 야후에서 코디네이션 및 복구 서비스로 사용되고 있다.  또한 수천대의 토픽을 관리하는 확장성있는 publish-subscribe 시스템의 메시지 브로커로서 사용된다. 또한 실패 복구 서비스를 위한 crawler로서 사용되며, 야후의 광고 시스템에서도 사용된다.



반응형

'Programming > ZooKeeper' 카테고리의 다른 글

ZooKeeper - 시작하기  (0) 2015.03.12
반응형

이제 ZooKeeper에 대해서 알아보자. 아무것도 모르고 ZooKeeper를 도입해야 하는 상황이 생겼다. 빨리 Getting Started를 마스터 해야한다.


다운로드


아래의 주키퍼 공식 사이트로 접근을 해서 최신 릴리이즈를 다운로드 하자. 설치를 진행할 버젼은 3.4.6이다.


http://zookeeper.apache.org/ 


Apache 미러 사이트에서 다운로드를 받을 수 있었고, 파일명이 zookeeper-3.4.6.tar.gz 이며, 용량은 16.9mb이다.


StandAlone 


역시 시작은 stand alone으로 해야겠지? 주키퍼 서버는 하나의 JAR 파일을 포함한다.. 그래서 설치는 설정을 포함한다. 일단 압축을 적당한 위치에 풀자. 


toddsonui-MacBook-Pro:zookeeper-3.4.6 devsun$ ls -al

total 3040

drwxr-xr-x  22 devsun  staff      748  2 20  2014 .

drwxr-xr-x  16 devsun  staff      544  3 12 16:36 ..

-rw-r--r--   1 devsun  staff    80776  2 20  2014 CHANGES.txt

-rw-r--r--   1 devsun  staff    11358  2 20  2014 LICENSE.txt

-rw-r--r--   1 devsun  staff      170  2 20  2014 NOTICE.txt

-rw-r--r--   1 devsun  staff     1585  2 20  2014 README.txt

-rw-r--r--   1 devsun  staff     1770  2 20  2014 README_packaging.txt

drwxr-xr-x  10 devsun  staff      340  2 20  2014 bin

-rw-r--r--   1 devsun  staff    82446  2 20  2014 build.xml

drwxr-xr-x   5 devsun  staff      170  2 20  2014 conf

drwxr-xr-x  10 devsun  staff      340  2 20  2014 contrib

drwxr-xr-x  22 devsun  staff      748  2 20  2014 dist-maven

drwxr-xr-x  49 devsun  staff     1666  2 20  2014 docs

-rw-r--r--   1 devsun  staff     3375  2 20  2014 ivy.xml

-rw-r--r--   1 devsun  staff     1953  2 20  2014 ivysettings.xml

drwxr-xr-x  11 devsun  staff      374  2 20  2014 lib

drwxr-xr-x   5 devsun  staff      170  2 20  2014 recipes

drwxr-xr-x  11 devsun  staff      374  2 20  2014 src

-rw-r--r--   1 devsun  staff  1340305  2 20  2014 zookeeper-3.4.6.jar

-rw-r--r--   1 devsun  staff      836  2 20  2014 zookeeper-3.4.6.jar.asc

-rw-r--r--   1 devsun  staff       33  2 20  2014 zookeeper-3.4.6.jar.md5

-rw-r--r--   1 devsun  staff       41  2 20  2014 zookeeper-3.4.6.jar.sha1


압축을 푼 모습이다. 하나의 jar를 포함한다는 말은 머지? 좀 헷갈린다. 오역인가?


주키퍼를 시작하기 위해서는 설정 파일이 필요하다고 한다. conf/zoo.cfg를 만드는데 일단 아래의 내용으로 만들어보자.


tickTime=2000

dataDir=/tmp/zookeeper

clientPort=2181


이 파일은 머라고 불려도 상관없다. 하지만 그냥 conf/zoo.cfg라고 부르자. dataDir은 존재하는 디렉토리로 명시하자.


tickTime - 주키퍼에서 사용하는 기본 시간 단위 밀리세컨드. heartbeat를 위해 사용되면 최소 세션 타임아웃은 tickTime의 두배이다.

dataDir - in-memory 데이터베이스의 스냅샷을 저장할 위치. 데이터베이스의 갱신 로그이다.

clientPort - 클라이언트 연결을 위한 사용되는 listen 포트


설정 파일을 생성하였으면 주키퍼를 시작할 수 있다.


toddsonui-MacBook-Pro:bin devsun$ ./zkServer.sh start

JMX enabled by default

Using config: /Users/devsun/dev/zookeeper-3.4.6/bin/../conf/zoo.cfg

Starting zookeeper ... STARTED


로그가 완전 심플하다. 주키퍼는 내부적으로 로깅을 위해 log4j를 사용한다. standalone모드에서는 replication이 없다. 더 좋은 개발 환경을 위해서는 repicated mode로 실행해야 하는데 그건 나중에 알아보자. 운영에서는 ZooKeeper 스토리지는 외부에서 관리되어야만 한다. 이에 대한 자세한 내용도 다음에 살펴보기로 한다.


이제 주키퍼로 연결을 수행해보자.


toddsonui-MacBook-Pro:bin devsun$ ./zkCli.sh -server 127.0.0.1:2181

Connecting to 127.0.0.1:2181

2015-03-12 17:05:39,565 [myid:] - INFO  [main:Environment@100] - Client environment:zookeeper.version=3.4.6-1569965, built on 02/20/2014 09:09 GMT

2015-03-12 17:05:39,569 [myid:] - INFO  [main:Environment@100] - Client environment:host.name=172.27.70.136

2015-03-12 17:05:39,570 [myid:] - INFO  [main:Environment@100] - Client environment:java.version=1.8.0_25

2015-03-12 17:05:39,571 [myid:] - INFO  [main:Environment@100] - Client environment:java.vendor=Oracle Corporation

2015-03-12 17:05:39,572 [myid:] - INFO  [main:Environment@100] - Client environment:java.home=/Library/Java/JavaVirtualMachines/jdk1.8.0_25.jdk/Contents/Home/jre

2015-03-12 17:05:39,572 [myid:] - INFO  [main:Environment@100] - Client environment:java.class.path=/Users/devsun/dev/zookeeper-3.4.6/bin/../build/classes:/Users/devsun/dev/zookeeper-3.4.6/bin/../build/lib/*.jar:/Users/devsun/dev/zookeeper-3.4.6/bin/../lib/slf4j-log4j12-1.6.1.jar:/Users/devsun/dev/zookeeper-3.4.6/bin/../lib/slf4j-api-1.6.1.jar:/Users/devsun/dev/zookeeper-3.4.6/bin/../lib/netty-3.7.0.Final.jar:/Users/devsun/dev/zookeeper-3.4.6/bin/../lib/log4j-1.2.16.jar:/Users/devsun/dev/zookeeper-3.4.6/bin/../lib/jline-0.9.94.jar:/Users/devsun/dev/zookeeper-3.4.6/bin/../zookeeper-3.4.6.jar:/Users/devsun/dev/zookeeper-3.4.6/bin/../src/java/lib/*.jar:/Users/devsun/dev/zookeeper-3.4.6/bin/../conf:

2015-03-12 17:05:39,572 [myid:] - INFO  [main:Environment@100] - Client environment:java.library.path=/Users/devsun/Library/Java/Extensions:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java:.

2015-03-12 17:05:39,572 [myid:] - INFO  [main:Environment@100] - Client environment:java.io.tmpdir=/var/folders/9r/9zffgr317j1cv6d8j_gr52n80000gn/T/

2015-03-12 17:05:39,572 [myid:] - INFO  [main:Environment@100] - Client environment:java.compiler=<NA>

2015-03-12 17:05:39,572 [myid:] - INFO  [main:Environment@100] - Client environment:os.name=Mac OS X

2015-03-12 17:05:39,572 [myid:] - INFO  [main:Environment@100] - Client environment:os.arch=x86_64

2015-03-12 17:05:39,572 [myid:] - INFO  [main:Environment@100] - Client environment:os.version=10.10.2

2015-03-12 17:05:39,572 [myid:] - INFO  [main:Environment@100] - Client environment:user.name=devsun

2015-03-12 17:05:39,572 [myid:] - INFO  [main:Environment@100] - Client environment:user.home=/Users/devsun

2015-03-12 17:05:39,572 [myid:] - INFO  [main:Environment@100] - Client environment:user.dir=/Users/devsun/dev/zookeeper-3.4.6/bin

2015-03-12 17:05:39,573 [myid:] - INFO  [main:ZooKeeper@438] - Initiating client connection, connectString=127.0.0.1:2181 sessionTimeout=30000 watcher=org.apache.zookeeper.ZooKeeperMain$MyWatcher@69663380

Welcome to ZooKeeper!

2015-03-12 17:05:39,596 [myid:] - INFO  [main-SendThread(127.0.0.1:2181):ClientCnxn$SendThread@975] - Opening socket connection to server 127.0.0.1/127.0.0.1:2181. Will not attempt to authenticate using SASL (unknown error)

JLine support is enabled

[zk: 127.0.0.1:2181(CONNECTING) 0] 2015-03-12 17:05:39,649 [myid:] - INFO  [main-SendThread(127.0.0.1:2181):ClientCnxn$SendThread@852] - Socket connection established to 127.0.0.1/127.0.0.1:2181, initiating session

2015-03-12 17:05:39,702 [myid:] - INFO  [main-SendThread(127.0.0.1:2181):ClientCnxn$SendThread@1235] - Session establishment complete on server 127.0.0.1/127.0.0.1:2181, sessionid = 0x14c0cfb83340000, negotiated timeout = 30000


WATCHER::


접속을 잘되었는지 확인하기 위해 help 명령을 수행해보자. 먼가 나온다.


[zk: 127.0.0.1:2181(CONNECTED) 0] help

ZooKeeper -server host:port cmd args

stat path [watch]

set path data [version]

ls path [watch]

delquota [-n|-b] path

ls2 path [watch]

setAcl path acl

setquota -n|-b val path

history

redo cmdno

printwatches on|off

delete path [version]

sync path

listquota path

rmr path

get path [watch]

create [-s] [-e] path data acl

addauth scheme auth

quit

getAcl path

close

connect host:port


CLI 상에서 ls 명령을 수행해보자.


[zk: 127.0.0.1:2181(CONNECTED) 1] ls /

[zookeeper]


다음은 znode를 생성해보자. 새로운 znode를 생성하고 string "my_data"와 해당 노드를 연관짓는 것이다.


[zk: 127.0.0.1:2181(CONNECTED) 4] create /zk_test my_data

Created /zk_test

[zk: 127.0.0.1:2181(CONNECTED) 5] ls /

[zookeeper, zk_test]


다음은 get 명령을 통해 znode와 연관된 데이터를 확인해보자.


[zk: 127.0.0.1:2181(CONNECTED) 6] get /zk_test

my_data

cZxid = 0x2

ctime = Thu Mar 12 17:13:32 KST 2015

mZxid = 0x2

mtime = Thu Mar 12 17:13:32 KST 2015

pZxid = 0x2

cversion = 0

dataVersion = 0

aclVersion = 0

ephemeralOwner = 0x0

dataLength = 7

numChildren = 0


set 명령을 통해 zk_test 노드에 연관된 데이터를 변경할 수 있다. 

[zk: 127.0.0.1:2181(CONNECTED) 8] set /zk_test junk

cZxid = 0x2

ctime = Thu Mar 12 17:13:32 KST 2015

mZxid = 0x5

mtime = Thu Mar 12 17:18:41 KST 2015

pZxid = 0x2

cversion = 0

dataVersion = 1

aclVersion = 0

ephemeralOwner = 0x0

dataLength = 4

numChildren = 0

[zk: 127.0.0.1:2181(CONNECTED) 9] get /zk_test junk

junk

cZxid = 0x2

ctime = Thu Mar 12 17:13:32 KST 2015

mZxid = 0x5

mtime = Thu Mar 12 17:18:41 KST 2015

pZxid = 0x2

cversion = 0

dataVersion = 1

aclVersion = 0

ephemeralOwner = 0x0

dataLength = 4

numChildren = 0


마지막으로 delete 명령으로 노드를 삭제해보자.

[zk: 127.0.0.1:2181(CONNECTED) 10] delete /zk_test


WATCHER::


WatchedEvent state:SyncConnected type:NodeDeleted path:/zk_test


반응형

'Programming > ZooKeeper' 카테고리의 다른 글

ZooKeeper - 개요  (0) 2015.03.12

+ Recent posts