콘텐츠로 이동

서버 애플리케이션 운영 배포 기준 (PM2)

  • 운영 서버(Production Server) 환경에서 서버 애플리케이션을 PM2로 안정적으로 배포하기 위한 표준 절차를 정의함.

1. 기본 원칙

  • 릴리즈 디렉터리 + current 심링크 구조를 유지함
  • Node.js / npm / pm2 등의 실행 경로는 절대 고정
  • 동시에 여러 배포가 실행되지 않도록 lock 을 적용함

2. 표준 디렉터리 구조

PM2 운영 서버의 배포 베이스는 다음 구조를 표준으로 함.

/var/www/[service-name]
├── releases/
│   ├── [release-id]/
│   └── [release-id]/
├── current -> releases/[release-id]
├── shared/
├── logs/
├── tmp/
├── downloads/
└── deploy.lock

3. 배포 스크립트 표준 계약

  1. 배포 lock 획득
  2. 신규 릴리즈 디렉터리 생성
  3. 릴리즈 산출물 배치
  4. 필수 파일 검증
  5. shared 파일 연결
  6. runtime 의존성 설치 또는 검증
  7. current 심링크 전환
  8. PM2 reload, restart 또는 start
  9. PM2 상태 저장
  10. 오래된 릴리즈 정리
  11. Rollback 가능성 유지

4. 동시 배포 방지 기준

  • 배포 스크립트는 반드시 lock을 사용해야 함.
  • 표준 방식은 flock 기반 lock임.

예시:

LOCK_FILE="$DEPLOY_BASE/deploy.lock"

mkdir -p "$DEPLOY_BASE"

exec 9>"$LOCK_FILE"

if ! flock -n 9; then
    echo "이미 배포가 진행 중임."
    echo "lock file: $LOCK_FILE"
    exit 1
fi

5. current 심링크 전환 기준

  • Linux 운영 서버에서는 임시 심링크를 먼저 생성한 뒤 mv -T로 교체하는 방식을 표준으로 함.

예시:

TMP_CURRENT_LINK="$CURRENT_LINK.tmp.$$"

ln -s "$NEW_RELEASE" "$TMP_CURRENT_LINK"
mv -Tf "$TMP_CURRENT_LINK" "$CURRENT_LINK"

6. PM2 실행 기준

  • PM2 실행 기준 파일은 current 내부 파일이어야 함
  • 환경 변수 변경이 반영되도록 --update-env를 사용
  • 실행 후 pm2 save --force를 수행
/var/www/[service-name]/current/ecosystem.config.cjs

예시:

if "$PM2" describe "$PM2_PROCESS_NAME" >/dev/null 2>&1; then
    "$PM2" reload "$CURRENT_LINK/ecosystem.config.cjs" --env production --update-env \
        || "$PM2" restart "$CURRENT_LINK/ecosystem.config.cjs" --env production --update-env
else
    "$PM2" start "$CURRENT_LINK/ecosystem.config.cjs" --env production --update-env
fi

"$PM2" save --force

7. 오래된 릴리즈 정리 기준

  • 배포 완료 후 오래된 릴리즈를 정리함.
  • 단, 즉시 롤백할 수 있도록 최소 1개 이상의 이전 릴리즈는 유지해야 함.

예시:

cd "$RELEASES_PATH"

CURRENT_REAL_PATH="$(readlink -f "$CURRENT_LINK")"

ls -1dt */ | tail -n +3 | while read dir; do
    DELETE_TARGET="$(readlink -f "$RELEASES_PATH/$dir")"

    if [ "$DELETE_TARGET" = "$CURRENT_REAL_PATH" ]; then
        echo "Skip current release: $dir"
        continue
    fi

    echo "Removing old release: $dir"
    rm -rf "$RELEASES_PATH/$dir"
done