서비스가 계속 재시작될 때 원인 찾는 법 — 35일간 3,348회 재시작한 서버 기록

35일 동안 3,348회 재시작 — journalctl 실측 기록

리눅스 서비스가 몇 분 간격으로 계속 재시작된다면, 원인이 그 서비스가 아니라 서비스를 지켜보는 쪽일 수 있어요. 로그에 오류가 안 남는데도 재시작만 반복된다면 특히 그렇습니다.

제 서버가 35일 동안 3,348번 재시작됐는데, 범인은 서비스를 살려두려고 제가 직접 붙인 감시 스크립트였어요. 이 글에서 원인을 3분 만에 확인하는 명령어와 감시 기준을 어떻게 바꿔야 하는지를 정리했습니다. 숫자는 전부 제 서버에서 뽑은 실제 값이에요.

📌 3줄 결론
- "살아 있음"을 파일 수정 시각으로 판정했더니, 감시 스크립트가 감시 대상을 계속 죽였습니다
- 35일 · 3,348회. 재시작 간격이 정확히 15분이라 로그만 봐도 인위적인 주기가 보였습니다
- 재시작은 직접 만든 스크립트가 아니라 systemd에 맡기고, 판정 기준을 "부산물"이 아니라 "결과"로 바꿔야 합니다

확인일 2026-08-14 · 대상: 제가 직접 운영하는 리눅스 서버 1대

어떤 구조였나

AI 에이전트가 계속 학습하며 결과를 데이터베이스에 쌓는 서버였어요. 이 작업이 멈추면 안 되니까, 살아 있는지 지켜보는 스크립트를 하나 붙였습니다. 판정 기준은 이랬어요.

  • 데이터베이스 파일의 수정 시각이 10분 넘게 그대로면 작업이 멈춘 것으로 본다
  • 멈췄으면 작업 서비스와 API 서비스를 함께 재시작한다
  • 이 검사를 cron으로 5분마다 돌린다

읽어보면 그럴듯해요. 실제로 저도 그럴듯해서 붙였습니다.

문제는 작업 프로세스가 한 번 도는 데 15분 넘게 걸렸다는 겁니다. 그 15분 동안 계산만 하고, 결과를 데이터베이스에 쓰는 건 맨 마지막이었어요. 그러니까 정상 작동 중에도 파일 수정 시각은 10분 넘게 그대로였습니다.

감시 스크립트 입장에서 정상 작동은 고장과 구분되지 않았어요.

15분마다 죽은 이유 — 로그에 그대로 찍힌다

재시작이 일어나면 파일 수정 시각이 갱신됩니다. 그다음 10분간은 임계값에 안 걸려요. 10분이 지나면 5분 주기 검사 중 하나에 걸립니다. 그래서 10분 + 최대 5분 = 약 15분마다 재시작이 반복됐어요.

추측이 아니라 로그에 그대로 있었습니다. 2026년 7월 20일 하루치를 뽑아보면 이렇습니다.

2026-07-20T00:00:02  Started ai-world.service
2026-07-20T00:15:01  Started ai-world.service
2026-07-20T00:30:02  Started ai-world.service
2026-07-20T00:45:01  Started ai-world.service
2026-07-20T01:00:02  Started ai-world.service

초 단위까지 규칙적이에요. 사람이나 부하가 만드는 패턴이 아니라 타이머가 만드는 패턴입니다. 로그 간격이 지나치게 규칙적이면 그 자체가 단서예요.

숫자도 맞아떨어집니다.

  • 사고 기간: 2026년 7월 3일 ~ 8월 7일, 35일
  • 총 재시작: 3,348회
  • 7월 20일 하루: 96회 = 24시간 × 시간당 4회 = 15분마다 1회
  • 감시 스크립트를 끈 뒤 7일간: 13회

마지막 줄이 대조군입니다. 같은 서버, 같은 서비스인데 감시만 뗐더니 하루 96회가 이틀에 4회 수준으로 떨어졌어요.

진짜 피해는 재시작 횟수가 아니었다

재시작 자체는 그러려니 할 수 있어요. 문제는 두 가지가 더 있었습니다.

첫째, 15분보다 긴 작업은 구조적으로 완주가 불가능했습니다. 작업이 15분 걸리는데 15분마다 죽으니까요. 완주하지 못하면 결과를 못 남기고, 결과가 없으니 파일 수정 시각은 또 그대로고, 그래서 또 죽습니다. 완벽한 순환이었어요.

둘째, API 서버까지 같이 재시작했습니다. 감시 스크립트가 두 서비스를 함께 껐다 켜도록 만들어져 있었거든요. 진행 중이던 요청이 통째로 날아갔습니다. 앱에서 간헐적으로 502 오류가 뜨고 응답 속도가 들쭉날쭉했는데, 원인이 전부 여기였어요.

여기에 설계 하나가 더 얹혀 피해를 굳혔습니다. 서버가 부팅될 때 "실행 중" 상태의 작업을 "대기 중"으로 되돌리도록 돼 있었는데, 대기 중인 작업을 다시 집어가는 코드가 없었어요. 재시작에 휘말린 작업은 되돌려진 채로 영원히 갇혔습니다.

여러분 서버에서 확인하는 법

같은 일이 벌어지고 있는지 3분이면 확인할 수 있어요. 서비스 이름 자리에 본인 서비스명을 넣으세요.

재시작 횟수를 세는 명령입니다.

systemctl show -p NRestarts --value 서비스이름

최근 일주일간 몇 번이나 시작됐는지 로그로 세는 방법이에요. 정상이라면 한 자릿수여야 합니다.

journalctl -u 서비스이름 --since "7 days ago" | grep -c "Started 서비스이름"

시작 시각만 뽑아 간격을 보는 명령입니다. 여기서 일정한 주기가 보이면 거의 확정이에요.

journalctl -u 서비스이름 --since "1 day ago" --output=short-iso | grep "Started 서비스이름"

cron에 감시 스크립트를 걸어뒀다면 여기서 보입니다.

crontab -l

제 경우 마지막 명령에서 */5 * * * *로 도는 스크립트 한 줄이 나왔고, 두 번째 명령의 숫자가 세 자리였습니다.

무엇을 잘못했나 — 감시의 세 가지 원칙

살아 있음을 부산물로 재지 마세요. 파일 수정 시각은 작업의 결과물이지 작업의 상태가 아닙니다. 지금은 작업 프로세스가 스스로 "나 살아 있다"를 주기적으로 남기고, 감시는 그 신호만 봅니다. 구글 SRE 책도 내부 구현이 아니라 사용자가 겪는 증상을 기준으로 감시하라고 권합니다.

재시작은 직접 만든 스크립트가 아니라 systemd에 맡기세요. systemd에는 Restart=on-failure, RestartSec=, 그리고 폭주를 막는 StartLimitIntervalSec=·StartLimitBurst=가 이미 들어 있어요. 제가 만든 스크립트에는 그 마지막 두 개, 즉 한계선이 없었습니다. 그래서 3,348번을 갈 수 있었어요.

회로차단기를 넣으세요. 정해진 횟수를 넘으면 고치려는 시도를 멈추고 사람을 부르는 장치가 있어야 합니다. "계속 되살리기"는 언제나 옳은 행동처럼 보이지만, 되살리는 대상이 그 행동 때문에 죽고 있을 때는 정반대예요.

자주 묻는 질문 (FAQ)

Q. 감시 스크립트를 아예 쓰지 말라는 뜻인가요?
아닙니다. 감시가 없었다면 진짜 고장을 훨씬 늦게 알았을 거예요. 문제는 감시를 붙인 것이 아니라 판정 기준과 한계선이었습니다.

Q. cron 대신 systemd 타이머를 쓰면 이 문제가 안 생기나요?
주기 실행 수단을 바꾸는 것만으로는 안 됩니다. 판정 기준이 그대로면 타이머로 옮겨도 같은 일이 벌어져요. 다만 systemd 쪽에는 재시작 폭주를 멈추는 한계선 설정이 이미 있어서, 최악의 경우를 막아줍니다.

Q. NRestarts가 0으로 나오는데 정상인가요?
이 값은 서비스를 수동으로 재시작하면 초기화됩니다. 그래서 최근에 손댄 적이 있다면 0이 나올 수 있어요. 과거 이력까지 보려면 journalctl 쪽을 함께 확인하는 게 정확합니다.

Q. 지금은 완전히 해결됐나요?
아니요. 감시를 끄니 재시작은 멈췄지만, 데이터베이스에 결과가 쌓이지 않는 원래 증상 자체는 아직 원인 미상입니다. 감시 스크립트가 그 증상을 가리고 있었을 뿐이에요. 이제는 15분마다 죽지 않으니 제대로 관찰할 수 있게 됐습니다.

Q. 서버가 여러 대거나 컨테이너 환경이면요?
이 글은 서버 1대 사례입니다. 오케스트레이터가 있는 환경이라면 헬스체크 정의와 재시작 정책을 그쪽 규칙에 맞춰 봐야 합니다. 다만 "살아 있음을 무엇으로 재는가"라는 질문 자체는 똑같이 유효해요.

실행 체크리스트

  • systemctl show -p NRestarts 로 재시작 횟수를 확인한다
  • journalctl 로 시작 시각을 뽑아 간격이 규칙적인지 본다
  • cron에 걸린 감시 스크립트가 있는지 crontab -l 로 확인한다
  • 감시 기준이 "작업이 남긴 부산물"인지 "작업이 직접 보내는 신호"인지 구분한다
  • 감시 대상 작업의 한 바퀴 소요 시간을 재고, 임계값이 그보다 긴지 확인한다
  • 재시작 폭주를 멈출 한계선(StartLimitBurst=)이 있는지 확인한다
  • 감시 스크립트를 지우기 전에 원본을 백업해둔다 — 저는 주석 처리하고 백업 파일을 남겼어요

참고한 공식 문서

다음 글 예고

같은 서버에서 발행 토큰이 60일 뒤 만료되는데 갱신 코드가 아예 없던 문제를 다룹니다. 이것도 오류 없이 조용히 멈추는 종류였어요. 자동화의 진짜 위험은 터지는 게 아니라 조용해지는 것이라는 이야기를 이어서 하겠습니다.

참고 자료

출처 확인일: 2026-08-14

댓글

이 블로그의 인기 게시물

일 잘하는 사람의 AI 도구 5개 — 업무 시간 반으로 (2026)

옛날 사진 AI로 복원하는 법 — 부모님 흑백사진 살리기 (2026)