Kafka Operations(Production Deployment)

이 글은 Kafka 클러스터를 production 환경에서 실행하기 위한 안내서가 아니며 고려해야할 핵심 사항을 다룬다.

  • 하드웨어 권장 사항 및 배포 전략과 같은 논리적 고려 사항
  • production 환경에 적합한 configuration 변경
  • data rebalancing, multi-data center 셋업과 같은 배포 후 고려 사항

1. Hardware

1. Memory

Kafka는 메시지를 저장하고 캐싱하기 위해 파일 시스템에 크게 의존한다. 모든 데이터는 disk에 flush 할 필요없이 파일 시스템에 영구적인 로그로 즉시 기록된다. 사실 이것은 커널의 페이지 캐시로 전송된다는 것을 의미한다. 최신 OS는 메모리를 회수할 때 사용 가능한 모든 메모리를 디스크 캐싱으로 전환하여 성능 저하가 거의 없다. 더욱이 Kafka는 힙 공간을 주의해서 사용하기 때문에 힙 크기를 5GB이상으로 설정하지 않아도 된다. 이 결과 32GB machine에 28-30GB 이상의 파일시스템 캐시 결과를 낸다.

reader와 writer를 버퍼를 위한 상당한 메모리가 필요한데, 30초간 buffer가 가능하길 원한다면 write_throughput * 30으로 계산할 수 있으며, 이는 다른 모든 메모리가 필요한 상황들을 무시한 계산이다.

RAM이 64GB인 machine이 좋은 선택이지만 32GB인 machine을 선택하는 일이 드문 일이 아니다. 32GB 미만은 비생산적인 경향이 있으며 결국 많은 small machine이 필요하게 될것이다.

2. CPUs

Kafka 배포에 있어서 CPU에 대한 요구사항은 비교적 가벼운 경향이 있다. 프로세서 설정은 다른 리소스의 설정보다 중요하지않다. 그러나 SSL을 사용하게 되면 CPU에 대한 요구 사항이 상당히 높아질 수 있다.

여러 개의 코어가 있는 최신 프로세서를 선택해야한다. 보통 cluster는 24 core machine을 이용한다. 만일 빠른 CPU, 많은 코어 이 둘에서 하나를 선택해야 할 경우 더 많은 코어를 선택하야한다. 멀티 코어가 제공하는 동시성은 약간 더 빠른 클럭 속도보다 훨씬 영향력있다.

3. Disks

좋은 처리량을 위해서는 여러 개의 드라이브를 사용하고, Kafka data를 위해 사용되는 드라이브는 Kafka를 위해서만 사용하는 것이 좋다. 0.8 버전부터 드라이브를 단일 볼륨 또는 형식으로 RAID하고 각 드라이브를 자체 디렉토리로 마운트 할 수 있다. Kafka는 복제 기능이 있으므로 RAID로 제공되는 중복성은 애플리케이션 레벨에서도 제공 될 수 있는데, 여러 단점이 있다.

여러 데이터 디렉토리를 구성하면 파티션에 라운드 로빈이 데이터 디렉토리에 지정된다. 각 파티션은 전적으로 데이터 디렉토리 중 하나에 있다. 파티션간에 데이터의 균형이 잘 맞지 않으면 디스크간에 로드 불균형이 발생할 수 있다.

RAID는 부하가 낮은 수준에서 균형을 유지하기 때문에 디스크 간에 로드 균형을 조정할 때 잠재적으로 더 효과적 일 수 있다. (항상 그렇지는 않지만). RAID의 주된 단점은 사용 가능한 디스크 공간을 줄인다. RAID의 또 다른 잠재적 이점은 디스크 오류를 견딜 수있는 능력이다.

쓰기 처리량과 디스크 장애시 어레이 재구성의 I / O 비용이 적기 때문에 RAID 5 또는 RAID 6을 권장하지 않는다. (일반적으로 재구성 비용은 RAID에 적용되지만 그것은 RAID 6와 RAID 5에서 최악이다).

추가 비용이 허용되는 경우 RAID 10을 사용하는 것이 좋다. 그렇지 않으면, 각 디렉토리가 별도의 드라이브에 마운트 된 여러 로그 디렉토리로 Kafka 서버를 구성하는 것을 추천한다.

마지막으로 NAS (Network Attached Storage)를 피해야한다. 사람들은 일상적으로 NAS 솔루션이 로컬 드라이브보다 빠르고 안정적이라고 주장하는데, NAS가 과대 광고에 부합한다는 것을 결코 보지 못했다. NAS는 더 느린 경우가 많으며, 평균 대기 시간의 편차가 더 큰 대기 시간을 표시하며 단일 실패 지점이다.

4. Network

빠르고 안정적인 네트워크는 분산 시스템에서 성능에 분명히 중요하다. 높은 대역폭으로 샤드의 이동 및 복제를 하는 동안에, 짧은 Latency는 노드가 쉽게 통신 할 수 있도록한다. 최신 데이터 센터 네트워킹 (1GbE, 10GbE)은 대다수의 클러스터에 충분하다.

데이터 센터가 가까이에 배치되어 있더라도 여러 데이터 센터에 걸쳐있는 클러스터를 피해야한다. 넓은 지리적 거리에 걸쳐있는 클러스터는 피하도록 해야한다. 노드의 절반이 실제로 다른 데이터 센터에 위치하여 떨어져 있다고 하더라도 Kafka Cluster는 모든 노드가 동일하다고 가정하기 때문인데,
대기 시간이 길어지면 분산 시스템의 문제가 악화되고 디버깅 및 해결이 더욱 어려워진다.

모든 사람들은 데이터 센터 간의 파이프가 견고하고 대기 시간이 짧다고 주장하지만, 이는 사실이 아니며, 크로스 데이터 센터 클러스터를 관리하는 번거로움과 비용은 그다지 이점이 없다.

5. Filesystem

XFS 또는 ext4에서 Kafka를 실행하는 것이 좋다. XFS는 일반적으로 ext4에 비해 약간의 튜닝으로 잘 수행되며, 많은 Linux 배포판의 기본 파일 시스템이다.

6. 일반적으로 고려해야할 사항들

수십 개의 CPU 코어, 수 백 기가바이트의 RAM을 갖춘 머신과 EC2와 같은 클라우드 플랫폼에서 수천 개의 작은 가상 시스템. 어떤 구성이 좋은 구성일까?

일반적으로 medium-to-large box를 선호하는 것이 좋다. 수 천개의 클러스터를 관리하고 싶지 않으므로 작은 머신을 피해야한다. 이는 명백하게 Kafka를 운영하는데 있어서 오버헤드가 커진다.

하지만 정말 고성능의 기계는 피해라. 리소스가 불균형 해지면,
(예 : 모든 메모리는 사용 가능하지만, CPU가 없는 경우) 종종 시스템 당 여러 노드를 실행해야하는 경우 병목 현상을 추가 할 수 있다.

2. JVM

최신 버전의 JDK 1.8을 G1 콜렉터와 함께 실행하는 것이 좋다 (전 버전이 보안 취약점이 발견되었다.) JDK 1.7에서 G1을 사용할 계획이라면 u51을 사용하고 있는지 확인해야한다. u21을 시도했지만 그 버전의 GC 구현에는 많은 문제점이있었다.

우리가 권장하는 GC 튜닝 (JDK 1.8 u5로 대규모 배포에서 테스트)은 다음과 같다 :

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

참고로 LinkedIn의 제일 바쁜 클러스터 중 하나의 통계는 다음과 같다.

  • 60 brokers
  • 50k partitions (replication factor 2)
  • 800k messages/sec in
  • 300MB/sec inbound, 1GB/sec +outbound

클러스터의 모든 브로커는 약 21ms 동안 90 % GC 일시 정지 시간을 가지며, 초당 1 개 미만의 GC를 수행한다.

3. Important Configuration Options

  • zookeeper.connect
  • broker.id
  • log.dirs
  • listeners
  • advertised.listeners
  • num.partitions
Replication configs
  • default.replication.factor
  • min.insync.replicas
  • unclean.leader.election.enable

4. File descriptors and mmap

Kafka는 매우 많은 수의 파일을 사용하며, 많은 수의 소켓을 사용하여 클라이언트와 통신한다. 때문에 많은 수의 가용 file descriptors를 필요로한다.

안타깝게도 대다수의 최신 리눅스 배포판에는 프로세스 당 허용되는 1,024 개의 file descriptors가 제공된다. 이는 수백 개의 파티션을 호스팅하는 노드는 물론 작은 Kafka 노드의 경우에도 너무 작은 양이다.

파일 설명자 수를 100,000과 같이 매우 큰 값으로 늘려야한다.

5. Zookeeper

Stable version

ZkClient는 Kafka가 ZooKeeper와 상호 작용하는 데 사용하는 클라이언트 계층이다.
플랫폼과 함께 제공되는 ZooKeeper start script는 3.4.6 ZooKeeper 서버를 시작한다. 전 버전의 앙상블을 이미 가지고 있다면, 3.4 branch 이상의 버전으로 업그레이드해서 호환성 문제가 발생하지 않도록하고, 이상적으로는 3.4.6 이상 버전을 사용하는 것이 좋다.

Operationalizing Zookeeper
  • 물리적 / 하드웨어 / 네트워크 계층의 잉여 자원 확보 :
    하드웨어를 같은 rack에 배치하지 말고, 여분의 하드웨어 리소스, 전원 및 네트워크 경로를 유지하도록 노력해라. 일반적인 ZooKeeper 앙상블은 2대 혹은 3대의 서버가 down 되었을 때의 내결함성을 위하여 5- 7 대의 서버로 구성된다. 규모가 작은 경우 3 대의 서버를 사용할 수도 있지만, 이 경우에는 1 대의 서버만이 down 되어야 장애 대응 가능하다.

  • I / O 분리 :
    쓰기에 대한 트래픽이 많으면, 트랜잭션 로그를 전용 디스크 그룹에 쌓길 원할텐데, 트랜잭션 로그를 쓰는 것은 동기적이지만 성능을 위해 일괄 처리되므로, 결과적으로 동시 쓰기는 성능에 심각한 영향을 줄 수 있다. ZooKeeper 스냅샷은 이러한 동시 쓰기 소스 중 하나 일 수 있으며, 이상적으로는 트랜잭션 로그와 별도의 디스크 그룹에 작성되어야한다. 스냅샷은 디스크에 비동기적으로 작성되므로 일반적으로 운영 체제 및 메시지 로그 파일과 공유하는 것이 좋으며, dataLogDir 파라미터를 사용하여 별도의 디스크 그룹을 사용하도록 서버를 설정할 수 있다.

  • 응용 프로그램 분리 :
    같은 앙상블을 사용하려는 응용 프로그램의 사용 패턴을 완벽하게 이해하지 못한다면 ZooKeeper를 독립적으로 실행하는 것이 좋다. 만약 앙상블 공유를 원한다면 chroot 기능을 사용하는 것이 좋다. chroot를 사용하면 각 애플리케이션에 자체 네임 스페이스를 부여 할 수 있다.

  • 가상화 사용시 주의 사항 :
    일반적으로 가상화 된 환경에서 ZooKeeper를 사용해도 문제가 없으며, 가용성을 위해 서버를 여러 가용 영역에 배치하는 것이 가장 좋다. 또한 클라우드환경에서 해결 된 몇 가지 문제가 있으므로 사용중인 버전에 따라 문제가 발생할 수 있으니, ZooKeeper 설명서를 읽거나 ZooKeeper 커뮤니티를 직접 확인해야한다.

  • ZooKeeper 구성 :
    힙 공간을 충분히 확보했는지 확인하고, 더 많은 ZooKeeper state를 허용하면 스냅샷이 커질 수 있고, 큰 스냅샷이 복구 시간에 영향을 미친다는 것을 명심해야한다. 실제로 스냅샷이 너무 커지면 (몇 기가 바이트) initLimit 파라미터를 늘려서 서버가 복구하고 앙상블에 참여할 수있는 충분한 시간을 확보해야 할 수도 있다.

  • 모니터링 :
    JMX와 4lw 명령은 모두 유용하다.

  • 앙상블을 과하게 구성하지 말아야한다. :
    특히 많은 양의 워크로드가 집중되어 있는 대규모 앙상블은 모든 서버 복제본에 업데이트가 전파되므로 많은 서버 간 통신을 의미한다. 또한 앙상블을 너무 적게 구성하면, 과한 읽기 요청으로 위험에 빠뜨릴 수 있다. 보다 많은 서버를 다루는 것은 더 많은 읽기 용량을 제공하지만, 쓰기 용량은 줄어든다. (읽기는 여러 서버에서 처리하지만, 쓰기는 그만큼 복제본 업데이트를 많이 해야하므로 부하가 생길 수 있다. ) 읽기 용량을 추가하는 동안 쓰기 용량에 미치는 영향을 줄이는 한 가지 방법은 옵저버를 추가하는 것이다.

6. Mirroring data between clusters

단일 클러스터의 노드간에 발생하는 복제와 혼동을 피하기 위해, Kafka 클러스터간에 데이터를 복제하는 프로세스를 “Mirroring”이라고 한다. Kafka는 Kafka 클러스터간에 데이터 Mirroring 도구를 제공한다. 이 도구는 하나 이상의 원본 클러스터에서 읽고, 대상 클러스터에 쓴다. 이러한 종류의 Mirroring을 사용하는 일반적인 경우는 다른 데이터 센터에 복제본을 제공할 때이다. 실제로 Mirroring maker는 Kafka consumer와 producer를 함께 연결 한 것이다. 원본 및 대상 클러스터는 완전히 독립적인 개체이고, 서로 다른 수의 파티션을 가질 수 있으며 오프셋은 동일하지 않다. 이러한 이유로 Mirroring 클러스터는 실제로 내결함성이 보장되지 않는다. 이를 위해서는 일반적인 클러스터 내 복제를 사용하는 것이 좋다. 그러나 Mirroring maker 프로세스는 파티셔닝 메시지 키를 보유하고 사용하므로 키 기준으로 정렬되어 보존된다.

다음은 단일 토픽(my-topic)을 Mirroring하는 방법을 보여주는 예이다.

$ bin / kafka-run-class.sh kafka.tools.MirrorMaker --consumer.config consumer-1.properties \   
   --consumer.config consumer-2.properties --producer.config producer.properties \   
   --whitelist my-topic   

whitelist 옵션을 사용하여 Topic을 지정하며, 이 옵션은 Java 스타일의 모든 정규 표현식을 허용한다. 따라서 A와 B라는 두 개의 주제를 --whitelist 'A | B'를 사용하여 대칭 적으로 표현할 수 있다. 또는 --whitelist '*'를 사용하여 모든 topic을 Mirroring할 수 있으며, 편의상 ‘|’대신 ‘,’를 사용하여 Topic을 지정할 수 있다. 때로는 원하지 않는 topic을 지정하는 것이 더 쉬운데, –blacklist를 사용하여 제외 할 대상을 지정할 수 있다. auto.create.topics.enable = true 구성으로 Mirroring을 결합하면 새 항목이 추가되는 경우에도 원본 클러스터의 모든 데이터를 자동으로 만들고 복제하는 클러스터를 가질 수 있다.

위 글은 confluent의 문서를 바탕으로 작성되었습니다.

comments powered by Disqus