백프레셔, 버퍼, 그리고 로깅 사이드카

발행: (2026년 2월 15일 오후 04:32 GMT+9)
11 분 소요
원문: Dev.to

Source: Dev.to

(위에 제공된 텍스트 외에 번역할 내용이 있으면 알려 주세요.)

9 PM – 모든 것을 시작하게 만든 충돌

YouTube를 캐주얼하게 스크롤하고 있었는데 Slack 알림이 조용함을 깨뜨렸다: 우리 로깅 사이드카가 충돌했습니다 – 종료 코드 137.

첫눈에 보기엔 단순한 백프레셔 문제처럼 보였다. “버퍼를 늘리고, 제한을 조정하고, 재배포하면 되겠지.”

그렇지 않았다.

그 충돌은 나를 Fluent Bit의 청크 수명 주기, 파일 시스템 버퍼링, 그리고 로깅 사이드카 같은 “단순한” 것조차도 미니 분산 시스템처럼 동작한다는 사실을 깨닫게 하는 토끼굴로 이끌었다 – 그에 수반되는 모든 트레이드오프와 함께.

이 글에서는 그 여정에서 얻은 경험과 교훈을 공유하고자 한다.

우리의 설정

  • Fluent Bit은 각 애플리케이션 컨테이너와 함께 사이드카로 실행됩니다.
  • 그 책임은 간단합니다: 애플리케이션에서 로그를 수집하고, 이를 처리한 뒤 외부 로깅 플랫폼으로 전달합니다.

왜 Fluent Bit이 OOM이 될 수 있는가

대부분의 OOM 사례는 파이프라인 내 불균형으로 귀결됩니다:

원인결과
느린 출력 – 입력이 로그를 계속 수집하는 동안 하위 시스템이 지연됩니다역압이 쌓여 → 메모리 사용량이 증가합니다
무거운 필터링 또는 처리 – 필터가 일시적으로 메모리 사용량을 증가시킵니다위와 동일
제한 없는 수집 vs. 버퍼 한계 – 로그 양이 설정된 메모리 한계를 초과합니다사이드카가 병목이 됩니다

가시성 문제

갑작스러운 컨테이너 충돌에서 까다로운 점 중 하나는 가시성 부족입니다. 사이드카가 종료되면서 디버깅에 필요한 신호 자체를 잃어버렸습니다. 컨테이너 수준의 메모리 그래프는 안정적으로 보였으며, 이는 충돌을 더욱 혼란스럽게 만들었습니다.

나중에 우리는 실시간으로 메모리를 관찰하고 있지 않다는 것을 깨달았습니다.

결국 스파이크를 확인한 방법

  1. Memory Input Plugin을 활성화했습니다.
  2. Fluent Bit 로그를 통해 내부 메트릭을 검사했습니다.

그때 비로소 외부 모니터링에서는 보이지 않았던 갑작스러운 메모리 스파이크를 확인할 수 있었습니다.

실험 타임라인

Iteration 1 – “메모리 제한이 문제를 해결할까?”

# Example: cap memory per input
Mem_Buf_Limit 5M
  • 우리가 기대한 바: 메모리를 제한하면 OOM을 방지할 수 있다.
  • 실제로 일어난 일:
    • 제한에 도달하면 입력이 일시 정지된다.
    • 애플리케이션은 멈추지 않고 로그를 계속 내보낸다.
    • forward input 플러그인을 사용하고 있었기 때문에(업스트림에 영구 저장이 없음) 일시 정지된 입력은 로그 손실을 의미했다.

결과: 메모리 압박은 제어됐지만 신뢰성은 떨어졌다.

Iteration 2 – “디스크 버퍼링으로 해결될까?”

# Enable filesystem buffering
storage.type filesystem
  • 목표: RAM에서 디스크로 압력을 옮겨 내구성을 유지한다.
  • 결과: 사이드카가 여전히 OOM 오류로 크래시했다.

디스크 버퍼링만으로는 충분하지 않았다.

Iteration 3 – “메모리는 또 어디서 쓰이고 있지?”

입력만이 RAM을 차지하는 것이 아니라 필터와 출력도 메모리를 소비한다는 것을 깨달았다.

구성 요소메모리 영향
Filters / Parsers (예: JSON, Multiline)레코드를 일시적으로 보관한다; 우리 경우 JSON 파서가 주요 원인이었다.
Outputs압축, 재시도, 혹은 디스크에서 청크를 다시 로드할 수 있다. 파일시스템 버퍼링을 사용하더라도 청크를 플러시하기 전에 메모리로 가져와야 한다.

시도한 내용

  • 컨테이너 CPU 및 메모리 증설 – 명백하지만 “경량 사이드카” 목표에 어긋난다.
  • 파싱 작업을 일부 다운스트림으로 이동 – 파서 메모리 압박을 감소시켰다.
  • 출력 백로그 제한 조정 – 한 번에 RAM에 로드되는 청크 수를 제한했다.

시스템은 안정적으로 유지되었지만, 다음 크래시가 발생하기 전까지는…

새로운 증상 – 종료 코드 139 (Segmentation Fault)

[2019/01/09 17:06:01] [error] [plugins/in_forward/forward_fs.c:218 errno=28] No space left on device
[2019/01/09 17:06:01] [error] [in_forward] could not register file into fs_events

컨테이너가 139 로 종료되었습니다 – 즉, segfault 발생.
로그를 보면 errno=28 → “No space left on device.” 라는 메시지가 나타났습니다.

로깅 사이드카가 디스크를 고갈시킨 과정

  • ECS Fargate는 각 작업에 20 GB의 임시 스토리지를 제공합니다 – 버퍼링 용량으로는 충분해 보입니다.
  • 입력 수집 메트릭출력 플러시 메트릭을 비교했을 때, 명확한 패턴이 드러났습니다: 수집 속도 ≫ 플러시 속도.

실제로 일어난 일

  1. 메모리 버퍼가 가득 찬다.
  2. 버퍼가 넘치면 디스크에 스필(over)된다.
  3. 출력 프로세스가 디스크에서 청크를 로드하고 플러시를 시도한다.
  4. 플러시 속도는 그대로(또는 제한된 상태) 유지되는 동안 갑작스러운 수집 급증이 발생한다.
  5. 디스크 사용량이 배출 속도보다 빨리 증가 → 디스크 가득참 → ENOSPC → segfault.

지속적인 불균형이 충분히 오래 지속되면, 디스크가 가득 차는 것은 단순히 가능할 뿐만 아니라 불가피합니다.

속도 제한 및 완화 전략

계층작업
애플리케이션- 샘플링
- 중복 제거
- 엄격한 운영 로그 레벨 적용
Fluent Bit- 출력당 디스크 사용량 제한
- 제한에 도달하면 가장 오래된 청크 삭제
- 버스트 제어를 위해 throttle filter 적용
운영- 수집량이 지속적으로 처리 용량을 초과할 때 알림
- 임시 스토리지 확대(단기 버퍼, 영구적인 해결책은 아님)

Takeaways

  • Memory pressure isn’t the only problem – 디스크 고갈도 동일한 불균형 상황에서 나타날 수 있습니다.
  • 로깅 사이드카는 실질적으로 미니 분산 시스템이며, 파이프라인(입력 → 필터 → 출력)을 전체로서 다루어야 합니다.
  • 실시간 가시성(메모리, 디스크, 수집/플러시 속도)은 불균형이 컨테이너를 중단시키기 전에 포착하는 데 필수적입니다.
  • 하나의 노브(예: Mem_Buf_Limit)만 튜닝하고 하위 영향을 고려하지 않으면 데이터 손실이나 새로운 장애가 발생할 수 있습니다.

세 가지 실험을 순차적으로 진행하고, 숨겨진 메트릭을 노출한 뒤, 적절한 속도 제한 및 디스크 사용 가드를 추가함으로써, 우리는 불안정하고 충돌이 잦던 사이드카를 로깅 파이프라인의 신뢰할 수 있는 구성 요소로 전환했습니다.

Observations

  • physics. → 물리학.
  • When you generate data faster than you can move it, pressure builds. → 데이터를 이동할 수 있는 속도보다 빠르게 생성하면 압력이 쌓입니다.
  • In our case — it escaped through disk exhaustion. → 우리 경우에는 디스크 용량 고갈을 통해 빠져 나갔습니다.
  • Logging is infrastructure. It deserves guardrails. → 로깅은 인프라입니다. 보호 장치가 필요합니다.
  • If you don’t design for bursts, bursts will design your outage. → 폭증에 대비하지 않으면 폭증이 장애를 일으키게 됩니다.
  • Curious how you’d approach it differently. → 어떻게 다르게 접근할지 궁금합니다.
0 조회
Back to Blog

관련 글

더 보기 »