FastAPI - Gunicorn hooks 사용하기

프로필 사진mingke

FastAPI Logo

목차

소개

FastAPI Application을 배포할 때 일반적으로 Gunicorn을 많이 사용합니다. Gunicorn hooks를 소개하고, 최근 Gunicorn hooks를 사용한 경험을 공유하려고합니다.

Gunicorn Hooks 종류

1. on_starting(server)

  • 서버가 시작될 때 실행됩니다.
  • 서버의 설정이 로드되고 마스터 프로세스가 초기화되기 전에 호출됩니다.
  • 이 Hook은 서버 설정을 수정하거나 로그 등의 초기화 작업에 사용될 수 있습니다.

2. on_reload(server)

  • 서버가 리로드될 때 실행됩니다.
  • 구성 변경이나 애플리케이션 코드 변경을 처리하기 위해 사용할 수 있습니다.

3. when_ready(server)

  • 서버가 요청을 받을 준비가 완료되었을 때 실행됩니다.
  • 서버가 준비되었음을 알리거나 추가적인 초기화 작업을 수행하는 데 사용할 수 있습니다.

4. pre_fork(server, worker)

  • 워커 프로세스가 fork되기 전에 실행됩니다.
  • 이 후크는 리소스를 생성하거나 상태를 변경하기 위해 사용될 수 있습니다.

5. post_fork(server, worker)

  • 워커 프로세스가 fork된 직후에 실행됩니다.
  • 각 워커 프로세스의 초기화를 위해 사용할 수 있으며, 예를 들어 특정 워커에서만 실행해야 하는 코드를 여기에 배치할 수 있습니다.

6. post_worker_init(worker)

  • 워커 프로세스가 초기화된 후에 실행됩니다.
  • 워커가 준비되었음을 알리거나 추가적인 초기화 작업을 수행하는 데 사용할 수 있습니다.

7. worker_int(worker)

  • 워커 프로세스가 인터럽트를 받을 때 (예: SIGINT 또는 SIGQUIT) 실행됩니다.
  • 워커가 중단되기 전에 정리 작업을 수행하기 위해 사용될 수 있습니다.

8. worker_abort(worker)

  • 워커 프로세스가 중단될 때 (예: 시간 초과 등으로 인해) 실행됩니다.
  • 비정상 종료에 대한 처리 로직을 구현하는 데 사용할 수 있습니다.

9. pre_exec(server)

  • 마스터 프로세스가 새로운 마스터 프로세스를 실행하기 직전에 실행됩니다.
  • 이는 주로 os.execvp()을 사용하여 새로운 마스터 프로세스를 시작하기 전에 실행됩니다.

10. pre_request(worker, req)

  • 워커 프로세스가 요청을 처리하기 전에 실행됩니다.
  • 요청 로깅이나 메트릭 수집 등에 사용될 수 있습니다.

11. post_request(worker, req, environ, resp)

  • 워커 프로세스가 요청을 처리한 후에 실행됩니다.
  • 요청 처리 후의 정리 작업이나 추가 로깅 등에 사용될 수 있습니다.

12. worker_exit(server, worker)

  • 워커 프로세스가 종료될 때 실행됩니다.
  • 워커의 정리 작업이나 자원 해제 등에 사용될 수 있습니다.

13. nworkers_changed(server, new_value, old_value)

  • 워커의 수가 변경될 때 실행됩니다.
  • 동적으로 워커의 수를 조절하는 환경에서 유용할 수 있습니다.

14. on_exit(server)

  • 마스터 프로세스가 종료될 때 실행됩니다.
  • 서버 종료 시의 정리 작업에 사용될 수 있습니다.

이런 훅들을 사용하면 Gunicorn 서버의 다양한 생명주기 이벤트에 대응하여 필요한 로직을 구현할 수 있습니다. 훅을 사용할 때는 각 훅이 언제 호출되는지, 그리고 어떤 목적으로 사용되는지를 이해하는 것이 중요합니다.

훅 사용 경험 공유

FastAPI 앱을 실행하면서 간단한 scheduler도 함께 동작시켰습니다.

from fastapi import FastAPI
from example import scheduler
...
 
app = FastAPI(
    openapi_url=app_settings.OPENAPI_URL,
    lifespan=lifespan,
    swagger_ui_parameters=app_settings.SWAGGER_UI_PARAMS,
    **app_settings.SWAGGER_META,
)
 
scheduler.start()

위 코드를 gunicorn과 함께 배포하면 발생하는 문제가 있습니다. gunicorn의 모든 worker process에서 scheduler가 같이 실행됩니다. 만약에 워커 프로세스가 4개라면 scheduler가 4개가 실행되는 것이죠.

저는 그것을 원하지 않았고 scheduler는 주기마다 1번씩 실행되게 하는것을 원했습니다. gunicorn on_starting hook을 사용해서 scheduler를 실행했습니다.

# gunicorn.config.py
import example import scheduler
 
# some settings...
 
def on_starting(server):
    server.log.info("Starting Scheduler...")
    scheduler.start()

마스터 프로세스가 실행될 때만 on_starting hook이 1번 실행됩니다.

이를 통해 worker 프로세스마다 scheduler가 실행되는 것을 방지할 수 있고 주기마다 한 번만 실행되도록 할 수 있었습니다. 아직 Gunicorn hooks를 여러 상황별로 사용해 본 경험은 없지만, 지원하는 hook들이 많아 다양한 상황에서 사용할 수 있을 것 같습니다.
이상으로 Gunicorn hooks에 대해 알아보았습니다. 감사합니다.