🎥 AI 시대 옵저버빌리티 전략 웨비나 | 무료 다시보기 (~4/9)
Top
도입문의
테크
2026-09-03

디스크가 여유 있는데 No space left on device가 뜨는 이유와 해결 방법

디스크 용량이 50% 넘게 남아 있는데도 파일이 써지지 않는 장애를 겪어본 적이 있으신가요?

프로덕션 서버에 No space left on device 에러가 발생했지만, df -h로 확인한 디스크 사용률은 43%에 불과했습니다. 절반이 넘는 공간이 남아 있는데도 시스템은 디스크가 가득 찼다며 파일 생성과 작업 요청을 거부했습니다. 리눅스와 컨테이너 환경에서 생각보다 흔하게 일어납니다. No space left on device 에러의 원인이 바이트 단위의 디스크 용량 부족만은 아니기 때문입니다.

현장에서 먼저 확인할 원인은 크게 세 가지입니다.

원인을 좁히는 3단계 진단 절차

No space left on device가 발생했다면 다음 순서로 원인을 좁혀갑니다. 세 원인은 각각 발생할 수도 있고 동시에 겹칠 수도 있으므로, 한 가지 원인을 찾았더라도 나머지 항목까지 확인하는 편이 안전합니다.

No space left on device를 좁히는 3단계 진단 흐름. 1단계는 블록 공간을 df -h로, 2단계는 inode를 df -i로, 3단계는 삭제됐지만 프로세스가 붙잡고 있는 파일을 lsof | grep deleted로 확인한다. 왼쪽에서 오른쪽으로 갈수록 관찰 대상이 블록 → inode → 삭제된 열린 파일로 좁혀진다.
블록 → inode → 삭제된 열린 파일 순으로 관찰 대상을 좁히는 3단계 진단 흐름

1단계, df -h 로 블록 공간부터 본다

df -h

먼저 마운트 포인트별 디스크 사용률을 확인합니다. 루트(/) 또는 문제가 발생한 경로의 Use%가 100%에 가깝다면 실제 블록 공간이 부족한 상태입니다. 어느 디렉터리가 공간을 차지하고 있는지는 du로 추적합니다.

du -sh /* 2>/dev/null | sort -rh | head -20

주요 확인 대상은 로그 디렉터리(/var/log), 애플리케이션 경로(/opt, /app), 컨테이너 이미지 저장 경로(/var/lib/docker, /var/lib/containerd)입니다. 오래된 로그나 사용하지 않는 컨테이너 이미지가 쌓여 공간을 차지하는 경우가 많습니다.

2단계, df -i 로 inode를 본다

블록 공간이 충분한데 에러가 계속 발생한다면 inode를 확인합니다.

df -i

inode는 파일과 디렉터리 같은 파일시스템 객체의 메타데이터를 저장하는 자원입니다. 파일 크기와 관계없이 객체마다 inode가 필요하므로, 작은 파일이 대량으로 쌓이면 디스크 용량보다 inode가 먼저 바닥날 수 있습니다. IUse%가 100%에 가깝다면 새 파일을 만들 수 없습니다.

어느 디렉터리에 파일이 많이 몰렸는지는 아래 명령으로 추적합니다.

find / -xdev -printf '%h\n' 2>/dev/null | sort | uniq -c | sort -rn | head -20

운영 노드에서 파일이 이미 수백만 개인 상황이라면 루트(/) 전체를 바로 훑기보다 /var/lib/containerd, /var/lib/kubelet, /var/log, /tmp 등 의심 경로부터 범위를 좁혀 실행하는 편이 I/O 부담이 적습니다.

inode를 빠르게 소모하는 대표적인 원인은 다음과 같습니다.

  • PHP 세션 파일(/tmp, /var/lib/php/sessions)
  • npm·pip 등의 패키지 빌드 캐시
  • 정리되지 않은 컨테이너 레이어와 임시 파일

쿠버네티스 노드에서는 파드가 재시작될 때마다 새로운 컨테이너 writable layer가 생성됩니다. 사용이 끝난 레이어와 이미지가 제대로 정리되지 않으면 inode 사용량이 계속 늘어날 수 있습니다.

inode가 고갈되면 파일시스템을 다시 만들지 않는 한 inode 수 자체를 늘릴 수 없습니다. ext4는 포맷 시점에 inode 수가 고정되기 때문입니다. xfs는 동적으로 할당하지만 상한이 있습니다. 컨테이너 환경에서는 이미지를 정리하고(crictl rmi --prune 또는 docker image prune -a), 미사용 파드 레이어를 주기적으로 청소하는 것이 근본 예방책입니다.

💡 실무 팁: 파일이 수백만 개라 rm -rf가 안 될 때

inode 고갈 경로에서 파일 수가 너무 많으면 rm -rf * 실행 시 Argument list too long 에러가 발생합니다. 이 때는 find 명령어로 직접 인라인 삭제를 수행해야 합니다.

find /path/to/target -type f -delete

3단계, 삭제됐는데 프로세스가 붙잡고 있는 파일을 찾는다

df의 블록 사용률은 높은데 du로 확인한 실제 파일 용량과 차이가 난다면, 삭제됐지만 프로세스가 계속 사용 중인 파일을 의심해야 합니다.리눅스는 파일을 삭제해도 해당 파일을 열어 둔 프로세스가 있으면 실제 블록을 바로 반환하지 않습니다. 디렉터리에서 파일 이름만 사라지고, inode와 블록은 마지막 파일 핸들이 닫힐 때까지 유지됩니다. 로그 로테이션으로 파일을 삭제했지만 Java 프로세스가 이전 로그 파일을 계속 열어 두고 있는 경우가 대표적입니다.

lsof | grep deleted

출력에서 SIZE/OFF 값이 큰 항목을 찾습니다. Java 프로세스가 수십 GB의 로그 파일을 삭제된 상태로 점유하고 있는 경우가 있습니다. 해결 방법은 해당 프로세스를 재시작해 파일 핸들을 반환하는 것입니다.

# 특정 프로세스(예: java PID 1234)가 잡고 있는 삭제 파일 확인
lsof -p 1234 | grep deleted
    
# 프로세스 재시작으로 핸들 반환
systemctl restart <service-name>


💡 긴급 대응: 서비스 재시작을 당장 할 수 없는 상황이라면?

운영 중인 주요 Pod나 프로세스를 당장 재시작하기 어렵다면, /proc/{PID}/fd/{FD_NUMBER}에 빈 값을 비워 써서(truncate) 디스크 공간만 즉시 확보할 수 있습니다.

# lsof로 확인한 파일 디스크립터(FD) 번호가 4번인 경우
: > /proc/1234/fd/4

주의: 애플리케이션의 동작에 영향을 줄 수 있으므로 긴급 장애 조치 시에만 제한적으로 사용하고, 점검 시간에 프로세스를 정상 재시작해야 합니다.

진단 순서 한눈에 정리

세 원인은 독립적이지 않습니다. 블록 부족과 inode 고갈이 동시에 발생하기도 하고, 삭제된 파일이 블록을 잡고 있는 상태에서 새 파일이 쌓여 진짜 블록 부족이 중첩되기도 합니다.

진단 순서를 정리하면 아래와 같습니다.

  1. df -h → 블록 사용률 100%이면 du로 공간 추적, 오래된 파일 정리
  2. df -i → inode 사용률 100%이면 소형 파일 대량 생성 경로 확인 후 정리
  3. lsof | grep deleted → df 사용률이 높은데 du 합계와 맞지 않으면, 삭제됐지만 열려 있는 파일 확인 후 프로세스 처리
  4. 쿠버네티스 노드라면 → kubectl describe node로 DiskPressure부터 확인하고 어느 파일시스템인지 좁힌 뒤 1~3번을 적용

컨테이너 환경이라면 Pod 내부의 파일시스템 문제인지 노드 파일시스템 문제인지 분리해서 봐야 합니다. df는 실행한 마운트 네임스페이스에서 보이는 파일시스템 기준이고, lsof는 권한·PID 네임스페이스 제약 때문에 컨테이너 안에서 실행해도 노드 전체 상황을 보지 못합니다. 노드 수준의 inode 고갈이나 컨테이너 런타임 저장소 문제는 Pod 내부 df만으로는 확인되지 않을 수 있으니, kubectl exec로 Pod를 보되 노드에도 직접 들어가 확인합니다.

쿠버네티스에서 이 에러가 더 자주 나는 이유

컨테이너와 마이크로서비스 아키텍처(MSA)로 옮겨 가면서 한 노드가 다루는 파일 수가 크게 늘었습니다. 단일 애플리케이션이 수십 개의 마이크로서비스로 분리되면 각 서비스가 독립적으로 로그를 생성하고 임시 파일을 씁니다. 모놀리식 아키텍처에서 하나였던 로그 스트림이 MSA 환경에서는 수십 개로 늘어나는 셈입니다.

여기에 컨테이너 스토리지 계층이 문제를 더 복잡하게 만듭니다. 컨테이너 런타임에서 흔히 사용하는 OverlayFS 구성에서는 이미지 레이어와 컨테이너의 writable layer에 파일이 쌓이고, 이를 저장하는 실제 파일시스템(underlying filesystem)의 inode가 빠르게 소모될 수 있습니다. 수백 개의 컨테이너가 올라간 노드에서는 블록 공간이 넉넉해도 inode가 먼저 고갈될 수 있습니다. 쿠버네티스도 노드 압박(Node-pressure)을 판단할 때 블록 여유(nodefs.available)뿐 아니라 inode 여유(nodefs.inodesFree)를 함께 확인합니다.

많은 모니터링 대시보드는 바이트 단위의 디스크 사용률만 추적합니다. 그래서 inode 고갈은 뒤늦게 발견되기 쉽습니다.

쿠버네티스에서 호스트와 Pod 중 어디가 찼는지 구분하기

쿠버네티스 노드라면 앞의 df 흐름을 바로 적용하기 전에, 어느 파일시스템이 찼는지부터 구분하는 편이 빠릅니다. /var/lib/kubelet, 컨테이너 런타임 저장소, 이미지 저장소가 같은 디스크인지 분리돼 있는지에 따라 df 결과 해석이 달라지기 때문입니다. 쿠버네티스는 디스크 압박을 파일시스템별로 나눠 판단합니다.

쿠버네티스가 디스크 압박을 판단하는 세 파일시스템 구분. nodefs는 /var/lib/kubelet·Pod 로그·emptyDir 등 노드 ephemeral storage, imagefs는 컨테이너 이미지와 writable layer, containerfs는 컨테이너 writable layer를 담는다. 각 파일시스템은 블록 여유(available)와 inode 여유(inodesFree)를 따로 보며, inode만 바닥나도 DiskPressure가 걸린다.
쿠버네티스는 디스크 압박을 nodefs·imagefs·containerfs로 나눠 보고, 각 파일시스템의 블록·inode 여유를 따로 판단한다.

  • nodefs: /var/lib/kubelet, Pod 로그, emptyDir 등 노드의 ephemeral storage
  • imagefs: 컨테이너 이미지, 환경에 따라 writable layer
  • containerfs: 지원되는 구성에서 컨테이너 writable layer

각 파일시스템은 블록 여유(*.available)와 inode 여유(*.inodesFree)를 따로 봅니다. 블록이 남아 있어도 inode가 바닥나면 DiskPressure가 걸립니다.

노드에 들어가기 전에 먼저 이벤트를 확인합니다.

kubectl describe node <node-name>

Conditions에서 DiskPressure=True인지, 어느 파일시스템이 임계치를 넘었는지 확인한 다음 해당 노드에 들어가 앞의 3단계 진단 절차를 그대로 적용합니다.

재발 방지를 위해 모니터링과 자동화 사각지대 없애기

로그 로테이션 자동화와 컨테이너 이미지 주기 정리(logrotate 설정 강화, 쿠버네티스 노드의 이미지 가비지 컬렉션 정책)로 상당수 케이스는 예방됩니다. 다만 알림을 디스크 사용률(%) 하나로만 걸면 이 글의 세 원인 중 두 개를 놓칩니다. 평소에 함께 봐야 할 지표는 다음과 같습니다.

봐야 할 것 어디서 보나
블록 여유 서버 모니터링의 파일시스템 사용률
inode 여유 서버 모니터링의 inode 사용률
컨테이너 런타임 저장소 사용량 쿠버네티스 노드 디스크
노드 DiskPressure 상태 쿠버네티스 노드 맵

자동화가 완벽하게 동작한다는 믿음이 오히려 사각지대를 만들기도 합니다. 로그 로테이션이 파일을 삭제했는데 프로세스가 옛 파일 핸들을 계속 붙잡고 있는 상황이 대표적입니다. 자동화를 믿더라도 lsof | grep deleted 확인 단계는 남겨 두는 편이 안전합니다.

자주 묻는 질문

디스크 사용률이 낮은데 왜 파일이 안 만들어지나요?

블록은 남았어도 inode가 다 찼을 수 있습니다. inode는 파일 하나당 하나씩 쓰이는 자원이라 파일 크기와 무관합니다. 작은 파일이 수십만 개 쌓이면 용량은 여유가 있는데 새 파일만 못 만듭니다. df -i로 IUse%를 확인하면 바로 드러납니다.

파일을 지웠는데 용량이 그대로입니다

그 파일을 열어 둔 프로세스가 남아 있으면 리눅스는 블록을 돌려주지 않습니다. 디렉터리에서 이름만 지워지고 실제 데이터는 마지막 핸들이 닫힐 때까지 남습니다. 로그 로테이션이 파일을 지웠는데 애플리케이션이 옛 핸들을 계속 쥐고 있는 경우가 대표적입니다. lsof로 삭제 표시된 항목을 찾아 그 프로세스를 재시작하면 반환됩니다.

inode가 부족하면 늘릴 수 있나요?

ext4는 파일시스템을 만들 때 inode 수가 정해져서 나중에 늘릴 수 없습니다. xfs는 동적으로 할당하지만 상한이 있습니다. 그래서 대응은 늘리는 쪽이 아니라 줄이는 쪽입니다. 쌓인 작은 파일을 정리하고, 컨테이너 환경이라면 쓰지 않는 이미지와 레이어를 주기적으로 걷어냅니다.

rm 하려는데 Argument list too long이 뜹니다

지우려는 디렉터리에 파일이 너무 많으면 rm -rf *가 셸의 인자 길이 한계에 걸려 실행되지 않습니다. 이때는 find로 직접 지웁니다. find /경로 -type f -delete처럼 쓰면 셸이 파일 목록을 한꺼번에 펼치지 않아, 파일 수와 무관하게 동작합니다.

마치며

No space left on device가 발생했다고 해서 디스크 용량부터 늘릴 필요는 없습니다. 먼저 df -h로 실제 공간을 확인하고, 공간이 남아 있다면 df -i로 inode를 확인합니다. df와 du의 사용량이 맞지 않을 때는 lsof로 삭제됐지만 프로세스가 잡고 있는 파일까지 확인하면 원인을 상당 부분 좁힐 수 있습니다.

쿠버네티스 환경에서는 같은 문제가 특정 노드나 볼륨에서만 발생할 수 있습니다. 장애가 발생할 때마다 모든 노드에 접속하기보다 디스크 사용률과 inode 사용률을 평소 함께 모니터링해 두면, 어느 노드와 볼륨부터 확인해야 할지 빠르게 좁힐 수 있습니다.

와탭에서는 이 두 가지를 보는 곳이 나뉩니다. inode 사용률은 서버 모니터링이 파일시스템별로 수집하고, 파일시스템 운영 정보 화면에서 여러 서버의 마운트를 한 번에 볼 수 있습니다. 쿠버네티스는 노드 맵에서 노드의 DiskPressure 상태를 확인할 수 있습니다.

더 읽을거리

와탭 모니터링을 무료로 체험해보세요!