Last Updated on 4월 30, 2023 by Jade(정현호)
안녕하세요.
이번 포스팅은 카프카의 내부 동작 원리와 구현에 대한 글로 실전 카프카 개발부터 운영까지 책을 정리한 글 입니다.
카프카 연재글로 아래 포스팅에서 이어지는 글 입니다.
Contents
카프카 리플리케이션
고가용성 분산 스트리밍 플렛폼 카프카는 무수히 많은 데이터 파이프라인의 정중앙에 위치하는 메인 허브 역할을 합니다.
이렇게 중앙에서 메인 허브 역할을 하는 카프카 클러스터가 만약 하드웨어의 문제나 점검 등으로 인해 정상적으로 동작하지 못한다거나, 카프카와 연결된 전체 데이터 파이프라인에 영향을 영향을 미친다면 이는 매우 심각한 문제가 아닐수 없습니다.
따라서 카프카는 초기 설계 단계에서부터 이러한 일시적인 하드웨어 이슈 등으로 대비를 해야 합니다.
리플리케이션 동작 개요
카프카 는 브로커의 장애에도 불구하고 연속적으로 안정적인 서비스 제공함으로써 데이터 유실을 방지하며 유연성을 제공 합니다.
카프카의 리플리케이션 동작을 위해 토픽 생성시 필수로 replication factor 라는 옵션을 설정해야 합니다.
아래와 같이 토픽을 생성하면서 리플리케이션을 설정하도록 하겠습니다.
kafka-topics.sh --bootstrap-server kafka1:9092 \ --create --topic jade-test01 --partitions 1 \ --replication-factor 3 Created topic jade-test01.
토픽이름은 jade-test-01 으로 생성 하였으며, kafka-topic.sh 를 명령어를 이용해 토픽을 생성하였으며, 파티션 수 1개,리플리케이션 팩터 수는 3으로 설정 하였습니다.
토픽 생성 된후 describe 옵션을 이용해서 토픽에 대한 상세한 정보를 출력해보도록 하겠습니다.
$ kafka-topics.sh --bootstrap-server kafka1:9092 \ --topic jade-test01 --describe 결과) Topic: jade-test01 PartitionCount: 1 ReplicationFactor: 3 Configs: segment.bytes=1073741824 Topic: jade-test01 Partition: 0 Leader: 1 Replicas: 1,2,3 Isr: 1,2,3
토픽 파티션의 상세 내용으로 파티션 개수는 1개이며 리더는 브로커 1(Leader : 1) 으로 되어 있는 것을 확인 할 수 있으며, 리플리케이션들은 브로커가 3개(1,2,3)가 있다는 것을 알 수 있습니다.
이전에도 언급 드린 내용처럼 리플리케이션 되는 것은 토픽이 아니라 토픽을 구성하는 각각의 파티션들을 의미하고, 위의 예제에서는 파티션이 1개 인 토픽 입니다.
이제 콘솔에서 프로듀서를 이용해서 테스트 메세지를 jade-test01 토픽으로 전송을 해보도록 하겠으며, kafka-console-producer.sh 명령어를 사용해서 진행 할 수 있습니다.
$ kafka-console-producer.sh --bootstrap-server \ kafka1:9092 --topic jade-test01 >test message1
메세지를 보내고 난 후 세그먼트 파일에 저장되어 있는지 확인 해봅니다.
참고로 프로듀서에 의해 브로커로 전송된 메세지는 토픽의 파티션에 저장 되며, 각 메세지들은 세그먼트(segment) 라는 로그 파일의 형태로 브로커의 로컬 디스크에 저장되게 됩니다.
kafka-dump-log.sh 를 통해서 세그먼트를 확인 합니다.
$ kafka-dump-log.sh --print-data-log \ --files /usr/local/kafka/logs/jade-test01-0/00000000000000000000.log 결과) Dumping /usr/local/kafka/logs/jade-test01-0/00000000000000000000.log Starting offset: 0 baseOffset: 0 lastOffset: 0 count: 1 baseSequence: -1 lastSequence: -1 producerId: -1 producerEpoch: -1 partitionLeaderEpoch: 0 isTransactional: false isControl: false position: 0 CreateTime: 1652886146674 size: 81 magic: 2 compresscodec: NONE crc: 3267944857 isvalid: true | offset: 0 CreateTime: 1652886146674 keysize: -1 valuesize: 13 sequence: -1 headerKeys: [] payload: test message1
* 가로길이에 따른 개행이 되어 있습니다.
위의 Dumping 결과에서 아래와 같은 정보를 확인 할 수 있습니다.
• Starting offset : 시작 오프셋 위치는 0 임을 알 수 있습니다.
• count : 1 - 메세지 카운트가 1 인 것을 알 수 있습니다.
• payload: test message 1 - 프로듀서를 통해 보낸 메세지는 test message1 라는 것을 알 수 있습니다.
프로듀서를 실행한 서버는 첫번째 브로커인 kafka1 에서 실행하였으나, 그외 브로커인 kafka2 나 kafka3 에서 위의 명령어를 실행하면 동일한 결과(즉 동일한 메세지가 있는)가 확인되는 것을 알 수 있습니다.
직접 디렉토리에서도 봤을때 토픽에 해당하는 관련 디렉토리도 있으며 파일이 있으며 strings 로 봐도 동일한 메세지가 존재함을 알 수 있습니다.
[root]# hostname kafka3 [root]# ls -arlt total 24 -rw-r--r--. 1 root root 10485756 May 18 23:09 00000000000000000000.timeindex -rw-r--r--. 1 root root 10485760 May 18 23:09 00000000000000000000.index -rw-r--r--. 1 root root 8 May 19 00:02 leader-epoch-checkpoint drwxr-xr-x. 2 root root 4096 May 19 00:02 . -rw-r--r--. 1 root root 81 May 19 00:02 00000000000000000000.log drwxr-xr-x. 20 root root 12288 May 19 02:35 .. [root]# strings 00000000000000000000.log test message1
위와 같이 확인한 것 처럼 모든 브로커가 동일한 메세지를 가지고 있음을 확인 할 수 있습니다.
토픽 생성시 지정한 리플리케이션 팩터 수에 따라서 복제를 유지하기 때문에 브로커 장애가 발생하더라도 메세지 손실 없이 안정적으로 메세지를 주고 받을 수 있습니다.
위의 예제에서는 3개의 리플리케이션이 있으므로 이중에서 2대 브로커의 장애까지는 문제가 없이 메세지를 처리할 수 있습니다.
리더 와 팔로워
토픽 상세보기 명령어를 실행해보면 출력 내용 중 파티션의 리더(leader) 라는 부분이 있습니다.
모두 동일한 리플리케이션이라고 하더라도 리더만의 역할이 따로 있으며, 카프카에서 리더를 특별히 강조해서 표시가 됩니다.
카프카는 내부적으로 모두 동일한 리플리케이션들을 리더와 팔로워로 구분을 하며, 각자의 역할이 분리가 되어있습니다.
카프카의 모든 읽기와 쓰기는 리더를 통해서만 가능 합니다. 프로듀서는 모든 리플리케이션에 메세지를 보내는 것이 아닌 리더에게만 메세지를 전송하게 됩니다.
또한 메세지를 가져오는 컨슈머도 리더로부터 메세지를 가져오게 됩니다.
위의 그림은 프로듀서와 컨슈머 그리고 리더 와 팔로워의 관계를 그림으로 표현 한 것 입니다.
토픽의 파티션은 1개 이며, 리플리케이션 팩터 수는 3 입니다. 리플리케이션은 토픽 단위가 아니라 파티션 단위로 수행 됩니다.
프로듀서는 토픽으로 메세지를 전송하면 파티션의 리더에 쓰기가 이루어지게 되고 컨슈머도 동일하게 리더를 통해서 메세지를 가져오게 됩니다.
팔로워는 리더에 문제가 발생할 경우를 대비해서 언제든지 새로운 리더가 될 준비를 해야 하고 그에 따라서 컨슈머가 토픽의 메세지의 읽어 가는 것과 비슷한 동작으로 지속적으로 리더가 새로운 메세지를 받았는지를 받았는지 확인하고, 새로운 메세지가 있다면 리더로 부터 복제를 하게 됩니다.
복제 유지 와 커밋
리더와 팔로워는 ISR(In Sync Replica) 이라는 논리적으로 그룹으로 묶여 있습니다.
이렇게 리더와 팔로워를 별도의 그룹으로 나누는 이유는 기본적으로 해당 그룹안에 속한 팔로워들만이 새로운 리더의 자격 대상이 될수 있기 때문 입니다.
그래서 다른 부분으로 설명하면 ISR 그룹에 속하지 못한 팔로워는 새로운 리더의 자격을 가질수 없습니다.
ISR 내의 팔로워들은 리더와 데이터 일치를 유지하기 위해서 지속적으로 리더의 데이터를 가져와서 복제를 하게 되고, 리더는 ISR 내 모든 팔로워가 메세지를 받을 때까지 기다립니다.
하지만 팔로워가 네트워크 오류, 브로커 장애 등으로 리더로 부터 복제를 할 수 없을 경우가 있을 것 입니다.
이렇게 뒤쳐진 팔로워는 이미 리더와의 데이터가 불일치한 상태가 될 것 입니다.
만약 이런 상황에서 이 팔로워에게 새로운 리더를 넘겨준다면 데이터의 정합성 문제가 발생하거나 메세지 손실 등의 문제가 발생할 수 있습니다.
따라서 파티션의 리더는 팔로워들이 리플리케이션 동작을 잘하고 있는지 체크(감시)하게 됩니다.
리더의 메세지가 상태를 뒤쳐지지 않고 복제를 잘 따라오고 있는 팔로워들만이 ISR 그룹에 속하게 됩니다.
그림으로 보면 아래와 같이 표현할 수 있을 것 같습니다.
쉽게 이해하는 방법으로 Replication Group 개념으로 볼 수도 있습니다.
위와 같은 ISR 내에서 리더가 장애가 발생할 경우 새로운 리더가 될 수 있는 자격을 얻을 수 있게 되는 것 입니다.
리더는 읽고 쓰고의 기본 동작이외에 팔로워가 리플리케이션 동작을 잘하고 있는지도 체크 하고 판단합니다.
만약 팔로워가 특정 주기의 시간만큼 복제 요청을 하지 않는다면 리더는 해당 팔로워의 복제 동작에 문제가 있다고 판단하여 ISR 그룹에서 추방하게 됩니다. 그리고 추방되게 되면 리더가 될 자격이 없게 되는 것 입니다.
토픽 상세보기(describe) 명령어를 통해서 ISR 목록이 표시되는 것을 확인할 수 있으며 현재 ISR 상태를 확인 함에 따라서 토픽의 상태가 양호한지 문제가 있는지를 확인 할 수 있습니다.
$ kafka-topics.sh --bootstrap-server kafka1:9092 \ --topic jade-test01 --describe 결과) Topic: jade-test01 PartitionCount: 1 ReplicationFactor: 3 Configs: segment.bytes=1073741824 Topic: jade-test01 Partition: 0 Leader: 1 Replicas: 1,2,3 Isr: 1,2,3 <<<----ISR
* 가로 길이에 따라서 개행 되어 있습니다
위의 describe 조회 결과처럼 ISR 의 정보를 확인 할 수 있습니다.
ISR 내 모든 팔로워가 복제가 완료 되었다면, 리더는 내부적으로 커밋 되었다는 표시를 하게 됩니다.
마지막 커밋 오프셋 위치를 하이워터마크(high-water mark) 라고 부릅니다.
커밋이 되었다는 것은 리플리케이션 팩터수의 팔로워가 메세지를 전부 저장하였음을 의미하며 이렇게 커밋된 메세지만이 컨슈머가 읽어갈수 있습니다.
카프카에서는 커밋되지 않은 메세지는 컨슈머가 읽을 수 없게 한 이유는 메세지의 일관성 때문입니다.
jade-test01 이라는 토픽을 그림으로 표현한 그림으로 토픽내 파티션 1개, 3개의 리플리케이션 팩터로 생성(설정) 되었습니다.
프로듀서가 첫 번째 test message1 이라는 메세지를 jade-test01 토픽으로 보내게 되고, 모든 팔로워가 리플리케이션 동작을 모두 완료 하여 모두 저장하고 있으며, 커밋이 완료된 상태 입니다.
그 다음으로 message2 라는 메세지를 전송 하게 됩니다. 이 메세지는 리더에만 저장되어 있는 상태이고 팔로워들은 아직 해당 메세지에 대한 리플리케이션 동작을 하기 전 상태 입니다.
만약 이러한 상태에서 여기서 커밋되기 전 메세지를 컨슈머가 읽을 수 있다고 한다면 아래와 같은 문제가 발생될 수 있게 됩니다.
1) 컨슈머 A 는 jade-test01 토픽을 컨슘 합니다.
2) 컨슈머 A는 jade-test01 이 토픽의 파티션 리더로부터 메세지를 읽어갑니다. 읽어간 메세지는 test message1,2 입니다.
3) jade-test01 토픽의 파티션 리더가 있는 브로커에 문제가 발생할 팔로워 중 하나가 새로운 리더가 됩니다.
4) 프로듀서가 보낸 test message2 메세지는 아직 팔로워들에게 리플리케이션이 되지 않은 상태에서 새로운 리더로 변경 됐으므로, 새로운 리더는 test message1 메세지만 갖고 있습니다.
5) 새로운 컨슈머 B가 jade-test01 토픽 컨슘합니다.
6) 새로운 리더로 부터 메세지를 읽어가고, 읽어간 메세지는 오직 test message1 입니다.
이런식의 시나리오 일 경우 컨슈머 A 와 B는 jade-test01 이라는 동일한 토픽의 파티션을 읽었지만, 컨슈머 A는 test message1,2 를 가져왔고, 컨슈머 B는 test message1 만 가져오게 됩니다.
이 처럼 커밋되지 않는 메세지를 읽게 된다면 같은 토픽을 읽었음에도 메세지가 일치하지 현상이 발생할 수 있게 됩니다.
따라서 카프카에서는 이러한 메세지 불일치 현상을 방지하고자 커밋된 메세지만 컨슈머가 읽어 갈수 있도록 구현되어 있습니다.
이러한 설계에 따라서 카프카에서는 커밋된 위치도 매우 중요한 요소가 되게 됩니다.
카프카의 브로커는 재시작 될 때 커밋된 메세지를 유지하기 위해서 로컬 디스크의 replication-offset-checkpoint 라는 파일에 마지막 커밋 오프셋 위치를 기록/저장하게 됩니다.
replication-offset-checkpoint 파일은 서버의 카프카에서 설정한 로그 디렉토리내에 위치해 있습니다.
[root]# cd /usr/local/kafka/logs/ [root]# ls -alrt | grep replication-offset-checkpoint -rw-r--r--. 1 root root 446 May 19 17:52 replication-offset-checkpoint [root]# cat replication-offset-checkpoint <중략> jade-test01 0 1 <중략>
체크포인트 파일에 기록 된 내용)
jade-test01 은 토픽명을 의미하고, 0은 파티션 번호, 1은 커밋된 오프셋 번호를 의미하게 됩니다.
프로듀서에서 다시 test message2 라는 메세지를 하나더 입력 후 다시 체크포인트 파일을 살펴보면 오프셋 번호가 증가 한 것을 확인할 수 있습니다.
[root@acs2 logs]# cat replication-offset-checkpoint jade-test01 0 2
만약 특정 토픽 또는 파티션에 복제가 되지 않거나 문제가 있다고 판단되는 경우, replication-offset-checkpoint 라는 파일의 내용을 확인하고 리플리케이션 되고 있는 다른 브로커들과 비교해 살펴보면, 어떤 브로커, 토픽, 파티션에 문제가 있는지를 파악할 수 있습니다.
리더와 팔로워의 단계별 리플리케이션 동작
메세지의 읽고 쓰기를 처리하는 리더는 매우 바쁘게 동작을 합니다.
이렇게 바쁜 리더가 리플리케이션 동작을 위해 팔로워들과 많은 통신을 주고 받거나 리플리케이션 동작에 많은 관여를 한다면 그 결과는 리더의 성능은 떨어지고 카프카의 장점의 빠른 성능을 내기도 어려울 것 입니다.
그래서 카프카는 리더와 팔로워간의 리플리케이션 동작을 처리할때 서로 통신을 최소화 할 수 있도록 설계함으로 리더의 불하를 줄였습니다.
리더와 팔로워 간의 리플리케이션 동작
1) 프로듀서가 토픽으로 메세지를 전송
2) 리더만 메세지를 수신 받아 저장(시나리오에서는 지금 메세지는 0번 오프셋에 위치함을 가정)
3) 팔로워들은 리더에게 메세지 가져오기(fetch) 요청을 보낸 후 새로운 메세지 가 있다는 사실을 인지하고 메세지를 리플리케이션 함
4) 리더는 모든 팔로워가 리더에게 메세지를 리플리케이션하기 위한 요청을 보냈다는 사실을 알고 있음
5) 하지만 리더는 팔로워들이 리플리케이션 동작ㅇ르 성공했는지 실패했는지 여부는 알지 못함
(카프카에서 리더와 팔로워간의 ACK 를 주고 받는 통신이 없으며, ACK 를 제거함으로 성능이 더욱 좋아짐)
(아래 부터 ACK 통신없이 안정적인 리플리케이션 처리에 대한 내용이 있음)
6) 리더는 프로듀서로 부터 다음 메세지를 수신 하게 되면 저장하게 됩니다(여기서 수신받은 메세지는 1번 오프셋이라고 가정)
7) 팔로워들은 리더에게 새로운 메세지인 1번 오프셋에 대한 리플리케이션을 요청 합니다.
8) 팔로워들로 부터 1번 오프셋에 대한 리플리케이션 요청을 받은 리더는 팔로워들의 0번 오프셋에 대한 리플리케이션 동작이 성공했음을 인지하고 오프셋 0번 대해서 커밋 표시를 한 후에 하이워터마크를 증가 시키게 됩니다.
9) 팔로워들로 부터 1번 오프셋 메세지에 대한 리플리케이션 요청을 받은 리더는 응답시 0번 오프셋의 메세지가 커밋되었다는 내용도 함께 전달하게 됩니다.
10) 리더의 응답을 받은 모든 팔로워는 0번 오프셋 메세지가 커밋이 된 내용을 인지를 하게 되고, 리더와 동일하게 커밋이 된것을 표시 하게 되며, 그 다음 1번 오프셋 메세지를 리플리케이션 하게 합니다.
여기까지가 리더와 팔로워간의 리플리케이션 동작에 대한 내용입니다.
위의 시나리오상에서 팔로워가 0번 오프셋에 대한 복제가 성공하지 못했다면 팔로워는 1번 오프셋에 대한 리플리케이션 요청이 아닌 0번 오프셋에 대한 리플리케이션 요청을 보내게 됩니다.
따라서 리더는 팔로워들이 보내는 리플리케이션 요청의 오프셋을 보고 팔로워들이 어느 위치의 오프셋까지 리플리케이션을 성공했는지를 인지 할 수 있게 됩니다.
다른 유사한 메세징 시스템에서는 ACK 통신을 통해서 메세지를 잘 받고 하였는지를 체크하지만, 카프카에서는 ACK 통신을 제거하였다는 부분이 중요한 차이점 입니다.
(대량의 메세지를 처리할 경우 ACK 통신을 주고 받는 것도 부하 이면서 성능저하의 요소가 됨)
그래서 카프카의 특징이 ACK 통신을 제외하였음에도 불구하고 팔로워와 리더간의 리플리케이션 동작이 매우 빠르면서도 신뢰할 수 있다는 점 입니다.
카프카에서 리더와 팔로워들의 리플리케이션 동작 방식은 리더가 푸시(push) 하는 방식이 아니라, 팔로워가 풀(pull) 하는 방식으로 동작하며, 리플리케이션에서 리더의부하를 줄여주기 위해서 팔로워 풀(pull) 형태로 구현되었습니다.
리더에포크와 복구
리더에포크(leader epoch) 는 카프카의 파티션들이 복구 동작을 할 때 메세지의 일관성을 유지하기 위한 용도로 이용되며, 컨트롤러에 의해 관리되는 32비트의 숫자로 표현됩니다.
리더에포크(leader epoch) 정보는 리플리케이션 프로토콜에 의해 전파 되며 리더가 변경 되면 변경 된 새로운 리더에 대한 정보를 팔로워에게 전달 합니다.
리더에포크는 복구 동작 시 하이워터마크를 대체하는 수단으로 활용 됩니다.
리더에포크를 사용하지 않을 경우
먼저 리더에포크를 사용하지 않는 상황에서는 장애와 복구 과정을 확인해보도록 하겠습니다.
[복구관련 이미지 1]
위의 이미지는 리더에포크를 사용하지 않는 장애 복구 과정을 의미 합니다.
위의 jade-test01 토픽은 파티션수 1 , 리플리케이션 팩터 2 , min.insync.replicas 1 로 설정 이며 예제에서는 리더에포크가 없다는 가정으로 장애 복구 과정은 아래와 같이 진행됩니다.
1) 리더는 프로듀서로 부터 message1 메세지를 받고 0번 오프셋에 저장, 팔로워는 리더에게 0 번 오프셋에 대한 가져오기 요청
2) 가져오기 요청을 통해 팔로워는 message1 메세지를 리더로부터 리플리케이션함
3) 리더는 하이워터마크를 1로 올림
4) 리더는 프로듀서로 부터 다음 메세지인 message2를 받은 뒤 1번 오프셋에 저장
5) 팔로워는 다음 메세지인 message2에 대해 리더에게 가져오기 요청을 보내고 응답으로 리더의 하이워터마크 변화를 감지하고 자신의 하이워터마크도 1로 올림
6) 팔로워는 1번 오프셋의 message2 메세지를 리더로 부터 리플리케이션함
7) 팔로워는 2번 오프셋에 대한 요청을 리더에게 보내고, 요청을 받은 리더는 하이워터마크를 2로 올림
8) 팔로워는 2번 오프셋인 message2 메세지까지 리플리케이션을 완료하였지만 아직 리더로부터 하이워터마크를 2로 올리는 내용을 전달받지 못한 상태
9) 이 상태에서 예상치 못한 장래로 팔로워가 다운됨
(여기까지가 위의 "복구관련 이미지 1" 에 해당되는 내용)
[복구관련 이미지 2]
위의 "복구관련 이미지2" 는 장애가 발생한 팔로워가 종료 된 후 장애 처리가 완료된 상태를 나타냅니다.
장애에서 복구된 팔로워는 카프카 프로세스가 시작되면서 내부적으로 메세지 복구 동작을 시작하게 됩니다.
1) 팔로워는 자신이 갖고 있는 메세지들 중에서 자신의 워터마크보다 높은 메세지들은 신뢰할 수 없는 메세지로 판단하고 삭제
(따라서 1번 오프셋의 message2는 삭제됨)
2) 팔로워는 리더에게 1번 오프셋의 새로운 메세지에 대한 가져오기 요청을 보냅니다.
3) 이 순간 리더였던 브로커가 예상치 못한 장애로 다운되면서, 해당 파티션에 유일하게 남아 있던 팔로워가 새로운 리더로 승격 합니다.
[복구관련 이미지 3]
위의 그림 "복구관련 이미지 3" 는 팔로워가 새로운 리더로 승격된 후의 상태를 나타내게 됩니다.
그림에서 알 수 있듯이 기존의 리더는 1번 오프셋의 message2 라는 메세지를 가지고 있었지만, 팔로워는 message2 라는 메세지가 없이 새로운 리더로 승격이 되었습니다.
결국은 새로운 리더는 message2 라는 메세지를 갖고 있지 않으며, 리더와 팔로워간의 리플리케이션이 작동하고 있었지만 리더가 변경되는 과정에서 message2 라는 메세지가 손실된 것 입니다.
리더에포크를 사용할 경우
이번에는 리더에포크를 사용 된다는 상황으로 장애 와 복구에 대한 내용을 확인 해보도록 하겠습니다.
[복구관련 이미지 4]
위의 그림 "복구관련 이미지4" 는 리더와 팔로워의 리플리케이션 동작 이후 그리고 팔로워가 장애로 종료 된 후 막 복구된 상태 이후의 과정을 나타내고 있습니다.
앞에서의 동작은 카프카 프로세스가 시작되면서 복구 시 자신의 하이워터마크 보다 높은 메세지를 즉시 삭제 하였습니다.
하지만 리더에포크를 사용하는 경우에는 하이워터마크 보다 앞에 있는 메세지를 무조건 삭제 하는 것 아닌 리더에게 리더에포크 요청을 보냅니다.
1) 팔로워는 복구 동작을 하면서 리더에게 리더에포크 요청를 보냄
2) 요청받은 리더는 리더에포크의 응답으로 "1번 오프셋의 message2까지" 라고 팔로워에게 보냄
3) 팔로워는 자신의 하이워터마크보다 높은 1번 오프셋의 message2를 삭제하지 않고 리더의 응답을 화인 후에 오프셋2 인 message2 까지 자신의 하이워터마크를 상향 조정 합니다.
[복구관련 이미지 5]
위의 그림 "복구관련 이미지 5" 는 리더가 예상치 못한 장애로 다운 되면서 팔로워가 새로운 리더로 승격된 후의 상태를 나타냅니다.
리더에포크를 적용하지 않는 경우에는 팔로워가 복구 과정에서 자신의 하이워터마크보다 높은 message2 를 삭제하였지만 리더에포크를 활용하는 경우에는 삭제 동작을 하기 전에 리더에포크 요청과 응답 과정을 통해서 팔로워의 하이워터마크를 올릴 수 있게 되며, 메세지 손실이 발생하지 않게 됩니다.
리더에포크 변화 과정 확인
위에서 리더에포크를 사용하지 않았을 경우 와 사용할 경우에 대해서 각각의 차이점 등을 확인 해보았습니다.
이번에는 실습을 통해서 리더에포크의 변화 과정을 살펴보도록 하겠습니다.
테스트를 위한 토픽은 포스팅에서 생성하여 사용중인 jade-test01 을 이용하도록 하겠습니다.
먼저 다시 describe 를 통해서 토픽의 현재 상태를 확인 해보도록 하겠습니다.
$ kafka-topics.sh --bootstrap-server kafka2:9092 --topic jade-test01 --describe Topic: jade-test01 PartitionCount: 1 ReplicationFactor: 3 Configs: segment.bytes=1073741824 Topic: jade-test01 Partition: 0 Leader: 1 Replicas: 1,2,3 Isr: 1,2,3
* 가로 길이에 따라서 개행 되어 있습니다.
현재 파티션의 리더의 1 번 브로커임을 알 수 있습니다.
그 다음에는 cat 명령을 이용해서 리더에포크 상태를 확인 하도록 하겠습니다. 먼저 1번 브로커 서버로 새로 접속 후에 log 디렉토리로 이동합니다.
그리고 리더에포크 파일을 cat 으로 조회합니다.
$ cd /usr/local/kafka/logs/jade-test01-0 $ cat leader-epoch-checkpoint 0 1 0 0
위의 조회 결과의 의미는 다음과 같습니다.
두번째 라인 1 : 현재의 리더에포크 번호
세번째 라인 0 0 : 첫 번재 0은 리더에포크 번호, 두 번째 0은 최종 커밋 후 새로운 메세지를 전송받게 될 오프셋 번호
리더에포크는 새로운 리더 선출이 발생하면 변경된 정보가 업데이트 됩니다.
강제로 새로운 리더가 선출되도록 리더가 위치한 브로커 1을 종료 합니다.
[root]# systemctl stop kafka
* 카프카 서비스명은 등록한 이름에 따라 다를 수 있습니다.
다시 describe 로 토픽을 조회해보도록 하겠습니다.
$ kafka-topics.sh --bootstrap-server kafka2:9092 --topic jade-test01 --describe Topic: jade-test01 PartitionCount: 1 ReplicationFactor: 3 Configs: segment.bytes=1073741824 Topic: jade-test01 Partition: 0 Leader: 2 Replicas: 1,2,3 Isr: 2,3
* 가로 길이에 따라서 개행 되어 있습니다.
리더가 변경 된 것을 확인할 수 있으며, 지금 리더는 2번 브로커 입니다.
2번 브로커에 접속하여 다시 리더에포크 정보를 조회해봅니다.
$ cat leader-epoch-checkpoint 0 2 0 0 1 2
처음 확인했던 리더에포크에서 변화가 되었습니다.
출력 결과의 의미는 다음과 같습니다.
두번째 라인 2 : 현재 리더에포크 번호. 새로운 리더가 선출이 있었고 리더에포크는 1에서 2로 변경 되었습니다. 리더에포크 번호는 리더가 변경될 때마다 하나씩 숫자가 증가 합니다.
세번째 라인 0 0 : 첫 번째 0은 리더에포크 번호, 두번째 0은 최종 커밋 후 새로운 메세지를 전송 받게 될 오프셋 번호
세번째 라인 1 2 : 첫 번째 1은 리더에포크 번호, 두번째 2는 최종 커밋 후 새로운 메세지를 전송 받게 될 오프셋 번호
위에서 jade-test01 토픽에 2개의 message 를 입력 하였습니다. 그래서 0,1 오프셋이었고 최종 커밋 후 새로운 메세지를 전송 받게 될 오프셋 번호인 2가 확인 되고 있습니다.
팔로워는 자신의 하이워터마크보다 높은 오프셋의 메세지를 무조건 삭제 하지 않고 먼저 리더에게 리더에포크 요청을 보내 응답을 받아서 최종 커밋된 오프셋 위치를 확인 합니다.
이와 같이 리더에포크 정보를 이용하여 장애 후 복구시 데이터의 손실 또는 데이터 불일치를 막게 됩니다.
이어지는 다음 글
해당 포스팅은 실전 카프카 개발부터 운영까지 책의 많은 내용 중에서 일부분의 내용만 함축적으로 정리한 것으로 모든 내용 확인 및 이해를 위해서 직접 책을 통해 모든 내용을 확인하시는 것을 권해 드립니다.
Reference
Reference Book
• 실전 카프카 개발부터 운영까지
연관된 다른 글
Principal DBA(MySQL, AWS Aurora, Oracle)
핀테크 서비스인 핀다에서 데이터베이스를 운영하고 있어요(at finda.co.kr)
Previous - 당근마켓, 위메프, Oracle Korea ACS / Fedora Kor UserGroup 운영중
Database 외에도 NoSQL , Linux , Python, Cloud, Http/PHP CGI 등에도 관심이 있습니다
purityboy83@gmail.com / admin@hoing.io