디스크가 가득 찼을 때

등록일: 2014. 09. 12

데브옵스: 개발자, QA, 관리자가 함께 보는 리눅스 서버 트러블슈팅 기법

  • 카일 랜킨 지음
  • 조남웅, 주성식, 홍성민 옮김
  • 216쪽
  • 22,000원
  • 2013년 05월 23일

리눅스는 디스크 공간을 다 써버렸다는 것을 꽤 분명하게 보여 준다.

$ cp /var/log/syslog syslogbackup
cp: writing `syslogbackup`: No space left on device

물론 시스템의 파티션 구성에 따라 어떤 파티션이 가득 찼는지 모를 수도 있다. 첫 번째 단계는 마운트돼 있는 모든 파티션의 크기, 사용 중인 공간, 사용 가능한 공간 정보를 보기 위해 df 도구를 사용하는 것이다. -h 옵션을 추가해서 사용하면 1K 블록 단위 대신 읽기 쉬운 형태로 데이터를 보여준다.

$ df -h
Filesystem   Size   Used   Avail   Use%   Mounted on
/dev/sda1    7.8G   7.4G    60K    100%   /
none         245M   192K   245M      1%   /dev
none         249M      0   249M      0%   /dev/shm
none         249M    36K   249M      1%   /var/run
none         249M      0   249M      0%   /var/lock
none         249M      0   249M      0%   /lib/init/rw

여기서는 단지 마운트된 파티션(/dev/sda1)이 하나 있는 것을 볼 수 있고, 그 파티션은 전체 공간인 7.8Gb 가운데 7.4Gb가 사용 중이며, 100% 가득 차서 60Kb만 사용 가능하다고 알려주고 있다. 파일 시스템이 가득 찬 상태에서 어떻게 시스템에 로그인해서 문제를 해결할 수 있을까? 이 장의 후반부에서 언급하는 바와 같이 파일 시스템 공간을 확보하는 일반적인 방법 중 하나는 압축되지 않은 로그를 압축하는 것이다. 그러나 만약 디스크에 여유 공간이 없다면 어떻게 압축할 수 있을까?

예약 블록

그러나 df 수치를 자세히 보면 “어라, 리눅스가 셈에 어둡네?”라고 말할지도 모른다. 7.4Gb를 7.8Gb로 나누면 95%에 더 가깝다.

여기서 일어난 일은 리눅스가 긴급 상황(그리고 단편화를 방지하기 위해)을 위해 예약 블록(reserved block)이라고 하는 파일 시스템의 여러 블록을 따로 떼어 놓는다는 것이다. 루트 사용자만이 그러한 예비 블록에 쓸 수 있기 때문에 파일 시스템이 가득 찼더라도 루트 사용자는 여전히 로그인하고 일부 파일을 옮길 수 있는 약간의 여유 공간을 확보하고 있다. ext 기반의 파일 시스템이 탑재된 대부분의 서버에서는 총 블록의 5%가 예약돼 있고, 루트 권한을 가지고 있다면 tune2fs 도구를 통해 확인할 수 있다.

예를 들어, 다음은 가득 찬 /dev/sda1 파티션의 예약 블록 수를 확인하는 방법이다.

$ sudo tune2fs -l /dev/sda1 | grep -i “block count”
Block count:            2073344
Reserved block count:   103667

103667을 2073344로 나눈다면 약 5%가 되는 것을 알 수 있다. 이 경우 루트 사용자에게는 문제 해결에 사용할 수 있는 약 400Mb의 공간이 있는 셈이다.

가장 큰 디렉터리 추적하기

df 명령어는 각 파일 시스템에서 사용 중인 공간이 얼마나 되는지 알려 준다. 그러나 해당 공간을 알고 난 이후에도 여전히 무엇이 디스크 공간을 차지하고 있는지 파악할 필요가 있다. df와 이름이 유사한 du 명령어가 이러한 목적에 매우 유용하다. 이 명령어에 올바른 인자만 지정해도 각 디렉터리가 파일 시스템에서 얼마나 많은 디스크 공간을 차지하고 있는지 확인할 수 있다. du 명령어에 sort 명령어를 파이프한다면 어떤 디렉터리가 가장 많은 디스크 공간을 소비하는지 쉽게 확인할 수 있다. /tmp에 결과를 저장하면(충분한 여유 공간이 있다면) 여러 번에 걸쳐 해당 출력 결과를 참조할 수 있고 du를 다시 실행할 필요가 없다. 이러한 기능을 수행하는 명령을 친근하게 “duck 명령어”라 한다.

$ cd /
$ sudo du -ckx | sort -n > /tmp/duck-root

이 명령어는 화면에 어떤 것도 결과를 보여주지 않지만 대신 가장 많은 공간을 소비하는 디렉터리의 정렬된 목록을 만들어 /tmp/duck-root에 저장한다. /tmp/duck-root에 대해 tail을 사용하면 공간을 가장 많이 차지하는 상위 10개의 디렉터리를 확인할 수 있다.

$ sudo tail /tmp/duck-root
67872  /lib/modules/2.6.24-19-server
67876  /lib/modules
69092  /var/cache/apt
69448  /var/cache
76924  /usr/share
82832  /lib
124164 /usr
404168 /
404168 total

이 경우 /usr이 가장 많은 공간을 차지하는 것을 볼 수 있다. 그다음으로 /lib, /usr/share, /var/cache 등이 많이 차지한다. 결과가 /var/cache/apt와 /var/cache로 구분되는 것에 주목하면 /var/cache/apt가 /var/cache 아래에서 가장 많은 공간을 소비하는 하위 디렉터리라고 할 수 있다. 물론 상위 10개 디렉터리 이외의 목록을 확인하려면 less나 텍스트 편집기 같은 도구로 duck-file을 열어 보면 된다.

이 결과를 가지고 뭘 할 수 있을까? 어떤 경우에는 대부분의 공간을 차지하는 디렉터리는 건드릴 수가 없다(/usr 같은 경우). 반면 종종 여유 공간이 빠르게 사라지는 경우가 있는데, 그것은 걷잡을 수 없이 늘어나는 로그 파일 때문이다. 디스크의 많은 비율을 차지하고 있는 /var/log를 보면 해당 디렉터리로 가서 크기별로 정렬된 모든 파일의 목록을 보기 위해 sudo ls -lS를 실행할 수 있다. 그 시점에 아래와 같은 명령으로 특정 파일의 크기를 줄일 수 있다(기본적으로 파일의 내용을 지울 수 있다).

$ sudo sh -c "> /var/log/messages"

다른 방안으로 대용량 파일 중 하나가 이미 순환돼 있다면(확장자가 .1, .2와 같이 끝나는 것) gzip으로 압축돼 있지 않은 파일을 압축하거나 해당 로그가 더는 필요하지 않다면 간단하게 지울 수 있다. 압축되지 않은 로그 때문에 디스크 공간 문제를 일상적으로 겪는다면 /etc/logrotate.conf와 /etc/logrotate.d/ 안에 있는 로그 순환 설정을 수정해서 순환된 로그를 자동으로 압축할 수 있다.

알아두기

필자는 /tmp 안의 대용량 파일 때문에 /(루트) 파일 시스템이 가득 찼다는(시스템이 종종 작동하지 않을 수 있는 위험한 상황) 경고를 얼마나 많이 받았는지 셀 수가 없다. 구체적으로 말하면 루트 디렉터리에는 대용량의 .swp 파일들이 있었다. vim이 파일을 열 때 vim은 전체 내용을 .swp 파일로 복사한다. vim의 어떤 버전에서는 이 .swp 파일을 /tmp에 저장하고 어떤 버전에서는 /var/tmp에 저장하며, 또 어떤 버전에서는 ~/tmp 안에 저장한다. 어쨌든 실제로 일어났던 일은 시스템의 특정 사용자가 크기가 기가바이트에 달하는 아파치 로그 파일을 보려고 했다는 것이다. 사용자가 그 파일을 열었을 때 /tmp 안에 수 기가바이트나 되는 .swp 파일이 만들어졌고 루트 파일 시스템이 가득 차고 말았다. 그 문제를 해결하기 위해 문제가 되는 vim 프로세스를 찾아서 죽여야 했다.