03시 새벽시간 서버의 업데이트와 백신 점검이 돌아가는 야심한 시간 서버에서 날아오는 CPU 100%가 찍히는 과부하 알림
범인은 의외의 프로그램 이였습니다. 리눅스 서버의 안전을 담당하는 ClamAV(백신)이 바이러스 대신 서버를 잡고 있었습니다.
사건 개요
매일 특정 시간만 되면 CPU 점유율이 100%로 치솟는 현상을 발견했습니다. 해당 내역을 조사해 보니 clamscan이라는 프로세스가 혼자서 CPU 코어 하나를 전부 잡아서 착취를 하고 있었습니다.
- 특정 코어 하나가 CPU 100% 점유
- 스캔 시작 후 1시간 이상 프로세스가 종료되지 않음
- 서버 전체에서 동일한 현상 보
원인 분석 : 왜 코어를 착취 하는가?
원인은 바로 ClamAV의 기본 실행 명령어인 clamscan의 구조적인 한계에 있었습니다.
- 싱글 스레드 방식
- clamscan은 한 번에 하나의 파일만 검사하며 비록 서버가 멀티 코어 라도 코어 하나만 집중적으로 사용하기 때문에 효율이 떨어지고 시간은 지연되며 1시간넘게 cpu를 통채로 착취하는 결과 값을 일으켰습니다.
- DB 로딩 오버헤드
- 실행될 때마다 수십MB ~수백MB의 바이러스 DB를 메모리에 새로 올립니다. 이 과정에서 발생하는 CPU와 I/O 소모가 원인이 되었습니다
해당 문제점을 들여다본 결과 구조적의 근본적 한계를 느끼고 공식문서를 열어본 결과
ClamAV 공식 문서에서도 "대용량 스캔"에는 clamscan 대신 데몬 방식인 "clamdscan" 을 쓰라고 강력히 권장하고 있습니다.
해결책
원인과 범인을 잡았으니 이제 잘못된 부분을 바로 잡고 해결을 해야겠습니다.
clamdscan + 멀티 스레드 전환 방식 바로 공식 문서에서 권장하고 있는 방식을 사용해서 착취 당하는 CPU에게 자유를 누리게 해줍시다.
백그라운드에서 항상 떠 있는 데몬의 역할을 할 Clamd을 설치하고, 멀티 스레드 옵션을 사용하여 검사 속도를 높이는 것이죠. 16대 서버에 일일이 접속할 수 없으니 여기서 자동화 플레이북의 역할을 해줄 Ansible을 기용해서 번거로울 필요없이 자동화와 전 서버 관리를 한번에 진행해 봅시다.
우선 기존에 사용하던 ClamScan 코드를 가져와 봅시다

- 03시에 작동하며 서버를 스캔 하도록 되어있습니다
- 하지만 CPU양보나 기타 편의성에 대해서 어떤 양보도 되어 있지 않은 착취꾼의 모습이 보이는군요.
여기서 수정 해줘야 할 부분은 기존 ClamScan 부분을 제거하고 리소스 제한의 역할을 하는 (nice , ionice) 기능과 멀티 스레드(multiscan)옵션을 추가로 적용을 해 줍니다.
- nice / ionice : CPU 점유율이 높아도 다른 중요한 서비스(Web, DB 등)가 자원을 요청하면 점유를 즉시 양보 해 주는 역할을 담당합니다.
- 이러면 더 이상 혼자 1시간 넘게 CPU를 착취하지 못하게 됩니다.
- multiscan : 싱글 코어 스레드 방식에서 여러 코어가 협업하는 방식으로 변경하는 역할을 합니다.
- 검사 시간이 싱글 스레드에 비해서 대폭 상향되게 조정 해줍니다.
우선 순위와 멀티코어 방식으로 변경했어도 가장 중요한게 남았습니다.
바로 경로 제외 옵션을 추가해 줘야 합니다.
- 가상화 /컨테이너 경로 제외 ( --exclude-dir=/var/lib/docker /var/lib/docker)
- 컨테이너 파일들이 얽혀 있어 스캔 지옥에 자주 오시는 단골 손님입니다.
- 이 친구들을 Scan 대상에서 빼지 않으면 그동안의 노력이 모두 헛수고가 되버리니 꼭 제거를 해줍니다.
- 컨테이너 파일들이 얽혀 있어 스캔 지옥에 자주 오시는 단골 손님입니다.
새롭게 도입한 시스템을 사용해서 Clamdscan을 구성해 봅시다.

이제 오래 걸리던 Scan을 획기적으로 단축할 뿐만 아니라 타 서비스 응답 속도에도 영향을 주지 않는 착취 없는 착한 백신이 완성 되었습니다
- CPU 숫자는 여전히 높게 찍힐 수 있지만, nice 옵션 덕분에 실제 서비스 응답 속도에는 전혀 지장을 주지 않게 되었습니다.
ref.
ClamAV Official Docs - Scanning with ClamAV