무중단 배포는 서비스 장애와 배포의 부담을 최소화하기 위해 운영 중인 서비스를 중단하지 않고 신규 소프트웨어를 배포하는 기술로, 무중단 배포의 핵심은 로드밸런서(Load Balancer) 를 통해 연결된 두 개 이상의 인스턴스에 트래픽을 제어해 배포하는 것이다.
잘 알려진 배포 방식으로는 Rolling, Blue-Green, Canary 배포가 있다. (이곳에 각각의 배포방법에 대해 설명이 잘 되어있다! 무중단 배포 아키텍처(Zero Downtime Deployment))
둘 중에 어떤 배포 방식을 선택할 지 꽤 고민을 했다.
중단 배포 방식을 적용할 때 생각했던 구현 방식은, 클라이언트단에서 미리 앱을 막고 이전에 받은 요청은 다 받고 앱을 중단하는 방법이었다. 무중단 방식보다 훨씬 구현하기 간단하다.
또, 많은 사용자들이 사용하지않는 시간대인 새벽에 배포를 하면 오후에 하는 것 보다는 부정적인 영향을 최소화할 수 있을 것 같은데! 라고 생각을 했다.
구현 방식도 간단하고, 사용자가 없어 언제 배포해도 문제없어서 지금 내가 운영하는 서비스는 중단하는 방식으로 배포해서 이에 대한 고민을 그쳐도 괜찮다고 생각했다.
하지만 무중단 배포의 장점이 너무 매력적이어서, 빠르게 구현할 수 있는 방법만 찾으면 적용하고 싶었다.
장점을 얘기하자면, 중단 배포 방식의 단점을 커버해주는 것이다.
백엔드 서버에 문제가 생겨서 버그를 잡고 바로 배포를 해야된다고 생각해보자.
중단 배포의 경우 업데이트 할 때 동안은 서비스를 이용하지 못할 것이다.
하지만 무중단 배포를 하게 되면 업데이트가 되는 동안에도 사용자가 서비스를 이용할 수 있다.
외에도 잦은 배포로 인한 부정적인 영향을 최소화하고, 경쟁력있는 서비스를 제공할 수 있다는 장점이 있다.
PM2는 Node.js 애플리케이션용 프로덕션 프로세스 관리자이며, 여기에는 기본 제공 로드 밸런서가 포함되어 있다. PM2를 이용하면 앱을 항상 작동 상태로 유지하고, 시스템 가동 중단 없이 앱을 다시 로드할 수 있으며, 일반적인 시스템 관리 태스크를 쉽게 처리할 수 있다. 또한 PM2를 이용하면 애플리케이션 로깅, 모니터링 및 클러스터링을 관리할 수 있다.
node.js 환경에서 작동하는 앱 중에서도 프로덕션 앱을 관리하는데 유용한 프로세스 매니저이다.
express 앱을 배포하기 위해 어떤 도구를 쓰면 좋을까 찾아봤고 공식 문서에서 배포 관련한 글(Express 앱용 프로세스 관리자)에서 PM2를 발견할 수 있었다.
해당 문서에서는 3개의 프로세스 관리자를 소개하고 있다.
PM2와 Strong-PM 사이에서 고민을 조금 했다. 둘 다 무중단 배포를 위한 기본 기능을 제공하고 있어서..
하지만 깃헙 스타수를 봤을 땐 PM2가 완전 윈이였고, 또 많은 사람들이 사용한 PM2를 선택해야지 비교적 빠르게 목표를 달성할 수 있을 것 같아 더 비교할 것도 없이 PM2를 선택했다.
무중단 배포를 하기위해 꼭 알아야하는 명령어들만 소개한다.
$ npm install pm2@latest -g
$ pm2 restart app_name
$ pm2 reload app_name
$ pm2 stop app_name
$ pm2 delete app_name
restart: 프로세스를 종료하고 다시 시작한다.
reload: 프로세스를 종료하지않고 다시 시작한다. (down time: 0)
stop: 프로세스를 종료한다.
delete: 작업 리스트에 올려진 모든 프로세스를 제거한다.
$ pm2 monit
실시간으로 로그를 확인할 수 있다.
$ pm2 start app.js -i max
앱을 클러스터 모드로 동작시킨다.
$ pm2 ecosystem
pm2 환경 설정 파일인 ecosystem.config.js 을 생성한다.
start 명령어로 실행시킬 수 있다.
PM2는 클러스터 모드를 제공하며 이를 통해 무중단 배포를 달성할 수 있다.
지금부터 무중단 배포를 구현해보자.
우선, 앱을 하나 만들고 PM2를 글로벌하게 설치한다. (어느 경로에서든 pm2를 사용해서 프로세스를 관리할 수 있어 편리하다.)
$ npm install pm2@latest -g
클러스터 모드를 실행하는 방법에는 2가지가 있다.
1번째는 명령어로 실행하는 방법이다.
$ pm2 start app.js -i max // i 옵션은 인스턴스의 개수를 의미한다.
2번째는 환경 설정파일로 실행한다.
$ pm2 ecosystem
module.exports = {
apps: [{
name: 'app',
script: './app.js',
instances: 'max',
exec_mode: 'cluster',
}]
}
cluster mode 로 실행하려면 exec_mode를 잊지않고 작성해야한다.
인스턴스 개수 설명
0 / max : 사용가능한 cpu 수만큼 인스턴스 생성
number: 지정한 수만큼 인스턴스 생성
(-1도 있다는데... 안 써봐서 모르겠다 ;-;)
$ pm2 start ecosystem.config.js
모든 프로세스들의의 mode가 cluster, status가 online이면 클러스터 모드가 제대로 동작한 것이다.
$ pm2 reload ecosystem.config.js
reload는 restart와는 반대로 down-time이 0초이다. restart은 앱을 재실행하기 위해 프로세스를 종료하고 재시작하는 방식이지만 reload는 그렇지 않다. (reload를 하는 상세한 흐름은 PM2를 활용한 Node.js 무중단 서비스하기 를 참고하길 바란다)
$ pm2 monit
모니터링을 하면 프로세스가 죽고 살아나는 것이나 프로그램 로그를 확인할 수 있다.
위의 과정을 거쳐, 이제 무중단 배포 서비스를 운영할 수 있게 됐다.
PM2를 활용한 무중단 배포를 직접 해보면서, 몇 가지 의문을 갖게 됐고 다른 사람들도 나와 같은 의문을 가졌을 것 같아 공유해보려고 한다.
reload를 할때 down-time이 0초라고 문서에 명시되어있다.
그래서 완벽한 무중단 배포 방법이라고 생각했다.!!
하지만 운영할 서비스의 크기가 너무 커서 프로세스 초기 구동하는 시간이 오래 걸린다면 예외 사항이 발생할 수 있다.
프로세스의 크기가 너무 커서 아직도 서비스를 구동하고 있다. 5초후에 작업이 완료된다. 이렇게 되면 사용자의 요청을 정상적으로 처리하지 못 한다.
준비 메시지를 너무 빨리 보낸 것이다. 이 문제는 준비 메시지를 보내는 시점을 적절히 조절해서 해결할 수 있다.
외에도 다른 예외사항들이 있을 수가 있기 때문에, 예상 가능한 예외사항들을 사전에 파악해서 운영할 서비스의 환경에 맞게 pm2를 적용해서 구현하면 되겠다.
PM2 cluster mode 사용 시 프로세스들이 포트를 공유해서 사용할 수 있다는 특징이 있다.
해당 작업은 내부적으로 node.js에서 제공하는 Cluster Module를 이용한다.
내부 동작은 Node.js 공식문서의 Cluster Module를 통해 알 수 있다.
해당 문서에 따르면 이를 처리하는 2가지 방법을 설명하고있다.
- master 프로세스는 포트를 열어 연결을 기다리고 있다가 연결이 생성되면 worker들에게 연결을 나눠준다.
- master 프로세스가 포트를 열면 listen socket을 worker에게 나눠줘서 worker가 직접 연결을 생성하게 한다.
중요한 건 worker프로세스들이 동일한 포트를 여러 개 여는 것이 아닌 것이다.
PM2의 cluster mode로 프로세스를 실행함으로써, 무중단 배포를 달성할 수 있었다. 또한 reload를 이용함으로써 특정 프로세스가 배포가 되는 동안에는 다른 프로세스들이 사용자 요청을 받아서 응답할 수 있었다.
하지만 실제 배포환경에서는 다양한 예외사항들이 발생할 수 있기 때문에, 보다 탄탄한 서비스를 사용자들에게 제공하기위해 노력해야할 것이다. 그렇기 때문에 PM2에서 제공하는 API들은 물론, 더 나아가 내부동작방식을 이해하여 예외사항에 대비해야 하는 것이 좋겠다.
무중단 배포 아키텍처(Zero Downtime Deployment)
PM2 공식 문서
PM2를 활용한 Node.js 무중단 서비스하기
node.js cluster
pm2를 이용해서 멀티 프로세싱을 달성할 수 있는지 몰랐네요... ㄷㄷ 게다가 무중단 배포를 이렇게 할 수 있다니!!
좋은 정보 정말 감사합니다. 🐶