cron 배치가 겹쳐 돌면서 같은 메일을 두 번 보낸 이유
문제 발생
5분마다 도는 알림 배치가 평소에는 30초에 끝났는데, 외부 API가 느려진 날 6분이 걸렸습니다. 그날 사용자들이 같은 알림을 두 번 받았습니다.
*/5 * * * * /usr/local/bin/send-notifications.sh시간이 더 밀리자 프로세스가 셋, 넷씩 쌓여 서버 부하까지 올라갔습니다.
원인 분석
cron은 스케줄만 봅니다. 이전 실행이 아직 돌고 있는지 확인하지 않고, 시간이 되면 새 프로세스를 띄웁니다. 실행 시간이 주기보다 길어지는 순간 중첩이 시작되고, 부하가 올라가면 더 느려져서 더 겹치는 악순환이 됩니다.
애플리케이션에서 "실행 중 플래그"를 파일이나 DB에 두는 방식은 흔하지만, 프로세스가 비정상 종료하면 플래그가 남아 다음 실행이 영영 막힙니다.
리눅스에는 이 용도의 도구가 있습니다. flock(1)은 셸 스크립트나 명령행에서 flock(2) 잠금을 관리하는 유틸리티입니다. 커널이 파일 디스크립터에 잠금을 걸기 때문에 프로세스가 죽으면 잠금도 함께 풀립니다 — 남는 플래그 문제가 구조적으로 없습니다.
해결 방안
- cron 항목을 flock으로 감쌉니다.
-n은 문서 설명대로 잠금을 즉시 얻을 수 없으면 기다리지 않고 실패합니다.
*/5 * * * * /usr/bin/flock -n /var/lock/notify.lock /usr/local/bin/send-notifications.sh- 스크립트가 스스로 잠그게 할 수도 있습니다. man 페이지가 제시하는 관용구로, 어디서 실행되든 중복이 막힙니다.
[ "${FLOCKER}" != "$0" ] && exec env FLOCKER="$0" flock -en "$0" "$0" "$@" || :- "건너뜀"을 오류로 취급하지 않습니다.
-n으로 실패하면 종료 코드가 1입니다(문서의 기본값). 모니터링이 이걸 장애로 잡지 않도록-E옵션으로 충돌 전용 종료 코드를 따로 주는 방법도 있습니다.
flock -n -E 75 /var/lock/notify.lock ./job.sh-
겹침을 로그로 남깁니다. 조용히 건너뛰기만 하면 "왜 알림이 안 갔지"를 나중에 추적할 수 없습니다. 건너뛴 횟수는 배치가 주기를 못 맞추고 있다는 신호이기도 합니다.
-
여러 서버에서 도는 배치라면 파일 잠금으로는 부족합니다. 호스트별 잠금이므로 클러스터 전체에는 Redis 락이나 DB 락이 필요합니다.
-
systemd timer도 검토합니다. 같은 서비스 유닛이 이미 실행 중이면 다시 시작하지 않으므로 중첩 방지가 기본으로 따라옵니다.
댓글0
댓글을 남기려면 로그인이 필요해요. 로그인
아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.