Python Gunicorn worker가 계속 재시작될 때
마지막 업데이트

Python Gunicorn worker가 계속 재시작될 때


Gunicorn worker가 계속 재시작된다면, 원인은 timeout 압박, 메모리 증가, boot-time failure, 의도된 recycle 설정, 혹은 signal 경로일 수 있습니다.

그래서 restart 장애는 읽기가 어렵습니다. 어떤 restart는 설정상 정상이고, 어떤 restart는 실제 런타임 문제를 뜻합니다. 이 둘을 먼저 나누지 않으면 멀쩡한 recycle을 crash처럼 디버깅하게 될 수 있습니다.

이 글은 실전 순서에 집중합니다.

  • boot failure, runtime restart, deliberate recycle을 어떻게 구분할지
  • restart timing이 어떤 힌트를 주는지
  • timeout, memory, startup 경로에서 무엇을 먼저 볼지

짧게 말하면 restart가 boot에서 나는지, runtime에서 나는지, 설정상 예상된 recycle인지 먼저 구분하고, 그다음 트래픽, 메모리, timeout-heavy 경로와 타이밍을 비교하는 편이 빠릅니다.

더 넓은 Python 분기부터 다시 보고 싶다면 Python 트러블슈팅 가이드로 가세요.


먼저 재시작 타이밍부터 보기

worker가 언제 재시작되는지부터 보세요.

  • 부팅 직후
  • 트래픽 spike 이후
  • 일정 시간이나 요청 수 이후
  • 메모리가 오른 뒤

이 타이밍은 stack trace 하나를 따로 읽는 것보다 훨씬 빨리 갈래를 좁혀줍니다.

특히 아래를 나누는 데 유용합니다.

  • startup failure
  • runtime instability
  • expected recycle behavior

이 첫 분기 없이 보면 모든 restart를 crash로 오해하기 쉽습니다.


boot failure와 runtime restart는 완전히 다르다

worker가 부팅 직후 재시작된다면 아래를 먼저 의심해야 합니다.

  • import failure
  • config mistake
  • environment mismatch
  • readiness 전 startup path 실패

반대로 traffic나 memory 변화 뒤에 재시작된다면 아래 branch가 더 가깝습니다.

  • worker timeout
  • memory pressure
  • request path instability
  • load 중 signal 또는 platform restart

이 두 branch는 해결책이 완전히 다릅니다.


자주 나오는 원인

1. worker timeout

요청이나 upstream dependency가 worker 시간 제한을 넘어갈 수 있습니다.

자주 보이는 단서는 아래와 같습니다.

  • 트래픽 spike 구간에서 restart가 집중됨
  • timeout-heavy endpoint가 로그에서 많이 보임
  • 긴 request나 blocked dependency 뒤에 restart가 나타남

이 경우 restart는 랜덤이 아니라, worker가 주어진 시간 안에 작업을 못 끝내는 결과에 가깝습니다.

2. 메모리 압박

메모리가 높아지면서 worker가 recycle되거나 kill될 수 있습니다.

보통은 아래처럼 보입니다.

  • restart timing이 메모리 증가와 비슷하게 움직임
  • 특정 worker class나 endpoint가 더 크게 할당함
  • memory-heavy request 뒤에 패턴이 심해짐

그래서 worker restart와 Python 메모리 장애는 자주 같이 보입니다.

3. boot-time import / config 실패

worker가 healthy 상태까지 올라오지 못해 반복 재시작될 수 있습니다.

자주 보이는 패턴은:

  • import-time exception
  • 누락된 env var
  • unavailable service에 의존하는 startup code
  • worker 초기화를 깨뜨리는 config 변경

이 branch라면 runtime traffic을 오래 보는 것보다 boot log를 먼저 보는 편이 맞습니다.

4. 정상 recycle을 장애로 오해

일부 restart는 Gunicorn 설정이나 플랫폼 동작상 예상된 현상일 수 있습니다.

예를 들면:

  • worker recycle policy
  • request-count 기반 제한
  • platform restart
  • deployment restart를 애플리케이션 불안정으로 오해

핵심은 이 restart가 실제로 해로운지, 아니면 단지 눈에 보이는 것인지 구분하는 것입니다.


실전 점검 순서

worker가 계속 재시작될 때는 아래 순서가 가장 도움이 됩니다.

  1. restart가 boot인지, runtime인지, expected recycle인지 구분
  2. restart timing과 트래픽 / 메모리 변화 비교
  3. timeout-heavy request path 확인
  4. boot log와 최근 import / config 변경 확인
  5. startup failure인지, runtime pressure인지, normal recycle인지 판단

이 순서가 중요한 이유는 두 가지 흔한 실수를 막기 때문입니다.

  • restart timing을 모른 채 worker 수부터 조정하는 실수
  • 실제 원인은 startup path나 request runtime인데 Gunicorn 설정 탓부터 하는 실수

CPU나 memory 압박이 같이 보인다면 Python CPU 사용량이 높을 때Python 메모리 사용량이 높을 때를 같이 보세요.


아주 작은 예시가 보여주는 핵심

gunicorn app:app --workers 4 --timeout 30

slow startup, memory spike, import failure, aggressive timeout 모두 worker restart loop를 만들 수 있습니다.

이 명령 자체가 원인을 알려주지는 않습니다. 실제 힌트는 restart 직전 worker가 무엇을 하고 있었는지, 로그와 타이밍이 무엇을 가리키는지에 있습니다.


restart 장애마다 물어볼 질문

각 restart 패턴마다 아래를 물어보면 도움이 됩니다.

  • restart 직전 worker는 무엇을 하고 있었는가
  • 트래픽 처리 중이었는가, 부팅 중이었는가, 그냥 대기 중이었는가
  • memory 압박이 먼저 왔는가, timeout이 먼저 왔는가
  • 사용자 트래픽이 전혀 없어도 이 restart가 계속 나는가

이 프레이밍이 좋은 이유는 Gunicorn 장애가 설정 문제이기 전에 timing 문제인 경우가 많기 때문입니다.


FAQ

Q. worker가 재시작되면 항상 Gunicorn이 고장 난 건가요?

아닙니다. application startup failure, timeout-heavy path, memory pressure, expected recycle behavior 때문에도 restart가 날 수 있습니다.

Q. 무엇부터 보는 게 가장 빠른가요?

restart timing부터 보세요. boot, runtime request path, expected recycle 중 어디 branch인지가 거의 결정됩니다.

Q. 왜 traffic spike와 worker restart가 같이 보이나요?

traffic spike가 timeout-heavy endpoint, memory-heavy path, blocked dependency를 훨씬 더 강하게 드러내기 때문입니다.


Sources:

먼저 읽어볼 가이드

검색 유입이 많은 핵심 글부터 이어서 보세요.