[리눅스 운영] 디스크 용량 부족 (df 100%) 해결 - 5가지 원인과 체계적 진단법 (DBA 환경 특화)
테스트 환경: Oracle Linux 8 / RHEL 8 / Ubuntu 22.04
새벽 3시. "ALERT: /u01 사용량 98%" 모니터링 알람이 옵니다. 1시간 후엔 운영 DB가 멈출 수 있습니다. 어디부터 정리해야 할까요?
리눅스 디스크 용량 부족은 가장 흔하면서도 가장 위험한 운영 장애입니다. 무작정 rm을 휘두르면 서비스가 죽고, 가만히 있으면 디스크가 100%가 됩니다. 더 까다로운 점은 다음 같은 상황이 자주 발생한다는 것입니다.
- df는 100%인데 du로 보면 공간이 안 차 있음 (어디가 차지하고 있는 건가?)
- 큰 파일을 지웠는데 공간이 회수되지 않음
- 공간은 있는데 "No space left on device" 발생
- 어제 정리했는데 오늘 또 가득 참
이번 글은 리눅스 디스크 용량 부족을 체계적으로 진단하는 3단계 명령부터 시작해서, 5가지 원인별 해결법, 그리고 오라클 운영 환경의 특화 케이스까지 정리했습니다.
장애 대응 중이라면 긴급 응급 처치부터 보세요.
★ 3차원 진단 명령 - 모든 시작점
대부분의 사람이 df -h만 보고 끝내는데, 이것만으로는 진단 못 하는 케이스가 30% 이상입니다. 반드시 3가지를 함께 확인하세요.
# 1) 블록 사용량 - 가장 흔한 진단
df -h
# 2) inode 사용량 - 작은 파일 많을 때 (자주 놓침)
df -i
# 3) 삭제됐는데 프로세스가 잡고 있는 파일 (df와 du가 다를 때)
sudo lsof +L1
결과 해석법
증상 원인
| df -h 100% + du도 큼 | 일반 디스크 부족 (원인 1, 3, 5 참고) |
| df -h 100% + du는 작음 | 삭제됐는데 잡힌 파일 (원인 2 참고) |
| df -h 여유 + 쓰기 실패 | inode 고갈 (원인 4 참고) |
| df -i 100% | inode 고갈 확정 |
이 표를 책갈피해 두면 진단 시간이 절반으로 줄어듭니다.
★ df vs du 차이 - 왜 결과가 다른가
운영자가 가장 헷갈리는 부분입니다. 둘은 완전히 다른 방식으로 디스크를 측정합니다.
명령 측정 방법 보는 것
| df | 파일시스템 superblock 카운터 | 할당된 모든 블록 (삭제된 파일 점유 포함) |
| du | 디렉토리 트리 순회 | 실제 접근 가능한 파일 |
이 구조적 차이 때문에 다음 같은 결과 차이가 발생합니다.
# df는 100% 사용 중이라고 함
$ df -h /
Filesystem Size Used Avail Use% Mounted on
/dev/sda1 50G 50G 0 100% /
# du로 합산하면 30GB만 있음
$ sudo du -sh /
30G /
# 차이 20GB는 어디로? → 삭제됐는데 프로세스가 잡고 있는 파일!
$ sudo lsof +L1
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NLINK NODE NAME
java 1234 app 77w REG 8,1 21474836 0 ... /var/log/app.log (deleted)
핵심: df와 du가 다르면 거의 100% "deleted but open" 파일입니다. 이게 ORA-12541(리스너 죽음)의 listener.log 4GB 케이스와 같은 메커니즘이에요.
긴급 응급 처치 (5분 안에)
운영 환경이 멈출 위기라면 즉시 다음을 시도하세요. 안전한 순서대로 정리했습니다.
# 1) 가장 큰 로그/임시 디렉토리 빠르게 확인
sudo du -sh /var/log /tmp /var/tmp /var/cache 2>/dev/null
# 2) journal 로그 7일치만 남기고 정리 (가장 안전)
sudo journalctl --vacuum-time=7d
# 3) 패키지 캐시 정리
sudo dnf clean all # RHEL/Oracle Linux
sudo apt clean # Ubuntu/Debian
# 4) 30일 이상 된 압축 로그 정리
sudo find /var/log -name "*.gz" -mtime +30 -delete
# 5) 100MB 이상 파일 식별 (지우기 전 검토 필수)
sudo find / -xdev -type f -size +100M -exec ls -lh {} \; 2>/dev/null | sort -k5 -rh | head -20
이 5단계로 보통 5~20GB는 즉시 회수됩니다. 그 후 원인을 진단해서 재발 방지를 하세요.
원인 1: 로그 파일 미회전 (가장 흔함)
리눅스 디스크 부족의 50% 이상이 이 케이스입니다.
진단 방법
# /var/log에서 가장 큰 파일 찾기
sudo du -sh /var/log/* | sort -rh | head -10
# 1GB 이상 로그 파일 식별
sudo find /var/log -type f -size +1G -exec ls -lh {} \;
해결 방법
1) 활성 로그 파일은 안전하게 truncate
# ❌ 위험: 단순 rm은 deleted but open 상태 만듦
rm /var/log/huge.log
# ✅ 안전: truncate (프로세스가 파일을 열어둔 채 유지)
sudo truncate -s 0 /var/log/huge.log
# 또는
sudo : > /var/log/huge.log
2) logrotate 강제 실행
sudo logrotate -f /etc/logrotate.conf
3) 오래된 회전 로그 정리
# 30일 이상 된 압축 로그 삭제
sudo find /var/log -name "*.gz" -mtime +30 -delete
sudo find /var/log -name "*.log.[0-9]*" -mtime +30 -delete
재발 방지 - logrotate 표준 설정
/var/log/app/*.log {
daily
rotate 14
compress
delaycompress
missingok
notifempty
copytruncate # ← 활성 파일도 안전하게 처리
size 100M
}
copytruncate 옵션이 핵심입니다. 이게 없으면 회전 후에도 프로세스가 옛 파일을 잡고 있어서 공간이 회수되지 않습니다.
★ 원인 2: 삭제됐는데 프로세스가 잡고 있는 파일 (deleted but open)
가장 까다로운 케이스입니다. 단순 rm으로는 해결 안 됩니다.
무엇이 문제인가
리눅스에서 rm은 파일을 디렉토리 트리에서만 제거합니다. 프로세스가 그 파일을 열고 있으면 inode와 디스크 블록은 그대로 유지됩니다. 프로세스가 닫거나 재시작될 때까지 공간이 회수되지 않습니다.
진단 방법
# 삭제된 채 열려 있는 파일 모두 확인
sudo lsof +L1
# 또는 "(deleted)" 키워드로 검색
sudo lsof | grep '(deleted)'
# 가장 큰 deleted 파일 찾기
sudo lsof +L1 | awk '{print $7, $NF}' | sort -rn | head -10
출력 예시:
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NLINK NODE NAME
java 1234 app 77w REG 8,1 21474836480 0 1283397 /var/log/app.log (deleted)
oracle 25575 oracle 33 REG 65,65 4294983680 0 31014933 /oradata/file.dbf (deleted)
해결 방법
방법 1: 프로세스 재시작 (가장 안전)
# nginx의 경우 - USR1 시그널로 로그 reopen (재시작 없이 처리)
sudo kill -USR1 $(cat /var/run/nginx.pid)
# 일반 systemd 서비스 재시작
sudo systemctl restart <service-name>
방법 2: /proc 파일 디스크립터를 통한 truncate (재시작 불가 시)
# lsof 출력에서 PID와 FD 번호 확인 후
# 예: PID 1234, FD 77
sudo truncate -s 0 /proc/1234/fd/77
# 또는
echo > /proc/1234/fd/77
위험 경고: 이 방법은 로그 파일에만 안전합니다. 데이터베이스 파일이나 진행 중인 작업 파일에 사용하면 데이터 손실 또는 손상이 발생합니다. 반드시 (deleted) 표시와 파일 종류를 먼저 확인하세요.
자주 발생하는 시나리오
- Java 애플리케이션의 로그 파일: 무한 append 후 rm
- 오라클 listener.log: 누군가 rm 했지만 리스너 프로세스가 잡고 있음
- nginx access.log: logrotate 미설정 환경에서 수동 rm
- rsyslog 로그: 회전 설정 오류
운영 환경에서 디스크 공간이 갑자기 회수되지 않는다면 99% 이 케이스입니다.
원인 3: 패키지 캐시 / 옛 커널 / Docker 누적
운영 시간이 길어질수록 자연 증가하는 영역입니다.
진단 방법
# 패키지 캐시
sudo du -sh /var/cache/dnf /var/cache/apt 2>/dev/null
# 설치된 커널 목록
rpm -q kernel # RHEL/Oracle Linux
dpkg --list | grep linux-image # Ubuntu
# Docker (있다면)
sudo docker system df
해결 방법
# 1) 패키지 캐시 정리
sudo dnf clean all # RHEL/Oracle Linux
sudo apt clean # Ubuntu/Debian
# 2) 옛 커널 정리 (현재 부팅 커널은 절대 삭제 금지)
uname -r # 현재 커널 확인 후
# RHEL/Oracle Linux: 최신 2개만 유지
sudo dnf remove --oldinstallonly --setopt installonly_limit=2 kernel-core
# Ubuntu
sudo apt autoremove --purge
# 3) Docker 정리 (있다면)
sudo docker system prune -a --volumes
★ 원인 4: inode 고갈 (df는 여유, 쓰기 실패)
이 케이스는 한국어 자료에 깊이 다루지 않는 영역입니다. 공간은 충분한데 새 파일 생성이 실패하는 황당한 상황입니다.
무엇이 문제인가
리눅스 파일시스템은 파일/디렉토리마다 inode 1개를 사용합니다. 작은 파일이 수백만 개 있으면 블록은 여유롭지만 inode가 고갈됩니다.
진단 방법
# inode 사용량 확인
df -i
# IUse%가 100% 또는 95% 이상이면 고갈
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/sda1 3276800 3276800 0 100% /
# 어느 디렉토리에 파일이 많은지 찾기
sudo find / -xdev -type d 2>/dev/null -exec sh -c '
count=$(ls -A "$1" 2>/dev/null | wc -l)
echo "$count $1"
' _ {} \; | sort -rn | head -20
자주 발생하는 시나리오
- 메일 큐: /var/spool/postfix/ 또는 /var/spool/mail/
- PHP 세션 파일: /tmp 또는 /var/lib/php/session
- 애플리케이션 캐시: 사용자별 캐시 파일이 누적
- 잘못 구성된 cron: 매 분마다 작은 파일 생성
해결 방법
# 1) 메일 큐 정리 (postfix)
sudo postsuper -d ALL
# 2) PHP 세션 정리
sudo find /var/lib/php/session -type f -mtime +7 -delete
# 3) 임시 파일 정리
sudo find /tmp -type f -atime +7 -delete
# 4) 특정 디렉토리의 오래된 작은 파일 일괄 삭제
sudo find /path/to/dir -type f -mtime +30 -delete
주의: 파일이 너무 많아 find -delete도 느릴 수 있습니다. rm -rf /path/to/dir/*은 "argument list too long" 에러가 날 수 있으니 find 사용을 권장합니다.
원인 5: 마운트가 가려진 디렉토리 (Hidden Mount)
드물지만 한 번 발생하면 미궁에 빠지는 케이스입니다.
무엇이 문제인가
마운트 포인트로 사용된 디렉토리에 이미 파일이 있던 상태에서 마운트하면, 원래 파일들이 보이지 않지만 공간은 계속 차지합니다.
# 예시: /data 디렉토리에 100GB 파일이 있는 상태에서
# 그 위에 새 디스크를 마운트
sudo mount /dev/sdb1 /data
# 이제 ls /data 하면 새 디스크 내용만 보임
# 하지만 원래 100GB는 여전히 디스크 어딘가에 존재
진단 방법
# 마운트 정보 확인
sudo lsblk
mount | column -t
# 마운트 풀고 원래 내용 확인
sudo umount /data
ls -la /data
# 큰 파일이 보이면 그게 숨겨져 있던 것
해결 방법
# 1) 일시 umount 후 정리
sudo umount /data
sudo rm -rf /data/* # ← 원래 숨겨져 있던 파일들 삭제
# 2) 다시 마운트
sudo mount /dev/sdb1 /data
이런 사고를 막으려면 마운트 포인트로 사용할 디렉토리는 빈 폴더로 만들어 두는 것이 표준입니다.
★ 오라클 운영 환경 특화 케이스
DBA가 운영하는 환경에서 자주 발생하는 디스크 고갈 시나리오입니다. 본 블로그의 다른 글들과 직접 연결되는 영역이에요.
1) listener.log가 4GB 초과
ORA-12541 글에서 다룬 케이스. Windows 32bit 또는 일부 파일시스템 환경에서 listener.log가 4GB를 넘으면 리스너가 죽고 디스크도 차지합니다.
# 위치 확인
lsnrctl status | grep "Listener Log File"
# 안전한 정리 (리스너 중지 후)
lsnrctl stop
mv listener.log listener.log.$(date +%Y%m%d).bak
lsnrctl start
# 영구 해결: ADRCI로 자동 로테이션
adrci
ADRCI> set home diag/tnslsnr/{호스트명}/listener
ADRCI> set control (SHORTP_POLICY = 168, LONGP_POLICY = 720)
2) alert log와 trace 파일 누적
오라클은 /u01/app/oracle/diag 아래에 진단 파일을 계속 쌓아갑니다.
# 진단 디렉토리 사용량 확인
sudo du -sh /u01/app/oracle/diag/*
# ADRCI로 일괄 정리 (가장 안전)
adrci
ADRCI> show homes
ADRCI> set home diag/rdbms/proddb/PRODDB
ADRCI> purge -age 10080 -type trace # 7일 이상 trace 삭제 (분 단위)
ADRCI> purge -age 43200 -type alert # 30일 이상 alert 삭제
ADRCI> purge -age 10080 -type incident
3) archive log 폭증
# 위치 확인
sqlplus / as sysdba
SQL> SHOW PARAMETER log_archive_dest
# 사용량 확인
sudo du -sh /u01/app/oracle/fast_recovery_area/
# RMAN으로 안전하게 정리
rman target /
RMAN> CROSSCHECK ARCHIVELOG ALL;
RMAN> DELETE EXPIRED ARCHIVELOG ALL;
RMAN> DELETE ARCHIVELOG ALL COMPLETED BEFORE 'SYSDATE-7';
경고: archive log를 무작정 rm으로 삭제하면 RMAN 카탈로그와 불일치가 발생합니다. 반드시 RMAN으로 처리하세요.
4) datapump dump 파일 누적
# 보통 위치
sudo ls -lh /backup/dpdump/*.dmp
# 30일 이상된 백업 자동 정리 스크립트
sudo find /backup/dpdump -name "*.dmp" -mtime +30 -delete
sudo find /backup/dpdump -name "*.log" -mtime +30 -delete
절대 하지 말아야 할 것 ★
장애 대응 중 다급해서 실수하기 쉬운 위험 작업들입니다.
❌ 1: rm -rf /* 패턴
# 변수가 비어 있으면 / 전체가 삭제됨
rm -rf $UNDEFINED_VAR/* # ← 절대 금지
rm -rf /tmp /var/tmp /... # 공백 실수 위험
항상 변수에 값이 있는지 먼저 확인하고, 절대 경로를 사용하세요.
❌ 2: 활성 데이터파일에 truncate
# 절대 금지: 운영 DB 데이터파일
sudo truncate -s 0 /u01/oradata/PRODDB/system01.dbf
# → DB 손상, 복구 불가
truncate -s 0은 로그 파일에만 사용하세요.
❌ 3: archive log 직접 삭제
# 절대 금지
rm /u01/app/oracle/fast_recovery_area/PRODDB/archivelog/*.arc
# → RMAN 카탈로그 불일치, 복구 시 실패
RMAN으로 처리해야 합니다.
❌ 4: 현재 부팅 커널 삭제
# 위험
sudo dnf remove kernel-$(uname -r)
# → 재부팅 시 부팅 실패
설치 커널 정리 시 반드시 uname -r로 현재 커널을 확인하세요.
❌ 5: /etc, /usr 임의 삭제
시스템 동작에 필수입니다. 큰 파일이 있어도 절대 건드리지 마세요.
재발 방지 - 자동 모니터링 + 알림
장애를 막는 가장 좋은 방법은 85% 차기 전에 알림을 받는 것입니다.
간단한 모니터링 스크립트
#!/bin/bash
# /home/oracle/scripts/disk_monitor.sh
THRESHOLD=85
MAIL_TO="dba@company.com"
df -h | awk -v threshold=$THRESHOLD '
NR > 1 && $5 != "" {
use = int(substr($5, 1, length($5)-1))
if (use >= threshold) {
print $6 " is " $5 " used (threshold " threshold "%)"
}
}' | while read line; do
echo "$line" | mail -s "DISK ALERT: $(hostname)" $MAIL_TO
done
# inode 점검도 함께
df -i | awk -v threshold=$THRESHOLD '
NR > 1 && $5 != "" {
use = int(substr($5, 1, length($5)-1))
if (use >= threshold) {
print $6 " inode " $5 " used"
}
}'
# cron에 등록 (5분마다)
*/5 * * * * /home/oracle/scripts/disk_monitor.sh
자동 정리 cron (안전한 영역만)
# 매일 새벽 2시 - journal, 패키지 캐시, 30일 로그
0 2 * * * journalctl --vacuum-time=7d
0 2 * * * find /var/log -name "*.gz" -mtime +30 -delete
# 매주 일요일 - ADRCI 오라클 진단 파일 정리
0 3 * * 0 /home/oracle/scripts/adrci_cleanup.sh
마무리
리눅스 디스크 용량 부족은 단순한 "쓰레기 정리" 작업이 아니라, 체계적 진단이 필요한 운영 사고입니다. 핵심을 한 번 더 정리하면:
- 3차원 진단 (df -h, df -i, lsof +L1) — 한 가지만 보지 말 것
- df와 du가 다르면 deleted-but-open 파일 — 단순 rm으로 안 풀림
- 로그 파일은 truncate -s 0 — rm보다 안전
- inode 고갈 확인 필수 — 공간 있어도 쓰기 실패
- 오라클 환경은 ADRCI / RMAN으로 정리 — 직접 rm 금지
운영 환경의 디스크 고갈을 근본적으로 막으려면 logrotate 표준화 + ADRCI 자동 정리 + 85% 임계값 모니터링 세 가지를 갖춰 두세요. 이 셋만 있으면 디스크 관련 새벽 호출의 90%는 사라집니다.
비슷한 케이스를 겪으셨거나, 더 좋은 재발 방지 패턴이 있다면 댓글로 공유해 주세요.