infra

PM2로 Node.js 앱을 서버에서 관리하는 방법 — 프로세스 자동 재시작과 startup 설정

nohup으로 Node.js를 띄우다가 PM2로 전환하면 프로세스 자동 재시작, 서버 재부팅 후 자동 실행, 로그 관리까지 한 번에 해결된다. 설치부터 ecosystem.config.js 설정까지 정리했다.

PM2Node.js프로세스관리infra서버
pm2 list 명령 실행 결과 화면 — Node.js 앱 이름, online 상태, CPU와 메모리 사용량이 테이블로 표시됨
  • ·PM2: 2013년 출시, Node.js 프로세스 관리 사실상 표준 도구
  • ·pm2 startup: OS 부팅 시 자동 실행을 위한 systemd 스크립트 자동 생성
  • ·cluster 모드: CPU 코어 수만큼 프로세스를 포크해 트래픽 분산
  • ·pm2 save: 현재 실행 중인 프로세스 목록을 저장해 재부팅 후 복원
Node.js 앱을 처음 서버에 올릴 때 nohup node app.js &로 띄웠는데, 앱이 크래시되면 아무도 모르게 서비스가 죽어 있는 걸 한참 후에 발견한 적이 있었다. PM2로 전환한 뒤에는 프로세스가 죽으면 자동으로 재시작되고 재시작 횟수도 pm2 list에서 바로 확인할 수 있어서 문제가 생겨도 즉시 인지할 수 있게 됐다. ecosystem.config.js 하나에 앱 설정과 환경변수를 모아두고 나니 서버를 새로 셋업할 때도 파일 하나만 올리면 환경이 그대로 재현됐다.

PM2란 무엇인가

PM2가 Node.js 프로세스 관리에 쓰이는 이유

PM2는 Node.js 애플리케이션을 서버에서 안정적으로 운영하기 위한 프로세스 매니저다. 개발 환경에서는 node app.js나 npm start로 직접 실행해도 되지만, 프로덕션 서버에서는 예기치 않은 크래시나 재부팅 이후에도 앱이 자동으로 복구되어야 한다. PM2는 이 요구를 채워주는 도구다. 프로세스가 에러로 종료되면 자동으로 재시작하고, pm2 startup 명령으로 서버 재부팅 후 자동 실행 스크립트를 OS에 등록할 수 있다. pm2 list 명령으로 실행 중인 프로세스 전체를 테이블 형식으로 확인할 수 있고, pm2 logs로 실시간 로그를 볼 수 있다. pm2 stop, pm2 restart, pm2 delete로 개별 앱을 관리하는 것도 직관적이다. cluster 모드를 사용하면 PM2가 CPU 코어 수에 맞게 프로세스를 여러 개 포크해서 트래픽을 분산시킨다. Node.js는 단일 스레드이기 때문에 싱글 프로세스로 운영하면 멀티코어 서버의 CPU를 제대로 활용하지 못한다. PM2 cluster 모드로 이 한계를 보완할 수 있다. nohup이나 screen으로 앱을 띄우던 방식과 달리 PM2는 프로세스 상태를 파일로 저장하기 때문에 관리 명령이 훨씬 체계적이고 직관적이다. 처음 서버에 Node.js 앱을 올린다면 PM2를 함께 설치하는 것을 강하게 권장한다.

PM2와 nohup의 차이 — Node.js 서버에서 PM2를 써야 하는 이유

nohup node app.js & 방식은 터미널 세션이 끊겨도 프로세스가 계속 실행되게 하는 가장 단순한 방법이다. 명령 한 줄이면 되니까 처음 서버에 앱을 올릴 때 이 방식으로 시작하는 경우가 많다. 문제는 프로세스가 죽었을 때 자동으로 재시작하는 기능이 없다는 점이다. 메모리 누수나 처리되지 않은 예외로 앱이 종료되면 그 상태 그대로 방치된다. 서버 재부팅 후에도 수동으로 다시 시작해야 한다. 로그도 nohup.out 파일 하나에 쌓이기 때문에 시간이 지나면 관리가 어렵다. PM2는 이 모든 단점을 해결한다. 프로세스 자동 재시작, 서버 재부팅 후 자동 실행, 표준 출력과 오류 로그를 별도 파일로 분리 저장하는 기능이 모두 내장돼 있다. pm2 list에서 각 프로세스의 상태, CPU 사용률, 메모리 사용량, 재시작 횟수를 한눈에 볼 수 있다. 재시작 횟수가 비정상적으로 높으면 앱에 문제가 있다는 신호다. 직접 비교해보면 nohup 방식으로 운영하다가 PM2로 전환했을 때 서버 안정성이 체감상 확연히 달라진다는 걸 알 수 있다. 단기 테스트 환경에서는 nohup을 써도 되지만 실제 서비스를 운영하는 서버라면 PM2가 올바른 선택이다.

PM2로 Node.js 앱 실행하기

PM2로 Node.js 앱을 시작하고 프로세스를 관리하는 방법

PM2는 npm으로 전역 설치한다. npm install -g pm2 명령으로 설치하면 pm2 명령을 어디서든 쓸 수 있다. 앱을 시작하려면 pm2 start app.js --name 앱이름 형태로 실행한다. --name 옵션으로 프로세스 이름을 지정해두면 나중에 pm2 restart 앱이름처럼 이름으로 관리할 수 있어서 편하다. Node.js 스크립트 직접 실행 외에 npm start를 PM2로 관리하려면 pm2 start npm --name 앱이름 -- start 형태로 쓴다. 실행 후 pm2 list로 프로세스 목록을 확인한다. status가 online이면 정상 실행 중이고, errored면 시작에 실패한 것이다. pm2 logs 앱이름으로 해당 앱의 로그를 실시간으로 확인할 수 있다. 앱을 중지하려면 pm2 stop, 재시작은 pm2 restart, 완전 삭제는 pm2 delete다. 코드 변경 없이 설정만 바꿀 때는 pm2 reload를 쓰면 무중단으로 재시작된다. PM2를 처음 쓸 때 pm2 start로 여러 번 실행하면 같은 앱이 여러 개 등록되는 실수를 하기 쉽다. 먼저 pm2 list로 실행 중인 프로세스를 확인하고 이미 있으면 pm2 restart를 쓰는 게 맞다.

# 설치
npm install -g pm2

# 앱 시작
pm2 start app.js --name my-app

# npm start 방식
pm2 start npm --name my-app -- start

# 상태 확인
pm2 list
pm2 logs my-app

# 재시작 / 중지 / 삭제
pm2 restart my-app
pm2 stop my-app
pm2 delete my-app

PM2 ecosystem.config.js로 Node.js 앱 설정을 관리하는 방법

매번 pm2 start 명령에 옵션을 일일이 입력하는 대신 ecosystem.config.js 파일에 설정을 정의해두면 관리가 훨씬 편하다. 프로젝트 루트에 ecosystem.config.js 파일을 만들고 module.exports = { apps: [...] } 형태로 작성한다. apps 배열 안에 각 앱의 설정 객체를 넣으면 된다. name, script, instances, exec_mode, env 같은 필드를 사용한다. instances를 'max'로 설정하면 CPU 코어 수만큼 프로세스를 생성하고, exec_mode를 'cluster'로 설정하면 cluster 모드로 실행된다. env 객체에 환경변수를 설정하면 PM2가 앱을 시작할 때 해당 환경변수를 주입한다. env_production처럼 환경별 설정을 분리할 수도 있다. pm2 start ecosystem.config.js로 실행하고, pm2 start ecosystem.config.js --env production으로 프로덕션 환경변수를 적용할 수 있다. 서버를 새로 셋업하거나 다른 서버로 이전할 때 ecosystem.config.js 파일 하나만 가져오면 동일한 설정을 그대로 재현할 수 있어서 인프라 관리가 단순해진다. Git 저장소에 올려두면 팀 전체가 같은 PM2 설정을 공유할 수 있다. 다만 환경변수에 시크릿 값이 포함된다면 .gitignore로 제외하거나 서버에서만 관리해야 한다.

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

# 실행
pm2 start ecosystem.config.js
pm2 start ecosystem.config.js --env production

PM2 서버 재시작 후 자동 실행과 로그 관리

PM2 startup 명령으로 Node.js 앱을 서버 재부팅 후 자동 실행하는 방법

PM2로 앱을 실행한다고 해서 서버가 재부팅됐을 때 자동으로 올라오는 건 아니다. 재부팅 후 자동 실행을 설정하려면 pm2 startup 명령이 필요하다. pm2 startup을 실행하면 OS에 맞는 설정 명령이 출력된다. Ubuntu 기반이라면 sudo env PATH=... pm2 startup systemd -u 사용자 --hp /home/사용자 형태의 명령이 출력된다. 이 명령을 복사해서 그대로 실행하면 systemd에 PM2 서비스가 등록된다. 이후 서버가 재부팅되면 systemd가 PM2를 자동으로 시작하고 PM2가 저장된 프로세스 목록을 불러와 앱을 자동으로 실행한다. 중요한 건 pm2 save 명령이다. startup 설정 후 현재 실행 중인 프로세스 목록을 저장해두어야 재부팅 후 그 목록을 기반으로 앱이 올라온다. pm2 startup을 해도 pm2 save를 빠뜨리면 재부팅 후 앱이 실행되지 않는다. 앱을 새로 추가하거나 설정을 변경할 때마다 pm2 save를 다시 실행해서 목록을 업데이트해야 한다. 이 두 명령의 관계를 처음에 헷갈려서 재부팅 후 앱이 안 뜨는 상황을 겪은 뒤에야 정확히 이해했다. startup은 PM2 자체를 자동 실행하는 것이고, save는 어떤 앱을 띄울지 목록을 저장하는 것이다.

PM2 로그를 확인하고 PM2 logrotate로 관리하는 방법

PM2는 각 앱의 표준 출력과 오류 출력을 자동으로 파일에 기록한다. 기본 로그 경로는 ~/.pm2/logs/ 디렉토리다. 앱 이름이 my-app이라면 my-app-out.log와 my-app-error.log가 생성된다. pm2 logs 명령으로 모든 앱의 로그를 실시간으로 확인할 수 있고, pm2 logs my-app처럼 특정 앱만 볼 수도 있다. --lines 옵션으로 최근 N줄만 출력하는 것도 가능하다. 로그 파일이 계속 쌓이면 디스크를 차지하기 때문에 로그 로테이션 설정을 해두는 게 좋다. pm2-logrotate 모듈을 설치하면 자동으로 로그 파일 크기와 보존 기간을 관리해준다. pm2 install pm2-logrotate로 설치하고, pm2 set pm2-logrotate:max_size 10M으로 파일 최대 크기를 설정할 수 있다. pm2 flush로 로그 파일 내용을 전부 비울 수도 있는데, 과거 로그가 필요 없을 때 디스크를 정리하는 용도로 쓴다. 직접 운영해보면 로그가 수백 MB까지 쌓이는 건 생각보다 빠르다. 처음 PM2를 셋업할 때 pm2-logrotate도 함께 설정해두는 게 나중에 로그 정리 작업을 따로 안 해도 되는 방법이다.

자주 묻는 질문

PM2 startup 후 서버 재부팅 시 앱이 자동으로 실행되지 않는 이유가 무엇인가요?+

pm2 startup 후 pm2 save를 실행하지 않은 경우가 가장 흔한 원인입니다. startup은 PM2 자체를 자동 실행하도록 OS에 등록하는 것이고, save는 현재 실행 중인 프로세스 목록을 저장하는 명령입니다. 두 가지를 모두 실행해야 재부팅 후 앱이 자동으로 올라옵니다.

PM2 cluster 모드와 fork 모드의 차이는 무엇인가요?+

fork 모드(기본값)는 프로세스를 단순히 포크해서 실행합니다. cluster 모드는 Node.js 내장 cluster 모듈을 사용해 여러 프로세스가 동일한 포트에서 HTTP 요청을 나눠 처리합니다. CPU 코어가 여러 개인 서버에서 성능을 최대로 활용하려면 cluster 모드에 instances: 'max'를 설정하는 게 좋습니다.

PM2로 Next.js 앱을 실행하려면 어떻게 해야 하나요?+

package.json의 start 스크립트가 next start로 설정되어 있다면 pm2 start npm --name next-app -- start로 실행할 수 있습니다. ecosystem.config.js에서는 script: 'node_modules/.bin/next', args: 'start'로 설정하는 방법도 있습니다. next build는 배포 전 별도로 실행해야 합니다.

pm2 list에서 앱이 errored 상태일 때 원인을 어떻게 확인하나요?+

pm2 logs 앱이름 명령으로 오류 로그를 확인하세요. ~/.pm2/logs/앱이름-error.log 파일에 스택 트레이스가 기록됩니다. 포트 충돌, 환경변수 누락, 모듈 경로 오류가 흔한 원인입니다.

관련 글