Posts container 안에서 pm2로 next.js 앱 실행하기
Post
Cancel

container 안에서 pm2로 next.js 앱 실행하기

next.js는 기본적으로 node.js runtime에서 작동된다. 하지만 node.js 는 싱글 스레드 기반이고, 따라서 트래픽이 많은 환경에서 multi-core를 사용하여 node.js 앱을 구동하기 위해선 다음과 같은 방법이 있다.

  • worker thread
  • child process
  • cluster

필자는 next.js에서 가장 사용하기 편한 방식인 cluster를 선택하였고, 편리하게 하기위해 pm2cluster 모드를 사용하기로 결정하였다. 환경은 kubernetes 환경으로 하나의 pod에 2~3개의 next.js 인스턴스를 pm2의 cluster 모드로 관리하고자 하였다.

따라서 가이드를 따라 다음과 같이 Dockerfile과 pm2 설정파일을 작성하였다.

1
2
3
4
5
6
7
8
9
10
11
12
// ./ecosystem.config.js
module.exports = {
  apps: [
    {
      name: "next-app",
      script: "node_modules/next/dist/bin/next",
      args: "start --port 80",
      instances: 2,
      exec_mode: "cluster",
    },
  ],
};
1
2
3
4
5
6
7
8
9
// package.json
{
  ...
  "script":{
    ...
    "start-pm2:dev": "pm2 start ecosystem.config.js",
    ...
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Stage 0 
FROM node AS nextjs-builder

# next.js 빌드
WORKDIR /home/deploy/next
COPY ./package.json ./package.json
COPY ./package-lock.json ./package-lock.json
RUN  npm ci
COPY ./ecosystem.config.js ./ecosystem.config.js
COPY ./.env ./.env
COPY ./next.config.js ./next.config.js
COPY ./public ./public
COPY ./src ./src
RUN npm run build

# 프로젝트 실행
ENTRYPOINT ["npm", "run", "start-pm2:dev"]

하지만 막상 도커 이미지를 빌드하고, 컨테이너를 실행하니 pm2가 실행되자마자 바로 컨테이너가 종료되었다. 그 후로도 다른 문제가 계속 발생하였는데, 차례대로 해결한 과정을 정리하였다.


pm2-runtime 사용

pm2를 container에서 실행하려면 pm2 start가 아닌 pm2-runtime start를 사용해야한다. 공식문서 따라서 package.json의 명령문을 다음과 같이 바꿔야한다.

1
2
3
4
5
6
7
8
{
  ...
  "script":{
    ...
    "start-pm2:dev": "pm2-runtime start ecosystem.config.js",
    ...
  }
}

하지만 컨테이너를 실행하니 다음과 같은 오류가 나타났다.

1
2
Error: Could not find a production build in the '/home/deploy/next/ecosystem.config.js/.next' directory. Try building your app with 'next build' before starting the production se
rver. https://err.sh/vercel/next.js/production-start-no-build-id

/home/deploy/next/ecosystem.config.js/.next라는 경로에 빌드 파일이 없다는 말이다. 단순히 빌드오류라고 생각할 수 있지만 뭔가 경로가 이상하다. ecosystem.config.js는 파일인데 이 파일을 마치 디렉터리처럼 인식하고, 이 파일의 하위에서 빌드파일을 찾는다.

현재 프로젝트의 파일 구조는 다음과 같다.

1
2
3
4
5
-- next
|-- .next (빌드파일)
|-- package.json
|-- ecosystem.config.js
|-- (...기타 소스파일들)

Dockerfile에서 마지막 WORKDIR은 /home/deploy/next이므로, 명령어를 실행한 위치는 /home/deploy/next이다. 따라서 home/deploy/next/.next로 빌드파일의 경로를 설정해야하나, ecosystem.config.js로 경로를 설정한 것을 모도 pm2의 문제가 있다고 생각하여 찾아보았다.


pm2 list

검색을 한 결과 나하고 비슷한 문제를 겪은 사람을 발견하였다. 링크 이 사람이 스스로 찾은 답은 다음과 같다.

Adding command pm2 list before pm2-runtime solves the issue, I suppose that the command pm2 list initializes the pm2 and sets the proper path for pm2-runtime.

즉, pm2-runtime을 실행하기 전에 먼저 pm2 list를 실행하라는 뜻이다. 그럼 pm2 가 초기화되어 올바른 경로를 설정한다고 한다. 따라서 package.json을 다음과 같이 변경하였다.

1
2
3
4
5
6
7
8
{
  ...
  "script":{
    ...
    "start-pm2:dev": "pm2 list && pm2-runtime start ecosystem.config.js",
    ...
  }
}

이후 컨테이너를 실행하니 제대로 실행되었다. status를 확인해보니 다음과 같이 나왔다.

1
2
3
4
ready - started server on 0.0.0.0:80, url: http://localhost:80
info  - Loaded env from /home/develop/deploy/next/.env
ready - started server on 0.0.0.0:80, url: http://localhost:80
info  - Loaded env from /home/develop/deploy/next/.env
This post is licensed under CC BY 4.0 by the author.

next.js - data fetching

next/image 컴포넌트

Comments powered by Disqus.