최근 node.js 기반의 백엔드 서비스를 설계 + 제작 + 운영을 해야 할 일이 생겼다.
이에 따라
Node.js + express 기반의 실 운용 서버 운영에 대한 고려를 하게 되었고 그에 따라
무중단 운영 + 로깅 시스템을 구축할 필요성이 생겼다.
또한 만든 시스템을 어디에서도 사용 할 수 있게 컨테이너화 또한 하고 싶었다.
따라서 컨테이너화를 위해 도커를 사용하기로 했고
로그 모듈로 가장 많이 사용하는 모듈이 winston.js
노드 프로세스 매니저로 가장 많이 사용되는 모듈이 pm2 여서 두가지를 같이 사용해서
docker-compose 를 활용해 이를 엮은 무중단 운영 시스템을 구축해보았다.
pm2 와 winston.js 에대한 정리는 아래 블로그 글에 정리해두었다.
pm2로 node.js 프로세스 관리하기 - https://loy124.tistory.com/379
winston.js로 node.js 로그 관리하기 - https://loy124.tistory.com/380
프로젝트를 구상할 때 먼저 흐름도를 작성해서 어떠한 흐름으로 관리를 할지 고려해보았다.
클라이언트에 특정 IP(or 도메인)에 접근 -> 해당 nginx 80포트에 도착 -> 80포트에 도착한 nginx는 proxy를 통해 container backend(8080포트)에 보낸다 -> 해당 포트는 node단의 pm2에 의해 클러스터링 되어 특정 express에 도착한다.
소스코드 주소
https://github.com/loy124/express-backend-system
실행법
//docker 및 docker-compose 가 설치되어 있다는 가정하에
docker-compose up -d --build
위 명령어만 실행하면 기본 구축은 완료된다.
backend container 접근 + pm2 모니터링
docker exec -it backend sh
pm2 monit
pm2 관련 명령어들은 https://loy124.tistory.com/379
상단에 기재하였으니 docker로 해당 backend에 접근 한 후에 테스트 해보면 좋을 것 같다.
로깅 방식
로그 방식은 크게 두가지로 나뉜다
winston을 사용한 express 사용중에 발생하는 logging + node가 에러로 인해 중단이 되는등 두가지 로그 방식으로 나뉘어져있다.
서버에서 에러로인해 중단이 되는등의 에러 log는 pm2의 error log에 나와있다.
error -1 error -2 등으로 나뉘어져있다면 pm2의 인스턴스 갯수(pm2.json에 정의되어있다. )에 따라 각 인스턴스 별 로그가 담겨있다.
그 외 에러는 server_날짜.log, server_날짜.error.log에 담겨있다.
위 토이프로젝트는 docker-compose로 엮기만 잘 하면 되는 그리 어렵지 않은 프로젝트였다.
토요일에 pm2를 공부하고 일요일에 winston.js를 공부했고 당일에 두개를 엮는 작업으로 까지 마무리 지었다.
고민했던 사항
데이터 백업
크고작은 프로젝트를 진행하다보면 data관리에 대한 필요성이 증대 된다. -> 데이터 백업이 중요할 뿐더러 관리도 힘들다.
그에따라 운영하던 data를 한번에 옮겨서 관리 할 방법들이 필요했다.
그래서
mysql 데이터와 volume 속성과 winston log파일들을 모두 data 폴더안에 집어넣었다.
다른 프로젝트를 진행하더라도 해당 부분만 압축후 data 만 옮기는 방식으로 진행하면 데이터를 통째로 옮길 수 있다.
무중단 운영법
reload를 통한 무중단 배포 방식은 다음과 같다.
- 기존 존재 프로세스를 old_app으로 이동 시키고 동작
- 변경된 코드로 새 프로세스 new_app을 연다 (ready 상태)
- 그 후 들어오는 요청은 new_app으로 들어오게된다.
- old_app은 남은 처리를 진행한다.
- old_app이 모든 처리를 진행했다고 시간이 지났다고 판단되면 SIGINT라는 신호를 부여한다.
- SIGINT를 받으면 해당 서버를 종료시켜버린다.
- SIGINT를 보내고 일정시간이 지났는데도 종료가 되지 않는다면 SIGKILL 을 통해 강제 종료시켜버린다.
위 방식에 따라 요청을 받게 되면 몇가지 주의할 사항이 있다.
1. new_app이 새로 동작하기 전에 요청을 받은 경우
new_app이 제대로 실행되기 전에는 잠깐의 시간동안 서비스가 끊길 수도 있다.
따라서 이를 위해 pm2에서는 wait_ready 라는 옵션을 제공한다.
wait _ready는 ready라는 신호가 오기 전까지 대기한다.
해당 ready 신호는 서버 listen 시에 ready 신호를 보냄으로써 정상적으로 서버가 동작 하는 경우 해당 ready를 보내준다.
2. old_app이 요청 처리중에 죽는 경우
old_app은 일정 시간이 지난 후 SIGKILL로 죽게 된다.
하지만 처리 작업 길어져서 SIGKILL을 받을 때 까지 처리가 완료되지 않는 다면 이에 따라 오류가 발생 할 수도 있다.
따라서 이에 따라 SIGKILL에 대한 딜레이를 넉넉하게 보내고 SIGINT시에는 바로 종료할 수 있는 처리를 해주면 된다.
3. http-Keep-Alive 사용 중 일 때
http-keep-Alive 란 두 지점간에 상대간의 상태를 조회하기 위해 패킷을 주기적으로 보내는것이다.
패킷에 대한 반응이 없으면 접속을 끊는 방식이다.
기본적으로 http는 접속 상태를 유지하지 않지만 해당 keep alive 를 진행하게되면 특정 시간동안 연결을 유지하게 된다.(연결이 유지되는 동안은 다른 사람들이 연결을 할 수 없게 된다)
그에따라 keepAlive일 경우 추가 요청을 진행해줘야 한다;
flag 변수인 isDisableKeepAlive를 활용해서 keepAlive 상태인 경우 Connection을 close 해주는 추가 처리를 진행해준다.
참고 블로그
https://engineering.linecorp.com/ko/blog/pm2-nodejs/