При развертывании своего проекта NestJS я обнаружил, что в сети нет информации о том, как написать Dockerfile для создания образа Docker, необходимого для контейнерного развертывания.
Поэтому я написал это руководство, которое шаг за шагом расскажет вам, как создать образ Docker для вашего проекта NestJs!
Готовы? Давайте погрузимся.
P.S. Если вы хотите просто скопировать и вставить готовый Dockerfile, просто перейдите к этому разделу.
Написание Dockerfile
Образ контейнера – это изолированный пакет программного обеспечения, который включает в себя все необходимое для запуска кода. Вы можете определить контейнерные образы, написав Dockerfile
, который содержит инструкции по сборке образа.
Давайте теперь добавим Dockerfile:
touch Dockerfile
А затем добавим инструкции в Dockerfile. Смотрите комментарии, которые объясняют каждый шаг:
# Base image
FROM node:18
# Create app directory
WORKDIR /usr/src/app
# A wildcard is used to ensure both package.json AND package-lock.json are copied
COPY package*.json ./
# Install app dependencies
RUN npm install
# Bundle app source
COPY . .
# Creates a "dist" folder with the production build
RUN npm run build
# Start the server using the production build
CMD [ "node", "dist/main.js" ]
Аналогично файлу .gitignore
, мы можем добавить файл .dockerignore
, который запретит включение определенных файлов в сборку образа.
touch .dockerignore
Затем исключите следующие файлы из сборки образа:
Dockerfile
.dockerignore
node_modules
npm-debug.log
Локальное тестирование контейнера
Теперь давайте проведем локальное тестирование, чтобы проверить, ведет ли себя Dockerfile так, как мы ожидаем.
Сначала соберем образ с помощью команды в терминале в корне вашего проекта (вы можете заменить nest-cloud-run
на имя вашего проекта). Не забудьте .
!
docker build -t nest-cloud-run .
Вы можете убедиться в том, что образ создан, выполнив команду docker images
, которая выведет список образов Docker, имеющихся на вашей локальной машине:
docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nest-cloud-run latest 004f7f222139 31 seconds ago 1.24GB
Теперь запустим контейнер и запустим образ с помощью этой команды (убедитесь, что имя образа то же самое, что использовалось выше):
docker run -p80:3000 nest-cloud-run
Теперь вы можете получить доступ к приложению NestJS, посетив https://localhost
в вашем браузере (просто https://localhost
без каких-либо номеров портов).
Я столкнулся с парой проблем на своей машине при запуске контейнера, в основном из-за конфликтов с портами других запущенных контейнеров.
Если у вас возникнут подобные проблемы, вы можете попробовать выполнить команду docker rm -f $(docker ps -aq)
, которая остановит и удалит все запущенные контейнеры.
Оптимизация Dockerfile для производства
Теперь, когда мы убедились, что образ работает локально, давайте попробуем уменьшить его размер.
Такие инструменты развертывания, как Cloud Run, учитывают размер образа при расчете стоимости, поэтому хорошей идеей будет сохранить размер образа как можно меньше.
Выполнив команду docker images
, мы получим размер нашего образа:
docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nest-cloud-run latest 004f7f222139 31 seconds ago 1.24GB
1,24 ГБ – это довольно много! Давайте вернемся к нашему Dockerfile
и сделаем некоторые оптимизации.
Используйте изображения узлов Alpine
Рекомендуется использовать изображения узлов Alpine, когда вы пытаетесь оптимизировать размер изображения. Использование node:18-alpine
вместо node:18
само по себе уменьшает размер изображения с 1.24GB до 466MB!
Используйте многоэтапные сборки
В своем Dockerfile вы можете определить многоступенчатые сборки, что является способом последовательной сборки наиболее оптимизированного образа путем создания нескольких образов.
На практике вот как мы можем использовать многоступенчатые сборки в нашем Dockerfile:
# Base image
FROM node:18-alpine As development
# Create app directory
WORKDIR /usr/src/app
# Copy application dependency manifests to the container image.
# A wildcard is used to ensure copying both package.json AND package-lock.json (when available).
# Copying this first prevents re-running npm install on every code change.
COPY package*.json ./
# Install app dependencies
RUN npm install
# Bundle app source
COPY . .
# Creates a "dist" folder with the production build
RUN npm run build
# Base image for production
FROM node:18-alpine As production
# Create app directory
WORKDIR /usr/src/app
# Copy application dependency manifests to the container image.
# A wildcard is used to ensure copying both package.json AND package-lock.json (when available).
# Copying this first prevents re-running npm install on every code change.
COPY package*.json ./
# Install production dependencies.
# If you have a package-lock.json, speedier builds with 'npm ci', otherwise use 'npm install --only=production'
RUN npm ci --only=production
# Bundle app source
COPY . .
# Copy the bundled code
COPY --from=development /usr/src/app/dist ./dist
# Start the server using the production build
CMD [ "node", "dist/main.js" ]
После обновления вашего Dockerfile
вам нужно будет заново выполнить команды для сборки образа:
docker build -t nest-cloud-run .
А затем команду для раскрутки контейнера:
docker run -p80:3000 nest-cloud-run
Если вы снова запустите docker images
, чтобы проверить размер нашего изображения, вы увидите, что оно стало значительно меньше:
docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nest-cloud-run latest 004f7f222139 31 seconds ago 189MB
Устранение неполадок
Вы можете столкнуться со следующими ошибками:
Ошибка: Cannot find module ‘webpack’
Скорее всего, вы используете неправильную версию node в своем базовом образе, если вы получаете ошибки, подобные приведенным ниже:
Error: Cannot find module 'webpack'
ERROR [development 6/6] RUN npm run build
npm ERR! nest-cloud-run@0.0.1 build: nest build
Вместо использования FROM node:14-alpine
, используйте FROM node:18-alpine
, чтобы решить эту проблему.
Заключение
В итоге, вот наш оптимизированный для производства образ Docker для проекта NestJS:
# Base image
FROM node:18-alpine As development
# Create app directory
WORKDIR /usr/src/app
# Copy application dependency manifests to the container image.
# A wildcard is used to ensure copying both package.json AND package-lock.json (when available).
# Copying this first prevents re-running npm install on every code change.
COPY package*.json ./
# Install app dependencies
RUN npm install
# Bundle app source
COPY . .
# Creates a "dist" folder with the production build
RUN npm run build
# Base image for production
FROM node:18-alpine As production
# Create app directory
WORKDIR /usr/src/app
# Copy application dependency manifests to the container image.
# A wildcard is used to ensure copying both package.json AND package-lock.json (when available).
# Copying this first prevents re-running npm install on every code change.
COPY package*.json ./
# Install production dependencies.
# If you have a package-lock.json, speedier builds with 'npm ci', otherwise use 'npm install --only=production'
RUN npm ci --only=production
# Bundle app source
COPY . .
# Copy the bundled code
COPY --from=development /usr/src/app/dist ./dist
# Start the server using the production build
CMD [ "node", "dist/main.js" ]
Есть ли у вас какие-либо дополнительные оптимизации, которые вы можете внести в приведенный выше образ? Напишите об этом в комментариях ниже!