포스트

PM2로 Python 애플리케이션 관리하기

목차

  1. PM2란 무엇인가?
  2. PM2 설치하기
  3. Python 애플리케이션 관리하기
  4. 고급 설정 및 운영

PM2란 무엇인가?

PM2(Process Manager 2)는 Node.js 애플리케이션을 위한 프로덕션 레벨의 프로세스 매니저다.
하지만 Python, Ruby, PHP 등 다른 언어로 작성된 애플리케이션도 관리할 수 있다.

핵심 기능

1. 프로세스 관리

  • 애플리케이션 시작, 중지, 재시작, 삭제
  • 자동 재시작
  • 클러스터 모드 지원 (CPU 코어별 프로세스 분산)
  • 메모리 제한 설정 및 자동 재시작

2. 모니터링

  • 실시간 CPU, 메모리 사용량 모니터링
  • 로그 관리 (stdout, stderr)
  • 프로세스 상태 확인
  • 성능 메트릭 수집

3. 배포 및 운영

  • 무중단 재시작
  • 서버 재부팅 시 자동 실행
  • 환경 변수 관리
  • 설정 파일 기반 관리

왜 PM2를 사용하는가?

단순 실행 vs PM2 관리

1
2
3
4
5
6
7
8
# 일반적인 실행 방식
python app.py

# 문제점:
# - 터미널을 닫으면 프로세스도 종료
# - 자동 재시작 불가
# - 로그 관리 어려움
# - 모니터링 불가
1
2
3
4
5
6
7
8
# PM2로 실행
pm2 start app.py --name my-app

# 장점:
# - 백그라운드에서 안정적으로 실행
# - 통합 로그 관리
# - 실시간 모니터링
# - 서버 재부팅 시 자동 실행

PM2 설치하기

PM2는 Node.js 기반이므로 Node.js와 npm이 필요

STEP 1: Node.js 설치

PM2를 사용하기 위해서는 Node.js가 설치되어 있어야 함

Windows

1
2
3
4
5
# Node.js 공식 사이트에서 설치 프로그램 다운로드
# https://nodejs.org/

# 또는 Chocolatey 사용
choco install nodejs

macOS

1
2
# Homebrew 사용
brew install node

Linux (Ubuntu/Debian)

1
2
3
# Node.js 20.x LTS 설치
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt-get install -y nodejs

설치 확인

1
2
node --version
npm --version

STEP 2: PM2 설치

Node.js가 설치되었다면 npm을 통해 PM2를 전역으로 설치

1
2
3
4
5
# npm을 사용한 전역 설치
npm install -g pm2

# 또는 yarn 사용
yarn global add pm2

STEP 3: 설치 확인

1
2
3
4
5
# PM2 버전 확인
pm2 --version

# PM2 명령어 도움말
pm2 --help

출력 예시:

1
2
3
4
5
6
7
Usage: pm2 [cmd] app

Commands:
  start [options] <file|json>        start and daemonize an app
  stop <id|name|all|json>           stop a process
  restart <id|name|all|json>        restart a process
  ...

Python 애플리케이션 관리하기

PM2로 Python 애플리케이션을 관리하는 방법

기본 사용법

1. Python 스크립트 실행

1
2
3
4
5
6
7
8
9
10
11
# 기본 실행
pm2 start app.py

# 애플리케이션 이름 지정
pm2 start app.py --name my-python-app

# Python 인터프리터 명시적으로 지정
pm2 start app.py --interpreter python3

# 특정 Python 가상환경 사용
pm2 start app.py --interpreter /path/to/venv/bin/python

2. 명령어 인자 전달

1
2
3
4
5
# Python 스크립트에 인자 전달
pm2 start app.py --name api-server -- --port 8000 --host 0.0.0.0

# 이는 다음과 같이 실행됨:
# python app.py --port 8000 --host 0.0.0.0

3. 환경 변수 설정

1
2
3
4
5
# 환경 변수와 함께 실행
pm2 start app.py --name my-app --env production

# 또는 직접 환경 변수 전달
pm2 start app.py --name my-app -- --env DATABASE_URL=postgresql://localhost/db

프로세스 관리 명령어

프로세스 상태 확인

1
2
3
4
5
6
7
8
9
# 모든 프로세스 목록 보기
pm2 list
# 또는
pm2 ls

# 특정 프로세스 상세 정보
pm2 show my-app
# 또는 프로세스 ID 사용
pm2 show 0

출력 예시:

1
2
3
4
5
┌─────┬───────────────┬─────────┬─────────┬───────────┬────────┬─────────┐
│ id  │ name          │ mode    │ ↺      │ status    │ cpu    │ memory  │
├─────┼───────────────┼─────────┼─────────┼───────────┼────────┼─────────┤
│ 0   │ my-python-app │ fork    │ 15      │ online    │ 0%     │ 50.2mb  │
└─────┴───────────────┴─────────┴─────────┴───────────┴────────┴─────────┘

프로세스 제어

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 프로세스 중지
pm2 stop my-app

# 프로세스 재시작
pm2 restart my-app

# 프로세스 삭제 (중지 후 목록에서 제거)
pm2 delete my-app

# 모든 프로세스 재시작
pm2 restart all

# 무중단 재시작 (0-downtime reload)
pm2 reload my-app

로그 관리

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 실시간 로그 보기
pm2 logs

# 특정 앱의 로그만 보기
pm2 logs my-app

# 로그 줄 수 제한
pm2 logs --lines 100

# 로그 파일 경로 확인
pm2 show my-app | grep log

# 로그 파일 비우기
pm2 flush

# 로그 파일 완전 삭제
pm2 flush my-app

모니터링

1
2
3
4
5
# 실시간 모니터링 대시보드
pm2 monit

# CPU/메모리 사용량 확인
pm2 list

설정 파일 기반 관리 (ecosystem.config.js)

복잡한 설정은 설정 파일로 관리하는 것이 편리함

ecosystem.config.js 생성

1
2
# 기본 설정 파일 생성
pm2 ecosystem

Python 애플리케이션을 위한 설정 예시

ecosystem.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
module.exports = {
  apps: [
    {
      name: "flask-api",
      script: "app.py",
      interpreter: "python3",
      args: "--host 0.0.0.0 --port 5000",
      instances: 1,
      autorestart: true,
      watch: false,
      max_memory_restart: "500M",
      env: {
        NODE_ENV: "development",
        FLASK_ENV: "development",
        DATABASE_URL: "postgresql://localhost/dev_db"
      },
      env_production: {
        NODE_ENV: "production",
        FLASK_ENV: "production",
        DATABASE_URL: "postgresql://localhost/prod_db"
      },
      error_file: "./logs/err.log",
      out_file: "./logs/out.log",
      log_date_format: "YYYY-MM-DD HH:mm:ss Z",
      merge_logs: true,
      min_uptime: "10s",
      max_restarts: 10,
      restart_delay: 4000
    },
    {
      name: "celery-worker",
      script: "celery_worker.py",
      interpreter: "/path/to/venv/bin/python",
      args: "-A tasks worker --loglevel=info",
      instances: 2,
      exec_mode: "fork",
      autorestart: true,
      watch: false,
      max_memory_restart: "1G",
      env: {
        CELERY_BROKER_URL: "redis://localhost:6379/0"
      }
    },
    {
      name: "data-processor",
      script: "process_data.py",
      interpreter: "python3",
      cron_restart: "0 0 * * *",
      autorestart: false,
      watch: false
    }
  ]
};

주요 설정 옵션

옵션설명
name애플리케이션 이름
script실행할 스크립트 파일
interpreter사용할 인터프리터 (python, python3, venv 경로 등)
args스크립트에 전달할 인자
instances실행할 인스턴스 수 (클러스터 모드)
exec_mode실행 모드 (fork 또는 cluster)
autorestart크래시 시 자동 재시작 여부
watch파일 변경 감지 및 자동 재시작
max_memory_restart메모리 제한 (초과 시 재시작)
env환경 변수
error_file에러 로그 파일 경로
out_file출력 로그 파일 경로
cron_restartCron 패턴으로 주기적 재시작
min_uptime최소 실행 시간 (이보다 빨리 종료되면 에러로 간주)
max_restarts최대 재시작 횟수

설정 파일로 실행

1
2
3
4
5
6
7
8
9
10
11
# 개발 환경으로 실행
pm2 start ecosystem.config.js

# 프로덕션 환경으로 실행
pm2 start ecosystem.config.js --env production

# 특정 앱만 실행
pm2 start ecosystem.config.js --only flask-api

# 설정 파일 재적용
pm2 reload ecosystem.config.js

실전 예제

1. Flask API 서버

app.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from flask import Flask, jsonify
import os

app = Flask(__name__)

@app.route('/health')
def health():
    return jsonify({"status": "healthy"})

@app.route('/')
def hello():
    env = os.getenv('FLASK_ENV', 'development')
    return jsonify({"message": f"Hello from {env}!"})

if __name__ == '__main__':
    port = int(os.getenv('PORT', 5000))
    app.run(host='0.0.0.0', port=port)

PM2로 실행

1
2
3
4
5
6
7
8
# 기본 실행
pm2 start app.py --name flask-api --interpreter python3

# 포트 지정
pm2 start app.py --name flask-api --interpreter python3 -- --port 8000

# 설정 파일 사용
pm2 start ecosystem.config.js

2. FastAPI 애플리케이션

main.py

1
2
3
4
5
6
7
8
9
10
11
from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def root():
    return {"message": "Hello World"}

@app.get("/health")
async def health():
    return {"status": "ok"}

PM2로 실행 (Uvicorn 사용)

1
2
3
4
# Uvicorn으로 FastAPI 실행
pm2 start "uvicorn main:app --host 0.0.0.0 --port 8000" --name fastapi-app

# 또는 ecosystem.config.js 사용

ecosystem.config.js for FastAPI

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
module.exports = {
  apps: [{
    name: "fastapi-app",
    script: "uvicorn",
    args: "main:app --host 0.0.0.0 --port 8000",
    interpreter: "python3",
    exec_mode: "fork",
    instances: 1,
    autorestart: true,
    watch: false,
    max_memory_restart: "500M",
    env: {
      PORT: 8000
    }
  }]
};

3. Django 애플리케이션

1
2
# Gunicorn으로 Django 실행
pm2 start "gunicorn myproject.wsgi:application --bind 0.0.0.0:8000" --name django-app

ecosystem.config.js for Django

1
2
3
4
5
6
7
8
9
10
module.exports = {
  apps: [{
    name: "django-app",
    script: "gunicorn",
    args: "myproject.wsgi:application --bind 0.0.0.0:8000 --workers 4",
    interpreter: "/path/to/venv/bin/python",
    autorestart: true,
    max_memory_restart: "1G"
  }]
};

고급 설정 및 운영

서버 재부팅 시 자동 실행

PM2로 관리하는 프로세스를 서버 재부팅 후에도 자동으로 실행되도록 설정할 수 있음

1
2
3
4
5
6
7
8
# 현재 실행 중인 프로세스 저장
pm2 save

# 부팅 시 자동 실행 설정
pm2 startup

# 출력되는 명령어를 복사해서 실행 (sudo 권한 필요)
# 예: sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u user --hp /home/user

설정 해제

1
pm2 unstartup

클러스터 모드

여러 인스턴스를 실행하여 부하를 분산할 수 있음

1
2
3
4
5
6
7
8
9
# CPU 코어 수만큼 인스턴스 실행
pm2 start app.py -i max

# 특정 개수의 인스턴스 실행
pm2 start app.py -i 4

# 스케일링 (인스턴스 추가/제거)
pm2 scale my-app +3
pm2 scale my-app 2

ecosystem.config.js에서 설정

1
2
3
4
5
6
7
8
9
module.exports = {
  apps: [{
    name: "api-cluster",
    script: "app.py",
    interpreter: "python3",
    instances: "max",
    exec_mode: "cluster"
  }]
};

메모리 관리

1
2
# 메모리 제한 설정 (초과 시 자동 재시작)
pm2 start app.py --max-memory-restart 500M

설정 파일에서

1
2
3
{
  max_memory_restart: "500M"
}

로그 로테이션

PM2는 기본적으로 로그 로테이션을 제공하지 않으므로 pm2-logrotate 모듈을 설치

1
2
3
4
5
6
7
8
9
10
# pm2-logrotate 설치
pm2 install pm2-logrotate

# 설정 확인
pm2 conf pm2-logrotate

# 로그 로테이션 설정
pm2 set pm2-logrotate:max_size 10M
pm2 set pm2-logrotate:retain 7
pm2 set pm2-logrotate:compress true

유용한 팁

1. 프로세스 ID를 이용한 관리

1
2
3
4
5
# ID로 재시작
pm2 restart 0

# 여러 프로세스 동시 제어
pm2 restart 0 2 4

2. JSON 출력으로 스크립트 연동

1
2
3
4
5
# JSON 형식으로 프로세스 목록 출력
pm2 jlist

# 프로그래밍 방식으로 활용
pm2 jlist | jq '.[0].pm2_env.status'

3. 환경별 설정 관리

1
2
3
4
5
6
7
8
# 개발 환경
pm2 start ecosystem.config.js --env development

# 스테이징 환경
pm2 start ecosystem.config.js --env staging

# 프로덕션 환경
pm2 start ecosystem.config.js --env production

트러블슈팅

문제: PM2가 Python을 찾지 못함

1
2
3
4
5
# Python 경로 확인
which python3

# 명시적으로 경로 지정
pm2 start app.py --interpreter /usr/bin/python3

문제: 가상환경의 Python 사용

1
2
3
4
5
6
# 가상환경 활성화 후 Python 경로 확인
source venv/bin/activate
which python

# 해당 경로로 실행
pm2 start app.py --interpreter /path/to/venv/bin/python

문제: 프로세스가 계속 재시작됨

1
2
3
4
5
# 로그 확인
pm2 logs my-app

# 최소 실행 시간 설정 (빠른 재시작 방지)
pm2 start app.py --min-uptime 10000

문제: 메모리 부족

1
2
3
4
5
# 메모리 사용량 확인
pm2 monit

# 메모리 제한 설정
pm2 start app.py --max-memory-restart 500M

PM2를 활용해서도 Python 애플리케이션을 안정적이고 효율적으로 운영할 수 있다.