Railway 재배포 때 뜨는 500 에러, SIGTERM 핸들링으로 없애기
Railway 재배포 순간에만 발생하는 500 에러의 원인과 SIGTERM 핸들링, graceful shutdown 구현을 Node.js 예제로 정리했어요.
배포할 때마다 딱 몇 초씩 500 에러가 뜬 적 없나요? 코드는 멀쩡한데 재배포 순간에만 실패하는 요청이 생기는 상황이요. 혼자 운영하면서 직접 겪어보니 원인은 코드가 아니라 종료 방식에 있었어요.
왜 재배포 순간에만 500이 뜰까요
Railway는 재배포 시 새 인스턴스를 띄운 뒤 기존 인스턴스에 SIGTERM 신호를 보내요. 이때 핸들러가 없으면 프로세스가 그 자리에서 죽어요.
문제는 타이밍이에요. 프로세스가 죽는 순간 처리 중이던 요청은 응답을 못 받고 끊기고, 사용자 화면에는 500이나 502가 떠요. 트래픽이 적어도 배포 시점에 요청이 걸려 있으면 반드시 발생해요.
graceful shutdown, 3단계면 끝나요
종료 신호를 받으면 바로 죽는 대신 순서대로 정리하는 게 핵심이에요.
Express 기준으로 20줄이 안 되는 코드예요.
const server = app.listen(process.env.PORT);
process.on('SIGTERM', () => {
server.close(async () => {
// release DB pool before exit
await db.end();
process.exit(0);
});
// force exit if in-flight requests hang
setTimeout(() => process.exit(1), 5000).unref();
});server.close()는 새 연결을 막고, 이미 들어온 요청이 끝날 때까지 기다려 줘요. 저는 대기 상한을 5초로 잡았어요. 어떤 요청이 안 끝나고 버티면 무한 대기에 빠지니, 타임아웃으로 탈출구를 만들어 두는 거예요.
Railway 쪽에서 챙길 두 가지
- healthcheck 경로 설정
- 종료 유예 시간 환경 변수 조정
healthcheck 경로를 설정하면 새 인스턴스가 준비된 뒤에 트래픽이 넘어와요. 이게 없으면 부팅이 덜 된 서버로 요청이 갈 수 있어요.
정리 작업이 오래 걸리는 서버라면 종료 유예 시간도 봐야 해요. Railway 공식 문서 기준으로 RAILWAY_DEPLOYMENT_DRAINING_SECONDS 환경 변수로 SIGTERM 이후 대기 시간을 조정할 수 있어요. 앱의 타임아웃보다 짧으면 정리가 끝나기 전에 강제 종료되니 맞춰 주세요.
적용해 봤더니
플랜엘(plan-l) API 서버를 Railway에서 운영하는데, 핸들러 추가부터 배포 확인까지 10분 정도 걸렸어요. 적용 후에는 재배포 중 500 에러가 0건이에요. 테스트해 보면 배포 직후 로그에서 기존 인스턴스가 요청을 마저 처리하고 내려가는 게 보여요.
한 가지 더, 로컬에서 Ctrl+C로 끄는 SIGINT도 같은 핸들러로 묶어 두면 개발 중 DB 커넥션이 깔끔하게 정리돼요.
배포가 무서우면 자주 못 하고, 자주 못 하면 개선이 느려져요. 시작은 종료를 설계하는 일이에요.
관련 글
댓글
아직 댓글이 없어요. 첫 댓글을 남겨주세요.