욱'S 노트

Spring XD - DIRT(Distributed Runtime) 본문

Programming/Spring XD

Spring XD - DIRT(Distributed Runtime)

devsun 2015. 3. 16. 17:10

Introduction


이 문서는 Spring XD 분산 런타임(DIRT)의 내부에서 일어나는 사항 특히, 런타임 아키텍처가 고가용성(HA)를 가지고 클러스트 프로덕션 환경에서 failover를 수행하는지에 대한 내용이다. 설치와 분산 모드 실행에 대한 더 많은 정보는 이전에 자료를 참조하자. Spring XD의 핵심 런타임 컴포넌트와 Spring XD의 상태를 관리하고 실패로부터 자동 복구가 가능하게 하는 Zookeeper의 역할에 대해 살펴본다.


Configuring Spring XD for High Availability(HA)


프로덕션 상태에서 Spring XD 환경은 일반적으로 다수의 호스트간에 분산되어 클러스터된 환경일 것이다. Spring XD는 컨테이너 인스턴스를 추가할 떄마다 수평적으로 확장될 수 있다. 가장 단순한 케이스에서는 모든 컨테이너가 복제품이 되는 것이다.  각 인스턴스는 지정된 호스트에서 설정되고 모든 모듈은 사용가능한 모든 컨테이너에 라운드-로빈 방식으로 디플로이 되는 것이다. 하지만 이러한 단순한 가정은 실제 프로덕션 시나리오를 반영하지 못한다. 실제 상황에서는 리소스  사용을 최적화하기 위해서 컨트롤이 요구된다. 이러한 결과로 Spring XD는 모듈배포 와 특정 컨테이너 설정을 매칭하기 위해 유연한 알고리즘을 지원한다. 컨테이너 매칭 알고리즘은 나중에 살펴보겠다. 일단 가장 심픔한 케이스를 가정하자. 다수 컨테이너 수행은 수평적 확장뿐만 아니라 실패에 대한 복구도 가능하게 한다. 만약 컨테이너가 복구할수 없는 연결 실패로 사용할 수 없게 된다면 모듈은 자동적으로 이용가능한 인스턴스로 배포될 것이다.


Spring XD는 컨테이너와의 stream 배포 요청과 같은 상호작용을 처리하기 위한 하나의 Admin Server가 필요하다. 만약 백업이 없다면 Admin 서버는 single point of failure가 발생할 수 잇다. 그러므로 Admin Server는 프로덕션 환경에서는 두 대 이상으로 설정하는 것을 추천한다. 모든 Admin server는 REST로 요청을 처리 할 수 있지만, 실제로는 하나의 인스턴스 leader만이 요청을 수행하여 런타임 상태를 업데이트해야 한다. 만약 리더가 다운되면 다른 Admin server가 leader 역할을 수행하게 된다. Leader Election은 Curator 프레임워크로 부터 제공된 분산 시스템의 공통적인 기능이다.


HA Spring XD 설치를 위해선 외부 서버가 요구된다. - ZooKeeper, messaging middleware, data store. 외부 컴포넌트에 대한 상세한 설정방법은 컨설팅을 받아라.


ZooKeeper Overview


이전 섹션에서 만약 컨테이너가 다운되면 Spring XD는 해당 인스턴스에 디플로이된 모듈을 다른 이용가능한 컨테이너에 디플로이를 수행한다고 했다. 또한 Admin 리더 서버가 다운된다면 다른 Admin 서버가 리더역할을 수행한다고 하였다. ZooKeeper가 이러한 것을 가능하게 한다. 주키퍼는 분산 시스템 관리 및 코디네이션을 주요하게 사용되는 Apache 프로젝트이다. 이번 섹션에서는 Spring XD에서의 주키퍼의 역할을 이해하기 위해 기본적인 컨셉을 살펴보자.


주키퍼는 단순한 계층적 데이터 구조를 기반으로 한다. 개념적으로나 의미적으로나 파일 디렉토리 구조와 유사하다. 데이터는 노드에 저장된다. 노드는 path로 표현된다. 각 노드는 추가적인 데이터를 저장할 수 있는데 byte array로 직렬화된다. Spring XD는 모든 데이터를 JSON로써 직렬화된 Map이다.




주키퍼 노드는 ephemeral 이거나 persistent이다. 임시 노드는 세션이 유지될 동안만 존재한다. 퍼시스턴트 노드는 당연히 퍼시스턴트 된다. 임시 노드는 컨테이너 인스턴스를 등록하기 위해서 사용된다. Spring XD 컨테이너가 시작하면 내부적으로 생성한 컨테이너ID를 이용하여 /xd/containers/<container-id> 임시 노드를 생성한다. 연결이 종료되어 컨테이너 세션이  종료되면 예를들어 컨테이너에 프로세스가 종료되면 노드는 사라진다. 임시노드는 또한 호스트명, IP, 런타임 메트릭스, 유저 정의 컨테이너 속성등의 메타정보를 유지한다. 퍼시스턴트 노드는 일반 수행 및 복구를 위한 상태를 유지한다. 이것은 스트림 정의, 작업 정의, 디플로이 매니페스트, 모듈 배포 및 스트림과 작업의 배포 상태를 포함한다.


명백하게 주키퍼는 Spring XD 런타임은 크리터컬 부분이다. 당연히 HA를 지원해야 한다. 주키퍼는 앙상블이라고 불리는 분산 아키텍처를 지원한다. 이 문서의 범위를 넘어 더 자세한 내용을 살펴보면 주키퍼 서버 인스턴스는 적어도 3개 이상이여야 한다. 컨테이너와 어드민 노드는 주키퍼 앙상블의 클라이언트이고., 시작시 주키퍼로 반드시 접속되어야 한다. Spring XD 컴포넌트는 zk_client_connect 프로퍼티로 설정할 수 있으며 <host>:<port> 형태의 콤마로 구분된 리스트이다. 주키퍼 클라이언트는 성공할때까지 각 서버로 접속을 수행할 것이다. 만약 접속을 할 수 없다면 계속 시도를 할 것이다. 만약 접속에 실패하면 주키퍼 클라이언트는 다른 서버로 접속을 시도한다. 주키퍼 클러스터는 앙상블 사이의 데이터 복제본 일치를 보장한다.


주키퍼는 다음을 보장한다.

  • Sequential Consistency - 클라이언트로부터 변경은 전송된 순서에 따라 적용된다.
  • Atomicity - 변경은 성공 혹은 실패이다. 부분성공은 없다.
  • Single System Image - 클라이언트는 접속한 서버에 상관없이 동일한 뷰를 본다.
  • Reliability - 한번 업데이트가 적용되면 클라이언트에 의해 변경이 일어날때까지 퍼시스턴트된다.
  • Timeliness - 클라이언트 뷰는 특정 시간의 최신 상태임을 보장한다.


주키퍼는 데이터는 메모리에 주로 유지되며 디스크 캐쉬에 의해 백업된다. 변경은 복구를 위해 디스크에 로깅 되고 인메모리 데이터베이스에 저장되기 전에 직렬화되어 저장된다. 게다가 노드에 대한 기본적인 CRUD를 수행하기 위해 주키퍼 클라이언트는 노드에 콜백을 등록할 수 있고, 노드나 노드의 자식으로부터 어떤 이벤트나 상태 변경에 응답할 수 있다. 그러한 노드 오퍼레이션과 콜백은 Spring XD 런타임을 관리하는 메커니즘이다.


The Admin Server Internals


하나 이상의 어드민 인스턴스가 수행중이라고 가정하자. 각 인스턴스는 시작할때 리더쉽을 요청한다. 만약 지정된 리더가 존재한다면 인스턴스는 xd/admin 노드를 watch할 것이며 리더가 사라지면 통보를 받을 것이다. Leader는 Curator에 의해 제공된 Leader Selector 방식에 의해 지정된 인스턴스이다.  Curator는 또한 Listener callback 인터페이스를 제공하는데 클라이언트는 노드에 등록된다. 어드민 서버는 최상위 레벨 노드를 생성한다.

  • /xd/admin - 자식은 임시 노드로서 각 이용가능한 Admin 인스턴스이며 Leader Selector를 위해 이용된다.
  • /xd/containers - 자식은 런타임 속성을 포함한 임시노드로서 호스트명, 프로세스 아이디, 아이피 주소 그리고 유저 정의 속성을 포함한다.
  • /xd/streams - 각 스트림 정의 (DSL)을 포함한 퍼시스턴트 노드
  • /xd/jobs - 각 작업 정의(DSL)을 포함한 퍼시스턴트 노드
  • /xd/taps - 각 배포된 tap의 정의한 퍼시스턴트 노드
  • /xd/deployments/streams - 스트림 디플로이 상태를 포함한 노드 (리프노드는 임시노드)
  • /xd/deployments/jobs - 작업 디플로이 상태를 포함한 노드 (리프노드는 임시노드)
  • /xd/deployments/modules/requested -  디플로이 조건을 포함한 모듈 디플로이 요청을 저장
  • /xd/deployments/modules/allocated - 현재 디플로이된 모듈의 정보를 저장
어드민 리더는 DeploymentSupervisor를 생성한다. DeploymentSupervisor는 /xd/deployments/modules/requested에 스트림 및 작업배포와 연관된 module 배포 요청을 처리하기 위한 listener를 등록한다. 그리고 /xd/containers/로부터 클러스터로부터 컨테이너가 추가되거나 삭제될 때 통보를 받는다. 어떤 어드민 인스턴스는 유저 요청을 처리할 수 있다. 예를 들면 XD Shell을 통해 다음과 같은 명령을 입력해보자.

xd>stream create ticktock --definition "time | log"

이 명령은 /xd/streams/ticktock이라는 새로운 노드를 생성하기 위해 접속된 어드민 인스턴스의 REST서비스를 호출한다. 

xd>stream deploy ticktock

배포가 성공했다고 가정하자. 이것은 결과적으로 디플로이된 리소스를 관리하기 위해 /xd/deployments/streams/ticktock에 몇몇 노드를 생성할 것이다. 만약 쉘이 접속한 어드민 인스턴스가 리더가 아니라면 더 이상 액션을 수행하지 않는다. 리더의 DeploymentSupervisor가 스트림 정의의 디플로이 매니페스트와 일치하는 각 모듈의 배포를 유효한 컨테이너에 시도할 것이다. 런타임 상태는 변경된다.


간단한 예제를 따라가 보자. 만약 Spring XD 클러스터가 설정되어 있지 않다면 이 예제는 Spring XD 싱글 노드 설정에서 쉽게 수행될 수 있다. 싱글노드 어플리케이션은 기본적으로 임베디드 주키퍼 서버를 포함하고 있고 랜덤으로 사용하지않는 포트를 할당한다. 임베디드 주키퍼 연결은 single node application의 콘솔 로그에 리포트된다.


...

13:04:27,016  INFO main util.XdConfigLoggingInitializer - Transport: local

13:04:27,016  INFO main util.XdConfigLoggingInitializer - Hadoop Distro: hadoop22

13:04:27,019  INFO main util.XdConfigLoggingInitializer - Hadoop version detected from classpath: 2.2.0

13:04:27,019  INFO main util.XdConfigLoggingInitializer - Zookeeper at: localhost:31316

...

  

ZooKeeper CLI tool을 이용하여 Spring XD의 현재 상태가 반영된 ZooKeeper 노드의 내용을 검사할 것이다. 먼저 우리는 임베디드 서버에 CLI 툴로 접속하기 위해 포트를 알아야 한다. 편의를 위해 우리는 싱글 노드 어플리케이션이 시작할 때 ZooKeeper 포트를 5555로 지정하였다.


$export JAVA_OPTS="-Dzk.embedded.server.port=5555"

$xd/bin/xd-singlenode


다른 터미널 세션에서 ZooKeeper CLI를 시작하자. 이 때 노드의 내용을 검사하기 위해 임베디드 서버에 대한 접속정보를 포함시켜야 한다.


$zkCli.sh -server localhost:5555


다음과 같은 프롬프트를 볼 수 있다.


WatchedEvent state:SyncConnected type:None path:null

[zk: localhost:5555(CONNECTED) 0]


ls command를 이용하여 탐색해보자. 유니크한 컨테이너 ID는 차이가 있을 수 있다.


[[zk: localhost:5555(CONNECTED) 0] ls /xd

[deployments, containers, admins, taps, streams, jobs]

[zk: localhost:5555(CONNECTED) 1] ls /xd/streams

[]

[zk: localhost:5555(CONNECTED) 2] ls /xd/deployments

[jobs, streams, modules]

[zk: localhost:5555(CONNECTED) 3] ls /xd/deployments/streams

[]

[zk: localhost:5555(CONNECTED) 4] ls /xd/deployments/modules

[requested, allocated]

[zk: localhost:5555(CONNECTED) 5] ls /xd/deployments/modules/allocated

[2ebbbc9b-63ac-4da4-aa32-e39d69eb546b]

[zk: localhost:5555(CONNECTED) 6] ls /xd/deployments/modules/2ebbbc9b-63ac-4da4-aa32-e39d69eb546b

[]

[zk: localhost:5555(CONNECTED) 7] ls /xd/containers

[2ebbbc9b-63ac-4da4-aa32-e39d69eb546b]

[zk: localhost:5555(CONNECTED) 8]


어드민과 컨테이너 인스턴스가 실행된 Spring XD의 초기 상태가 위와 같다. 아직 배포가 되지 않아 stream이나 작업 정의가 존재하지 않는다. /xd/deployments/modules/allocated는 /xd/containers의 ID와 일치하는 퍼시스턴트 자식을 가진다.  만약 분산환경에서 실행한다면 같은 앙상블에 속한 하나의 주키퍼 서버에 접속하게 되면 /xd/containers 및 /xd/admins 하위에 다수의 노드를 볼 수 있을 것이다. 외부의 앙상블이 Spring XD 클러스터의 상태를 저장하기 때문에 모든 디플로이먼트는 Spring XD 클러스터가 종료되어도 존재할 것이다.


xd-shell을 통해 스트림을 생성해보자.


xd:>stream create ticktock --definition "time | log"

Created new stream 'ticktock'


ZK CLI로 돌아가보자.


[zk: localhost:5555(CONNECTED) 8] ls /xd/streams

[ticktock]

[zk: localhost:5555(CONNECTED) 9] get /xd/streams/ticktock

{"definition":"time | log"}

cZxid = 0x31

ctime = Mon Jul 14 10:32:33 EDT 2014

mZxid = 0x31

mtime = Mon Jul 14 10:32:33 EDT 2014

pZxid = 0x31

cversion = 0

dataVersion = 0

aclVersion = 0

ephemeralOwner = 0x0

dataLength = 27

numChildren = 0

[zk: localhost:5555(CONNECTED) 10]


get 커맨드를 이용하여 새로운 스트림 노드를 조회화면 JSON으로 표현된 stream definition을 조회할 수 있다. ephemeralOwner = 0x0은 임시노드가 아님을 나타낸다. 이제 스트림을 디플로이 해보자.


xd>stream deploy ticktock

Deployed stream 'ticktock'


주키퍼를 확인해보자.


zk: localhost:5555(CONNECTED) 10] ls /xd/deployments/streams

[ticktock]

[zk: localhost:2181(CONNECTED) 11] ls /xd/streams/deployments/ticktock

[modules, status]

[[zk: localhost:2181(CONNECTED) 12] get /xd/deployments/streams/ticktock/status

{"state":"deployed"}

....

zk: localhost:2181(CONNECTED) 13] ls /xd/deployments/streams/ticktock/modules

[source.time.1.2ebbbc9b-63ac-4da4-aa32-e39d69eb546b, sink.log.1.2ebbbc9b-63ac-4da4-aa32-e39d69eb546b]


스트림의 status 노드는 디플로이먼트 상태가 디플로이로 나타난다. 


Spring XD는 스트림 디플로이 요청을 개별적인 모듈 디플로이 요청으로 분리한다. 그러므로, 각 스트림의 각 모듈이 컨테이너 인스턴스에 연관되어 있는 것을 알 수 있다. 컨테이너 인스턴스가 하나이기 때문에 같지만 분산환경에서는 스트림의 소스와 싱크는 각각의 컨테이너에 디플로이될 수 있다. 노드명의 형식은 <module_type>.<module_name>.<module_sequence_number>.<container_id> 이다.


[zk: localhost:2181(CONNECTED) 14] ls /xd/deployments/modules/allocated/2ebbbc9b-63ac-4da4-aa32-e39d69eb546b/ticktock.source.time.1

[metadata, status]


디플로이된 모듈의 상세 정보를 저장한 메타데이터 그리고 상태노드는 임시 노드이다.  정보는 XD Shell 쿼리로 제공된다.


xd:>runtime modules

  Module                  Container Id                          Options

          Deployment Properties

  ----------------------  ------------------------------------

 -----------------------------------------------  ---------------------

  ticktock.sink.log.1     2ebbbc9b-63ac-4da4-aa32-e39d69eb546b  {name=ticktock, expression=payload,

 level=INFO}  {count=1, sequence=1}

  ticktock.source.time.1  2ebbbc9b-63ac-4da4-aa32-e39d69eb546b  {fixedDelay=1, format=yyyy-MM-dd

 HH:mm:ss}       {count=1, sequence=1}


Module Deployments


이번 섹션은 Spring XD 런타임이 디플로이먼트를 내부적으로 어떻게 관리하는지 알아볼 것이다. 스트림 디플로이먼트 요청을 처리하기 위해 StreamDeploymentListener는 ContainerMatcher를 호출하여 각 모듈이 배포될 컨테이너 인스턴스를 선택하고 /xd/deployments/modules/requested/ 노드에 모듈의 디플로이먼트 프로퍼티를 기록한다. 만약 매치가 발견되면 StreamDeploymentListener는 /xd/deployments/modules/allocated/<container_id>에 모듈을 위한 노드를 생성한다. 컨테이너는 새로운 모듈 배포를 위해 container node를 모니터하는 DeploymentListener를 포함한다. 디플로이먼트가 성공하면 컨테이너는 새로운 모듈 노드 하위에 상태와 메타데이터에 해당하는 임시노드를 작성한다.


컨테이너가 분리되면 임시노드는 삭제되고 모듈은 언디플로이된다. ContainerListener는 사라진 노드에 응답하여 유효한 모듈을 또다른 인스턴스에 배포를 시도한다.


예를 들어 두개의 컨테이너 인스턴스가 존재하고, 단순한 스트림이 배포되어 있다.


xd:>runtime containers

  Container Id                          Host            IP Address   PID    Groups  Custom Attributes

  ------------------------------------  --------------  -----------  -----  ------  -----------------

  0ddf80b9-1e80-44b8-8c12-ecc5c8c32e11  ultrafox.local  192.168.1.6  19222

  6cac85f8-4c52-4861-a225-cdad3675f6c9  ultrafox.local  192.168.1.6  19244

xd:>stream create ticktock --definition "time | log"

Created new stream 'ticktock'

xd:>stream deploy ticktock

Deployed stream 'ticktock'

xd:>runtime modules

  Module                  Container Id                          Options

          Deployment Properties

  ----------------------  ------------------------------------

 -----------------------------------------------  ---------------------

  ticktock.sink.log.1     0ddf80b9-1e80-44b8-8c12-ecc5c8c32e11  {name=ticktock, expression=payload,

 level=INFO}  {count=1, sequence=1}

  ticktock.source.time.1  6cac85f8-4c52-4861-a225-cdad3675f6c9  {fixedDelay=1, format=yyyy-MM-dd

 HH:mm:ss}       {count=1, sequence=1}


이제 하나의 컨테이너 프로세스를 종료시키고 나머지 컨테이너에 모듈이 리디플로이 되는 것을 확인해보자.


xd:>runtime containers

  Container Id                          Host            IP Address   PID    Groups  Custom Attributes

  ------------------------------------  --------------  -----------  -----  ------  -----------------

  6cac85f8-4c52-4861-a225-cdad3675f6c9  ultrafox.local  192.168.1.6  19244

xd:>runtime modules

  Module                  Container Id                          Options

          Deployment Properties

  ----------------------  ------------------------------------

 -----------------------------------------------  ---------------------

  ticktock.sink.log.1     6cac85f8-4c52-4861-a225-cdad3675f6c9  {name=ticktock, expression=payload,

 level=INFO}  {count=1, sequence=1}

  ticktock.source.time.1  6cac85f8-4c52-4861-a225-cdad3675f6c9  {fixedDelay=1, format=yyyy-MM-dd

 HH:mm:ss}       {count=1, sequence=1}


나머지 컨테이너도 종료시키면 다음과 같은 경고 메시지를 볼 수 있다.


14:36:07,593  WARN DeploymentSupervisorCacheListener-0 server.DepartingContainerModuleRedeployer - No

 containers available for redeployment of log for stream ticktock

14:36:07,599  WARN DeploymentSupervisorCacheListener-0 server.DepartingContainerModuleRedeployer - No

 containers available for redeployment of time for stream ticktock


'Programming > Spring XD' 카테고리의 다른 글

Spring XD - DIRT(Distributed Runtime) 시작하기  (0) 2015.03.12
Spring XD - Source Module 개발하기  (0) 2015.03.05
Spring XD - Modules  (0) 2015.03.04
Spring XD - Streams  (0) 2015.03.03
Spring XD - Job Module 개발하기  (0) 2015.02.05
Comments