ZooKeeper 는 CPU 나 메모리보다 디스크 쓰기 지연이 성능을 결정한다. 쓰기 요청이 들어오면 리더가 트랜잭션 로그에 기록하고 fsync 로 디스크에 내려쓴 뒤에야 커밋한다. 이 fsync 시간이 곧 쓰기 응답 시간이다. 따라서 사이징은 디스크부터 정한다.
데이터는 전부 메모리에 올라간다. 대신 담는 것이 설정·잠금·리더 선출용 메타데이터라 양이 작다. 애플리케이션 데이터를 넣는 저장소가 아니므로 메모리 요구량이 크지 않다.
| 구분 | vCPU | 메모리 | 디스크 | 노드 수 |
|---|---|---|---|---|
| 개발·시험 | 2 | 4GB | SSD 32GB | 1 또는 3 |
| 일반 운영 | 2~4 | 4GB | SSD 64GB | 3 |
| 안정 운영 | 4 | 4~8GB | SSD 100GB, 로그 디스크 분리 | 3 |
| 대규모 · 쓰기 많음 | 4 이상 | 8GB 이상 | NVMe, 로그 전용 디스크 필수 | 5 |
노드 수는 반드시 홀수다. 과반이 살아 있어야 앙상블이 동작하므로 3대면 1대, 5대면 2대까지 잃어도 된다. 4대는 3대와 내구성이 같으면서 쓰기 지연만 늘어 의미가 없다. 대부분의 환경에서 3대면 충분하고, 노드를 늘리면 읽기 처리량은 늘지만 쓰기는 오히려 느려진다.
JVM 힙은 물리 메모리보다 작게 잡는다. ZooKeeper 가 스왑을 타면 응답 시간이 통째로 무너지므로, 힙과 OS 캐시가 물리 메모리 안에 들어오게 둔다. 8GB 노드라면 힙 2~4GB 정도다.
| 항목 | dataDir |
dataLogDir |
|---|---|---|
| 담는 것 | 스냅샷 | 트랜잭션 로그 |
| 쓰기 시점 | 주기적 | 모든 쓰기 요청마다 |
fsync |
스냅샷 생성 시 | 커밋 전 매번 |
| 성격 | 큰 파일을 가끔 | 작은 쓰기를 순차로, 지연에 민감 |
I/O 가 몰리는 쪽은 dataLogDir 이다. 두 디렉터리가 같은 디스크에 있으면 스냅샷을 쓰는 동안 트랜잭션 로그의 fsync 가 밀려 쓰기 지연이 튄다. 쓰기가 많은 환경에서 dataLogDir 을 별도 디스크로 빼는 것은 선택이 아니라 기본이다.
dataDir=/data/zookeeper/data
dataLogDir=/data/zookeeper/datalog
dataDir 에는 myid 파일이 있어야 한다. 디스크 사용량은 자동 정리 설정에 따라 정해진다. 켜지 않으면 스냅샷과 로그가 계속 쌓여 디스크를 채운다.
autopurge.snapRetainCount=3
autopurge.purgeInterval=24
다른 지연 민감 서비스와 같은 노드에 두지 않는다. Kafka 나 HDFS NameNode 와 같은 디스크를 쓰면 서로의 I/O 에 영향을 준다. 클러스터 규모가 작아 어쩔 수 없이 겹친다면 최소한 dataLogDir 만은 전용 디스크로 뺀다.
네트워크 지연이 낮은 곳에 모은다. 앙상블 노드끼리는 리더 선출과 커밋을 위해 계속 통신한다. 데이터센터를 가로질러 배치하면 쓰기 지연이 그대로 늘고, 네트워크가 흔들릴 때 리더 선출이 반복된다. 여러 사이트에 걸쳐야 한다면 ZooKeeper 자체를 사이트별로 두는 구성을 검토한다.
스왑을 끄거나 최소화한다. 힙이 스왑으로 밀리면 GC 중에 응답이 멈추고 세션 타임아웃으로 이어진다.
느려졌다는 신고가 들어오면 디스크 지연부터 본다.
echo mntr | nc localhost 2181
zk_avg_latency · zk_max_latency · zk_outstanding_requests 를 본다. 지연이 튀는데 CPU 와 메모리가 한가하다면 거의 디스크다. 리더 선출이 반복된다면 tickTime · initLimit · syncLimit 대비 실제 네트워크·디스크 지연이 큰 것이다.
iostat -x 1
await 와 %util 이 높은 장치가 dataLogDir 이 올라간 디스크라면 분리나 교체가 답이다.
ZooKeeper · ZooKeeper systemd 서비스가 기동하지 못할 때