급한 마음에 일단 대충 만들었던 Dockerfile, 처음에는 이게 그렇게 큰 문제가 될 줄 몰랐는데...
최근 NestJS 에 익숙해질 겸 작은 사이드 프로젝트를 진행하고 있는데, Backend 를 NestJS 로 구축하고 이를 컨테이너 기반으로 운용하기 위해 Dockerfile 을 작성했다.
하지만 그때 당시에는 내가 작성한 Dockerfile 이 그렇게 큰 문제가 될 거라고 생각하지 못했다. 500MB 가 넘는 이미지 사이즈와 빌드에 1분이 넘게 걸리는 문제를 마주하기 전까지는.
이대로는 안되겠다 싶어 결국 서비스 오픈 전 개선을 위해 칼을 빼들었다. 애플리케이션 사이즈에 비해 커진 Docker Image 를 다이어트 시키기 위한 눈물겨운 노력이 이제 시작된다.
FROM node:20.12.2-alpine3.18 AS base
LABEL maintainer="gwangin1999@naver.com"
RUN npm install -g pnpm
FROM base as deps
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile
FROM base as builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN pnpm build
FROM base 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"]
처음 Docker 를 입문하고 나서 작성했던 초기 Dockerfile 은 아래와 같은 문제를 안고 있다.
/node_modules
을 builder 와 runner Stage 두 차례에 걸쳐 COPY 하고 있다. devDependencies
에 포함된 라이브러 리까지 /node_modules
에 담아야 할까?상당히 큰 이미지이며 프로젝트 규모가 작음을 고려하면 더욱 개선이 시급하다.
pnpm install
) 과 두 차례에 걸친 /node_modules
COPY 작업에서 많은 시간을 소요하고 있다./node_modules
COPY 작업을 또 다시 처리한다./dist
파일을 COPY 하는 작업은 CACHED 되었다.FROM node:20.12.2-alpine3.18 AS base
LABEL maintainer="gwangin1999@naver.com"
RUN npm install -g pnpm
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
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=pnpm,target=/pnpm/store pnpm install --frozen-lockfile
RUN pnpm build
FROM base AS runner
COPY --from=builder --chown=node:node /app/dist ./dist
RUN pnpm install --prod --frozen-lockfile
ENV NODE_ENV production
USER node
CMD ["node", "dist/main"]
위에서 기술한 문제를 해결하여 새롭게 Dockerfile 을 구축했다.
--mount=type=cache
를 사용하여 /var/pnpm/store
디렉토리에 캐시를 마운트하면, pnpm
은 패키지 설치 시 캐시를 활용한다./node_modules
을 COPY 하지 않고 --prod
옵션을 킨 상태로 install 을 진행했다./node_modules
COPY 작업이 생략되어 빌드 시간이 소폭 줄었다./node_modules
을 COPY 하는 작업에서 5초가 소요되었으나, 이를 install 로 대체하여 2초로 감소했다.⚠️ 위의 Dockerfile 중에서 pnpm 관련한 명령어 두 가지에 대해서 짚고 넘어가자.
pnpm config set store-dir /var/pnpm/store
pnpm config set package-import-method copy
copy
로 지정하기 위한 명령어다.overlay2
등) 중 일부는 Hard Link 를 지원하지 않는다.기존의 이미지 빌드 과정에서 발생했던 여러 문제들을 종합적으로 해결하고, 이를 기반으로 Docker Image 의 용량을 줄여 배포 시간이 더욱 빨라지도록 설계했다.
사실 조금만 더 초반에 신경썼으면 진작에 해결 가능한 문제였는데, 서비스 초기 구축을 대충 해버린 나머지 이런 참사가 발생했다고 생각한다.
다음 글에서는 Github Action 을 기반으로 한 CI / CD 과정에서 어떻게 배포 시간을 단축했는지를 소개하려 한다.