В этой статье мы собираемся расширить наш текущий проект NestJS, чтобы быстро добавить реальный рабочий пример Dockerfile для запуска в качестве контейнера.

В этой статье основное внимание будет уделено распространенной проблеме NestJS: базовое изображение может быть очень большим без какой-либо обработки. Мы сосредоточимся на создании уменьшенного размера изображения для нашего приложения NestJS REST!

Если вас интересует только часть Dockerfile, просто пропустите краткое введение и двигайтесь вперед.

Если вы не знакомы с моей серией статей, вы можете начать с первой статьи здесь.

Мы создаем REST API с использованием NestJS, но в течение предыдущих недель работы с NestJS (но с использованием GraphQL) я столкнулся с общей проблемой в нескольких проектах: Dockerfile. Если вы знаете основы использования докера, вы, вероятно, можете пойти в Google и проверить, как создать образ докера, и для большинства простых бэкэнд-скаффолдингов или аналогичных вы можете найти что-то очень похожее, например:

FROM node:14.15.0-alpine3.10

USER 2000
RUN mkdir -p /home/node/app/node_modules && chown -R 2000:2000 /home/node/app

WORKDIR /home/node/app
COPY --chown=2000:2000 . /home/node/app
RUN yarn install
RUN yarn build

EXPOSE 3000

ENTRYPOINT ["node"]
CMD ["/home/node/app/dist/main.js"]

Это здорово, работает хорошо. Вы можете быстро выполнить Google, чтобы понять, почему мы устанавливаем пользователя вместо использования root (что вам следует).

Скрытая проблема здесь заключается в том, что если вы запустите это в проекте NestJS, окончательное изображение (только с базовым API Rest с Express под капотом, даже без модуля GraphQL) будет иметь более 900 МБ!

Чтобы убедиться в этом, возьмите этот образец репозитория из моей предыдущей статьи. После того, как код будет на вашем компьютере, в корне каталога добавьте файл с именем Dockerfile и вставьте указанный выше код. Затем не забудьте добавить файл .dockerignore, а также добавить туда node_modules /. Теперь вы готовы к запуску:

docker build -t nestjs-api .

Это создаст вашу сборку, и сразу после того, как вы сможете проверить размер изображения (я использую Mac):

docker images | grep nestjs-api

На данный момент я пишу эту статью, вы найдете ее размер ~ 927 МБ. Для Node.js проект слишком велик. И в основном, но не только:

Как тогда решить? Первым делом всегда проверяйте изображение, которое вы фактически используете. Я обычно использовал образы alpine, а официальные образы node alpine можно найти в докере. В этой статье я использую узел: 14.15.0-alpine3.10, поскольку версия 16 только что прибыла, и некоторые модули безопасности могут не работать с ним (проверьте свой собственный сценарий). Но даже при его использовании у нас все еще есть проблема с размером.

Не забудьте отсканировать изображение перед тем, как использовать:

docker scan node:14.15.0-alpine3.10

Просто замените узел: 14.15.0-alpine3.10 на изображение, которое вы используете, и будьте счастливы.

Следующим шагом будет сокращение вашего Dockerfile с помощью node prune, не забудьте заблокировать файл yarn и удалить кеш из установленного / обновленного apk.

FROM node:14.15.0-alpine3.10

RUN apk update && apk add yarn curl bash make && rm -rf /var/cache/apk/*

RUN curl -sfL https://install.goreleaser.com/github.com/tj/node-prune.sh | bash -s -- -b /usr/local/bin

USER 1000
RUN mkdir -p /home/node/app/node_modules && chown -R 1000:1000 /home/node/app

WORKDIR /home/node/app

RUN yarn --frozen-lockfile
COPY . .
RUN yarn install
RUN yarn build

RUN npm prune --production

RUN /usr/local/bin/node-prune

EXPOSE 3000

ENTRYPOINT ["node"]
CMD ["/home/node/app/dist/main.js"]

Использование обновленного файла Dockerfile позволит установить некоторые необходимые модули для добавления узла-узла. После новой сборки вы можете видеть, что размер образа теперь уменьшен до ~ 411 МБ.

docker images | grep nestjs-api
nestjs-api     ... 411MB

Большое улучшение, правда? Вдохновленные этой замечательной статьей, давайте разделим ее на 2 изображения: одно для создания того, что нам нужно, а другое - просто для копирования того, что действительно требуется.

FROM node:14.16.1-alpine3.10 AS BUILD_IMAGE

RUN apk update && apk add yarn curl bash make && rm -rf /var/cache/apk/*

RUN curl -sfL https://install.goreleaser.com/github.com/tj/node-prune.sh | bash -s -- -b /usr/local/bin

WORKDIR /usr/src/app

# install dependencies
RUN yarn --frozen-lockfile

COPY . .
RUN yarn install
RUN yarn build

RUN npm prune --production

RUN /usr/local/bin/node-prune

FROM node:14.16.1-alpine3.10

USER 1000
RUN mkdir -p /home/node/app/
RUN mkdir -p /home/node/app/node_modules
RUN mkdir -p /home/node/app/dist

RUN chown -R 1000:1000 /home/node/app
RUN chown -R 1000:1000 /home/node/app/node_modules
RUN chown -R 1000:1000 /home/node/app/dist

WORKDIR /home/node/app

COPY --from=BUILD_IMAGE /usr/src/app/dist /home/node/app/dist
COPY --from=BUILD_IMAGE /usr/src/app/node_modules /home/node/app/node_modules

EXPOSE 3000
ENTRYPOINT ["node"]
CMD ["/home/node/app/dist/main.js"]

После запуска этого обновленного докера вы можете снова проверить размер изображения, и вы найдете это невероятным:

docker images | grep nestjs-api
nestjs-api       ... 162MB

162 МБ против ~ 900 МБ. Это супер уменьшенное изображение!

Теперь мы можем попробовать второй раунд, используя последнюю версию node 16 alpine:

FROM node:16-alpine3.11 AS BUILD_IMAGE

RUN apk update && apk add yarn curl bash make && rm -rf /var/cache/apk/*

RUN curl -sfL https://install.goreleaser.com/github.com/tj/node-prune.sh | bash -s -- -b /usr/local/bin

WORKDIR /usr/src/app

# install dependencies
RUN yarn --frozen-lockfile

COPY . .

RUN yarn install
RUN yarn build

RUN npm prune --production

RUN /usr/local/bin/node-prune

FROM node:16-alpine3.11

USER 1000
RUN mkdir -p /home/node/app/
RUN mkdir -p /home/node/app/node_modules
RUN mkdir -p /home/node/app/dist

RUN chown -R 1000:1000 /home/node/app
RUN chown -R 1000:1000 /home/node/app/node_modules
RUN chown -R 1000:1000 /home/node/app/dist

WORKDIR /home/node/app

COPY --from=BUILD_IMAGE /usr/src/app/dist /home/node/app/dist
COPY --from=BUILD_IMAGE /usr/src/app/node_modules /home/node/app/node_modules

EXPOSE 3000
ENTRYPOINT ["node"]
CMD ["/home/node/app/dist/main.js"]

Конечный результат будет ~ 156 МБ.

Еще одна вещь, о которой следует помнить, - это также использовать комплект для сборки докеров для повышения производительности. Чтобы использовать его, достаточно выполнить следующее:

time DOCKER_BUILDKIT=1 docker build -t nestjs-api .

В этой статье я поделился несколькими советами о том, как уменьшить размер сборки образа NestJS. Возможно, это не окончательное решение, но оно действительно уменьшит размер вашего изображения после его создания. Несколько заключительных мыслей:

  • У каждого проекта свои потребности. Убедитесь, что вы удовлетворили все свои потребности, а не только скопируйте и вставьте Dockerfile где-нибудь еще.
  • Всегда сканируйте изображение с помощью сканирования докеров.
  • Не забудьте увидеть обновления в версиях node-alpine или даже другое любимое изображение, которое вы, возможно, захотите использовать.
  • Если вы будете использовать GraphQL в своем проекте NestJS, размер изображения увеличится примерно на 100 МБ из-за его собственных требований (хотя это не должно быть проблемой).
  • Вы можете найти исходный образец работающий здесь.
  • Особая благодарность Soner Çökmen, который очень вдохновил меня своей статьей.

Больше контента на plainenglish.io