nestjs를 docker로 빌드하는 것을 최적화하는 실험을 해보았다.
javascript에 경우, 패키지를 설치하면서 사이즈가 커지는 문제가 있다.
패키지들을 많이 설치 하면할수록 빌드의 시간이 너무 오래걸리게 된다.
이로인해 배포하는 시간이 오래걸리게 되는 문제가 발생한다.
이거는 운영 혹은 이슈가 터져서 다시 재배포할때 문제가 될 수 있는 부분이다.
이를 사전에 방지하는 것이 좋다고 생각한다.
나는 pnpm을 사용하기로 했다. 일반 npm 혹은 yarn v1 은 package intall이나 build가 느리다는 말이 많다.
pnpm을 사용한 이유는 글로벌로 패키지를 설치해서 사용할 수 있기때문에 다른 곳에서도 유용하게 쓸 수 있을거라
판단해서 사용했다. 물론 yarn berry를 사용할 수도 있지만 yarn berry를 사용하면 점점 성능저하가 발생할 수도
있다해서 사용하지 않았다.
FROM node:18.7.0-alpine3.15 AS builder
WORKDIR /app
RUN npm install -g pnpm
COPY . .
# Install dependencies with pnpm and cache mount
RUN pnpm config set store-dir /var/pnpm/store && \
pnpm config set package-import-method copy && \
pnpm install --prefer-offline --ignore-scripts --frozen-lockfile
RUN pnpm build
FROM node:18.7.0-alpine3.15 AS runner
WORKDIR /app
COPY --from=builder --chown=node:node /app/dist ./dist
COPY --from=builder --chown=node:node /app/node_modules ./node_modules
ENV NODE_ENV production
USER node
CMD ["node", "dist/main"]

17초 정도 빌드된 것을 확인할 수 있다.
다시 한번 빌드했을 때 어떻게 되는지 확인해보겠다.

단축이 되었지만 패키지 install 하는 부분은 cached 처리가 되지 않은 것을 확인했다.
FROM node:18.7.0-alpine3.15 AS base
WORKDIR /app
RUN npm install -g pnpm
# Install dependencies with pnpm and cache mount
RUN --mount=type=cache,id=pnmcache,target=/var/pnpm/store \
--mount=type=bind,source=package.json,target=package.json \
--mount=type=bind,source=pnpm-lock.yaml,target=pnpm-lock.yaml \
pnpm config set store-dir /var/pnpm/store && \
pnpm config set package-import-method copy && \
pnpm install --prefer-offline --ignore-scripts --frozen-lockfile
FROM base as builder
COPY . .
RUN --mount=type=cache,id=pnmcache,target=/var/pnpm/store \
pnpm build
FROM node:18.7.0-alpine3.15 AS runner
WORKDIR /app
COPY --from=builder --chown=node:node /app/dist ./dist
COPY --from=builder --chown=node:node /app/node_modules ./node_modules
ENV NODE_ENV production
USER node
CMD ["node", "dist/main"]
아래와 같은 결과를 얻을 수 있었다.
처음 빌드 시, 18.4초가 걸리는 것을 확인했다.

이제 코드에 console.log를 하나 추가해서 pnpm이 cache를 잘 사용하는지 확인해보겠다.
다시 빌드해보니 install 한 부분은 cached가 된 것을 확인할 수 있었다. 이를 통해서 8초 정도 빌드가 단축되었다.

FROM node:18.7.0-alpine3.15 AS base
WORKDIR /app
RUN npm install -g pnpm
# Install dependencies with pnpm and cache mount
RUN --mount=type=cache,id=pnmcache,target=/var/pnpm/store \
--mount=type=bind,source=package.json,target=package.json \
--mount=type=bind,source=pnpm-lock.yaml,target=pnpm-lock.yaml \
pnpm config set store-dir /var/pnpm/store && \
pnpm config set package-import-method copy && \
pnpm install --prefer-offline --ignore-scripts --frozen-lockfile
FROM base as builder
RUN --mount=type=cache,id=pnmcache,target=/var/pnpm/store \
--mount=type=bind,source=package.json,target=package.json \
--mount=type=bind,source=tsconfig.json,target=tsconfig.json \
--mount=type=bind,source=src,target=src \
pnpm build
FROM node:18.7.0-alpine3.15 AS runner
WORKDIR /app
COPY --from=builder --chown=node:node /app/dist ./dist
COPY --from=builder --chown=node:node /app/node_modules ./node_modules
ENV NODE_ENV production
USER node
CMD ["node", "dist/main"]
이미지 내용을 보았을 때, 소스 코드를 COPY 하는 부분이 없어졌다. COPY하는 데 들었던 소요시간이 없어졌다.
지금은 그렇게 큰 차이가 없지만 사이즈가 커지면 확연한 차이를 알 수 있을것으로 보인다.

테스트에서 확연한 차이가 나지 않아서 아쉬운 부분이 있다.
이거는 파일 및 패키지들이 그리 크지 않아서 그런거 같다.
그래도 나는 3번째 테스트로 나온 것을 사용할 거 같다.