pile·
백엔드·channel-talk채널톡·

카프카 파티션 개수 산정식 설계 여정

채널톡이 Kafka 토픽을 만들 때 쓸 파티션 개수 산정식을 정리하고, 그 식에 들어가는 기준값을 Confluent Cloud Enterprise 환경에서 직접 측정한 기록이다. 목표는 모든 상황에 맞는 정답이 아니라 토픽을 만드는 사람이 같은 기준으로 초기 파티션 수를 판단하게 만드는 것이었다. 결론은 파티션 수가 프로듀서 처리량이 아니라 컨슈머의 레코드당 처리 시간과 catch-up 목표에 의해 결정되는 경우가 많다는 것이다.

핵심 포인트
  • 파티션이 많을수록 안전한 것이 아니다. 늘어나면 브로커가 관리할 메타데이터·파일 핸들·리더 선출·복구 비용이 함께 늘고 장애 회복 시간도 길어진다.
  • 순서가 중요한 토픽은 생성 이후 파티션을 늘리기 어렵다. key 기반 라우팅에서 파티션 수가 바뀌면 key가 매핑되는 파티션도 달라지기 때문이다.
  • 컨슈머 요구량은 평소 유입을 따라가는 것뿐 아니라 멈춘 뒤 쌓인 lag까지 일정 시간 안에 따라잡아야 해서, 평균 기반 catch-up 요구량과 피크 처리량 중 큰 값을 쓴다.
  • egress_per_partition은 Kafka가 읽어줄 수 있는 최대 속도가 아니라 컨슈머 애플리케이션의 레코드 처리 시간에 지배된다.
  • 실측에서 파티션당 약 19.4MB/s까지 안정적이었지만 산정식에는 공식 계획값 쪽을 넣었다. 실측이 더 높아도 둘 중 작은 값을 취하는 편이 안전하기 때문이다.
  • 파티션 수는 ingress나 connection과 달리 유연하지 않다. 파티션 때문에 1eCKU를 넘으면 이후 트래픽이 줄어도 더 큰 eCKU를 최소 기준으로 유지해야 한다.
상세 정리
  • 개발자가 준비할 값은 평균 처리량, 피크 처리량, 컨슈머가 lag을 따라잡아야 하는 시간 기준 세 가지다.
  • 산정 구조: 토픽이 처리해야 하는 요구량을 파티션 하나가 안정적으로 처리할 수 있는 양으로 나누는 문제이며, 요구량이 처리 능력을 넘지 않아야 한다는 점에서 리틀의 법칙과 같은 관점으로 본다.
  • 프로듀서 요구량은 단순하다. 피크 시점에도 받아낼 수 있어야 하므로 피크 처리량 기준이다.
  • 컨슈머 요구량 유도: 컨슈머가 멈춘 동안 평균 유입량 × 중단 시간만큼 lag이 쌓이고, 복구 후 catch-up 하는 동안에도 프로듀서는 계속 쓴다. 그래서 catch-up 시간 동안 필요한 평균 처리량은 평균 처리량 × (1 + 중단 시간 / catch-up 시간)이 된다.
  • 여기에 피크도 놓치면 안 되므로 이 값과 피크 처리량 중 큰 값을 컨슈머 요구량으로 삼는다. 평균을 쓰는 이유는 중단 동안 쌓이는 lag이 순간 피크가 아니라 일정 기간 누적된 데이터이기 때문이다.
  • 최종 파티션 수는 프로듀서 요구량을 ingress_per_partition으로 나눈 값과 컨슈머 요구량을 egress_per_partition으로 나눈 값 중 큰 쪽의 올림이다. 6.2가 나오면 최소 7개다.
  • catch-up 배수 예시: 4일 중단을 1일 안에 따라잡으려면 배수가 5가 되어 평균 처리량의 5배가 필요하고, 7일 중단을 1일에 따라잡으려면 8배가 필요하다.
  • 공개 가이드 비교: Confluent Cloud는 Basic/Standard 기준 파티션당 ingress 약 5MB/s, Enterprise 약 6MB/s를 계획용으로 제시하고, Alibaba Cloud는 1~5MB/s를 권장하며 저지연 워크로드는 낮은 쪽을 쓰라고 안내한다.
  • Grafana Mimir의 Kafka backend 문서는 처리량이 아니라 파티션 수가 한 zone의 ingester 수 이상이어야 한다는 조건을 안내해, 일반 토픽의 MB/s 기준으로 바로 가져오기 어렵다고 짚는다.
  • egress 계산 예시: 16개 worker 병렬 처리 모델에서 레코드당 25ms 기준으로 이론상 약 0.64MB/s가 나오지만 운영에서는 여유를 둬 0.5MB/s처럼 낮춰 잡을 수 있다.
  • 처리 시간이 50ms가 되면 약 0.32MB/s로 절반, 100ms면 약 0.16MB/s로 1/4 수준이 된다. 외부 API 호출이나 DB 쓰기가 포함된 컨슈머는 Kafka 자체보다 애플리케이션 처리 시간이 병목이 된다.
  • 그래서 개발자가 이 값을 직접 입력하기보다, 토픽에 적용된 egress 기준을 만족하도록 컨슈머 처리 시간을 일정 수준 이하로 유지하는 방향으로 운영 기준을 세웠다.
  • 측정 방법론: 최대 처리량이 아니라 운영에서 지속 가능한 보수적 기준값을 찾는 것이 목적이었다. 같은 평균이라도 윈도우마다 크게 흔들리면 기준으로 쓸 수 없기 때문이다.
  • 안정성 판단에 CV(표준편차를 평균으로 나눈 값)를 썼다. 1분 윈도우 기준 CV가 임계 이하이고 retry·throttle·rebalance·error가 없는 구간만 안정적인 결과로 봤다.
  • ingress 측정: 파티션 수 후보별로 총 throughput을 단계적으로 올리며 처리량과 produce request latency의 관계를 봤고, latency가 급격히 튀기 직전인 knee point 직전의 총 처리량을 기준으로 삼아 safety factor를 적용했다.
  • 운영 경계는 시스템이 완전히 망가지는 지점이 아니라 낮은 throughput 구간 대비 latency가 2배 이상 증가하는 등의 조건이 처음 나타나는 지점으로 정의했다.
  • 파티션 수를 1, 2, 4, 8, 16으로 늘려도 파티션당 처리량 저하는 거의 없었다.
  • egress 측정: 실제 비즈니스 로직 대신 sleep으로 처리 시간을 시뮬레이션하고, 신규 ingress 없이 충분한 backlog를 미리 쌓아 backlog-only replay 방식으로 측정했다. 측정 중 backlog가 먼저 고갈되면 실패로 봤고 rebalance·poll timeout·commit stall·retry·error도 없어야 했다.
  • 여러 파티션 조건에서 모두 측정한 결과 파티션 수가 결과에 큰 영향을 주지 않았고, 레코드 처리 시간이 조금만 커져도 파티션당 처리량이 빠르게 낮아진다는 것이 핵심 발견이었다.
  • 기본값 결정 과정: Enterprise eCKU 1개의 ingress 한도와 파티션 한도(3,000)를 산정식에 대입해, ingress 한도까지 써도 파티션 한도 안에 들어오는 경계를 계산했다. 조건은 catch-up 배수를 egress_per_partition으로 나눈 값이 50 이하가 되는 것이다.
  • 이 경계를 보는 이유는 처리량이나 connection은 eCKU를 늘려 대응할 수 있지만 이미 만든 토픽의 파티션 수는 늘리기도 어렵고 줄이는 것은 일반 운영 절차로 불가능하기 때문이다.
  • 7일 중단까지 기본값으로 잡으려면 레코드당 처리 시간을 30ms 수준으로 유지해야 하고, 4일 중단·1일 catch-up 기준이면 순차 처리 모델에서 여유가 생긴다.
  • 최종 기본값: Kafka를 처음 도입하는 단계라 per-partition 처리량을 공격적으로 잡지 않고 egress 0.1MB/s와 catch-up 배수 5로 정했다. 이 조합에서 eCKU의 ingress 한도까지 써도 파티션 한도 안에 들어온다.
  • 파티션을 줄이고 싶다면 parallel consumer나 컨슈머 내부 병렬 처리, 비동기 I/O로 per-partition 처리량을 높일 수 있지만 실제 병렬 효율을 다시 측정해야 한다.
  • 마지막 예시에서 프로듀서 쓰기 처리량만 보면 5개면 충분한 토픽이 컨슈머 처리 속도를 고려하면 최소 500개가 필요한 경우를 든다.
  • 다른 플랫폼 주의: 이 기준은 Confluent Cloud Enterprise eCKU의 한도에 맞춘 값이고, 예컨대 AWS MSK Express broker는 브로커 크기마다 권장/최대 파티션 수가 달라 같은 산정식을 쓰더라도 한도를 다시 대입해야 한다.
왜 읽나Kafka 토픽을 새로 만들 때마다 파티션 수를 감으로 정해 온 팀이, 산정식의 유도 과정과 자기 환경에서 기준값을 측정하는 절차를 그대로 가져다 쓸 수 있다.
channel-talk
채널톡 블로그
원문은 여기서 이어서 읽을 수 있어요
원문 읽기
읽음 (0)

이 글과 비슷한

  1. 백엔드·twilio-engTwilio Engineering·

    Programmable Messaging에서 Verify API로 마이그레이션하기

    Twilio의 Programmable Messaging API로 자체 OTP 솔루션을 운영하던 서비스가 Verify API로 전환하는 방법을 코드 예시와 함께 설명한다. Verify는 OTP 전송·검증을 위한 전용 API로, 전화번호 구매, 토큰 생성, DB 저장·만료 관리를 내부에서 처리해 개발자가 직접 구현할 코드를 크게 줄인다.

    요약 이어보기
    #authentication#twilio#sms+2
  2. 백엔드·포스타입포스타입·

    포스타입이 개인화 추천을 하는 방법 2부

    포스타입 백엔드 엔지니어가 벡터 기반 개인화 추천 시스템을 실제 운영하며 맞닥뜨린 성능 장애와 용량 문제를 해결한 과정을 담은 2부다. 수백만 개의 벡터 KNN 검색이 피크 시간대에 전체 Elasticsearch 검색 성능을 흔드는 문제부터 클러스터 OOM 사태까지, 쿼리 최적화와 인프라 분리 두 가지 경로로 근본 해결에 이른다.

    요약 이어보기
    #elasticsearch#vector-search#recommendation-system+2
  3. 백엔드·포스타입포스타입·

    포스타입이 개인화 추천을 하는 방법 1부

    포스타입이 태그 기반 추천의 한계를 극복하고 벡터 임베딩 기반 개인화 추천 시스템을 구축한 과정을 담은 1부다. 유사한 콘텐츠가 다른 용어를 쓰거나 동일한 태그가 전혀 다른 톤의 콘텐츠를 가리키는 문제를 임베딩 벡터로 해결하고, OpenSearch의 HNSW ANN 검색으로 수백만 벡터를 실시간 검색하는 시스템을 구축해 구매율 15% 향상을 달성했다.

    요약 이어보기
    #opensearch#vector-search#recommendation-system+2