dev/Cloud & Infra

kafka 운영 - 기본적인 환경 설정 경험담

lugi 2019. 8. 23. 06:40

kafka 를 접한지 1년여의 시간이 흘렀고, 2018년 말부터 로컬 테스트부터 시작해, 테스트망, 검증망을 거쳐 운영망에서 kafka를 운영한지 3개월 정도가 되었습니다.

kafka를 개인 컴퓨터에서 테스트용으로 쓸 때는 환경에 크게 고민을 하지 않고 기능적으로 제대로 돌아가는지를 신경 쓰지만 운영 시스템에 올리기 시작하면서부터는, 이 시스템의 환경을 어떻게 구성해야 최대한 안정적으로 돌아가게 될지, 성능을 더 끌어낼 방법은 무엇일지 이런 저런 고민을 하였습니다.

카프카, 데이터 플랫폼의 최강자, 카프카 핵심 가이드와 같은 좋은 책들의 도움을 받아 기본적인 것들을 공부하고, 운영 환경을 구성하면서, kafka를 구축하기 이전 ElasticSearch 를 구축했던 경험을 떠올리며, ElasticSearch는 약 27~30GB 의 HEAP을 권장하기 때문에 kafka도 OOP를 넘기지 않을 정도의 메모리를 잡으면 되지 않을까? 하고 생각할 때도 있었고, 엘라스틱 서치는 FileDescriptor 설정을 엄청 키워야 했는데 kafka는? 동작 방식을 생각하면 비슷할 것 같은데? 그리고 주키퍼는 어쩌지? 하는 생각이 들 때도 있었습니다.

 

운영 환경의 설정을 잡기 위해 참고했던 내용들을 요약해 보겠습니다.

 

CPU

kafka는 일반적으로 CPU 사용량이 높은 편은 아닙니다. 다만 SSL의 적용은 CPU 사용량에 상당한 영향을 줄 수 있으므로 세심한 고려가 필요합니다.

더 많은 코어 vs 더 빠른 클럭 속도 중에서 택해야 한다면 더 많은 코어에 중점을 두시기 바랍니다.

zookeeper도 CPU에 크게 영향을 받지는 않습니다, 다만 성능에 대한 세밀한 고려가 필요할 경우는 컨텍스트 스위칭이 문제가 되지 않도록 zookeeper에 전용 코어를 할당하는 것을 고려해야 합니다.

 

Memory 설정

kafka는 파일시스템에 데이터를 즉시 기록하는 구조입니다. 구조상 6GB 이상의 HEAP을 권장하지 않습니다. 메모리가 32기가인 머신에서, 파일시스템 캐시는 최대 28~30GB 정도 생성됩니다. 메모리의 소비량은 대체로 활성 읽기/쓰기 작업의 스루풋 * 버퍼 대기 시간과 같은 방식으로 추정할 수 있습니다.

 

zookeeper는 kafka의 메타데이터를 저장하고 분산 코디네이팅을 수행합니다. zookeeper는 데이터 처리에 따른 메모리 소비는 별로 없지만, zookeeper 앙상블의 znode 컨텐츠의 사이즈에 따라 영향을 받습니다.

kafka-zookeeper 사이에는  클러스터의 파티션 갯수가 이와 연관이 있습니다.

zookeeper가 구동되는 호스트의 메모리는 최소 8GB 정도를 권장하며, swap(물리적 메모리가 모자랄 경우 메모리 내용의 일부를 디스크의 스왑 공간으로 이동시켜 메모리를 효율적으로 쓰게 하는 기능)은 zookeeper 의 사용에 영향을 주기 때문에 zookeeper를 운영하는 호스트에서는 swap 해제를 권장합니다.

 

zookeeper의 heap 사이즈는 대부분의 경우 1GB 정도면 적절합니다. HEAP 사이즈를 키우는 것은 GC동작시 pause 되는 시간을 늘릴 수 있습니다. 이 부분에서 문제가 생기지 않게 조심해야 합니다.

 

디스크와 파일시스템

kafka는 디스크 엑세스가 많습니다. 다른 응용 프로그램들과 디스크를 분리하는 것이 좋습니다.

RAID 구성을 할 수도 있지만, kafka 자체의 복제 기능이 있기 때문에, 프로그램적으로 이를 보완하는 효과를 줄 수도 있습니다. 파티션 디렉토리를 여러개로 나눌 경우에는 파티션 간의 데이터 균형에 신경 써야 합니다. 파티션 간의 라운드-로빈에서 디스크 로드의 불균형을 초래할 수 있습니다. (이 점 때문에 저는 파티션을 구성할 때도 kafka 노드의 배수로 파티션을 구성하는 편이었습니다. 사실 얼마나 정확한 선택인지는 더 검증 해 보아야 합니다.)

파일시스템은 XFS 혹은 EXT4 를 권장합니다.

 

zookeeper도 디스크 엑세스에 민감합니다. SSD를 강력하게 권장합니다. zookeeper의 부하를 줄이기 위해 퍼징(일종의 가비지 컬렉팅)을 활용할 수 있습니다. 

autopurge.purgeInterval(퍼징이 발생하는 시간 간격), autopurge.snapRetainCount (퍼징시 유지할 스냅샷 갯수) 프로퍼티를 적절히 조절하여 성능 향상을 꾀할 수 있습니다.

 

OS

Unix 계열 혹은 Linux 계열을 권장합니다. Windows 에서는 그다지 잘 동작하지 않습니다.

 

파일디스크립터는 100,000 이상을 권장합니다.

mmap area 는 파티션 1개당 2영역이 할당 됩니다. 일반적으로 vm.max_map_count 를 65536 정도로 설정한 시스템에서 4~5만개 이상의 파티션을 생성했다면 이 경우 fail이 날 수 있습니다.

※ 이 부분에 대한 설명은 ElasticSearch 설치시 확인할 사항 포스트에도 비슷한 내용이 언급되어 있습니다. 

(ElasticSearch는 파일디스크립터 65536, mmap 262144를 권장합니다)

 

JAVA 버전

kafka 0.9.0 버전부터 JAVA6 이하 버전에 대한 지원이 중단되었습니다.

kafka 1.0.0 버전부터 JAVA9 를 지원합니다.

kafka 2.0.0 버전부터 JAVA7 이하 버전에 대한 지원이 중단되었습니다.

kafka 2.1.0 버전부터 JAVA11 을 지원합니다.

GC 설정

아무런 설정을 하지 않았을 경우 kafka 에서 기본적으로 설정된 GC 옵션은 다음과 같습니다

-XX:+UseG1GC -XX:MaxGCPauseMillis=20 -XX:InitiatingHeapOccupancyPercent=35 -XX:+ExplicitGCInvokesConcurrent

 

Linkedin 에서 사용하고 있는 GC 설정은 다음과 같습니다

(60브로커, 5만 파티션, 초당 30만 메시지, 300Mbps 인바운드, 1Gbps 아웃바운드)

-XX:MetaspaceSize=96m -XX:+UseG1GC -XX:MaxGCPauseMillis=20 
-XX:InitiatingHeapOccupancyPercent=35 -XX:G1HeapRegionSize=16M 
-XX:MinMetaspaceFreeRatio=50 -XX:MaxMetaspaceFreeRatio=80

 

기타 영향을 주는 설정과 참고사항

각 환경별 참고할만한 설정은 아래와 같습니다.

  • 프로듀서
    • acks : 안정성과 속도에 영향을 미칩니다. (0 -> 1 -> all 순으로 안정성은 증가하지만 속도는 떨어짐)
      • 0 : 자신이 보낸 메시지에 대하여 브로커의 확인을 기다리지 않습니다/
      • 1 : 자신이 보낸 메시지에 대해 리더의 수신을 확인 (팔로우 브로커로 복제가 일어나는 사이에 리더가 죽으면 메시지의 손실이 일어날 가능성 있습니다)
      • all, -1 : 자신이 보낸 메시지에 대해 리더의 수신 및 하나 이상의 팔로우 브로커로 복제까지 확인합니다.
    • compression과 batch size : 프로듀서는 batch size 개수만큼 모아서 전송을 하게 되며 batch 작업에 대해서 compression으로 압축을 적용할 수 있습니다, 이 때 압축의 사용은 버퍼 메모리 사용량에도 영향을 줍니다.
    • linger.ms : 비동기 모드에서 데이터를 버퍼링하는 최대 시간, 이 값으로 설정된 시간만큼 대기하고 데이터를 전송합니다. 이 값이 늘어나면 대기 시간은 늘어나지만 1회당 처리량은 늘어날 것입니다.
  • 컨슈머
    • 컨슈머의 성능은 파티션과 컨슈머 숫자의 비율을 맞추는 것이 중요합니다.
    • fetch.min.bytes : 컨슈머의 fetch 요청에 서버가 응답하는 최소 바이트 숫자, 이 숫자가 너무 작으면 서버와 잦은 통신이 발생하고, 이 숫자가 커질수록 서버의 요청은 감소하는 대신 1회당 전송량은 늘어나며, 컨슈머의 대기시간은 길어집니다.
  •  브로커
    • min.insync.replicas : write 작업을 승인하기 위한 최소 복제본의 숫자, producer의 ack 설정 all(-1) 조합해서 사용합니다. acks all 은 하나 이상의 팔로워 브로커로 복제를 확인하면 성공으로 간주하는데, 이 옵션과 조합하여 브로커에서 복제본의 숫자까지 지정할 수 있다. 예를 들어 ack all, 복제본 3, min.isync.replicas 2 로 설정된 경우, 3개 중 과반인 2개 이상의 복제가 성공해야 write를 승인합니다.
    • queued.max.requests : 네트워크 스레드에서 허용하는 대기 요청의 수, 만약 로드가 불규칙하다면 조절을 고려할만 합니다.

kafka는 확장이 용이한 시스템입니다. 파티션이 늘어나고 컨슈머가 늘어나는 것에 비례해서 처리 속도가 빨라집니다. 그럼에도 불구하고 무작정 파티션을 늘리는 것이 좋은 것은 아닙니다. 파티션이 늘어나는 것은 파일디스크립터의 점유를 늘리며, kafka의 관리 부담을 늘어나게 합니다. 또한 파티션-컨슈머의 연결은 컨슈머와의 연결당 스레드를 점유하므로 CPU의 성능이 충분하지 않다면 파티션을 늘렸음에도 불구하고 충분하게 처리량을 늘릴 수 없게 됩니다.

 

아직은 운영 중인 서비스가 본격적인 궤도에 오르지 않아, 현재는 크게 부하는 없는 상태에서 실제 피크에 도달했을 때를 가정하여 계속 테스트를 하며 개선을 하고 있습니다. 올 상반기 정도까지만 하더라도, kafka에 관심은 많지만 실제로 내가 kafka로 무엇을 하고 있는가? 에 대한 질문을 하면 답을 할 것이 없어서 사실 스스로를 kafka 운영자라고 하면 좀 사기를 친다(?)는 느낌이 있었는데, 지금은 kafka를 운영한다고 말을 할 수가 있습니다. 사실 본업은 개발에 좀 더 비중이 있는지라, kafka를 활용한 애플리케이션과의 연계에도 관심이 많습니다. 그런 부분도 꾸준히 공유할 수 있으면 좋겠습니다.

 

 

 

 

 

참고 사이트 :

https://www.popit.kr/author/peter5236

https://kafka.apache.org/documentation/#operations

https://docs.confluent.io/current/kafka/deployment.html

https://docs.confluent.io/current/zookeeper/deployment.html

https://community.cloudera.com/t5/Support-Questions/Any-tips-on-how-to-optimize-Kafka-broker-performance/m-p/169478

https://engineering.linkedin.com/kafka/benchmarking-apache-kafka-2-million-writes-second-three-cheap-machines

https://www.cloudera.com/documentation/enterprise/latest/topics/kafka_performance.html

https://www.confluent.io/blog/how-choose-number-topics-partitions-kafka-cluster

https://www.cloudkarafka.com/blog/2018-09-12-performance-optimization-apache-kafka-brokers-cloudkarafka.html