
리눅스 시스템 기초 · 입문편 — 본문의 "N편"은 입문 과정 번호다. 번호별 글과 전체 250편 구성은 통합 로드맵에서 확인할 수 있다.
리눅스 시스템 기초 40 / 50 · Part 4. 프로세스·서비스·리소스
실습 환경: Rocky Linux 9 (10.0.0.200) ·du·df·lsof +L1재현은 실습 환경 실제 출력
이전 글: 39. CPU · Memory 관리
Part 4의 마지막 글은 디스크다. 디스크 가득 참은 서버 장애 원인 중 가장 흔한 것 중 하나지만, 관제 관점에서는 더 심각한 의미가 있다.
/var가 가득 차면 로그가 기록되지 않는다. 인증 로그, 감사 로그, 웹 로그가 모두 /var/log 아래에 쌓이기 때문이다. 로그가 끊긴 시간 동안 일어난 일은 나중에 분석할 방법이 없다. 그래서 디스크 사용량은 가용성 지표이면서 동시에 보안 가시성 지표 다.
이번 글에서 답할 질문:
df와 du는 무엇이 다르고, 왜 결과가 다를 수 있는가?| 도구 | 기준 | 보여주는 것 |
|---|---|---|
df -h | 파일시스템 전체 (슈퍼블록 정보) | 파티션별 크기·사용·남은 용량·마운트 지점 |
df -i | 파일시스템의 inode | 전체·사용·남은 inode 개수 |
du -sh DIR | 디렉터리를 순회 하며 파일 크기 합산 | 특정 디렉터리가 차지한 용량 |
df는 "전체적으로 얼마나 찼나", du는 "어디가 크냐"를 본다.
파일은 inode(메타데이터) + 데이터 블록 으로 저장된다. 둘 다 개수가 정해져 있으므로 어느 쪽이든 다 떨어지면 새 파일을 만들 수 없다.
| 고갈 대상 | 증상 | 흔한 원인 |
|---|---|---|
| 블록 | df -h Use% 100% | 큰 로그, 백업, 코어 덤프 |
| inode | df -h는 여유, df -i IUse% 100% | 세션·캐시·메일 큐 같은 작은 파일 대량 |
rm은 디렉터리에서 이름(하드 링크) 을 지울 뿐이다. 커널은 링크 수가 0이고, 동시에 그 파일을 연 프로세스가 없을 때 비로소 inode와 데이터 블록을 해제한다. 로그 파일을 쓰고 있는 데몬이 있는 상태에서 로그를 rm하면:
du: 디렉터리에 이름이 없으니 안 보인다.df: 블록은 아직 해제되지 않았으니 사용량 그대로.
실습 환경에서 200MB 파일을 연 채로 삭제해 재현한 실제 출력이다.
# 200MB 파일 생성 후, 백그라운드 프로세스가 열어 둠
$ dd if=/dev/zero of=/tmp/big.log bs=1M count=200
$ (exec 3</tmp/big.log; sleep 20) &
$ df -h /tmp # rm 전
/dev/vda 252G 13G 30G 29% /
$ rm /tmp/big.log
$ df -h /tmp # rm 후 — 줄지 않음
/dev/vda 252G 13G 30G 29% /
$ lsof -nP +L1 # 링크 수(NLINK) 0 인데 열린 파일
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NLINK NODE NAME
sleep 778 root 3r REG 254,0 209715200 0 950303 /tmp/big.log (deleted)
$ kill %1 # 파일을 연 프로세스 종료
$ df -h /tmp
/dev/vda 252G 12G 30G 29% / ← 이제 해제됨
NLINK 0 + (deleted)가 핵심이다. 실제 운영에서는 로그를 rm한 뒤 rsyslog·httpd 같은 데몬을 재시작하지 않아 같은 일이 생긴다. 그래서 로그는 rm이 아니라 logrotate 로 관리하고, 급하게 비워야 한다면 : > file(truncate)로 이름은 두고 내용만 비운다.
du로 "어디가 큰가"를 찾는 실제 예(테스트 디렉터리)다.
$ du -sh dlab/* | sort -rh
121M dlab/logs
36M dlab/app
68K dlab/cache ← 빈 파일 3000개: 용량은 거의 없지만 inode 3000개 사용
cache는 용량으로는 작지만 inode를 3000개 쓴다. 이런 디렉터리가 수백만 개로 불어나면 df -h는 여유가 있는데도 파일을 만들 수 없게 된다.
# 1) 파티션별 사용량과 inode
df -hT
df -i
# 2) 큰 디렉터리 찾기 (-x: 다른 파일시스템으로 넘어가지 않음)
sudo du -xh --max-depth=1 / 2>/dev/null | sort -rh | head
sudo du -xh --max-depth=1 /var | sort -rh | head
sudo du -sh /var/log/* | sort -rh | head
# 3) 큰 파일 찾기 (22편)
sudo find / -xdev -type f -size +500M -exec ls -lh {} \; 2>/dev/null
# 4) inode 를 많이 쓰는 디렉터리
sudo find /var -xdev -type f | cut -d/ -f1-4 | sort | uniq -c | sort -rn | head
# 5) 삭제됐지만 열린 파일
sudo lsof -nP +L1
sudo lsof -nP +L1 | awk 'NR>1 {s+=$7} END {printf "%.1f MB\n", s/1024/1024}'
# 6) 삭제된 열린 파일 비우기 (프로세스 재시작이 어려울 때)
sudo ls -l /proc/<PID>/fd | grep deleted
sudo sh -c ': > /proc/<PID>/fd/<N>'
# 7) journal 용량
journalctl --disk-usage
sudo journalctl --vacuum-size=500M
# 8) logrotate 설정·수동 실행 테스트
cat /etc/logrotate.conf; ls /etc/logrotate.d/
sudo logrotate -d /etc/logrotate.d/httpd # dry-run
아래 이상 상황 출력은 형식 설명용 예시다.
① /var 가득 참
$ df -h /var
Filesystem Size Used Avail Use% Mounted on
/dev/mapper/rl-var 10G 10G 0 100% /var
$ sudo du -xh --max-depth=1 /var/log | sort -rh | head -3
8.9G /var/log
7.2G /var/log/httpd
1.1G /var/log/audit
/var/log/httpd가 7.2GB다. ls -lh로 보면 access_log 하나가 수 GB라면, 짧은 시간에 대량 요청(스캐닝·DoS, 25편)이 있었거나 logrotate가 동작하지 않은 것이다.
② inode 고갈
$ df -h /var → Use% 41%
$ df -i /var → IUse% 100%
$ sudo find /var -xdev -type f | cut -d/ -f1-4 | sort | uniq -c | sort -rn | head -1
4812331 /var/lib/php
PHP 세션 파일이 480만 개다. 세션 정리(gc)가 동작하지 않았거나, 대량의 자동화 요청이 세션을 계속 만든 경우다.
| 상황 | 보안 의미 |
|---|---|
/var 100% | 로그 기록 중단 → 분석 공백. auditd는 설정(disk_full_action)에 따라 기록 중지·시스템 정지 |
| 의도적 디스크 채우기 | 공격자가 로그를 못 남기게 대용량 파일 생성 (T1499 / 방어 회피) |
(deleted) 열린 파일 | 공격자가 실행 후 지운 도구·로그. 프로세스가 살아 있으면 /proc/PID/fd/N, /proc/PID/exe에서 복구 가능 (29편) |
| 큰 숨김 파일 | /tmp/.x/ 등에 수집한 데이터 압축본 → 유출 준비 (T1074 Data Staged) |
| 로그 급증 | 무차별 대입(49편), 웹 스캔(25편)의 부산물 |
auditd의 /etc/audit/auditd.conf에는 space_left_action, disk_full_action이 있다. 보안 등급이 높은 서버는 감사 로그를 못 쓰면 시스템을 멈추도록(halt) 설정하기도 한다. 가용성과 추적성 중 무엇을 우선할지 정책으로 정해야 한다.
{
df -hT; df -i
sudo du -xh --max-depth=1 /var/log | sort -rh | head
sudo lsof -nP +L1 2>/dev/null
sudo find /tmp /var/tmp /dev/shm -xdev -type f -size +50M -ls 2>/dev/null
} > /root/evidence/disk_$(date +%Y%m%d_%H%M).txt
[Alert] SIEM: 서버 A 로그 수신 02:40 이후 중단 / 모니터링: /var 100%
↓
[df/du] /var/log/httpd/access_log 7GB, 02:10 이후 급증
↓
[원인] access_log: 단일 IP 초당 수백 요청, 디렉터리 스캔 패턴 (25편)
↓
[lsof] +L1 → 관리자가 rm 한 access_log.1 (deleted) 4GB → httpd reload 로 해제
↓
[공백] 02:40~03:10 로컬 로그 없음 → SIEM 에 먼저 전송된 로그와 방화벽 로그로 보완
↓
[Response] IP 차단, logrotate 크기 기준 추가, /var/log 별도 파티션·용량 경보
df는 파일시스템 전체, du는 디렉터리 순회 합계 다. df -i로 inode도 본다.rm해도 열린 파일은 해제되지 않는다 → lsof +L1로 찾고 프로세스 재시작 또는 truncate.rm이 아니라 logrotate, 급할 땐 : > file./var가 차면 로그 기록이 멈춘다 — 디스크는 보안 가시성 지표다.(deleted) 열린 파일은 증거 복구 지점이기도 하다.Part 4(프로세스·서비스·리소스)를 마쳤다. 프로세스가 어떻게 태어나고(31·35), 어떻게 관리되며(36~38), 어떤 자원을 쓰는지(33·39·40) 정리했다.
다음 글 「41. Linux 네트워크 구조」 부터 Part 5(네트워크·보안·SOC)가 시작된다. 애플리케이션의 데이터가 소켓 → 커널 TCP/IP 스택 → 네트워크 인터페이스를 거쳐 나가는 경로와, 그 경로 위에서 방화벽·로그·관제가 어디에 위치하는지를 먼저 그려 본다.
+L1 — https://man7.org/linux/man-pages/man8/lsof.8.html