Как упаковать Node.js приложение в Docker контейнер за 3 минуты?

| May 9, 2022

В этой статье мы познакомимся с простейшим способом упаковки своeго собственного приложения в Docker контейнер.

Учится мы будем на примере крошечного Node.js приложения. Если Вы не знакомы с Node.js - ничего страшного. Ниже Вы найдёте инструкцию по созданию тестового приложения, с которым мы и будем работать.

Если Вы у Вас уже есть своё Node.js приложение, которые Вы хотите упаковать в Docker контейнер - следующий раздел Вы можете пропустить.

Создание тестового приложения Node.js

Перед тем как мы начнём, убедитесь, что у Вас установлена среда разработки Node.js. Скачать её можно на официальном сайте или установить можно напрямую с Homebrew. Делается это следующей командой:

brew install node

Теперь, Вы готовы к созданию тестового приложения. Следуйте данной инструкции:

  1. Откройте терминал и перейдите в папку, в которой Вы создадите свой проект
  2. Выполните следующую команду:
npm init

В процессе, Вам будет задан ряд вопросов о мета-информации о проекте. Для экономии Вашего времени, предлагаю на каждый вопрос отвечать нажатием клавиши Enter. В этом случае, будут использованы значения по умолчанию.

  1. Установите библиотеку Express.js командой
npm i express

Express.js нам понадобится только для того, чтобы научить наше тестовое приложение отвечать на http-запросы. Мы это используем для проверки работоспособностии нашего тестового приложения в контейнере.

  1. Создайте javascript файл с Вашим приложением. Сделать это можно, выполнив следующую команду в терминале:
touch server.js
  1. Скопируйте следующий код в Ваш файл приложения. В моём случае, код ниже, нужно скопировать в файл server.js:
const express = require('express')
const app = express()
const port = 9876

app.get('/', (req, res) => {
  res.send('Hello fullstackguy.com!')
})

app.listen(port, () => {
  console.log(`Example app listening on port ${port}`)
})

Вот и всё. Приложение готово. Чтобы проверить его работоспособность, можно выполнить следующие команды в терминале:

node server.js
curl http://localhost:9876

Первая - запустит Ваше приложение и оно начнёт слушать запросы на 9876 порту. Вторая команда - шлёт запрос на порт Вашего приложения и, в результате, оно должно ответить сообщением, которое мы в указали в коде приложения.

Теперь, когда у нас есть тестовое приложение, мы можем перейти к его упаковке.

Как упаковать приложение в Docker контейнер?

Первое, что необходимо сделать - это создать Dockerfile. Dockerfile - описывает образ Вашего приложения в контейнере; то, какими ресурсами оно будет обладать, будучи запущеным в контейнере, и как именно оно будет запущено в нём.

Создание Dockerfile’а

Для этого нужно создать файл под названием Dockerfile и скопировать в него следующее содержимое:

# команда FROM позволяет указать базовый образ, 
# который мы будем использовать для образа с нашим приложением
FROM node:18.0.0

# WORKDIR позволяет установить рабочую директорию, как следует из названия.
# Вы можете думать о WORKDIR, как о команде cd, для смены директории
WORKDIR /app

# COPY позволяет скопировать файлы и директории нашего приложения в Docker образ
# Копируются именно те файлы, которые нужны для работы нашего приложения.
# Как видно ниже, в первую очередь копируются файлы-манифесты.
COPY package.json package.json
COPY package-lock.json package-lock.json

# RUN команда позволяет выполнить произвольную команду. Обычно это команды запуска или команды установки зависимостей.
# В данном случае, выполняется команда npm install, которая установит зависимости Вашего приложения
RUN npm install

# Снова команда COPY, но теперь копируется контент текущей директории в рабочую директорию контейнера.
# То есть, копируются файлы самого приложения, включая наш server.js файл
COPY . .

# И наконец, команда CMD запускает приложение. Первым аргументом передаётся название команды, 
# которую необходимо вызвать. Все остальные, это аргументы первой команды.
CMD ["node", "server.js"]

Директория приложения теперь должна выглядеть следующим образом:

Dockerfile
node_modules
package-lock.json
package.json
server.js

Создание Docker-образа

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

docker build --tag node-docker:1.0.0 .

Разберём эту команду подробнее.

docker build - отвечает за сборку новых Docker-образов.

--tag node-docker:1.0.0 - параметр говорит о том, что наш новый образ, будет иметь название node-docker и будет отмечен тегом 1.0.0. Тег не обязан включать в себя номер версии. Вместо него можно использовать и словестное описание, например, есть тег по умолчанию latest.

. - точка в конце означает текущую директорию, а точнее - директорию с Dockerfile’ом

После выполнения команды docker build, новый Docker-образ с именем node-docker будет создан с нашим приложением внутри. Самое время попытаться его запустить.

Запуск приложения в Docker-контейнере

Запуск контейнера с нашим приложеним осуществляется с помощью следующей команды:

docker run -p 8000:9876 --name node-docker-try node-docker:1.0.0

Разберём её подробнее:

docker run - отвечает за запуск контейнеров с приложениями из имеющихся Docker-образов.

-p 8000:9876 - параметр, означает проброс портов. Если Вы помните, в нашем файле server.js описан запуск приложения на 9876 порту. Так вот, параметр -p показывает с какого порта хоста на какой порт контейнера будут переадресованы запросы. Если совсем просто - когда вы шлёте запрос на localhost:8000 он автоматически пробрасывается в контейнер на порт 9876, где уже входящий трафик прослушивается нашим приложением.

--name node-docker-try - параметр, который позволяет задать имя контейнера, с нашим приложением. Если этот параметр явно не указать, то Docker сгенерирует название контейнера за нас.

node-docker:1.0.0 - в качестве обязательного параметра команды docker run идёт указание названия Docker-образа и тега (версии), из которого и будет запущен контейнер.

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

curl http://localhost:8000

Обратите внимание, что номер порта в этот раз, выставлен на 8000. Это говорит о том, что наш запрос пойдёт в наш контейнер через этот порт, а внутри, приложение получит его на 9876.

Заключение

В этой статье мы рассмотрели простейший способ вручную упаковать Node.js приложение в Docker контейнер. Этот способ уместно использовать в скриптах в своём CI/CD pipeline’е.

Помимо этого способа, есть и другие. Например, у Java приложений, есть вариант использовать специальные плагины (Jib-plugin для Maven), который под капотом, делает примерно то же самое.

В любом случае, знать этот процесс полезно и имея уже эти знания в своём арсенале, Вы без проблем сможете упаковать любое приложение, написанное на любом языке программирования, в Docker.

Список материалов

Документация по Dockerfile

Полный плейлист

Плейлист “Docker: Tips & Tricks”

Ещё материалы по теме