대용량 AI 모델 파일을 S3 게이트웨이로 Ozone 에 올리는 클러스터에서 두 가지 자원 문제가 연달아 발생했다. 하나는 Ozone Recon 역할이 OutOfMemory 로 반복 종료되는 현상이고, 다른 하나는 업로드 도중 파일 디스크립터가 한도에 부딪히는 현상이다. 둘 다 워크로드가 만들어 내는 메타데이터·연결량에 비해 기본값이 작아서 생긴다.
Recon 은 OM 과 SCM 의 메타데이터 스냅샷을 받아 클러스터 상태의 오프라인 복사본을 구성하는 모니터링 서비스다. 키와 컨테이너 수가 늘면 처리할 메타데이터도 함께 커져 기본 힙으로는 버티지 못한다.
Cloudera Manager → Ozone → 구성에서 Java Heap Size of Ozone Recon 을 찾아 올린다. 기본값이 512MB 인 환경에서 1GB 로 올리는 정도로는 부족했고, 4096MiB 에서 비로소 기동을 유지했다. 클러스터가 크면 6~8GB 까지 잡는다.
설정이 실제로 반영됐는지는 프로세스 인자로 확인한다.
ps -ef | grep -i recon | grep -o '\-Xmx[0-9a-zA-Z]*'
-Xmx4096M 처럼 의도한 값이 떠야 한다. 예전 값 그대로면 저장만 하고 역할 재시작이나 재배포가 누락된 것이다.
기동 직후 로그에 SequenceNumber Lag from OM 값이 배치마다 찍힌다. 이 값이 꾸준히 줄고 있으면 죽는 중이 아니라 밀린 백로그를 따라잡는 중이다.
16542 → 14537 → 12535 → 10532 → 8530 → 6526 → 4523 → 2520
이 구간이 메모리 피크다. 힙이 작으면 catch-up 도중 다시 죽고, 재기동하면 또 처음부터 replay 하는 악순환에 빠진다. 한 번은 넉넉한 힙으로 lag 이 0 근처까지 완주시켜야 이후 정상 상태에서 메모리 사용량이 내려간다.
멀티파트 업로드가 많은 환경에서는 다음 WARN 이 다량으로 찍히지만 치명적이지 않다.
Expected value type: OmKeyInfo, Actual value type: OmMultipartKeyInfo
멀티파트 업로드 진행 중 키가 openKeyTable 과 multipartInfoTable 사이를 오가면서 델타 처리기가 기대한 타입과 어긋난 이벤트를 만나 해당 이벤트를 건너뛰는 것이다. 경고가 쏟아지는 동안에도 lag 이 줄고 있으면 처리는 정상 진행 중이며, 이 로그는 오히려 "지금 멀티파트 업로드가 매우 많다"는 워크로드 지표로 읽으면 된다.
수 GB 짜리 모델 파일(.safetensors, .onnx, model.onnx_data, pytorch_model.bin)은 S3 게이트웨이를 통해 멀티파트 업로드로 올라간다. 멀티파트는 파일 하나당 수많은 파트 엔트리를 openKeyTable · multipartInfoTable 에 만들고, Recon 이 뒤처진 뒤 재기동하면 이 거대한 백로그를 한꺼번에 replay 해야 한다. 초기 catch-up 구간의 메모리 피크를 기본 힙이 감당하지 못해 OOM 이 났다.
업로드 중 FD 가 폭증하면 먼저 어느 쪽이 한계인지 가른다.
cat /proc/sys/fs/file-nr # 할당 / 미사용 / 최대치
sysctl fs.file-max fs.nr_open
dmesg -T | grep -iE 'too many open files|VFS: file-max'
프로세스를 특정하고 한도와 실제 사용량을 비교한다.
PID=$(pgrep -f ozone | head -1)
grep 'Max open files' /proc/$PID/limits
ls /proc/$PID/fd | wc -l
ls -l /proc/$PID/fd | awk '{print $NF}' | sed -E 's/[0-9]+$//' | sort | uniq -c | sort -rn | head
socket:[ 이 다수면 연결 누수, 데이터 경로 파일이 다수면 스트림 미해제, pipe: · eventpoll 이 다수면 스레드·셀렉터 누수다. 업로드를 멈췄는데도 수치가 안 줄면 누수로 확정한다.
watch -n5 'ls /proc/'"$PID"'/fd | wc -l'
grep -i "Too many open files" /var/log/hadoop-ozone/*.log
한도를 올리는 방법은 대상에 따라 다르다.
Ozone 데몬(OM · SCM · DataNode · S3 Gateway)은 셸의 ulimit 이 먹지 않는다. CM Agent 가 supervisord 로 띄우기 때문이다. Cloudera Manager → Ozone → 구성에서 rlimit_fds(Maximum Process File Descriptors)를 역할별로 65536 이상으로 올리고 해당 역할을 재시작한다.
직접 실행하는 스크립트나 CLI 는 OS 레벨에서 올린다.
ulimit -n 65536 # 현재 세션
cat > /etc/security/limits.d/99-ozone.conf <<'EOF'
* soft nofile 1048576
* hard nofile 1048576
EOF
systemd 서비스는 limits.conf 를 무시하므로 유닛에 직접 지정한다.
[Service]
LimitNOFILE=1048576
systemctl daemon-reload && systemctl restart <service>
커널 상한보다 큰 값은 적용되지 않으므로 fs.nr_open 도 함께 확인하고, 필요하면 /etc/sysctl.conf 에 fs.nr_open=1048576 을 넣는다.
distcp 를 쓴다. ofs:// 사용 시 FileSystem 인스턴스를 재사용하고 OzoneClient · OzoneOutputStream 은 try-with-resources 로 닫는다.