서버 용량 부족 사태, 어떻게 해결했나
어제 밤 고객들이 Kanjideck 디지털 파일을 다운로드할 수 있는 간단한 서버를 운영하기 시작했습니다. 서버는 Hetzner의 작은 머신에서 NixOS를 돌리고 있는데, RAM 4GB, 디스크 40GB 정도의 사양이죠. 다운로드 가능한 파일 중에는 2.2GB짜리도 있습니다.
기술적으로는 간단했습니다. Haskell로 만든 프로그램이 정적 파일을 제공하고(인증 로직은 좀 복잡하지만), nginx 리버스 프록시가 요청을 이 프로그램으로 넘기는 구조였거든요.
첫 번째 위기: 갑작스러운 트래픽
파일 공개 소식을 알린 지 몇 분도 안 되어 수백 명의 고객이 서버에 몰렸습니다.
로그가 화면을 휩쓸고 지나가는데, 같은 메시지가 계속 반복되는 게 보였어요.
Mar 31 20:43:03 mogbit kanjideck-fulfillment[2528300]: user error
(Unexpected reply to: MAIL "<...> at kanjideck.com",
Expected reply code: 250, Got this instead: 452 "4.3.1 Insufficient system storage\r\n")
이미 고객 불만 이메일이 들어오고 있었습니다. 서버 문제를 해결 중이라는 메시지를 보냈는데, Grafana를 보니 디스크가 40GB/40GB 완전히 찼다고 뜹니다. df -h도 /dev/sda가 100% 사용 중이라고 표시했어요.
급하게 du -sh로 용량을 확인해보니 두 가지가 주범이었습니다.
/var/lib의 Plausible Analytics (ClickHouse 데이터베이스): 8.5GB/nix/store(서버 설정, 설치 파일, 실행 파일): 15GB
응급 조치: 디스크 정리
일단 /nix/store부터 정리하려고 했습니다. 불필요한 실행 파일과 이전 설정들이 있을 테니까요.
$ nix-collect-garbage -d
그런데 이 명령이 실패했습니다.
removing old generations of profile /nix/var/nix/profiles/system
error: opening lock file '/nix/var/nix/profiles/system.lock': No space left on device
디스크가 너무 찼아서 정리 명령 자체도 못 하는 상황이었네요. 한편 고객들은 계속 다운로드 시도를 하고 있고, 서버는 계속 에러를 뱉어내고 있었습니다.
먼저 로그를 삭제하기로 했습니다.
journalctl --vacuum-time=1s
겨우 이 정도로 공간이 조금 확보되었고, 이제 nix 정리가 가능해졌습니다.
다음으로 ClickHouse 로그 테이블을 정리하려고 했어요.
clickhouse-client -q "TRUNCATE TABLE system.query_log"
그런데 또 실패했습니다.
Code: 243. DB::Exception: Cannot reserve 1.00 MiB, not enough space. (NOT_ENOUGH_SPACE)
이상했어요. 분명히 nix 스토어를 정리했는데 여전히 부족하다니요. 게다가 사용자 접속이 늘어날수록 더 빠르게 차는 것 같았습니다. 뭔가 계속 쌓이고 있다는 뜻이었죠.
해결책: Nix Store를 별도 볼륨으로 분리
긴급 상황에서 할 수 있는 가장 빠른 해결책은 공간을 늘리는 것입니다. Hetzner에 더 큰 인스턴스가 없었으므로 별도 Volume을 구매했어요.
/nix/store는 불변 저장소라서 다른 드라이브에 옮길 수 있습니다. 또한 현재 가장 큰 시스템 컴포넌트(12GB)였으니 최적의 후보였죠.
NixOS 덕분에 이 작업이 정말 부드럽게 진행되었습니다. NixOS Wiki의 "Moving the store" 가이드를 따라가니까 바로 됐거든요.
새로운 Volume에 ext4 파일 시스템을 만들고:
mkfs.ext4 -L nix /dev/sdb
NixOS 설정에 마운트 포인트를 추가했습니다.
fileSystems."/nix" = {
device = "/dev/disk/by-label/nix";
fsType = "ext4";
neededForBoot = true;
options = [ "noatime" ];
};
서버를 재부팅한 후, /nix/store가 별도 볼륨에서 정상 작동했고, 루트 드라이브에 충분한 공간이 생겼습니다.
Grafana 대시보드가 빨간색에서 초록색으로 바뀌었어요. 에러 로그도 멈췄습니다. 디스크 사용률이 50% 정도였고, 사용자들이 2.2GB 파일을 다운로드할 때도 60~65% 정도로만 올라갔습니다. 이제 정상 작동합니다.
진짜 문제 발견: Nginx
아침에 메일함을 확인했습니다.
2.2GB 파일 다운로드가 중간에 멈추는 현상에 대한 불만이 몇 건 있었어요. 하지만 대부분의 사용자는 다운로드 페이지에 접속할 수 있었고, 다른 파일들은 정상적으로 받을 수 있었습니다. 상황이 많이 나아졌네요. Grafana도 여전히 초록색입니다.
지금까지는 일단 불 끈 것 같은데, 2.2GB 파일 다운로드 실패 문제는 별개의 이슈네요. 이건 다음에 파고들어야 할 문제입니다.