В этой статье мы собираемся расширить наш текущий проект 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