기미기미
기미기미

기미기미

PostsTopicNestJS

NestJS를 Serverless로 배포하기

데이터센터 NestJS를 이용하여 API 서비스를 개발하였다. 이제 배포를 하면 되는데... 서버 인프라가 넉넉히 갖춰져있다면 어느위치에 배포하든 크게 상관없겠지만, 그렇지 않은 스타트업이나 개인 개발자의 경우 클라우드에 배포를 한다해도 비용문제가 걸릴 것 이다. AWS EC2를 기준으로 했을때... OS도 올리고 이것저것 하려면 최소 t4g.small 정도는 써야 할텐데... 아무것도 하지 않아도 매달 2만원 정도의 금액이 청구될 것이다. 물론 비용대비 성과가 충분히 나타난다면야 상관없겠지만, 우리의 서비스가 얼마나 잘될지 알수가 없다. Serverless 클라우드 서비스중 Serverless라는 것이 있다. Serverless란 서버를 직접 관리할 필요없이, 애플리케이션만 빌드/배포하여 즉시 서비스할수 있는 클라우드 네이티브 개발 모델이다. 클라우드 벤터마다 조금씩 다른 이름으로 서비스를 제공하는데, AWS의 경우 Lambda라는 이름으로 존재한다. Serverless라 하더라도 몇가지 단점이 있긴한데, 그중 하나가 바로 Cold/Warm start 개념이다. Serverless로 배포한 애플리케이션에 최초 요청이 발생하면 인스턴스를 구동하기 위해 컨테이너 이미지를 읽어들여 구동 시킨 후 응답이 이루어진다. 그리고 해당 인스턴스가 구동중인 상황에서는 즉시 응답이 이루어지지만, 특정시간 동안 요청이 더이상 발생하지 않으면 해당 인스턴스는 다운된다. 이때 최초 요청 상황이 Cold start이고, 인스턴스가 구동중인 상황이 Warm start이다. Cold start 상황에서는 요청 후 응답까지 1초에서 그이상의 시간이 소요될수 있지만, Warm start 상황에서는 즉시 응답이 이루어 진다. 요청이 빈번하지 않은 서비스인 경우 응답시간이 왔다갔다 하는 상황이 벌어질수도 있는 것이다. 그 외에도 Serverless라고 하는 방식의 한계 탓에 실행시간이 긴 작업은 실행하지 못한다. 보통 길어야 30초 정도의 응답시간을 허용할 뿐 그 이상은 타임아웃 처리가 되어 버린다. Maintenance 와 같은 작업은 Serverless로 처리하기는 힘들수 있다. 이 경우에는 다른 방법을 찾아봐야 한다. 이러한 점만을 감안한다면 NestJS 애플리케이션을 충분히 Serverless로 배포할수 있다. 비용 The Lambda free tier includes 1M free requests per month and 400,000 GB-seconds of compute time per month. 먼저 Lambda는 무기한 프리티어를 제공한다고 한다. 매달 100만건의 요청은 무료로 제공하고 있고, 그 이후부터 실행시간, 사용한 메모리량에 따라 과금이 된다. 대략 1000만 호출, 실행시간 200ms, 메모리량 256MB로 했을때 매달 $3.47가 과금된다고 한다. 굉장히 적은 금액이다. Serverless로 배포하기 위한 준비 이제 실제 NestJS 애플리케이션을 배포해보자. 먼저 몇가지 모듈을 설치해줘야 한다. $ npm i aws-serverless-express aws-lambda $ npm i -D @types/aws-serverless-express @types/aws-lambda $ npm i -g serverless serverless.yml service: nestjs-serverless frameworkVersion: '3' plugins: - serverless-offline - serverless-plugin-optimize provider: name: aws region: ap-northeast-2 runtime: nodejs18.x timeout: 30 memorySize: 512 stage: v1 ecr: images: appimage: path: ./ file: Dockerfile functions: main: image: name: appimage command: - dist/lambda.handler entryPoint: - '/lambda-entrypoint.sh' events: - http: method: any path: /{proxy+} Dockerfile FROM public.ecr.aws/lambda/nodejs:18 COPY . . RUN npm install RUN npx prisma generate RUN npm run build ENV NODE_ENV production CMD ["dist/lambda.handler"] src/lambda.ts import { Handler, Context } from "aws-lambda"; import { Server } from "http"; import { createServer, proxy } from "aws-serverless-express"; import { eventContext } from "aws-serverless-express/middleware"; import express from "express"; import { NestFactory } from "@nestjs/core"; import { ValidationPipe } from "@nestjs/common"; import { ExpressAdapter } from "@nestjs/platform-express"; import { AppModule } from "./app.module"; import { PrismaService } from "./services"; const binaryMimeTypes: string[] = []; let cachedServer: Server; async function bootstrap(): Promise<Server> { if (!cachedServer) { const expressApp = express(); const app = await NestFactory.create( AppModule, new ExpressAdapter(expressApp), ); const prismaService = app.get(PrismaService); prismaService.enableShutdownHooks(app); app.useGlobalPipes( new ValidationPipe({ whitelist: true, transform: true, }), ); app.enableCors({ credentials: true, origin: true, }); app.use(eventContext()); await app.init(); cachedServer = createServer(expressApp, undefined, binaryMimeTypes); } return cachedServer; } export const handler: Handler = async (event: any, context: Context) => { cachedServer = cachedServer ?? (await bootstrap()); return proxy(cachedServer, event, context, "PROMISE").promise; }; tsconfig.json 수정 배포과정에서 발생하는 오류처리를 위해 다음 내용을 tsconfig.json에 추가한다. "tsBuildInfoFile": ".tsbuildinfo", "esModuleInterop": false 배포하기 $ npm run build $ serverless login $ serverless deploy 이제 AWS 콘솔로 이동하여 애플리케이션이 Lambda에 잘 배포되었는지 확인해보자.

3 min readMon Nov 27 2023
NestJS를 Serverless로 배포하기