Middleware/Kafka&RabbitMQ 2019. 3. 13. 10:38

Kafka - Kafka(카프카) cluster(클러스터) 구성 및 간단한 CLI사용법





▶︎▶︎▶︎카프카란?


이전 포스팅에서는 메시징 시스템은 무엇이고, 카프카는 무엇이며 그리고 카프카의 특징과 다른 메시지 서버와의 차이점에 대한

포스티이었습니다. 이번 포스팅은 간단하게 카프카3대를 클러스터링 구성을 하여 서버를 띄우고 CLI를 이용하여 간단히

카프카를 사용해보려고 합니다. 카프카는 중앙에서 많은 서비스 시스템의 데이터를 받아서 다른 시스템으로 받아주는 역할을 하는

메시지 시스템으로 MSA에서는 없어선 안되는 존재가 되었습니다. 그렇다면 이렇게 중요한 카프카를 한대만 띄워서 프로덕트 환경에서

운영한다는 것은 과연 안전한 생각일까요? 아닙니다. 여러대를 클러스터링 구성하여 고가용성을 높혀야 운영환경에서도 안전하고 신뢰성있는

메시지 시스템 구성이 될것입니다. 






위의 그림은 카프카를 여러대 클러스터링을 했을 경우의 구성도 입니다. 일단 중요한 것은 Zookeeper라는 존재입니다. 주키퍼는

이전 포스팅에서 Zookeeper란 무엇인가? 그리고 Solr 클러스터링 구성을 했을 때 다뤘던 내용이므로 이번 포스팅에서는 생략합니다.




▶︎▶︎▶︎Zookeeper란?

▶︎▶︎▶︎Solr Cluster 구성


간단하게 이야기하면 주키퍼란 분산환경 애플리케이션을 중앙에서 관리해주는 디렉토리 구조의 분산 코디네이터라고 보시면 됩니다. 지금부터는

실습에 초점을 마추겠습니다.



우선은 주키퍼를 설치해줍니다. 이번 실습의 버전은 3.4.12 버전기준입니다.

설정파일을 만지기 전에 주키퍼를 위한 디렉토리 구성을 진행하겠습니다. 저는 카프카 실습을 위해 별도의 하나의 디렉토리를 구성하였고,

그안에 주키퍼와 카프카에 대한 파일들을 모두 넣어놓았습니다. 우선 다음 예제는 주키퍼와 카프카를 별도의 디렉토리에 설치했다는 가정하에 진행합니다.

주키퍼는 링크를 참조하셔서 총 3대로 띄워주시면 됩니다.(Master-slave 관계의 클러스터)


카프카도 동일한 디렉토리에 설치해줍니다. 편의상 카프카의 디렉토리 루트를 $KAFKA로 지칭합니다.


우선 진행하기 앞서 클러스터 구성에서 인스턴스 각각이 자신의 메타 데이터와 카프카가 데이터를 쓸 디렉토리를 만들어줍니다.

kdata1,kdata2,kdata3 디렉토리를 만들어줍니다. 그리고 $KAFKA/config 밑에 있는 server.properties를 2장 복사해줍니다.

그리고 해당 파일 안에 설정들을 클러스터 구성에 가장 기본이 되는 설정으로 바꿔줍니다.


broker.id = 1,broker.id = 2,broker.id = 3 으로 총 3개의 serverN.properties에 작성해줍니다. 이것은

주키퍼 myid와 비슷한 역할을 하는 클러스터 인스턴스 구분용 ID값입니다.

그리고 지금 실습은 로컬에 3대의 카프카서버를 띄워서 클러스터링 구성을 할것임으로 각각 서버의 포트를 달리 지정해줍니다.


listeners=PLAINTEXT://:9092,listeners=PLAINTEXT://:9093,listeners=PLAINTEXT://:9094 으로 각각 파일에 포트를 구분해줍니다.


log.dirs=$KAFAKA/kdata1,log.dirs=$KAFAKA/kdata2,log.dirs=$KAFAKA/kdata3 으로 각각 카프카 서버가 자신의

메타 데이터를 쓸 디렉토리를 구분지어줍니다. 필자는 처음에 하나의 디렉토리만 생성해서 공유했더니 충돌 문제가 있어

각각 별도의 디렉토리 구성을 가져갔습니다.



마지막으로 주키퍼와의 연동 설정입니다.

zookeeper.connect=localhost:2181,localhost:2182,localhost:2183/kafka-broker 으로 각 설정 파일에 동일하게 작성해줍니다.

여기에서 조금 특이한것이 ~/kafka-broker 입니다. 이것은 무엇이냐면 만약 각각 다른 용도의 카프카 클러스터를 2세트 운영한다고 가정하고,

주키퍼는 같은 주키퍼 클러스터를 이용한다고 합니다. 만약 ~/kafka-broker를 입력하지 않으면 카프카 클러스터 2세트가 주키퍼의 루트 디렉토리에 동일한

구조의 데이터를 쓰게 되고 그러면 카프카 클러스터 2세트가 데이터 충돌이 나게됩니다. 그렇기 때문에 ~/kafka-broker라고 입력하여 주키퍼의 루트디렉토리가

아닌 /kafka-broker/.. 라는 디렉토리를 하나 새로 생성하여 아예 카프카 클러스터 1대의 전용 디렉토리를 구성해주는 겁니다. 여기까지 진짜 기본적인

설정으로 클러스터 설정을 마쳤습니다. 사실은 이정도 설정으로는 운영환경에서는 운영하기 힘들것입니다. 나머지 설정관련해서는 카프카 공식 홈페이지에

영어로 친절하게? 작성되어 있으니 참고 부탁드립니다.






이제 $KAFKA/bin/kafka-server-start.sh -daemon ../config/server.properties 명령으로 3개의 카프카를 실행시켜줍니다. 물론 선행조건은

주키퍼가 실행되었다는 조건입니다. 만약 지금까지 잘 따라오셨다면 문제없이 실행됩니다. 마지막으로 간단한 CLI를 이용한 producer,consumer 예제입니다.



위의 명령으로 큐의 역할로 사용될 토픽을 생성해줍니다. 만약 토픽을 잘못 생성하신 분들을 위해서 토픽을 삭제하는 명령입니다.



Noting 메시지와 함께 토픽이 삭제되었습니다. 



프로듀서를 실행시켜줍니다. 설정 인자들은 설명없어도 직관적으로 보입니다.



메시지를 보냅니다.



또 한번 보냅니다.



컨슈머 쪽을 확인해보면 메시지가 잘와있는 것을 확인할 수 있습니다. 여기까지 카프카 클러스터 구성과 간단한 CLI를 이용하여 카프카를 사용해보았습니다.

posted by 여성게
:
Middleware/Zookeeper 2018. 12. 31. 23:31

Zookeeper란?,분산코디네이터



분산 처리 환경에서 필수로 언급되는 ZooKeeper란 무엇일까? 한 마디로 정의하면 "분산 처리 환경에서 사용 가능한 데이터 저장소"라고 말할 수 있겠다. 기능은 매우 단순하지만 분산 서버 환경에서는 활용 분야가 넓다. 예를 들어 분산 서버 간의 정보 공유, 서버 투입/제거 시 이벤트 처리, 서버 모니터링, 시스템 관리, 분산 락 처리, 장애 상황 판단 등 다양한 분야에서 활용할 수 있다.

'어떻게 다양한 활용이 가능할까?'라는 의문을 해결하기 위해서 기능에 대해서 한 마디로 설명하면 다음과 같이 설명할 수 있다.

ZooKeeper는 데이터를 디렉터리 구조로 저장하고, 데이터가 변경되면 클라이언트에게 어떤 노드가 변경됐는지 콜백을 통해서 알려준다. 데이터를 저장할 때 해당 세션이 유효한 동안 데이터가 저장되는 Ephemeral Node라는 것이 존재하고, 데이터를 저장하는 순서에 따라 자동으로 일련번호(sequence number)가 붙는 Sequence Node라는 것도 존재한다. 조금 과장하면 이러한 기능이 ZooKeeper 기능의 전부다. 이런 심플한 기능을 가지고 자신의 입맛에 맞게 확장해서 사용하면 된다.



기능을 하나씩 자세히 살펴보자. 우선 ZooKeeper가 데이터를 저장하는 방식인 데이타 모델에 대해서 살펴보자.

ZooKeeper는 <그림 1>과 같은 디렉터리 구조로 데이터를 저장한다. 특징을 살펴보면 Persistent를 유지하기 위해서 트랜잭션 로그와 스냅샷 파일이 디스크에 저장되어 시스템을 재시작해도 데이터가 유지된다. 각각의 디렉터리 노드를 znode라고 명명하며, 한 번 설정한 이름은 변경할 수 없고 스페이스를 포함할 수도 없다.

zookeeper1

그림 1 ZooKeeper의 데이터 모델(이미지 출처)

데이터를 저장하는 기본 단위인 노드에는 Persistent Node와 Ephemeral Node, Sequence Node, 3가지가 있다.

첫 번째로 Persistent Node는 한 번 저장되고 나면 세션이 종료되어도 삭제되지 않고 유지되는 노드다. 즉, 명시적으로 삭제하지 않는 한 해당 데이터는 삭제 및 변경되지 않는다.

두 번째로 Ephemeral Node는 특정 노드를 생성한 세션이 유효한 동안 그 노드의 데이터가 유효한 노드다. 좀 더 자세히 설명하면 ZooKeeper Server에 접속한 클라이언트가 특정 노드를 Ephermeral Node로 생성했다면 그 클라이언트와 서버 간의 세션이 끊어지면, 즉 클라이언트와 서버 간의 Ping을 제대로 처리하지 못한다면 해당 노드는 자동으로 삭제된다. 이 기능을 통해 클라이언트가 동작하는지 여부를 쉽게 판단할 수 있다.

세 번째로 Sequence Node는 노드 생성 시 sequence number가 자동으로 붙는 노드다. 이 기능을 활용해 분산 락 등을 구현할 수 있다.

다음으로 ZooKeeper 서버 구성도를 살펴보자.

zookeeper2

그림 2 ZooKeeper의 서버 구성도(이미지 출처)

ZooKeeper 서버는 일반적으로 3대 이상을 사용하며 서버 수는 주로 홀수로 구성한다. 서버 간의 데이터 불일치가 발생하면 데이터 보정이 필요한데 이때 과반수의 룰을 적용하기 때문에 서버를 홀수로 구성하는 것이 데이터 정합성 측면에서 유리하다. 그리고 ZooKeeper 서버는 Leader와 Follower로 구성되어 있다. 서버들끼리 자동으로 Leader를 선정하며 모든 데이터 저장을 주도한다.



클라이언트에서 Server(Follower)로 데이터 저장을 시도할 때 Server(Follower) -> Server(Leader) -> 나머지 Server(Follower)로 데이터를 전달하는 구조이고, 모든 서버에 동일한 데이터가 저장된 후 클라이언트에게 성공/실패 여부를 알려주는 동기 방식으로 작동한다. 즉, 모든 서버는 동일한 데이터를 각각 가지고 있고, 클라이언트가 어떤 서버에 연결되어 데이터를 가져가더라도 동일한 데이터를 가져가게 된다.

사용/운영 시 몇 가지 주의 사항이 있다. 개발에 급급한 나머지 ZooKeeper를 캐시 용도로 사용해 서비스를 오픈했다가 장애가 발생한 사례도 종종 있다고 한다. 데이터의 변경이 자주 발생하는 서비스에서 ZooKeeper를 데이터 저장소로 사용하는 것은 추천하지 않는다. ZooKeeper에서 추천하는 Read : Write 비율은 10 : 1 이상이다.

그리고 ZooKeeper 서버가 제대로 실행되지 않을 때가 있는데, 대부분 서버간의 데이터 불일치로 인한 데이터 동기화 실패가 그 원인이다. 주로 베타 테스트 후 운영 직전에 ZooKeeper 서버를 증설해서 사용하는데, 이럴 때 기존에 테스트했던 서버와 신규로 투입한 서버의 데이터 차이로 인해 이런 현상이 종종 발생한다. 이때는 데이터를 초기화한 후 서버를 실행하면 된다.

그리고 마지막으로, zoo.cfg라는 설정(configuration) 파일의 ZooKeeper 서버 목록을 꼼꼼히 확인해야 한다. 서버 목록이 정확히 맞지 않아도 서버가 실행되긴 하지만, 로그 파일을 확인하면 ZooKeeper 서버가 지속적으로 재시작할 때도 있고 데이터를 엉뚱한 곳에 저장하기도 한다.


출처:https://d2.naver.com/helloworld/294797

posted by 여성게
:

Solr&Zookeeper(솔라&주키퍼) cloud 환경 구성



Mac OS 환경에서 작성되었습니다.


solr와 zookeeper를 연동하여 cloud 환경구성하기 입니다.


우선 진행하기 전에 수정 혹은 생성 되어야할 설정 목록입니다.


1)solr.xml : solr cloud를 이루는 solr instance에 관한 설정파일입니다.

2)zoo.cfg : zookeeper 관련 설정파일입니다.

3)collection config file : solr collection들이 가지게 될 schema.xml,solrConfig.xml 등의 파일이 들어가는 config file입니다. 이 파일은 zookeeper에 upconfig하여 모든 solr                                         instance들이 공유하게 됩니다.

4)zooServer Dir : zookeeper들은 파일로써 data snapshot을 저장합니다. 그러한 snapshot파일과 zookeeper 식별 파일들이 들어가는 디렉토리입니다.




<solr.xml>

solr의 폴더의 위치를 편하게 $SOLR_HOME이라고 표현합니다.


$SOLR_HOME/server/solr

$SOLR_HOME/server/solr2

$SOLR_HOME/server/solr3


이렇게 3개의 폴더를 생성해줍니다.(solr는 원래 존재하는 폴더) 그리고 solr folder 밑에 solr.xml파일을 복사하여 solr2/solr3 folder 밑에 복사해줍니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="UTF-8" ?>
<solr>
  <solrcloud>
    <str name="host">localhost</str>
    <int name="hostPort">${jetty.port:8983}</int>
    <str name="hostContext">${hostContext:solr}</str>
    <bool name="genericCoreNodeNames">${genericCoreNodeNames:true}</bool>
    <int name="zkClientTimeout">${zkClientTimeout:30000}</int>
    <int name="distribUpdateSoTimeout">${distribUpdateSoTimeout:600000}</int>
    <int name="distribUpdateConnTimeout">${distribUpdateConnTimeout:60000}</int>
    <str name="zkCredentialsProvider">${zkCredentialsProvider:org.apache.solr.common.cloud.DefaultZkCredentialsProvider}</str>
    <str name="zkACLProvider">${zkACLProvider:org.apache.solr.common.cloud.DefaultZkACLProvider}</str>
  </solrcloud>
  <shardHandlerFactory name="shardHandlerFactory"
    class="HttpShardHandlerFactory">
    <int name="socketTimeout">${socketTimeout:600000}</int>
    <int name="connTimeout">${connTimeout:60000}</int>
  </shardHandlerFactory>
</solr>
 
cs

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="UTF-8" ?>
<solr>
  <solrcloud>
    <str name="host">localhost</str>
    <int name="hostPort">${jetty.port:8984}</int>
    <str name="hostContext">${hostContext:solr}</str>
    <bool name="genericCoreNodeNames">${genericCoreNodeNames:true}</bool>
    <int name="zkClientTimeout">${zkClientTimeout:30000}</int>
    <int name="distribUpdateSoTimeout">${distribUpdateSoTimeout:600000}</int>
    <int name="distribUpdateConnTimeout">${distribUpdateConnTimeout:60000}</int>
    <str name="zkCredentialsProvider">${zkCredentialsProvider:org.apache.solr.common.cloud.DefaultZkCredentialsProvider}</str>
    <str name="zkACLProvider">${zkACLProvider:org.apache.solr.common.cloud.DefaultZkACLProvider}</str>
  </solrcloud>
  <shardHandlerFactory name="shardHandlerFactory"
    class="HttpShardHandlerFactory">
    <int name="socketTimeout">${socketTimeout:600000}</int>
    <int name="connTimeout">${connTimeout:60000}</int>
  </shardHandlerFactory>
</solr>
 
cs


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="UTF-8" ?>
<solr>
  <solrcloud>
    <str name="host">localhost</str>
    <int name="hostPort">${jetty.port:8985}</int>
    <str name="hostContext">${hostContext:solr}</str>
    <bool name="genericCoreNodeNames">${genericCoreNodeNames:true}</bool>
    <int name="zkClientTimeout">${zkClientTimeout:30000}</int>
    <int name="distribUpdateSoTimeout">${distribUpdateSoTimeout:600000}</int>
    <int name="distribUpdateConnTimeout">${distribUpdateConnTimeout:60000}</int>
    <str name="zkCredentialsProvider">${zkCredentialsProvider:org.apache.solr.common.cloud.DefaultZkCredentialsProvider}</str>
    <str name="zkACLProvider">${zkACLProvider:org.apache.solr.common.cloud.DefaultZkACLProvider}</str>
  </solrcloud>
  <shardHandlerFactory name="shardHandlerFactory"
    class="HttpShardHandlerFactory">
    <int name="socketTimeout">${socketTimeout:600000}</int>
    <int name="connTimeout">${connTimeout:60000}</int>
  </shardHandlerFactory>
</solr>
 
cs


총 3개의 solr.xml이 각각의 solr,solr2,solr3 폴더에 위치하게 됩니다. 보시는 것과 같이 service port 만 달라지게 됩니다. 이렇게 solr instance가 3개가 뜨게 되며, 각각 8983,8984,8985 port로 요청을 받게 됩니다. 여기서 인스턴스를 홀수개로 띄우는 이유는 리더선출에 관련되어 있습니다. 반드시 클라우드 환경에서는 홀수개의 인스턴스를 띄워줘야 합니다.


그리고 $SOLR_HOME에 적절한 위치에 zookeeper에 upconfig할 config file folder를 위치시켜줍니다. 본인은 server folder와 같은 위치에 solr-config 라는 folder로 만듬. 그리고 이 solr-config 밑에는 conf라는 폴더가 위치해야하고 conf라는 폴더 밑에 upconfig를 위한 설정파일들이 위치해야함.







<zoo.cfg>

zookeeper가 위치한 폴더경로를 편하게 $ZK_HOME이라고 표현하겠습니다.


1
2
3
4
5
6
7
8
9
#tickTime=2000
initLimit=10
syncLimit=5
dataDir=/Users/yun-yeoseong/zooServer/zookeeper/1
clientPort=2181
 
server.1=127.0.0.1:2888:3888
server.2=127.0.0.1:2889:3889
server.3=127.0.0.1:2890:3890
cs


1
2
3
4
5
6
7
8
9
#tickTime=2000
initLimit=10
syncLimit=5
dataDir=/Users/yun-yeoseong/zooServer/zookeeper/2
clientPort=2182
 
server.1=127.0.0.1:2888:3888
server.2=127.0.0.1:2889:3889
server.3=127.0.0.1:2890:3890
cs

1
2
3
4
5
6
7
8
9
#tickTime=2000
initLimit=10
syncLimit=5
dataDir=/Users/yun-yeoseong/zooServer/zookeeper/3
clientPort=2183
 
server.1=127.0.0.1:2888:3888
server.2=127.0.0.1:2889:3889
server.3=127.0.0.1:2890:3890
cs


$ZK_HOME/conf 밑에 총 3개의 zoo.cfg,zoo2.cfg,zoo3.cfg 파일들을 생성해줍니다. 여기서 dataDir는 zookeeper의 식별자파일 및 data snapshot들이 저장되는 경로입니다. 3개의 zookeeper 인스턴스는 각각의 zoo.cfg를 갖습니다.(zookeeper도 동일한 이유로 홀수개의 인스턴스를 띄움). clientPort는 말그대로 zookeeper가 사용할 포트입니다. 그 밑에 server.1=~는 총개의 zookeeper의 인스턴스를 뜻하며 2개의 포트는 zookeeper들끼리 통신할 포트와 리더선출을 위한 포트로 사용됩니다.



<dataDir>



zoo.cfg에 작성한 dataDir와 동일한 위치에 디렉토리 구조를 잡아줍니다. 그리고 그 디렉토리 밑에 확장자가 없는 myid파일들을 각각 만들어줍니다.(~$vi myid) 그리고 각 내용은 상위 폴더와 같이 1/myid는 1이라는 숫자하나 2/myid는 2라는 숫자하나 3/myid는 3이라는 숫자하나만 넣어줍니다.(version-2 폴더는 필자가 cloud환경을 운영하다 생긴 폴더입니다. zookeeper의 data snapshot 파일등이 들어가있습니다. 별도로 생성할 필요 x)


여기까지 모두 세팅이 완료되었습니다.


zookeeper 실행

bin/zkServer.sh start conf/zoo.cfg

bin/zkServer.sh start conf/zoo2.cfg

bin/zkServer.sh start conf/zoo3.cfg 


zookeeper 종료

bin/zkServer.sh stop conf/zoo.cfg

bin/zkServer.sh stop conf/zoo2.cfg

bin/zkServer.sh stop conf/zoo3.cfg


solr 실행

bin/solr start -c -s server/solr -p 8983 -z localhost:2181,localhost:2182,localhost:2183 -noprompt

bin/solr start -c -s server/solr2 -p 8984 -z localhost:2181,localhost:2182,localhost:2183 -noprompt

bin/solr start -c -s server/solr3 -p 8985 -z localhost:2181,localhost:2182,localhost:2183 -noprompt


solr 종료
bin/solr stop -c -s server/solr -p 8983 -z localhost:2181,localhost:2182,localhost:2183 -noprompt
bin/solr stop -c -s server/solr2 -p 8984 -z localhost:2181,localhost:2182,localhost:2183 -noprompt
bin/solr stop -c -s server/solr3 -p 8985 -z localhost:2181,localhost:2182,localhost:2183 -noprompt

이렇게 하면 zookeeper 3개 solr 3개가 올라가는 cloud환경을 구성할 수 있습니다. 지금은 로컬환경이라 한 서버에 3개를 모두 올렸지만 나중에 서버환경에서는 각각 다른 서버에 올리시면 됩니다. 여기서 중요한 것은 만약 다른 서비스 프로젝트에서 solr와 연동되어 사용될때, solr 요청 ip가 localhost가 아니라 server_ip로 요청(즉, 로컬환경이 아니라 서버배포환경)이 간다면 처음에 solr.xml에 설정된 <str name="host">server_ip</str>로 변경해주셔야합니다. 다른 서비스프로젝트에서는 server_ip로 요청을 보내고 cloud환경의 solr는 localhost로 띄운다면 정상적으로 동작하지 않을 것입니다.

여기까지입니다.

여기서 생략된 부분이 몇가지 있다면 solr의 zk upconfig 명령어를 이용하여 solr-config에 있는 config file을 zookeeper에 업로드하는 명령어와 기타 색인 및 쿼리 작업들은 생략되었습니다. 이 부분들은 대부분 구글링 하면 많은 것이라 생각하기에...생략하였습니다. 하지만 중요한 부분입니다. collection을 생성할때에는 반드시 config파일의 이름을 물고 생성이 되기때문에 해당 config파일이 upconfig되어있어야지만 collection이 생성됩니다.(default config파일도 있지만 사용자들이 커스터마이징한 config file을 사용한다는 전제하에) 혹시라도 이 부분에 대해 확실한 답을 찾지 못하셨거나 궁금하신 점이 있으면 댓글 달아주시면 개인적으로 메일드리겠습니다.

posted by 여성게
: