[Docker] Express 서버를 Docker에서 PM2로 기동하는 방법
module.exports = {
apps: [{
name: "app",
script: "./index.js",
instances: "5",
exec_mode: "cluster",
watch: false,
}]
}
ㅁ 관련글
ㅇ [Node.js] PM2를 사용한 Node.js 관리하기(PM2 사용법 정리)
ㅇ [Docker] Docker와 pm2를 함께 사용하는 것이 불리한 이유
ㅇ [Docker] Express 서버를 Docker에서 PM2로 기동하는 방법
ㅇ [Grafana] grafana k6로 테스트 환경 구성(grafana, influxdb, k6)
ㅁ 들어가며
[Node.js] PM2를 사용한 Node.js 애플리케이션 관리하기에서 PM2의 기능에 대해서 알아보았다. PM2는 Node.js 애플리케이션을 위한 프로세스를 관리하며, 어플의 성능을 모니터링하고 장애 발생 시 자동으로 재시작하는 기능을 제공한다. 이번 글에서는 Node.js 기반의 Express 서버를 Docker에서 PM2로 실행하는 방법을 정리하였다.
ㅁ Node.js Express 애플리케이션 설정
mkdir express-docker-pm2
cd express-docker-pm2
npm init -y
npm install express
ㅇ 프로젝트 생성
const express = require('express');
const app = express();
const PORT = process.env.PORT || 3000;
app.get('/', (req, res) => {
res.send('Hello World!');
});
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});
ㅇ index.js 파일을 생성하고 기본 서버 코드를 작성한다.
ㅁ PM2 설치
npm install pm2 --save
ㅇ PM2를 프로젝트에 설치한다.
ㅇ 기본적으로 pm2-runtime도 함께 설치 된다.
$ pm2-runtime -h
Usage: pm2-runtime app.js
pm2-runtime is a drop-in replacement Node.js binary for containers
Options:
-V, --version output the version number
-i --instances <number> launch [number] of processes automatically load-balanced. Increase overall performances and performance stability.
--secret [key] [MONITORING] PM2 plus secret key
--no-autostart add an app without automatic start
--no-autorestart start an app without automatic restart
--stop-exit-codes <exit_codes...> specify a list of exit codes that should skip automatic restart
--node-args <node_args> space delimited arguments to pass to node in cluster mode - e.g. --node-args="--debug=7001 --trace-deprecation"
-n --name <name> set a <name> for script
--max-memory-restart <memory> specify max memory amount used to autorestart (in octet or use syntax like 100M)
-c --cron <cron_pattern> restart a running process based on a cron pattern
--interpreter <interpreter> the interpreter pm2 should use for executing app (bash, python...)
--public [key] [MONITORING] PM2 plus public key
--machine-name [name] [MONITORING] PM2 plus machine name
--trace enable transaction tracing with km
--v8 enable v8 data collecting
--format output logs formated like key=val
--raw raw output (default mode)
--formatted formatted log output |id|app|log
--json output logs in json format
--delay <seconds> delay start of configuration file by <seconds> (default: 0)
--web [port] launch process web api on [port] (default to 9615)
--only <application-name> only act on one application of configuration
--no-auto-exit do not exit if all processes are errored/stopped or 0 apps launched
--env [name] inject env_[name] env variables in process config file
--watch watch and restart application on file change
--error <path> error log file destination (default disabled) (default: /dev/null)
--output <path> output log file destination (default disabled) (default: /dev/null)
--deep-monitoring enable all monitoring tools (equivalent to --v8 --event-loop-inspector --trace)
-h, --help output usage information
Commands:
*
start <app.js|json_file> start an application or json ecosystem file
ㅇ pm2-runtime은 컨테이너를 위한 드롭인 대체 Node.js 바이너리이다.
ㅁ Dockerfile 작성
ㅇ 이제 Dockerfile을 작성하여 애플리케이션을 Docker 이미지로 생성해야 한다.
# Node.js 공식 이미지 사용
FROM node:14
# 작업 디렉토리 생성
WORKDIR /usr/src/app
# 패키지 파일 복사
COPY package*.json ./
# 패키지 설치
RUN npm install
# 애플리케이션 코드 복사
COPY . .
# PM2를 전역으로 설치
RUN npm install pm2 -g
# PM2를 사용하여 애플리케이션 실행
CMD ["pm2-runtime", "index.js"]
# 컨테이너가 수신할 포트 설정
EXPOSE 3000
ㅇ 프로젝트 루트 디렉토리에 Dockerfile을 생성.
ㅁ Docker Compose 설정
version: '3'
services:
app:
build:
context: .
dockerfile: Dockerfile
ports:
- "3000:3000"
volumes:
- .:/usr/src/app
- /usr/src/app/node_modules
environment:
- NODE_ENV=production
restart: unless-stopped
ㅇ 편의성을 위해 docker-compose.yml 파일을 작성할 수도 있다.
ㅇ version: '3': Docker Compose 파일 형식의 버전을 지정
ㅇ services: 실행할 서비스를 정의.
ㅇ app: 서비스의 이름.
ㅇ build: Dockerfile을 사용하여 이미지를 빌드하도록 지정
ㅇ "context: ." 현재 디렉토리를 빌드 컨텍스트로 사용합니다.
ㅇ dockerfile: Dockerfile: 사용할 Dockerfile을 지정
ㅇ ports: 호스트의 3000번 포트와 컨테이너의 3000번 포트를 매핑
ㅇ volumes:
ㄴ .:/usr/src/app: 현재 디렉토리를 컨테이너의 /usr/src/app에 마운트
ㅇ /usr/src/app/node_modules: node_modules 디렉토리를 위한 익명 볼륨을 생성
ㅇ environment: NODE_ENV 환경 변수를 production으로 설정
ㅇ restart: unless-stopped: 컨테이너가 중지되지 않는 한 항상 재시작하도록 설정
ㅁ Docker 이미지 빌드 및 실행
# Docker 이미지 빌드
docker build -t express-docker-pm2 .
# Docker Compose를 사용하여 실행
docker compose up
ㅇ Dockerfile 혹은 Docker compose로 빌드를 수행한다.
ㅇ docker build를 수행하였다.
docker run -d -p 3000:3000 express-docker-pm2
ㅇ docker를 실행한다.
ㅁ 애플리케이션 테스트
ㅇ 브라우저에서 http://localhost:3000
에 접속하여 "Hello World!" 메시지가 나타나면 성공이다.
pm2 fork vs cluster 모드 비교
# PM2를 사용하여 애플리케이션 실행
CMD ["pm2-runtime", "index.js"]
ㅇ Dockerfile의 커멘드로 실행할 경우 pm2의 인스턴스 모드가 fork이다.
ㅇ 이런 경우 단일 인스턴스로 사용할 경우에는 적당하지만 scale up down 시 에러가 발생한다.
ㅇ scale up을 수행하였는데, errored 상태가 되었다.
ㅁ cluster 모드 실행 방법1
module.exports = {
apps : [{
name : "app",
script : "./index.js",
exec_mode: "cluster",
instances: 10,
watch: false,
}]
}
ㅇ ecosystem.config.js을 작성하여 mode를 설정한다.
# PM2를 사용하여 애플리케이션 실행
CMD ["pm2-runtime", "ecosystem.config.js"]
ㅇ Dockerfile을 수정
# 도커 재빌드
$ docker build -t express-docker-pm2 .
# 도커 실행
$ docker run -d -p 3100:3000 --name pm2test express-docker-pm2
# 컨테이너 쉘 실행
$ docker exec -it pm2test bash
ㅇ cluster 모드로 실행된 것을 확인할 수 있다.
# scale up
$ pm2 scale app 15
[PM2] Scaling up application
[PM2] Scaling up application
[PM2] Scaling up application
[PM2] Scaling up application
[PM2] Scaling up application
ㅇ scale up하여도 error가 발생하지 않는다.
ㅁ 함께 보면 좋은 사이트
ㅇ NGINX + HTTPS + NodeJS + PM2 도커로 만들기
ㅇ nodejs express 서버 docker에서 pm2로 기동하기
ㅇ Docker + PM2 + winston.js를 활용한 무중단 운영 시스템 구축하기
ㅇ 도커와 PM2를 이용한 Node.js scale-out 및 부하 테스트