본문 바로가기

프로젝트/미니 프로젝트

Docker + PM2 + nestjs + winston.js를 활용한 무중단 운영 시스템 구축하기

반응형

최근 node.js 기반의 백엔드 서비스를 설계 + 제작 + 운영을 위한 템플릿을 작성한적이 있다

https://loy124.tistory.com/381?category=747622 

 

Docker + PM2 + winston.js를 활용한 무중단 운영 시스템 구축하기

최근 node.js 기반의 백엔드 서비스를 설계 + 제작 + 운영을 해야 할 일이 생겼다. 이에 따라 Node.js + express 기반의 실 운용 서버 운영에 대한 고려를 하게 되었고 그에 따라 무중단 운영 + 로깅 시스

loy124.tistory.com

위의 방식을 그대로 따라가되 nest.js에 맞게 변경시켜보았다. 

 

 

소스코드 주소 

 

https://github.com/loy124/nestjs-backend-system

 

GitHub - loy124/nestjs-backend-system: Docker 기반의 pm2 + winston.js 기반 프로세스 매니저 + 로깅 시스템 + DB

Docker 기반의 pm2 + winston.js 기반 프로세스 매니저 + 로깅 시스템 + DB 구축하기 - GitHub - loy124/nestjs-backend-system: Docker 기반의 pm2 + winston.js 기반 프로세스 매니저 + 로깅 시스템 + DB 구축하기

github.com

 

pm2로 node.js 프로세스 관리하기 - https://loy124.tistory.com/379

 

node - pm2로 node.js 프로세스 관리하기 - 기본 명령어, 실행하기

최근 node.js 기반의 백엔드 서비스를 설계 + 제작 + 운영을 해야 할 일이 생겼고 Node.js + express 기반의 실 운용 서버 운영에 대한 고려를 하게 되었다. 기존에 supervisord를 사용한 supervisord에 대한 경

loy124.tistory.com

 

winston.js로 node.js 로그 관리하기 - https://loy124.tistory.com/380

 

node - winston node.js 로그 관리하기 - 기본 명령어, 적용하기

개발을 하다보면 새삼 로그의 중요성을 느낄 때가 많다. 어느 시점에 에러가 났는지 그에 따라서 어느시점에 복구가 되었는지 로그를 보고 대응이 가능하며 에러 시점을 파악하기 위해 터미널

loy124.tistory.com

위 내용을 기반으로 작성되었습니다.

 

 

 

프로젝트를 구상할 때 먼저 흐름도를 작성해서 어떠한 흐름으로 관리를 할지 고려해보았다. 

 

순서도

 

 

 

클라이언트에 특정 IP(or 도메인)에 접근  -> 해당 nginx 80포트에 도착 -> 80포트에 도착한 nginx는 proxy를 통해 container backend(3000번 포트)에 보낸다 -> 해당 포트는 node단의 pm2에 의해 클러스터링 되어 특정 nest의 dist/main.js에 도착한다. 

 

 

 

실행법 

//docker 및 docker-compose 가 설치되어 있다는 가정하에 
docker-compose up -d --build

위 명령어만 실행하면 기본 구축은 완료된다. 

 

backend container 접근 + pm2 모니터링

docker exec -it backend sh
pm2 monit

 

현재 기동중인 서버

 

이번엔 docker를 자동으로 재시작하기위해 shell을 작성해 두었다. 

 

import os
import sys
import subprocess
os.system("docker exec -it backend npm run build")
os.system("docker exec -it backend pm2 reload all")

정말 간단한 쉘이지만 늘 일일히 치기는 번거로운 점이 있어 쉘을 추가하였다.

 

위 내용대로 

docker exec -it 를 통해 nest를 한번 빌드하고 

reload를 통해 새로 빌드된 코드를 적용시키는 방식을 추가하였다. 

 

 

nestjs에서 loggin을 적용한 부분 

일단 폴더구조는 다음과같다.

 

 

 

logger.ts에는 기존 express에서 작성했던 winston및 daily-roate-file을 그대로 옮겨두었다. 

 

logger.ts

import winston from 'winston';
import 'winston-daily-rotate-file';
const { combine, timestamp, printf } = winston.format;

const customFormat = printf((info) => {
  return ` ${info.timestamp} ${info.level}: ${info.message}`;
});

export const logger = winston.createLogger({
  format: combine(
    timestamp({
      format: 'YYYY-MM-DD HH:mm:ss',
    }),
    customFormat,
  ),
  transports: [
    new winston.transports.DailyRotateFile({
      level: 'info',
      datePattern: 'YYYY-MM-DD',
      dirname: './logs',
      filename: `server_%DATE%.log`,
      maxSize: '20m',
      maxFiles: '7d',
      zippedArchive: true,
    }),
    new winston.transports.DailyRotateFile({
      level: 'error',
      datePattern: 'YYYY-MM-DD',
      dirname: './logs',
      filename: `server_%DATE%.error.log`,
      maxSize: '20m',
      maxFiles: '7d',
      zippedArchive: true,
    }),
  ],
});

if (process.env.NODE_ENV !== 'production') {
  logger.add(
    new winston.transports.Console({
      format: winston.format.combine(
        winston.format.colorize(),
        winston.format.simple(),
      ),
    }),
  );
}

export const stream = {
  write: (message) => {
    logger.info(message);
  },
};

 

이제 옮겨둔 ts파일을 

 

AppLogginService.ts 에 적용시켜주었다.

import { LoggerService } from "@nestjs/common";
import { logger } from "./logger";

export class AppLoggingService implements LoggerService{
  log(message: string, context: string){
    logger.info(message, {
      from: context
    })
  }
  warn(message: string, context: string){
    logger.warn(message, context);
  }
  error(message: string, trace: string, context: string){
    logger.error(message, {stack:trace, context});
  }
  debug(message: string, context: string){
    logger.debug(message, context);
  }
  verbose(message: string, context: string){
    logger.verbose(message, context);
  }
}

 

기본적으로 이런 Service까지 작성되었다면

app.module.ts에서 해당 로깅을 전역적으로 사용할 수 있게 하였다.

 

App.module.ts

import { Logger, Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ConfigModule } from '@nestjs/config';

@Module({
  imports: [ConfigModule.forRoot(),
  ],
  controllers: [AppController],
  providers: [AppService, Logger],
})
export class AppModule {}

 

 

 

사실 기존에 고안했던 프로젝트를 단순히 nest에 맞게 옮긴거라 제작기간은 정말 짧았으나

최근에 nestjs에 엄청난 요소들을 경험하고 이를 통해 프로젝트를 진행할예정이라 발빠르게 작성해보았다. 

 

 

 

반응형