비동기 메시지 브로커의 디스크 용량 포화와 메시지 유실

증상 확인: 메시지가 사라지고 시스템이 느려진다

RabbitMQ, Kafka, ActiveMQ와 같은 비동기 메시지 브로커를 운영 중, 디스크 사용률이 90%를 넘어가고 있나요? 혹은 프로듀서(Producer)는 메시지를 성공적으로 발행했다고 보고하는데, 컨슈머(Consumer)가 메시지를 받지 못하는 현상이 발생하고 있습니까? 이는 브로커의 디스크 용량 포화로 인한 메시지 유실 또는 성능 저하의 전형적인 증상입니다. 시스템 로그에서 ‘disk full’, ‘no space left on device’, ‘queue is full’과 같은 에러 메시지를 확인했다면 즉시 조치가 필요합니다.

스마트폰 화면에서 글리치 현상이 발생하며 문자 메시지가 디지털 잡음으로 흩어지고 시스템 아이콘이 끊김과 지연을 보이며 느리게 조각나는 모습을 표현한 이미지입니다.

원인 분석: 왜 디스크가 차고 메시지가 사라지는가

비동기 메시지 브로커는 처리되지 않은 메시지를 메모리와 디스크에 임시 저장합니다. 디스크 용량 포화의 근본 원인은 대체로 두 가지입니다. 첫째, 메시지 생산 속도가 소비 속도를 지속적으로 초과하여 메시지가 브로커에 누적되는 경우입니다. 둘째, 메시지 보존 정책(Retention Policy)이 제대로 설정되지 않아 처리 완료된 오래된 메시지가 자동으로 삭제되지 않고 디스크를 계속 차지하는 경우입니다, 디스크가 가득 차면 브로커는 새 메시지를 거부하거나, 가장 오래된 메시지를 강제로 삭제하여 공간을 확보하려 시도하며, 이 과정에서 의도하지 않은 메시지 유실이 발생합니다.

긴급 조치: 시스템 멈춤 방지 및 데이터 보호

디스크 사용률이 95% 이상인 경우, 시스템이 완전히 멈출 수 있습니다. 가장 먼저 해야 할 일은 추가적인 데이터 손실을 막고 시스템을 안정화시키는 것입니다.

주의사항: 아래 조치를 수행하기 전, 가능하다면 현재 실행 중인 컨슈머 애플리케이션을 모두 정지시키는 것이 안전합니다. 더불어, 디스크 정리 작업은 운영 환경에 영향을 미칠 수 있으므로 비상 조치로 간주하고, 이후 근본적인 해결책을 반드시 적용해야 합니다.

1단계: 디스크 공간 확보 (임시 조치)

브로커 서버에 직접 접속하여 즉시 디스크 공간을 확보해야 합니다. 가장 간단한 방법은 불필요한 로그 파일이나 임시 파일을 삭제하는 것이지만, 메시지 브로커의 데이터를 함부로 삭제해서는 안 됩니다.

  1. 디스크 사용 현황 확인: df -h 명령어로 어느 마운트 포인트가 가득 찼는지 확인합니다. 일반적으로 브로커의 데이터 디렉토리(예: /var/lib/rabbitmq, /tmp/kafka-logs)가 위치한 파티션을 찾습니다.

  2. 대용량 파일/디렉토리 찾기: 해당 파티션에서 du -sh * | sort -rh | head -20 명령어를 실행하여 용량이 큰 상위 20개 항목을 확인합니다. 브로커 로그 파일(예: .log 파일)이 과도하게 커졌을 수 있습니다.

  3. 로그 파일 정리: 로그 파일이 원인이라면, 파일 내용을 지우기보다 truncate 명령이나 로그 로테이션 설정을 사용합니다. 예를 들어, truncate -s 0 /var/log/rabbitmq/rabbit@localhost.log로 파일 크기를 0으로 만들 수 있으나, 이 작업 전에 해당 로그 파일을 다른 위치로 백업하는 것이 좋습니다.

해결 방법 1: 메시지 보존 정책 조정 (가장 효과적)

임시 조치로 공간을 확보했다면, 이제 메시지가 무한정 쌓이지 않도록 시스템적인 한계를 설정해야 합니다. 이는 각 브로커의 설정을 통해 이루어집니다.

RabbitMQ의 경우

RabbitMQ는 큐(Queue) 단위로 정책(Policy)을 적용합니다. 큐의 최대 길이(메시지 개수) 또는 최대 바이트 수를 제한할 수 있습니다.

  1. 관리 콘솔 또는 CLI를 통해 정책을 생성합니다. 예를 들어, ‘max-length-policy’라는 정책을 모든 큐에 적용하려면:

  2. 명령어: rabbitmqctl set_policy max-length-policy ".*" '{"max-length":10000}' --apply-to queues

  3. 이 정책은 모든 큐(“.*”)에 적용되며, 큐에 10,000개의 메시지만 유지합니다. 10,001번째 메시지가 들어오면 큐의 맨 앞(가장 오래된) 메시지가 자동 삭제됩니다. 이 동작은 "overflow":"drop-head" (기본값)로 설정됩니다.

  4. 디스크 용량 기반 제한은 ‘max-length-bytes’ 인자를 사용합니다: '{"max-length-bytes":1073741824}' (1GB 제한).

Apache Kafka의 경우

Kafka는 토픽(Topic) 단위로 로그 보존 설정을 합니다. 보존 시간과 보존 크기를 조합하여 사용합니다.

  1. 기존 토픽 설정 변경: kafka-configs --bootstrap-server localhost:9092 --entity-type topics --entity-name your_topic_name --alter --add-config retention.ms=604800000,retention.bytes=1073741824

  2. 이 명령은 ‘your_topic_name’ 토픽의 메시지를 최대 7일(604800000ms) 또는 1GB(1073741824 bytes)까지만 보존하도록 설정합니다. 두 조건 중 먼저 도달하는 기준으로 오래된 데이터가 삭제됩니다.

  3. 신규 토픽 기본값 설정: 브로커 설정 파일(server.properties)에서 log.retention.hourslog.retention.bytes 값을 전역적으로 설정할 수 있습니다.

해결 방법 2: 메시지 생산/소비 속도 불균형 해소 (근본적 해결)

보존 정책은 증상 완화제입니다. 근본 원인인 생산/소비 속도의 격차를 해결하지 않으면, 설정한 제한에 계속 도달하여 메시지 유실이 빈번히 발생할 수 있습니다.

  1. 모니터링 강화: 브로커의 큐/토픽 별 메시지 적체(Backlog) 수치를 Grafana, Prometheus 등으로 시각화하고 알림(Alert)을 설정합니다. 적체량이 지속적으로 증가하는 추세를 보이면 경고해야 합니다.

  2. 컨슈머 병렬성(Parallelism) 증가: 컨슈머 애플리케이션의 파티션 구독 수나 컨슈머 스레드/프로세스 수를 늘려 처리량(Throughput)을 높입니다. Kafka에서는 컨슈머 그룹 내 인스턴스를 추가하면 자동으로 파티션이 재배분됩니다.

  3. 프로듀서 속도 제한(Throttling): 프로듀서 측에 흐름 제어를 적용합니다. 예를 들어, RabbitMQ의 ‘Publisher Confirm’ 메커니즘을 사용하여 브로커의 부하 상태에 따라 발행 속도를 조절하는 로직을 구현할 수 있습니다.

  4. 데드 레터 큐(DLQ) 활용: 처리 실패한 메시지를 별도의 DLQ로 보내는 전략을 명확히 합니다. DLQ 역시 보존 정책을 적용해야 하며, 주기적으로 모니터링하고 비워야 합니다.

해결 방법 3: 스토리지 아키텍처 재검토 (장기적 솔루션)

데이터 양이 지속적으로 많다면 인프라 측면의 접근이 필요합니다.

주의사항 및 예방 조치

설정 변경은 신중하게, 그리고 모니터링 아래에서 진행해야 합니다.

전문가 팁: 용량 계획과 자동화
메시지 브로커의 디스크 관리는 사후 처리가 아닌 사전 계획의 영역입니다. 시스템 설계 단계에서 예상 메시지 처리량(TPS), 평균 메시지 크기, 최대 허용 지연 시간을 기반으로 필요한 디스크 용량을 계산하십시오. 공식은 다음과 같습니다: [필요 용량] = [평균 메시지 크기] * [초당 메시지 수] * [최대 보존 시간(초)] * [안전 계수(예: 1.5)]. 또한, 디스크 사용률이 80%에 도달하면 자동으로 알림을 받고, 85%에 도달하면 중요도 낮은 토픽의 보존 시간을 임시로 줄이는 자동화 스크립트를 구축하는 것이 운영 부하를 크게 줄여줍니다. 결국, 안정적인 메시징 시스템은 모니터링과 자동화 위에서 작동합니다.