운영 중인 Node API 파드가 새벽 3~4시쯤 되면 재시작돼 있습니다. kubectl describe pod로 보면 Last State: Terminated, Reason: OOMKilled가 찍혀 있어요. 메모리 limit은 512Mi로 잡았습니다. 낮에는 트래픽이 더 많은데 멀쩡하고 왜 하필 한가한 새벽에 죽는지 모르겠습니다.
운영 중인 Node API 파드가 새벽 3~4시쯤 되면 재시작돼 있습니다. kubectl describe pod로 보면 Last State: Terminated, Reason: OOMKilled가 찍혀 있어요. 메모리 limit은 512Mi로 잡았습니다. 낮에는 트래픽이 더 많은데 멀쩡하고 왜 하필 한가한 새벽에 죽는지 모르겠습니다.
댓글 6
"한가한 새벽에 죽는다"가 결정적 힌트입니다. 트래픽이 아니라 새벽에 도는 무언가(배치·크론)가 메모리를 순간적으로 크게 쓰는 패턴이 거의 확실해요.
먼저 확인할 것- 새벽에 도는 작업이 있나? — 정산·리포트·대용량 조회·로그 정리 같은 크론잡. 이런 게 한 번에 큰 배열을 메모리에 올리면 순간 피크가 512Mi를 넘겨 OOMKilled 됩니다.
- 메모리 추이 그래프 — Grafana 등에서 파드 메모리를 보면 새벽에 톱니처럼 치솟는 지점이 보일 겁니다.
흔한 원인 — 대량 데이터를 한꺼번에 로드당장의 완화--max-old-space-size=384(512Mi 컨테이너 기준). 이러면 OOMKilled(강제 종료) 대신 Node가 먼저 GC를 돌려 버티거나 명시적 에러를 냅니다.아주 좋은 판단입니다. 배치를 API 파드와 분리하는 게 정답이에요. 같은 파드에서 돌리니 API가 배치 메모리에 휩쓸려 죽는 거니까요.
CronJob으로 빼고, 거기만 메모리를 넉넉히 줍니다. API 파드는 512Mi 그대로 두고요.이러면 배치가 무거워져도 API 서비스는 안전하고, 배치 파드는 작업 끝나면 사라져서 평소 자원도 안 씁니다. API와 배치의 자원·장애를 분리하는 건 운영에서 아주 중요한 원칙이에요.
동시 실행 방지는
concurrencyPolicy로 잡습니다.startingDeadlineSeconds로 너무 늦게 시작되는 잡은 아예 건너뛰게 합니다.대개 Forbid가 안전합니다. 그리고 잡 자체를 멱등하게(같은 날 두 번 돌아도 결과가 같게) 짜두면 겹쳐 돌아도 데이터가 안 깨져서 더 든든합니다.