서버가 “매일 새벽만 느려져요” 같은 패턴을 보이면 cron이 원인일 가능성이 높습니다. 중요한 건 감으로 고치지 않고 측정 → 병목 → 개선 순서로 접근하는 겁니다.
지금부터는 증상 정리부터, 점검 체크리스트, 그리고 해결 단계를 순서대로 진행해볼게요.
참고로 웹은 nginx 기준으로 예시를 넣지만, 핵심은 어떤 스택에서도 동일합니다.
증상: “특정 시간에만” 느려지는 패턴부터 잡기
cron 이슈는 대체로 피크 시간이 정해져 있습니다. 아래 중 2개 이상이면 cron부터 의심하는 게 빠릅니다.
- 매일/매시간 비슷한 시각에 CPU가 치솟거나 Load Average가 급등
- nginx 응답이 느려지며 499/502/504가 늘거나, upstream 지연이 보임
- DB 쿼리 지연(슬로우 쿼리 증가) 또는 디스크 I/O 대기(iowait) 증가
- 백업/압축/로그 로테이션/배치가 도는 시점과 겹침
이 단계에서는 “느리다”를 숫자로 바꾸는 게 목표입니다.
점검 순서(체크리스트): 측정으로 범위를 좁히기
아래 체크리스트를 위에서 아래로 진행하면, 원인 후보가 빠르게 줄어듭니다.
- 언제 느려지는지: 시작/종료 시각, 주기(매시간/매일/매주) 기록
- 무슨 cron이 도는지: 사용자/시스템 cron 목록 확인
- 자원 병목이 뭔지: CPU/메모리/디스크/네트워크 중 어디가 튀는지 확인
- nginx 관점: access.log의 응답시간, upstream 지연, 에러 증가 여부
- 로그: cron 실행 로그와 해당 스크립트의 stdout/stderr 수집 여부
체크 중 “딱 그 시간에만 튄다”가 보이면, 이제부터는 거의 추적 게임이 아니라 데이터 기반으로 좁혀집니다.
측정 1) cron 실행 흔적과 스케줄부터 확인
먼저 “누가 무엇을 돌리는지”를 정리합니다.
- 사용자 crontab: crontab -l (서비스 계정도 포함)
- 시스템 cron: /etc/crontab, /etc/cron.d/, /etc/cron.daily|hourly|weekly|monthly
- systemd timer 사용 여부: systemctl list-timers
실행 로그는 배포판마다 다르지만, 보통은 아래 중 하나에 있습니다.
- syslog 계열: /var/log/syslog 또는 /var/log/cron
- systemd journal: journalctl -u cron (또는 crond)
여기서 중요한 포인트는 “실행 시각”과 “실제로 어떤 커맨드가 돌았는지”를 한 줄로라도 남기는 겁니다.
측정 2) CPU/메모리/디스크 중 병목을 숫자로 찍기
cron이 성능을 망치는 방식은 보통 3가지 중 하나입니다: CPU를 태우거나, 메모리를 잡아먹거나, 디스크 I/O를 막습니다.
- CPU/Load: top/htop에서 특정 프로세스가 계속 상단을 점유하는지
- 메모리/스왑: free -m, vmstat에서 swap-in/out이 발생하는지
- 디스크 I/O: iostat -x에서 util이 높고 await가 커지는지(iowait 증가 포함)
새벽 배치가 압축(tar/gzip), 백업(rsync), 로그 처리, 대량 파일 스캔(find) 같은 작업이면 디스크 병목이 자주 나옵니다.
반대로 데이터 변환/이미지 처리/대량 암호화 같은 작업이면 CPU가 주범인 경우가 많습니다.
측정 3) nginx에서 “느린 게 진짜 서버 때문인지” 확인
웹이 느리다고 해서 항상 nginx가 원인은 아닙니다. 하지만 nginx 로그는 “느림이 언제 시작됐는지”를 정확히 찍어주기 때문에 관제 포인트로 좋습니다.
- access.log: 특정 시각에 응답시간이 급증하는지(로그 포맷에 $request_time이 있어야 유리)
- error.log: upstream timed out, connect() failed, 499 증가 여부
- upstream: PHP-FPM/Node/uwsgi/DB 같은 뒤쪽이 막히는지
만약 nginx는 정상인데 upstream만 느려진다면, cron이 DB나 애플리케이션 리소스를 잡아먹는 흐름을 의심할 수 있습니다.
해결 단계: “임시 완화 → 구조 개선 → 재발 방지” 순으로
측정으로 병목이 어느 쪽인지 잡았다면, 이제는 위험을 낮추는 순서대로 바꿉니다.
- 1) 임시 완화: 실행 시간을 피크에서 빼거나(새벽이라고 안전하진 않습니다), 동시에 도는 작업을 분산
- 2) 자원 사용 제한: CPU/IO 우선순위 조정(nice/ionice), 동시성 낮추기, 배치 크기 줄이기
- 3) 병목 제거: 디스크 작업이면 증분/스냅샷/파일 수 줄이기, DB 작업이면 인덱스/쿼리 튜닝/배치 쪼개기
- 4) 로그/가시성: cron stdout/stderr를 파일로 남기고, 실행 시간(duration)을 기록
- 5) 재발 방지: systemd timer로 이관해 timeout, 리트라이, 리소스 제한을 명시
특히 같은 시간대에 여러 cron이 몰리면 “각각은 괜찮은데 합치면 폭발”이 흔합니다. 스케줄을 5~10분 단위로라도 흩어두는 것만으로 체감이 크게 좋아질 때가 많아요.
또 하나: 로그 로테이션(logrotate)이나 백업이 nginx 로그 디스크를 꽉 잡으면, 웹 응답도 같이 밀릴 수 있습니다. 디스크 병목이면 가장 먼저 의심해볼 만합니다.
마무리
cron 성능 문제는 “무슨 작업이 언제 자원을 얼마나 쓰는지”만 잡히면, 대부분은 정리 가능합니다. 측정으로 근거를 만들고, 병목을 하나로 좁힌 뒤, 안전한 순서로 개선해보세요.
다음 글로는 $request_time 로그 포맷 추가나, systemd timer로 옮길 때의 운영 팁처럼 “재발 방지” 쪽을 더 깊게 다뤄도 좋습니다.