Node.js 프로젝트에서 PM2로 무중단 배포 구성하기

문제 상황

운영 중인 Node.js API 서버를 배포할 때마다 pm2 restart 명령으로 재시작하고 있었다. 문제는 재시작 중 몇 초간 서비스가 중단되면서 클라이언트에서 502 에러가 발생한다는 것이었다.

PM2 Cluster Mode

PM2는 클러스터 모드로 여러 인스턴스를 실행할 수 있다. CPU 코어 수만큼 프로세스를 띄워 부하 분산도 가능하다.

// ecosystem.config.js
module.exports = {
  apps: [{
    name: 'api-server',
    script: './app.js',
    instances: 'max',
    exec_mode: 'cluster',
    env: {
      NODE_ENV: 'production',
      PORT: 3000
    }
  }]
};

Reload vs Restart

핵심은 restart 대신 reload 명령을 사용하는 것이다.

# 기존 방식 - 모든 프로세스 동시 재시작
pm2 restart api-server

# 개선 방식 - 하나씩 순차적으로 재시작
pm2 reload api-server

reload는 클러스터 모드에서 프로세스를 하나씩 재시작한다. 한 인스턴스가 종료되고 새 버전이 시작될 때 다른 인스턴스들이 요청을 처리하므로 중단 시간이 없다.

Graceful Shutdown

애플리케이션 코드에서도 SIGINT 시그널을 처리해 진행 중인 요청을 완료한 후 종료되도록 했다.

const server = app.listen(3000);

process.on('SIGINT', () => {
  console.log('Received SIGINT, shutting down gracefully');
  server.close(() => {
    console.log('Server closed');
    process.exit(0);
  });
});

배포 스크립트

최종적으로 배포 스크립트는 다음과 같이 구성했다.

#!/bin/bash
git pull origin master
npm install --production
pm2 reload ecosystem.config.js

결과

배포 중에도 API 응답이 끊기지 않게 되었다. 모니터링 결과 배포 시점에 에러율 증가도 관찰되지 않았다. 단순한 설정 변경만으로 큰 개선을 얻을 수 있었다.