server 부분의 프로젝트는 NestJS로 구성되어 있었다.
처음에는 dependency package를 설치하고, build 하고, 실행하는 커맨드 지정하는 식으로 간단한 수준의 Dockerfile을 작성했다.
FROM node:16-alpine
WORKDIR /usr/src/app
COPY ./package.json ./
RUN yarn
COPY . .
RUN yarn build
CMD [ "yarn", "start:prod" ]
이렇게 하면 간단하게 구현 가능하지만, 전혀 최적화되지 않은 상태로 이미지가 빌드된다.
그래서 처음에는 이미지의 크기가 대략 570MB 정도였다.
이미지 크기를 줄이기 위해 블로그를 찾아보니, 다단계 도커를 활용하여 효율적으로 이미지를 만든 포스팅을 찾을 수 있었다.
https://www.tomray.dev/nestjs-docker-production
해당 내용을 프로젝트에 맞게 일부 수정한 후 새롭게 이미지를 빌드해보니 이미지 크기가 290MB 정도로 절반으로 줄일 수 있었다!
아래는 해당 내용들 각각에 대한 설명이다.
FROM node:18-alpine AS base
FROM base AS deps
WORKDIR /usr/src/app
COPY --chown=node:node package.json yarn.lock ./
RUN yarn --frozen-lockfile;
USER node
yarn을 이용해서 production에 필요한 패키지들만 설치하기 위해서는 --production
옵션을 사용하면 된다.
하지만 이렇게 할 경우 이후에 production용 빌드를 얻기 위해 yarn build
-> nest build
명령어를 실행해야 하는데, nest 키워드를 사용할 수 없다는 결과를 얻게 된다.
#9 0.495 yarn run v1.22.19
#9 0.527 $ nest build
#9 0.542 /bin/sh: nest: not found
#9 0.552 error Command failed with exit code 127.
#9 0.552 info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
nest는 dev dependency에 추가되도록 package manager가 구성되어 있기 때문에 발생하는 문제이다.
이 때문에 1차적으로는 dev dependency까지 모두 받아야 한다.
FROM base AS build
WORKDIR /usr/src/app
COPY --chown=node:node --from=deps /usr/src/app/node_modules ./node_modules
COPY --chown=node:node . .
RUN yarn build
ENV NODE_ENV production
RUN yarn --frozen-lockfile --production;
RUN rm -rf ./.next/cache
USER node
yarn build
-> yarn build
를 이용하여 프로젝트를 빌드한다.
전 단계에서 dev dependency를 모두 설치했기 때문에 nest 명령어 사용이 가능한 상태이다.
빌드가 완료되면 yarn --production
을 통해 production에 필요한 패키지만 골라서 재설치한다.
이렇게 하면 이전 단계의 dev dependency가 포함된 node_modules가 덮어씌어져서 최적화된 패키지 목록만 가지고 있게 된다.
FROM base AS production
WORKDIR /usr/src/app
COPY --chown=node:node --from=build /usr/src/app/node_modules ./node_modules
COPY --chown=node:node --from=build /usr/src/app/dist ./dist
CMD [ "node", "dist/main.js" ]
yarn build 결과 dist 하위 폴더에 최적화된 빌드 파일이 형성된다.
해당 빌드 파일들과 node_modules를 최종 이미지에 copy하고, 빌드 파일을 실행하는 명령어인 node dist/main.js
를 컨맨드로 등록한다.
FROM node:18-alpine AS base
# INSTALL DEPENDENCIES FOR DEVELOPMENT (FOR NEST)
FROM base AS deps
WORKDIR /usr/src/app
COPY --chown=node:node package.json yarn.lock ./
RUN yarn --frozen-lockfile;
USER node
# INSTALL DEPENDENCIES & BUILD FOR PRODUCTION
FROM base AS build
WORKDIR /usr/src/app
COPY --chown=node:node --from=deps /usr/src/app/node_modules ./node_modules
COPY --chown=node:node . .
RUN yarn build
ENV NODE_ENV production
RUN yarn --frozen-lockfile --production;
RUN rm -rf ./.next/cache
USER node
# PRODUCTION IMAGE
FROM base AS production
WORKDIR /usr/src/app
COPY --chown=node:node --from=build /usr/src/app/node_modules ./node_modules
COPY --chown=node:node --from=build /usr/src/app/dist ./dist
CMD [ "node", "dist/main.js" ]