하드웨어와 OS

하드웨어와 OS (Hardware and OS)

이 페이지는 Kafka 브로커를 돌릴 하드웨어 사양과 OS·파일시스템 튜닝 권장 사항을 다뤄요. 디스크·메모리 요구 사항, 파일 디스크립터 한계, EXT4/XFS 선택, 플러시 정책 같은 운영 실무 노하우가 담겨 있어요.

출처: 문서

본문

우리는 24GB 메모리를 가진 듀얼 쿼드 코어 Intel Xeon 머신을 사용하고 있습니다.

활성 리더·라이더를 버퍼링하기에 충분한 메모리가 필요합니다. 30초 동안 버퍼링할 수 있기를 원한다고 가정하고 메모리 요구를 write_throughput*30으로 계산하는 대략적인(back-of-the-envelope) 추정을 할 수 있습니다.

디스크 처리량이 중요합니다. 우리는 8x7200 rpm SATA 드라이브를 사용합니다. 일반적으로 디스크 처리량이 성능 병목이며, 디스크가 많을수록 좋습니다. 플러시 동작을 어떻게 구성하는지에 따라 더 비싼 디스크로 혜택을 얻을 수도 있고 아닐 수도 있습니다(자주 강제 플러시한다면 더 높은 RPM SAS 드라이브가 더 좋을 수 있습니다).

OS

Kafka는 모든 unix 시스템에서 잘 실행되어야 하며 Linux와 Solaris에서 테스트되었습니다.

Windows에서 실행할 때 몇 가지 문제를 본 적이 있으며, Windows는 현재 잘 지원되는 플랫폼은 아닙니다. 다만 바꾸게 된다면 기꺼이 하겠습니다.

많은 OS 수준 튜닝을 필요로 하지는 않지만, 잠재적으로 중요한 세 가지 OS 수준 구성이 있습니다.

  • 파일 디스크립터 한계: Kafka는 로그 세그먼트와 열린 연결에 파일 디스크립터를 사용합니다. 브로커가 많은 파티션을 호스팅한다면, 브로커가 맺는 연결 수 외에도 모든 로그 세그먼트를 추적하려면 브로커가 최소 (number_of_partitions)*(partition_size/segment_size)의 파일 디스크립터가 필요하다는 점을 고려하세요. 시작 지점으로 브로커 프로세스에 최소 100000개의 파일 디스크립터를 허용할 것을 권장합니다. 참고: mmap() 함수는 파일 디스크립터 fildes와 연관된 파일에 추가 참조를 더하며, 이 참조는 그 파일 디스크립터에 대한 이후의 close()로 제거되지 않습니다. 이 참조는 파일에 대한 매핑이 더 이상 없을 때 제거됩니다.
  • 최대 소켓 버퍼 크기: 여기에 설명된 대로 데이터센터 간 고성능 데이터 전송을 활성화하기 위해 늘릴 수 있습니다.
  • 프로세스가 가질 수 있는 최대 메모리 맵 영역 수(일명 vm.max_map_count): Linux 커널 문서를 참고하세요. 브로커가 가질 수 있는 최대 파티션 수를 고려할 때 이 OS 수준 속성을 주시해야 합니다. 기본적으로 많은 Linux 시스템에서 vm.max_map_count의 값은 대략 65535 부근입니다. 파티션마다 할당되는 각 로그 세그먼트는 index/timeindex 파일 쌍을 필요로 하며, 각 파일은 1개의 map 영역을 소비합니다. 즉, 각 로그 세그먼트는 2개의 map 영역을 사용합니다. 따라서 각 파티션은 단일 로그 세그먼트만 호스팅하는 한 최소 2개의 map 영역을 필요로 합니다. 즉, 브로커에 50000개의 파티션을 만들면 100000개의 map 영역이 할당되며, 기본 vm.max_map_count를 가진 시스템에서는 OutOfMemoryError (Map failed)로 브로커 크래시가 발생할 가능성이 높습니다. 파티션당 로그 세그먼트 수는 세그먼트 크기, 부하 강도, 보존 정책에 따라 달라지며 일반적으로 1보다 많은 경향이 있다는 점을 명심하세요.

디스크와 파일시스템 (Disks and Filesystem)

우리는 좋은 처리량을 얻기 위해 여러 드라이브를 사용하고, 좋은 지연시간을 보장하기 위해 Kafka 데이터용 드라이브를 애플리케이션 로그나 기타 OS 파일시스템 활동과 공유하지 않는 것을 권장합니다. 이 드라이브들을 RAID로 묶어 단일 볼륨으로 만들거나, 각 드라이브를 자신의 디렉터리로 포맷·마운트할 수 있습니다. Kafka에는 복제가 있으므로 RAID가 제공하는 중복성도 애플리케이션 수준에서 제공될 수 있습니다. 이 선택에는 여러 트레이드오프가 있습니다.

여러 데이터 디렉터리를 구성하면 파티션이 데이터 디렉터리에 라운드 로빈 방식으로 할당됩니다. 각 파티션은 데이터 디렉터리 중 하나에 완전히 존재합니다. 데이터가 파티션 간에 잘 균형 잡히지 않으면 디스크 간 부하 불균형이 발생할 수 있습니다.

RAID는 더 낮은 수준에서 부하를 균형 잡기 때문에 디스크 간 부하 균형을 더 잘할 가능성이 있습니다(항상 그런 것 같지는 않지만). RAID의 주요 단점은 보통 쓰기 처리량에 큰 성능 타격을 주고 사용 가능한 디스크 공간을 줄인다는 것입니다.

RAID의 또 다른 잠재적 이점은 디스크 장애를 견딜 수 있는 능력입니다. 그러나 우리의 경험상 RAID 배열을 재구축하는 것은 너무 I/O 집약적이라 사실상 서버를 비활성화하므로, 실질적인 가용성 개선을 제공하지 못합니다.

애플리케이션 vs OS 플러시 관리 (Application vs. OS Flush Management)

Kafka는 항상 모든 데이터를 즉시 파일시스템에 기록하며, OS 캐시에서 데이터를 디스크로 강제로 내보낼 때를 제어하는 플러시 정책을 구성할 수 있는 기능을 지원합니다. 이 플러시 정책은 일정 시간 후 또는 일정 수의 메시지가 쓰여진 후 데이터를 디스크로 강제하도록 제어할 수 있습니다. 이 구성에는 여러 선택지가 있습니다.

Kafka는 데이터가 플러시되었는지 알기 위해 결국 fsync를 호출해야 합니다. 크래시에서 복구할 때 fsync되지 않은 것으로 알려진 로그 세그먼트에 대해 Kafka는 각 메시지의 CRC를 확인하여 무결성을 검사하고, 시작 시 실행되는 복구 프로세스의 일부로 동반 오프셋 인덱스 파일도 재구축합니다.

Kafka의 내구성은 데이터를 디스크에 동기화할 것을 요구하지 않는다는 점에 유의하세요. 실패한 노드는 항상 복제본에서 복구되기 때문입니다.

우리는 애플리케이션 fsync를 완전히 비활성화하는 기본 플러시 설정을 권장합니다. 이는 OS가 수행하는 백그라운드 플러시와 Kafka의 자체 백그라운드 플러시에 의존한다는 뜻입니다. 이는 대부분의 사용에서 최상의 선택을 제공합니다. 튜닝할 노브가 없고, 훌륭한 처리량과 지연시간, 완전한 복구 보장을 제공합니다. 우리는 일반적으로 복제가 제공하는 보장이 로컬 디스크 동기화보다 강하다고 생각하지만, 과도한 우려가 있는 사람은 둘 다 원할 수 있으며 애플리케이션 수준 fsync 정책도 여전히 지원됩니다.

애플리케이션 수준 플러시 설정을 사용하는 단점은 디스크 사용 패턴이 덜 효율적이고(OS가 쓰기 재정렬을 할 수 있는 여지가 적음) 대부분의 Linux 파일시스템에서 fsync가 파일 쓰기를 차단하는 반면 백그라운드 플러시는 훨씬 더 세분화된 페이지 수준 잠금을 수행하므로 지연시간이 발생할 수 있다는 것입니다.

일반적으로 파일시스템의 저수준 튜닝을 할 필요는 없지만, 유용할 경우를 대비해 다음 몇 섹션에서 일부를 다루겠습니다.

Linux OS 플러시 동작 이해 (Understanding Linux OS Flush Behavior)

Linux에서 파일시스템에 기록된 데이터는 (애플리케이션 수준 fsync 또는 OS 자체 플러시 정책으로 인해) 디스크로 기록되어야 할 때까지 pagecache에 유지됩니다. 데이터의 플러시는 pdflush(또는 2.6.32 이후 커널에서는 "flusher threads")라는 백그라운드 스레드 집합이 수행합니다.

Pdflush에는 캐시에 유지할 수 있는 더티 데이터의 양과 디스크로 다시 기록되기 전에 얼마나 오래 유지할지 제어하는 구성 가능한 정책이 있습니다. 이 정책은 여기에 설명되어 있습니다. Pdflush가 기록되는 데이터 속도를 따라잡지 못하면 결국 쓰기 프로세스를 차단시켜 쓰기의 지연시간을 발생시키고 데이터 축적을 늦춥니다.

다음으로 OS 메모리 사용의 현재 상태를 볼 수 있습니다.

$ cat /proc/meminfo

이 값들의 의미는 위 링크에 설명되어 있습니다.

디스크로 기록될 데이터를 저장할 때 pagecache를 사용하는 것은 프로세스 내 캐시보다 여러 이점이 있습니다.

  • I/O 스케줄러가 연속된 작은 쓰기를 더 큰 물리적 쓰기로 배치해 처리량을 개선.
  • I/O 스케줄러가 디스크 헤드 이동을 최소화하도록 쓰기를 재정렬해 처리량을 개선.
  • 머신의 모든 여유 메모리를 자동으로 사용.

파일시스템 선택 (Filesystem Selection)

Kafka는 디스크의 일반 파일을 사용하므로 특정 파일시스템에 대한 하드 의존성이 없습니다. 가장 많이 사용되는 두 파일시스템은 EXT4와 XFS입니다. 역사적으로 EXT4가 더 많이 사용되었지만, 최근 XFS 파일시스템의 개선으로 안정성 저하 없이 Kafka 워크로드에 더 나은 성능 특성을 보이는 것으로 나타났습니다.

상당한 메시지 부하가 있는 클러스터에서 다양한 파일시스템 생성·마운트 옵션을 사용해 비교 테스트가 수행되었습니다. 모니터링된 Kafka의 주요 지표는 append 연산이 걸리는 시간을 나타내는 "Request Local Time"이었습니다. XFS는 훨씬 더 나은 로컬 시간(최상의 EXT4 구성의 250ms+ 대비 160ms)과 더 낮은 평균 대기 시간을 보였습니다. XFS 성능은 디스크 성능의 변동성도 덜 보였습니다.

일반 파일시스템 노트 (General Filesystem Notes)

Linux 시스템에서 데이터 디렉터리에 사용되는 모든 파일시스템에 대해 마운트 시 다음 옵션을 사용하는 것이 권장됩니다.

  • noatime: 이 옵션은 파일을 읽을 때 파일의 atime(마지막 접근 시간) 속성 업데이트를 비활성화합니다. 이는 특히 부트스트랩 컨슈머의 경우 상당수의 파일시스템 쓰기를 제거할 수 있습니다. Kafka는 atime 속성에 전혀 의존하지 않으므로 비활성화해도 안전합니다.

XFS 노트 (XFS Notes)

XFS 파일시스템은 상당한 양의 자동 튜닝이 마련되어 있어 파일시스템 생성 시나 마운트 시 기본 설정을 변경할 필요가 없습니다. 고려할 가치가 있는 유일한 튜닝 파라미터는 다음과 같습니다.

  • largeio: 이는 stat 호출이 보고하는 선호 I/O 크기에 영향을 줍니다. 더 큰 디스크 쓰기에서 더 높은 성능을 허용할 수 있지만, 실제로는 성능에 미미하거나 전혀 영향을 주지 않았습니다.
  • nobarrier: 배터리 백업 캐시가 있는 기본 디바이스의 경우 이 옵션은 주기적인 쓰기 플러시를 비활성화해 약간 더 높은 성능을 제공할 수 있습니다. 그러나 기본 디바이스가 잘 동작한다면 파일시스템에 플러시가 필요하지 않다고 보고하므로 이 옵션은 효과가 없습니다.

EXT4 노트 (EXT4 Notes)

EXT4는 Kafka 데이터 디렉터리에 사용 가능한 파일시스템 선택이지만, 최대 성능을 얻으려면 여러 마운트 옵션을 조정해야 합니다. 게다가 이러한 옵션은 일반적으로 장애 시나리오에서 안전하지 않으며 훨씬 더 많은 데이터 손실과 손상을 초래합니다. 단일 브로커 장애의 경우 디스크를 지우고 클러스터에서 복제본을 재구축할 수 있으므로 큰 문제가 되지 않습니다. 정전 같은 다중 장애 시나리오에서는 쉽게 복구할 수 없는 기본 파일시스템(그리고 데이터) 손상을 의미할 수 있습니다. 다음 옵션을 조정할 수 있습니다.

  • data=writeback: Ext4는 기본적으로 data=ordered이며 일부 쓰기에 강한 순서를 둡니다. Kafka는 모든 미플러시 로그에 대해 매우 세심한 데이터 복구를 수행하므로 이 순서가 필요하지 않습니다. 이 설정은 순서 제약을 제거하고 지연시간을 크게 줄이는 것으로 보입니다.
  • 저널링 비활성화: 저널링은 트레이드오프입니다. 서버 크래시 후 재부팅을 더 빠르게 하지만, 쓰기 성능에 변동성을 더하는 많은 추가 잠금을 도입합니다. 재부팅 시간을 신경 쓰지 않고 쓰기 지연시간 스파이크의 주요 원인을 줄이려는 사람들은 저널링을 완전히 끌 수 있습니다.
  • commit=num_secs: 이는 ext4가 메타데이터 저널에 커밋하는 빈도를 튜닝합니다. 이것을 더 낮은 값으로 설정하면 크래시 중 미플러시 데이터의 손실이 줄어듭니다. 더 높은 값으로 설정하면 처리량이 개선됩니다.
  • nobh: 이 설정은 data=writeback 모드 사용 시 추가 순서 보장을 제어합니다. Kafka는 쓰기 순서에 의존하지 않으므로 이는 Kafka와 함께 사용해도 안전하며 처리량과 지연시간을 개선합니다.
  • delalloc: 지연 할당(delayed allocation)은 물리적 쓰기가 발생할 때까지 파일시스템이 블록 할당을 피하는 것을 의미합니다. 이는 ext4가 더 작은 페이지 대신 큰 extent를 할당할 수 있게 하고 데이터가 순차적으로 기록되도록 보장합니다. 이 기능은 처리량에 훌륭합니다. 파일시스템에 일부 잠금이 관련되어 지연시간 변동을 조금 더하는 것으로 보입니다.
  • fast_commit: Linux 5.10에 추가된 fast_commit은 data=ordered 저널링 모드와 함께 사용할 수 있는 더 가벼운 저널링 방법입니다. 이를 활성화하면 지연시간이 크게 줄어드는 것으로 보입니다.

KRaft 컨트롤러 디스크 교체 (Replace KRaft Controller Disk)

Kafka가 KRaft를 사용하도록 구성되면 컨트롤러는 metadata.log.dir에 지정된 디렉터리(또는 metadata.log.dir이 구성되지 않았다면 첫 번째 로그 디렉터리)에 클러스터 메타데이터를 저장합니다. metadata.log.dir에 대한 자세한 내용은 문서를 참고하세요.

클러스터 메타데이터 디렉터리의 데이터가 하드웨어 장애 때문에 손실되거나 하드웨어를 교체해야 한다면, 새 컨트롤러 노드를 프로비저닝할 때 주의를 기울여야 합니다. 컨트롤러 과반수(majority)가 커밋된 데이터를 모두 가질 때까지 새 컨트롤러 노드를 포맷하고 시작해서는 안 됩니다. 컨트롤러 과반수가 커밋된 데이터를 가지는지 확인하려면 kafka-metadata-quorum.sh 도구를 실행해 복제 상태를 설명(describe)합니다.

$ bin/kafka-metadata-quorum.sh --bootstrap-server localhost:9092 describe --replication
NodeId	DirectoryId          	LogEndOffset	Lag	LastFetchTimestamp	LastCaughtUpTimestamp	Status
1    	dDo1k_pRSD-VmReEpu383g	966        	0  	1732367153528    	   1732367153528        	Leader
2    	wQWaQMJYpcifUPMBGeRHqg	966        	0  	1732367153304    	   1732367153304        	Observer
...     ...             ...     ...                     ...                     ...

컨트롤러 과반수의 Lag가 작아질 때까지 확인하고 기다리세요. 리더의 end offset이 증가하지 않는다면 과반수의 lag가 0이 될 때까지 기다릴 수 있습니다. 그렇지 않으면 최신 리더 end offset을 선택해 모든 복제본이 그것에 도달할 때까지 기다릴 수 있습니다. 컨트롤러 과반수의 LastFetchTimestampLastCaughtUpTimestamp가 서로 가까워질 때까지 확인하고 기다리세요. 이 시점에 컨트롤러의 메타데이터 로그 디렉터리를 포맷하는 것이 안전합니다. 이는 kafka-storage.sh 명령을 실행해 수행할 수 있습니다.

$ bin/kafka-storage.sh format --cluster-id uuid --config config/server.properties

bin/kafka-storage.sh format 명령이 Log directory ... is already formatted 같은 메시지와 함께 실패하는 것이 가능합니다. 이는 combined 모드가 사용되고 메타데이터 로그 디렉터리만 손실되었지만 다른 디렉터리는 손실되지 않았을 때 발생할 수 있습니다. 이 경우에만(그리고 오직 이 경우에만) --ignore-formatted 옵션과 함께 bin/kafka-storage.sh format 명령을 실행할 수 있습니다.

로그 디렉터리를 포맷한 후 KRaft 컨트롤러를 시작하세요.

$ bin/kafka-server-start.sh config/server.properties

더 알아보기 (Learn more)

  • 파일 디스크립터와 vm.max_map_count 한도가 파티션 수 제약을 좌우해요.
  • XFS가 Kafka 워크로드에서 더 나은 성능을 보이는 경우가 많아요.
  • KRaft 컨트롤러 디스크 교체 시 컨트롤러 과반수의 데이터 동기화를 먼저 확인하세요.