재배포 때마다 사라지던 요청, Railway와 FastAPI 안전 종료로 지키기
Railway 재배포 시 SIGTERM을 제대로 받아 FastAPI의 진행 중 요청과 BackgroundTasks를 지키는 설정을 정리했어요.
재배포 버튼을 누를 때마다 마음 한구석이 불편했어요. 그 순간 진행 중이던 요청이 조용히 끊기니까요. 혹시 배포 직후에만 이상한 에러가 찍힌 적 없나요?
라이프케어로그의 플랜엘(plan-l)은 국가법령정보센터와 대법원 데이터를 조회하는 검색이라 응답에 몇 초 걸리는 요청이 있어요. 하필 그 사이 재배포가 겹치면 사용자는 빈 화면을 봐요. 월 3건 무료인 검색에서 한 건이 이렇게 날아가면 저도 아깝고 사용자도 억울하죠.
원인은 SIGTERM이 앱까지 안 오는 것
Railway는 재배포하면 기존 컨테이너에 SIGTERM을 보내고, 유예 시간이 지나면 강제 종료해요. 직접 재현해 보니 문제는 신호 자체가 아니라, 신호가 uvicorn까지 도착하지 못하는 구조였어요.
Dockerfile의 CMD를 셸 형태로 쓰면 PID 1이 셸이 되고, SIGTERM은 셸에서 멈춰요. 앱은 아무것도 모른 채 강제 종료당하는 거예요. 고치는 건 두 줄이면 돼요.
# 셸 형태 (신호가 앱에 안 감)
CMD uvicorn main:app --host 0.0.0.0 --port $PORT
# exec 형태 (신호가 uvicorn에 직접 감)
CMD ["sh", "-c", "exec uvicorn main:app --host 0.0.0.0 --port $PORT"]진행 중 요청은 uvicorn이 기다려 줘요
exec 형태로 바꿔 보면 uvicorn은 SIGTERM을 받는 순간 새 연결을 막고, 이미 처리 중인 요청은 끝까지 기다려요. 다만 무한정 기다리게 두면 느린 요청 하나가 종료를 붙잡아요. --timeout-graceful-shutdown 30 옵션으로 30초 상한을 걸어두면 균형이 맞았어요.
BackgroundTasks는 따로 챙겨야 해요
FastAPI의 BackgroundTasks는 응답을 보낸 뒤 같은 프로세스에서 돌아요. 응답은 나갔는데 프로세스가 죽으면 작업만 사라지는 구조예요. 테스트해 보면 응답 성공 로그는 있는데 후속 작업 기록이 없는, 찾기 어려운 형태로 남아요.
그래서 저는 기준을 하나 세웠어요. 몇 초 안에 끝나는 가벼운 작업만 BackgroundTasks에 두고, 잃으면 안 되는 작업은 DB에 먼저 기록한 뒤 처리해요. lifespan의 shutdown 구간에서 남은 작업을 마무리할 시간도 벌 수 있고요.
적용 결과
코드 변경은 10줄 안팎, 작업 시간은 5분 정도였어요. 재배포 도중 검색 요청을 돌려 보니 응답이 정상적으로 완료된 뒤에 프로세스가 내려갔어요. 배포 직후에만 찍히던 에러도 사라졌고요.
혼자 작은 도구를 운영하면서 배운 건, 배포는 시작보다 끝맺음이 중요하다는 거예요. 오늘 여러분 서비스의 Dockerfile CMD가 셸 형태인지부터 확인해 보세요. 두 줄 고치는 걸로 사용자 한 명의 요청을 지킬 수 있어요.
관련 글
댓글
아직 댓글이 없어요. 첫 댓글을 남겨주세요.