$ df -h /var/log
Filesystem Size Used Avail Use% Mounted on
/dev/mapper/vg-log 400G 400G 0 100% /var/log
$ du -sh /var/log
30G /var/log
보이는 파일을 다 더해도 30GB 인데 파일시스템은 400GB 를 다 썼다고 한다. 지울 것이 없어 보이는데 디스크는 꽉 차 있는 상태다.
du 는 디렉터리 트리를 훑어 이름이 붙어 있는 파일의 크기를 더한다. df 는 파일시스템이 실제로 할당한 블록을 센다. 이 둘이 벌어지는 경우는 정해져 있다.
| 원인 | 판별 |
|---|---|
| 지워졌지만 프로세스가 아직 열고 있는 파일 | lsof +L1 에 나온다. 가장 흔하다 |
| 마운트 지점 아래에 가려진 파일 | 마운트를 풀면 보인다 |
| 다른 파일시스템이 그 아래 마운트돼 있다 | du -x 로 비교한다 |
| 예약 블록 | tune2fs -l 의 reserved block count. 보통 5% |
로그 파일을 rm 하거나 로테이션이 잘못 돌아가면, 쓰고 있던 프로세스가 그 파일을 닫을 때까지 블록이 반환되지 않는다. 디렉터리에서 이름만 사라지므로 du 에는 잡히지 않는다.
lsof +L1 | awk '$5=="REG"' | sort -k7 -n -r | head -20
NLINK 열이 0 인 것이 그런 파일이다. 출력 예에서 SIZE/OFF 열이 잡고 있는 용량이다.
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NLINK NODE NAME
nginx 1234 root 4w REG 253,3 5.0e+10 0 123 /var/log/nginx/access.log (deleted)
되돌리는 방법은 둘이다.
# 방법 1 — 프로세스를 재시작한다. 파일이 닫히며 공간이 반환된다
systemctl restart nginx
# 방법 2 — 중단 없이 내용만 비운다. lsof 의 PID 와 FD 번호를 쓴다
: > /proc/1234/fd/4
방법 2 는 프로세스를 건드리지 않으므로 서비스 중단이 없다. 다만 그 파일에 남아 있던 로그는 사라진다. 쓰기 중인 파일을 rm 하지 말고 truncate -s 0 또는 : > 로 비우는 습관을 들이면 애초에 이 상황이 생기지 않는다.
저널은 별도 한도로 관리되므로 du 로 보고 놀라는 일이 잦다.
journalctl --disk-usage
journalctl --vacuum-size=5G
journalctl --vacuum-time=14d
영구 설정은 /etc/systemd/journald.conf 의 SystemMaxUse 에 둔다.
노드에서는 재시작으로 해결하는 선택지가 좁다. containerd 나 kubelet 을 함부로 재시작하면 노드 위의 파드가 함께 영향을 받는다. 확인 순서를 바꾼다.
# 무엇이 차지하고 있는가
du -xh --max-depth=1 /var/lib/containerd /var/log | sort -h | tail
crictl images
crictl ps -a
# 컨테이너 런타임이 잡고 있는 삭제된 파일
lsof +L1 | grep -E 'containerd|dockerd|kubelet'
컨테이너 로그는 /var/log/pods 와 /var/log/containers 아래에 있고 대개 심볼릭 링크다. 지우면 로테이션이 꼬이므로 kubelet 의 containerLogMaxSize · containerLogMaxFiles 로 한도를 정하는 것이 맞는 대응이다. 당장 공간이 필요하면 쓰지 않는 이미지를 먼저 지운다.
crictl rmi --prune
노드를 cordon 하고 drain 한 뒤 정리하면 파드 영향 없이 손댈 수 있다.
lsof 와 /proc 으로 프로세스 상태 보기.