Apache Kafka 초기 설정 주의점 정리

taehee kim·2023년 4월 7일
3

1. 개요

  • 이전 시리즈 포스팅에서 kafka의 핵심적인 개념과 spring boot연동및 설정방법에 대해서 실습을 해보았습니다.
  • 개인 프로젝트에서 이를 적용해서 사용하던 중 몇가지 문제가 발생하여 검색을 해서 해결책들을 찾아보다 보니 몰랐던 내용들을 알게 되어서 정리 하려고 합니다.

2. kafka사용 시 유의 사항

2-0. kafka를 통해 원하는 로직을 단 한번만 실행시키는 것이 보장 되지 않을 수 있음.

2-0-1.Consume 방식이 잘못된 경우 한번 실행이 보장되지 않을 수 있음

실제 프로젝트에서 이벤트를 consume하게 되면 RDB에 데이터를 저장하는 로직과 Redis에 채널에 pub을 하는 기능이 있었습니다. Redis pub로직을 먼저 실행하고 RDB에 데이터를 저장하는데 만약 트랜잭션에서 문제가 발생하면 롤백되므로 Kafka commit이 실패합니다. RCB는 롤백되지만 Redis 채널에 이미 pub이 보내지게 되고 Kafka event를 다시 consume하기 때문에 Redis pub은 여러번 발생하게 됩니다.

  • 이 경우 Redis, RDB라는 두 개의 Datasource에 동시에 커밋이 보장되지 않기 때문에 생기는 문제이고 Consumer Group을 분리하여 DB에 데이터를 쓰는 Consumer Group과 Redis에 pub하는 Consumer Group을 분리해야합니다.

2-0-2.Producer idempotent

  • producer retry시 이벤트가 중복 생성될 수 있습니다. kafka는 단 하나의 이벤트 생성을 보장하는 것이 아니라 최소한 하나의 이벤트를 보장할 수 있습니다.
  • 단 하나의 이벤트 produce를 보장하고 싶다면 enable.idempotent옵션을 true로 변경해야합니다.

    이를 위해서는 enable.idempotence true를 위해서는 retry가 0이상,
    max.in.flight.requests.per.connection 은 5이하여야합니다.

2-1. 메시지 produce시에 acks:all 옵션과 함께 min.insync.replicas를 적용하지 않아 생기는 문제

문제 상황

  • acks:all 옵션은 kafka에 메시지를 produce할 때 leader와 follower에 produce가 모두 되었을 때 ack 신호를 보내는 옵션입니다.

    하지만 이 옵션을 설정했음 에도 follower에 produce가 되지 않을 수 있음을 확인했습니다.

원인

  • acks:all 옵션은 제대로 적용되는 것이 맞습니다. 문제는 이때 ack 신호를 받아야하는 최소의 in-sync replicas의 수를 명시해주지 않은 것이 문제입니다.
  • 기본적으로 min.insync.replicas 은 1로 되어있기 때문에 leader에만 ack를 받으면 produce가 됩니다. 따라서 모든 replica에 produce가 잘 되는 것을 보장 받기 위해서는 이 옵션을 replication factor와 맞추어 주는 설정을 해주어야 합니다.

해결법

  • kafka Admin설정 시 min.insync.replicas를 최소 2 이상으로 설정해줍니다.
@Bean
    public KafkaAdmin kafkaAdmin() {
        Map<String, Object> configs = new HashMap<>();
        configs.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapAddress);
        configs.put("spring.kafka.admin.properties.min.insync.replicas", minInSyncReplicas);
        return new KafkaAdmin(configs);
    }

2-2 Round-Robin 방식의 성능 문제

Kafka에서 Event를 생성하는 3가지 방법

  1. 특정 파티션 지정: 특정 파티션을 지정하여 이벤트를 생성하게 할 수 있다.
  2. key값 지정: key값을 지정하게 되면 hash를 통해 생성된 값으로 파티션을 지정하여 해당 파티션에 produce된다.
  3. Round-Robin: 1, 2를 하지 않을 경우 기본적으로 최대한 고르게 partition에 이벤트가 프로듀스 된다.

Round-Robin 방식이 배치성능을 저하시키는 이유

Round-Robin방식은 기본적으로 최대한 고르게 Paritiion에 이벤트를 프로듀스하려는 개념이다. produce, consume시에 모두 성능 저하의 문제가 발생할 수 있다.

  • produce: 이벤트는 produce, comsume시 모두 batch를 통해 이벤트를 여러개 모아서 처리하게 된다. RR에서는 모든 파티션에 이벤트를 고르게 생성해야하기 때문에 produce 배치 작업에서 특정 파티션에 produce가 어렵다면 대기하는 시간이 필요하게 된다.
  • consume: 이벤트는 consume시에 하나의 partition에 대해서 하나의 consumer가 특정 batch size를 가지고 이벤트들을 consume한다. RR방식에서는 partition마다 이벤트가 분산되어 있기 때문에 한번의 배치작업에서 가져올 수 있는 이벤트 숫자가 적고 더 여러번 배치 작업을 수행할 수 밖에 없다.
  • 위의 그림에서 생성된 9개의 이벤트를 소비하기 위해서는 적어도 3번의 consume배치 작업이 수행되어야하지만 만약 이를 하나의 partition에 event가 몰려있었다면 1번의 batch로도 수행할 수 있다.

해결책: Stciky Partitioner

  • Stciky Partitioner한번의 이벤트 produce 배치 작업 시 하나의 partition에 모든 이벤트를 생성한다. 그리고 다음 배치 작업 시 다른 parition에 이벤트를 생성한다.
  • 하나의 partition에 이벤트가 여러개 생성되기 때문에 consume배치 작업이 1번으로 줄어드는것을 그림에서 볼 수 있다.
    참고 링크

2-3. 초기에 적절한 parition 개수를 설정 하는 것이 중요하다.

너무 적은 partition개수를 생성하는 경우

  • consumer 가 많아도 parition개수가 적으면 병렬성이 떨어지기 때문에 성능이 저하됨.
  • 파티션을 늘리면 key 값으로 항상 하나의 파티션에 이벤트가 생성되어 순서가 보장되던 것이 다른 파티션에 저장되면서 순서가 달라질 수 있다.

너무 많은 parition개수를 생성하는 경우

  • parition 개수가 많으면 하나의 consumer가 여러 parition을 담당할 수 있는데 이것 자체는 문제가 되지 않는다.
  1. Round-Robin 문제와 마찬가지로 하나의 parition에 여러 이벤트가 생성되지 못하기 때문에 상대적으로 consume배치를 여러번 수행해야한다.
  2. Broker가 fail하면 해당 parition에서 leader였던 경우 다른 브로커에서 leader를 찾아주어야 한다. 이 시간이 매우 오래걸린다. 1000개의 파티션에서 이를 수행할 경우 대략 5초정도가 걸린다고 한다.
  3. 파티션은 늘릴 수는 있지만 줄일 수는 없다.

2-4. 존재하지 않는 topic에 이벤트 발행 시 topic 생성되는 문제

  • kafka는 default로존재하지 않는 topic에 이벤트를 발행했을 때 이벤트가 생성된다.
  • auto.create.topics.enable를 false로 하여 이를 방지해야한다.
    broker 설정

2-5. Publisher 설정 시 충분히 설정하지 않는 문제

필수적으로 같이 설정해주어야 하는 옵션

  • acks: 0, 1, all 의 옵션으로 설정
    • min.insync.replicas: acks all 일 때 앞에서 언급했듯 몇개의 in-sync-replica에서 ack신호를 받아야 ack로 판단할지 설정해주어야 한다.
  • retries: acks 신호를 받지 못할 경우 몇번까지 retry할지 이다.
    • max.in.flight.requests.per.connection: 한번의 pulish에서 몇개까지의 이벤트를 생성할지를 결정한다.

      2 이상인 경우 앞에서 먼저 있었던 이벤트가 실패하게 되면 retry에 의해서 순서가 바뀔 수 있습니다. 하지만 enable.idempotence를 true로 설정하면 순서가 바뀌지 않습니다.

    • enable.idempotence: true로 설정하면 retry로 인해서 broker에 여러개의 이벤트가 생기는것을 방지할 수 있다.
    • true가 되기 위해서는 retry가 0보다 커야하고 max.in.flight.requests.per.connection은 5(MAX_IN_FLIGHT_REQUESTS_PER_CONNECTION_FOR_IDEMPOTENCE)보다 작거나 같아야한다.

2-6. Consumer heartbeat와 설정값

https://d2.naver.com/helloworld/0974525
https://docs.confluent.io/platform/current/installation/configuration/consumer-configs.html

구성요소역할
ConsumerNetworkClientKafka Consumer의 모든 Network 통신을 담당
SubscriptionStateTopic / Partition / Offset 정보를 저장하고 관리하는 담당
ConsumerCoordinatorConsumer Reblance / Offset 초기화 및 커밋을 담당
HeartBeatThread백그라운드에서 동작하며, Consumer가 살아있다고 Coordinator 에게 알려주는 담당
Fetcher브로커로부터 데이터를 가져오는 담당

2-6-1. Fetcher

데이터를 토픽에서 가져오는 부분.

  • fetch.min.bytes는 의 기본설정은 1byte 이며
  • max.partition.fetch.bytes 의 기본 설정은 1mebibytes 이며 약 1.048MB 입니다.
  • max.poll.records는 가져오는 Record의 개수입니다. ( default는 500개 )

신뢰성 있고 주기적인 polling을 위해서는 max.poll.records 를 1로 하는 것이 좋음. 이유는 max.poll.interval 시간이 넘어 Consumer가 장애 상황으로 받아들여질 수 있기 때문입니다.

2-6-2. HeartBeatThread

  • coordinator가 살아있음을 알림.
옵션정책
max.poll.interval.ms ( default = 300000ms = 5분 )해당 시간동안 poll 메서드가 호출되어야 한다. 만일, 해당 시간 내에 poll이 호출되지 않으면 Group에서 제외된다. HeartBeat 쓰레드가 poll 호출 간격을 측정하게 된다.
heartbeat.interval.ms ( default = 3000ms = 3초 )해당 주기동안 HeartBeat를 Group Coordinator 로 전송한다. 일반적으로 session.timeout.ms 의 1/3 로 활용한다 ( session.timeout 보다 consumer는 Group에 포함될 수 없다 )
session.timeout.ms ( default = 10000ms = 10초)해당 시간동안 HeartBeat이 도착하지 않으면 Group Coordinator 는 해당 Consumer를 Group 에서 제외한다

2-6-3. Consumer Group

  • event를 Consume하는 그룹을 묶은 것이며 offset이 개별 동작하고 event를 consume하는 방법을 각자 정의.

2-6-4. Cosumer Ack

  • Comsumer Ack는 자동으로 호출 되게 하는 것보다 수동으로 호출되도록 설정하는 것이 좋습니다.

idleBetweenPolls(spring kafka에만 있음)

  • 각각의 poll마다 얼마나 대기할것인지 설정한다.

idleBetweenPolls"
Used to slow down deliveries by sleeping the thread between polls. The time to process a batch of records plus this value must be less than the max.poll.interval.ms consumer property.

spring kafka

profile
Fail Fast

0개의 댓글