read

Kafka 면접 질문


KAFKA

일단, 가장 많이 받은 것은 무엇으로 로깅처리를 구현하였나 라는 질문이다. 나의 경우, Kafka와 FluentD를 이용해 로깅을 처리 했기 때문에, 이것에 대해 좀 자세히 알아둘 필요가 있었다.

앞서, 문서 해석을 통해 Kafka에 대한 성격은 알아볼 수 있었다. 기본적인 사항만 요약을 해보자면, Distributed Streaming Platform 분산형 스트리밍 플랫폼이며, 결함감내 의 내구성 방식으로 레코드의 스트림을 저장하고, 레코드 스트림이 발생하면 처리하는 Event Driven의 성격을 가지고 있다. 또한, 무엇보다 기존의 메시지 시스템의 큰 모델이었던, Queue 방식과 publish-subscribe 방식 두가지를 다 가능하도록 만들었다는 것이 장점이다.

잠깐 이부분을 정리하자면,

  Queue Publish-Subscribe
설명 Consumer 풀은 서버에서 읽을 수 있으며 각 레코드는 그 중 하나에 저장한다 레코드는 모든 Consumer에게 브로드캐스트 한다
장점 여러 Consumer 인스턴스에서 데이터 처리를 분할할 수 있으므로 처리 규모 확장 가능 여러 프로세스에 데이터를 브로드 캐스트 할 수 있다
단점 다중 subscriber가 아니므로, 한번 한 프로세스가 읽은 데이터는 사라짐 모든 메시지가 모든 Consumer에게 전달되므로 처리를 조정할 방법이 없다

그러나, Kafka는 이 두 개념의 장점을 Consumer 그룹 을 이용해 들고왔는데, queue에서와 마찬가지로 Consumer 그룹은 프로세스 모음(Consumer 그룹의 구성원)을 통해 처리를 나눌 수 있고(consumer instance가 각 partition에 대해 queue로 작용), publish-subscribe처럼 여러 Consumer 그룹에 메세지를 브로드 캐스트 할 수 있다.

게다가 순서에서도 효율이 높은데, Queue의 경우 병렬 처리의 개념이 없어, 서버에서 순차적으로 레코드를 가지고 있다가 여러 Consumer가 큐에서 Consume하려 할 때, 순차적으로 보내주더라도 레코드가 비동기적으로 전달되기 때문에 순서성이 사라진다. (물론, 각각 단일 Queue를 붙이면 가능하지만, 효율이 떨어질 수 밖에 없다)

그러나, Kafka는 TOPIC 내에서 파티션을 이용해 순서 보증과 로드 밸런싱을 모두 제공 할 수 있다. 즉, 파티션 하나를 Consumer 그룹의 Consumer에게 할당하면서, 각각 개별적으로 Consume 될 수 있게 만든 것이다.

다만, Consumer 그룹에서 파티션보다 많은 수의 Consumer를 가질 수가 없다.

또한, 파일로 저장된다는 것도 기억하자.

이렇게 Kafka가 정리되었고, FluentD 에 대해 알아보자.


FluentD


Fluentd is an open source data collector for unified logging layer

FluentD는 통합 로깅 계층을 위한 오픈 소스 데이터 수집기이다.

FluentD의 홈페이지에 들어가면 첫 문구이다.

즉, 말 그대로 데이터 수집기다.

또한, 이 성격으로는 다음과 같이 볼 수 있다고 한다.

아키텍쳐의 단순성과 이를 기반으로 한 안정성을 초점을 두고 있다. 그래서 아키텍쳐 구성이나 설정이 간단하다.

출처: http://bcho.tistory.com/1115 [조대협의 블로그]

즉, 나의 경우 FluentD만으로도 로그를 수집해 저장할 수 있지만, 하나의 Producer로써 사용하였다.

FluentD가 읽어드린 데이터는

tag: 이벤트를 어디로 보낼지 결정하기 위한 구분값
time: 이벤트가 발생한 시간
record: 데이터 (JSON)

로 구성되며 이벤트로 처리된다.

tag 가 중요한 역할을 하는데, 어떻게 적냐에 따라 이동의 기준이 된다.

사용 이유는 td-agent가 죽더라도, 메모리와 파일 기반의 버퍼(Buffer) 시스템이 있고, tail 명령어로 읽어드린 이후부터 다시 읽을 수 있다.

파일로 저장된 로그가 끊기지 않게 읽어드릴 수 있는 것이다.

Chunk 별로 로그 파일을 읽고, stdout으로 보내거나, Kafka Producer 로서 기능을 하게 만들면 된다.

FluentD의 Chunk 개념을 이용해, 각기 다른 레벨의 로그를 수집하고, 각각을 Producer로써 Kafka에 제공한 뒤, HTTP Client, DB Client로 Consumer를 만들고 DB에 저장하거나, 웹으로 보일 수 있게 만들었다.

해당사항에 대해서는 내가 했었던 Mini_Clova 내의 pdf에 나와 있다.

Zookeeper 라는 것도 Kafka를 사용할 떄 사용하게 되는데, 카프카의 노드 관리를 해주고, Topic의 offset 정보등을 저장하기 위해 필요하다.

출처: https://www.popit.kr/kafka-%EC%9A%B4%EC%98%81%EC%9E%90%EA%B0%80-%EB%A7%90%ED%95%98%EB%8A%94-%EC%B2%98%EC%9D%8C-%EC%A0%91%ED%95%98%EB%8A%94-kafka/ [Kafka 운영자가 말하는 처음 접하는 Kafka]


카프카가 왜 분산처리에 좋은가?


해당 질문은 앞서 이미 설명한 듯하다. 말 그대로 카프카가 분산형 스트리밍 플랫폼이다.

Queue와 Publish-Subscribe 모델의 장점을 가져온 Kafka는 Consumer 개념을 이용해, 브로드캐스팅 처리와 각 개별적인 병렬 처리도 가능하게 한다.

또한 무엇보다 확장성에 있다.

Broker와 Partition으로 확장가능한 점이 부하를 분산해 Consume 할 수 있게 도와준다.


카프카를 사용하다가 죽으면 어떻게 되냐?


사실 가장 알고 싶었던 개념이다.

Partition의 경우, 하나의 Leader를 가지고 있고, Leader중 하나가 죽었을 경우, Follower중 하나가 Leader가 되는 형태가 된다. Leader가 입출력을 처리한다면, Follower는 단순히 Leader의 복제본이다.

그러나, Kafka가 죽는다면 이라는 질문이 Partition을 의미했던 것인지 알기가 힘들다. 따라서 이리저리 알아보려 했다.

가장 핵심적인 설명은 Kafka 운영자가 말하는 Topic Replication 여기서 볼 수 있었다.

앞서, 설명한 것과 같이 Partition의 경우가 아닌, ALL DOWN의 경우를 설명해 두었다.

즉, 더이상 Leader가 될 수 있는 Replica가 없는 경우, Read/Write가 멈추는 것은 당연하다. 다만, 복구될 때 대응 방법이 나눠어 진다.

  1. 마지막까지 leader였던 broker1번이 up이 되고 다시 leader가 될 때까지 기다린다.(모든 데이터를 가지고 있을 가능성이 높다)
  2. ISR과 상관없이 누구라도 가장 빨리 up이 되는 topic이 leader가 된다.(데이터 손실이 되더라도 장애 대응이 빠르다)

확실히 정리해보자


Kafka는 여러 서버의 클러스터 로서 동작한다.

하나의 서버를 Broker 라고 하는데, TOPIC이 있을 경우, 하나의 Broker에만 저장할 수도 있고, 여러 서버에 해당 TOPIC을 다 저장시켜 분산처리를 할 수도 있다.

만약, 파티션 갯수를 늘린다면, TOPIC을 다수의 파티션에 나누어 여러 브로커에 분산 배치하여 프로듀서가 TOPIC을 통해 메시지를 보내면 여러 브로커를 통해 분산 전송이 가능하다.

즉, TOPIC을 저장하는 방법은 Broker의 갯수에 따라 다르게 지정할 수 있고, 해당 TOPIC에 대한 파티션 역시 여러개로 나눌 경우, 알고리즘에 따라 분산해 저장시킬 수 있다. 그 예시는 다음과 같다.

Kafka example

만약, Topic1라는 TOPIC이 있으며, 파티션은 1개로 설정했을 때, Replication 을 3개로 지정했다고 하자.

Kafka는 복제를 파티션 단위 로 하기 때문에, Broker 3개에 한개씩 복제를 한다. (Broker 갯수보다 많이 복제할 수는 없다.)

또한, 복제는 Master/Slave 형태로 Leader 가 모든 요청을 처리하며, Replica 들은 이를 복제해 가지고 있게된다.

따라서, Leader가 죽으면 Slave중 한개가 Leader로 올라와 진행되기 때문에, N개의 Replica가 있으면, N-1개가 죽더라도 장애를 버텨낸다.

여기서 Leader의 선출은 주키퍼 가 담당한다.

그 예시는 다음과 같다.

Kafka example

파티션이 1개가 아니라 여러개 일 경우를 알아보자.

앞서, 파티션 갯수를 늘린다면 TOPIC을 다수의 파티션에 나누어 여러 브로커에서 처리할 수 있다고 했다.

그 예시로 파티션이 3개이고 Replication이 1개일 경우는 다음과 같다.

Kafka example

이렇게 여러 노드로 분산을 시키면, 프로듀서는 이들 중 하나의 파티션으로 메시지를 전송한다.

전송알고리즘은 직접 설정할 수도 있고, 라운드로빈을 사용할 수도 있다.

그럼 여러 파티션이 Replica도 여러개면 어떻게 되는지 다음 그림을 보자.

Kafka example

위 그림은 P0, P1, P2 라는 파티션이 3개이고, 각각 R0, R1, R2 라는 Replica를 가지게 되었다.

또한, 붉은 색으로 표시 된 부분이 Leader로써, 모든 요청을 처리하는데, P0에 대한 요청은 Broker 1에서, P1은 Broker 2, P2는 Broker 3 이 담당하게 되는 것이다.

Leader가 한곳에 모여 있으면 부하 분산이 안된다고 볼 수 있는데, 알고리즘 상 Broker에 균등하게 분배되도록 되어 있다.

그럼 Consumer 에 대해 알아보자.

앞서 말했듯이 Queue형식과 publish-subscribe형식 둘다 Consumer Group를 사용해 일반화 했다고 했다.

Kafka의 파티션은 Consumer Group 당, 오로지 한개의 Consumer Instance 만 접근을 허용해서 같은 Group에 속한 Consumer끼리는 같은 파티션에 접근할 수가 없다.

그렇게 한 파티션에 정해진 Consumer Instance를 Partition Owner 라고 하는데, broker나 consumer의 변동이 없는 한 계속 유지가 된다.

만약 Consumer가 추가/제거가 된다면, 해당 Group 내에서 Consumer들이 파티션의 재분배 되며, Broker가 추가/제거가 되면 전체 Consumer Group에서 재분배가 된다.

만약, Consumer Group 내의 Consumer의 수가 파티션보다 적으면, 하나의 Consumer에서 여러 파티션의 Owner가 되고, 파티션 수가 Consumer보다 적으면, Consumer가 메세지 처리를 하지 못하게 되거나 재분배에 문제가 생길 수 있으므로 적절히 조절해야 한다.

Queue 동작과 publish-subscribe에 대해 설명해보면,

만약, 다수의 Consumer Instance를 가진 Consumer Group이 있을 때, 각 consumer마다 별도의 partition으로부터 메시지를 받으므로 하나의 Queue처럼 행동하게 되고,

Consumer Group은 별로 동일한 파티션에 접근해 동일한 메세지를 받을 수 있으므로, publish-subscribe 처럼 행동할 수 있는 것이다.

주의할 점이 있다.

한 파티션에 대한 내용은 Producer에서 보낸 순서대로 저장되며, 그 순서대로 Consumer가 받아 처리할 수 있지만, 다른 파티션에 대해서는 그렇지 않다.

만약, 한개의 Consumer가 여러개의 파티션의 Owner라면 각자의 순서는 보장되어도 합쳐진 순서는 보장되지 않는다.

따라서, 전체 메세지를 순차적으로 Consumer가 받고 싶다면 해당 TOPIC이 한개의 파티션만 가지도록 해야한다.

여기서 Replica는 말 그대로 복제본이기 때문에 한 파티션이 여러개의 Replica를 들고 있다면, 그중 Leader와 연결되어 있는 것이고 다른 Replica는 Leader의 내용을 가지고 있는 것이기 때문에 전혀 다른 내용이다.

이에 대해 실험을 한 건 다음 출처를 확인해보도록 하자.

출처 : https://www.popit.kr/kafka-%EC%9A%B4%EC%98%81%EC%9E%90%EA%B0%80-%EB%A7%90%ED%95%98%EB%8A%94-%EC%B2%98%EC%9D%8C-%EC%A0%91%ED%95%98%EB%8A%94-kafka/

개인적으로 해당 부분을 구현했던 것으로 설명을 덧붙이자면, 전체 로그를 저장하는 Topic인 Log 라는 Topic이 있고, Error가 난 로그만 저장하는 Error라는 Topic이 있다고 하자.

또한 Error 로그DB에 저장 시킬 Consumer GroupWeb에 파일내용 그대로 보여줄 Consumer Group 2개가 있다고 하자.

Log라는 Topic과 Error라는 Topic이 있고 로깅은 일반적으로 시간순이 보장되는 것이 좋기 때문에, Partition은 1개로 해야 시간순이 보장 될 것이다.

또한, Broker의 수가 3개라고 할 때, 최대 3개의 Replica로 설정해 장애에 버텨낼 수 있게 설정할 수 있다.

그럼 2개의 Consumer Group은 각각 1개의 Consumer Instance를 가져야 할 것이고 (Error 파티션이 1개이므로)

각자, Error에 대한 내용을 Pull로 가져와서 처리하면 된다.

또한, 만약에 Topic을 하나만 하고싶다고 하자. 그럼 파티션을 2개로 나누어야 하는데, Producer에서 보내는 메시지의 키를 활용하여 Info Log는 P0에만 전송하고, Error Log는 P1에만 전송하는 형태의 구성할 수도 있다.

다만, 그렇게 설정했을 경우, 각각에 대한 순서는 보장하지만, 전체 순서는 보장할 수 없다.

출처 : http://epicdevs.com/17

출처 : http://m.dbguide.net/about.db?cmd=view&boardConfigUid=19&boardUid=183426

출처 : https://www.joinc.co.kr/w/man/12/Kafka/about

이로써 두번에 걸친 Kafka에 대한 설명은 끝이 났다.

다음에는 Node js와 Javascript 쪽에 대한 면접 질문에 대해 적어보겠다.

Blog Logo

구찌


Published

Image

구찌의 나도한번 해블로그

구찌의 개발 블로그 입니다.

Back to Overview