OOM 카나리는 실험적 기능이며 기본적으로 비활성화되어 있습니다. 프로덕션 환경에서의 검증이 완료될 때까지는 ClickHouse 버전에 따라 동작이
변경될 수 있습니다.
개요
SIGKILL로 프로세스를 종료합니다. 보통 가장 많은 메모리를 사용하는 프로세스가 대상이 되는데,
전용 호스트에서는 대개 clickhouse-server 자체입니다. 이 경우 서버가 복구할 기회를 얻지 못한 채
전체 서버 프로세스가 중단됩니다.
OOM 카나리는 먼저 종료되는 대상을 바꿉니다. 작은 희생용
자식 프로세스를 실행해 해당 프로세스가 OOM 대상이 되기 가장 쉬운 상태를 만들므로,
커널은 서버 대신 이 프로세스를 종료합니다. 그러면 서버는 이 종료를 감지하고,
OOM 이벤트였는지 확인한 뒤 메모리 압박을 완화하여 계속 살아남을 수 있습니다.
카나리는 메모리 한도를 높여 주지 않으며, 올바른 한도 설정을 대체하지도 않습니다
(메모리 오버커밋 및
max_server_memory_usage 참조). 이는 최후의 방어선으로서, 적은 고정 메모리를
대가로 일시적인 메모리 급증 상황에서 살아남을 가능성을 확보합니다.
작동 방식
clickhouse oom-canary 프로세스입니다. 자체
oom_score_adj를 최댓값(1000)으로 설정해 커널이 이를 가장 먼저 대상으로 삼게 한 다음,
oom_canary_size 바이트(기본값 100 MB)를 할당하고, 메모리 페이지를 실제로 터치한 뒤, mlock하여
상주 집합이 실제 메모리를 차지하도록 합니다. 서버가 종료되면 자동으로 함께 종료됩니다.
서버에서는 모니터 스레드가 카나리를(pidfd를 통해) 감시하다가,
카나리가 종료되면 다음과 같이 대응합니다:
- cgroup OOM 증거가 있는 상태에서
SIGKILL로 종료됨 → OOM 대응을 실행한 후 새 카나리를 재시작합니다. - OOM 증거 없이 종료됨(예: 수동
kill -9) 또는 일시적 실패로 종료됨 → 대응은 실행하지 않고 재시작만 합니다. - 영구적인 설정 실패 또는 서버 종료 → 카나리가 스스로 비활성화됩니다.
memory.events.local oom_kill
카운터에서만 확인합니다. 이는 의도적으로 해당 cgroup 로컬 값만 사용합니다. 계층형 카운터나 호스트 전체 카운터는
무관한 프로세스로 인해 증가할 수 있으므로, 잘못된 대응이 트리거될 수 있습니다.
OOM이 확인되면 대응은 서로 독립적인 다음 단계를 실행합니다: FATAL
메시지를 기록하고, allocator(jemalloc) arenas를 purge하며, 실행 중인 모든
쿼리를 가능한 범위에서 취소하고, 모든 머지와 뮤테이션을 취소하며,
system.crash_log에 이벤트를 큐에 넣습니다.
메모리 압박 상황에서 강제로 I/O를 발생시키면 상황이 더 악화될 수 있으므로 시스템 로그는 동기적으로
플러시하지 않습니다.
요구 사항
- Linux ≥ 5.3. 모니터는
pidfd_open을 통해 카나리를 관리합니다. 이보다 오래된 커널에서는 카나리가 시작 시 자체적으로 비활성화됩니다. Linux가 아닌 플랫폼에서는 아무 동작도 하지 않습니다. - OOM 대응을 위해
memory.events.local이 있는 cgroup v2. 이것이 없으면 카나리는SIGKILL이후에도 재시작되지만 OOM을 확인할 수 없으므로 대응은 실행되지 않습니다(시작 시 경고가 로그에 기록됩니다). mlockcapability(선택 사항). 카나리 메모리를 잠그려면CAP_IPC_LOCK또는 충분한RLIMIT_MEMLOCK가 필요합니다. 실패하면 카나리가 경고를 로그에 기록하며 해당 메모리가 스왑될 수 있어 OOM 대상 역할이 약해질 수 있습니다.
구성
관측성
system.crash_log에 signal = 9이고
signal_description에 OOM Canary가 언급된 행이 생성됩니다: