Гид по технологиям

Контейнеризация Node.js с Docker

• 10 min read • DevOps • Обновлено 24 Nov 2025
Docker для Node.js: контейнеризация Express
Docker для Node.js: контейнеризация Express

Кратко

Логотип Node.js на графическом фоне

Быстрые ссылки

  • Создание проекта Node

  • Написание Dockerfile

  • Сборка образа

  • Запуск контейнера

  • Итоги

Docker — это платформа контейнеризации, которая упрощает упаковку и запуск приложений. Контейнеры работают как изолированные процессы со своей файловой системой, но используют общий для хоста ядро. Docker стал популярным инструментом для воспроизводимых сред разработки и распределённого развёртывания.

Node.js — ведущая среда выполнения JavaScript на сервере. Для надёжного запуска веб-сервиса на Node.js требуется: установленная среда выполнения, доступный код приложения и механизм, автоматически перезапускающий приложение при падении.

В этом руководстве мы используем Docker, чтобы контейнеризовать простое приложение на Node.js, написанное с использованием Express. Docker даёт предсказуемую среду, содержащую всё необходимое для работы сервиса. Демон Docker умеет автоматически рестартовать контейнеры при падении их основного процесса, что решает одну из распространённых задач при развёртывании Node.js.

Создание проекта Node

Мы не будем останавливаться на логике приложения. Создайте директорию проекта и добавьте код сервера внутри неё. Вот базовый файл приложения.

app.js

Пример простого приложения, слушающего порт 8080 и возвращающего фиксированный ответ:

const express = require("express");
const app = express();

app.get("*", (req, res) => res.send("

It works!

")); app.listen(8080, () => console.log("Listening on 8080"));

Добавьте Express в проект с помощью npm:

npm init

npm install --save express

Запустите приложение локально, чтобы убедиться, что оно работает:

node app.js

Откройте в браузере localhost:8080 — вы должны увидеть тестовый ответ.

Скриншот: запущенное примерное Node.js-приложение

Написание Dockerfile

Пришло время поместить приложение в контейнер. Сначала нужен базовый образ. Образы инкапсулируют код и зависимости в единый пакет; инструкции Dockerfile определяют начальное состояние файловой системы контейнера.

Ниже — Dockerfile, который подходит для примера выше:

FROM node:16
WORKDIR /app
COPY package.json .
COPY package-lock.json .
RUN npm ci
COPY app.js .
CMD ["app.js"]

Этот Dockerfile использует официальный образ Node.js как базовый. Он наследует содержимое базового образа и добавляет своё.

WORKDIR задаёт рабочую директорию внутри образа — /app. Инструкции COPY добавляют файлы в эту директорию внутри образа.

Установка зависимостей

Следующий шаг — добавить package.json и выполнить npm ci. Это установит зависимые пакеты (в нашем случае — Express) внутри файловой системы образа.

Не копируйте локальную папку node_modules в образ (не делайте COPY node_modules/ .). Это ломает воспроизводимость сборки и увеличивает контекст сборки. Dockerfile должен позволять создавать образ по содержимому репозитория — все игнорируемые в .gitignore файлы не должны попадать в инструкции COPY.

Копирование кода приложения

После npm ci добавляем код приложения. Расположение инструкции COPY после RUN сделано намеренно: каждая инструкция создаёт слой образа, а Docker кеширует слои для ускорения последующих сборок. Поскольку код приложения обычно меняется чаще, чем lock-файл, помещая инсталляцию зависимостей до копирования кода, мы уменьшаем частоту повторной установки зависимостей при правках кода.

Команда запуска образа

Последняя инструкция CMD запускает приложение, когда контейнер стартует. Базовый образ Node настроен с entrypoint, который запускает бинарь node, поэтому CMD ["app.js"] будет интерпретирован как node app.js.

Сборка образа

Построим образ:

docker build -t node-app:latest .

Скриншот: вывод лога сборки docker build

Команда возьмёт Dockerfile в текущей директории, выполнит инструкции и пометит итоговый образ тегом node-app:latest. Точка . указывает контекст сборки — корень, откуда Docker будет брать файлы, доступные для COPY.

Оптимизация сборки

Добавьте файл .dockerignore в корень проекта, чтобы исключить лишние файлы из контекста сборки. Например:

node_modules/

Это сократит объём данных, которые Docker копирует в временное место для сборки, и ускорит подготовку контекста.

Запуск контейнера

Теперь можно запустить приложение в контейнере:

docker run -d \
  -p 8080:8080 \
  --name my-app \
  --restart on-failure \
  node-app:latest

Команда создаёт и запускает новый контейнер из образа. Основные флаги:

  • -d — запускает контейнер в фоновом режиме.
  • -p 8080:8080 — пробрасывает порт 8080 хоста в порт 8080 внутри контейнера. Если нужно использовать другой порт на хосте, измените первый номер (например, 8100:8080).
  • --name — задаёт удобное имя контейнера.
  • --restart — политика перезапуска; on-failure перезапустит контейнер, если он завершился с ненулевым кодом.

ID контейнера будет выведен в терминал. Доступ к приложению снова по localhost:8080, но теперь сервис работает внутри Docker.

Полезные пояснения по флагам рестарта

Docker поддерживает несколько политик рестарта:

  • no — не перезапускать (значение по умолчанию);
  • on-failure[:max-retries] — перезапускать при ненулевом коде выхода, опционально ограничивая число попыток;
  • always — всегда перезапускать контейнер при его остановке (полезно для сервисов, которые должны всегда быть в сети);
  • unless-stopped — как always, но не перезапускать контейнер после ручной остановки.

Выбор зависит от требований к жизненному циклу сервиса.

Итого

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

Для обновления сервиса пересоберите образ, удалите старый контейнер (docker rm ) и запустите новый экземпляр. В продакшне обычно используют Docker Compose или оркестраторы (Kubernetes) вместо ручных docker run, чтобы управлять конфигурацией и масштабированием.

Ниже — расширенная практическая часть: рекомендации для продакшна, чек-листы, шаблоны и runbook для отката.

Когда подход с Docker не подходит

  • Если у вас одноплатформенное окружение с жёсткими ограничениями на ядро (например, сервисы, требующие специфичных модулей ядра), контейнеры могут не обеспечить необходимой функциональности.
  • Для крайне мелких скриптов и одноразовых задач накладные расходы на создание образа могут быть неоправданными.
  • Если у вас требования к очень тонкой интеграции с хостовой ОС (специальные устройства, драйверы), классический контейнерный подход может не подойти.

В этих случаях рассматривайте виртуальные машины, Systemd-сервисы или иные инструменты развёртывания.

Альтернативные подходы

  • Docker Compose — удобен для локальной разработки и описания многоконтейнерных приложений (база данных + кэш + сервисы).
  • Kubernetes — для производства с требованиями к масштабированию, self-healing, SLO/SLI, управлению конфигурацией.
  • Heroku/Platform-as-a-Service — если нужно упростить инфраструктуру и делегировать управление платформой.

Ментальные модели и эвристики

  • Образ = снимок файловой системы и метаданных. Слой — слой изменений. Чем меньше слоёв и чем они стабильнее, тем быстрее сборка и меньше размер результата.
  • Lock-файлы (package-lock.json) должны копироваться и применятьс я до установки зависимостей, чтобы обеспечить детерминированную установку.
  • Код меняется чаще, чем зависимости — располагайте COPY с кодом после RUN npm ci, чтобы использовать кэш установки зависимостей.

Факто-бокс: ключевые команды

  • Локальная проверка: node app.js
  • Сборка образа: docker build -t node-app:latest .
  • Пуск контейнера: docker run -d -p 8080:8080 –name my-app –restart on-failure node-app:latest
  • Просмотр логов: docker logs -f my-app
  • Удаление контейнера: docker rm -f my-app
  • Список образов: docker images

Мини-методология: от разработки до продакшна (шаги)

  1. Локальная разработка и тесты.
  2. Подготовка package.json и package-lock.json.
  3. Создание Dockerfile и .dockerignore.
  4. Локальная сборка и тестирование образа.
  5. Репозиторий образов (Docker Registry): push тестового образа.
  6. Развёртывание на staging-среде (Compose/Kubernetes).
  7. Нагрузочное тестирование, smoke-тесты.
  8. Продакшн-роллаут с канареечными релизами.

Чек-листы по ролям

Разработчик

  • Код запускается локально с node.
  • package.json и package-lock.json находятся под версионным контролем.
  • Написаны базовые unit/интеграционные тесты.
  • Локальный образ успешно собирается и запускается.

Операции / DevOps

  • Собраны образы для staging и prod.
  • Образы загружены в registry с тегами (sha, версия).
  • Настроены мониторинг и логирование контейнеров.
  • Проверены политики рестарта и лимиты ресурсов.

Критерии приёмки

  • Приложение отвечает на запросы на ожидаемом порту.
  • Образ собран без ошибок; сборка использует кеширование слоёв.
  • Контейнер стартует и перезапускается при падении (политика рестарта работает).
  • Логи доступны для чтения и агрегирования.
  • Образ загружен в реестр и помечен версией/sha.

Runbook отката (короткий)

  1. Если новый контейнер падает — проверяем логи: docker logs -f <имя>.
  2. Если ошибка вызвана новым образом, останавливаем новый контейнер: docker rm -f <имя>.
  3. Запускаем предыдущую версию: docker run -d -p … –name <имя> <тег-старого-образа>.
  4. При необходимости откатываем конфигурацию в Orchestrator (K8s rollback).
  5. Документируем причину и меры.

Тестовые случаи и критерии приёмки

  • Unit-тесты: 100% критических функций работают локально.
  • Smoke-тест: endpoint /health или корневой маршрут возвращает 200.
  • Интеграция с внешними сервисами: симуляция отказа внешней зависимости и поведение приложения.

Шаблон docker-compose (пример для локальной разработки)

version: "3.8"
services:
  app:
    build: .
    image: node-app:dev
    ports:
      - "8080:8080"
    restart: on-failure

Compose удобен для описания стеков и запуска несколькими командами (docker-compose up -d).

Советы по безопасности и харднинг

  • Не храните секреты в образе. Используйте переменные окружения, секреты Docker Swarm или Kubernetes Secrets.
  • Запускайте контейнеры с минимально необходимыми правами: указывайте пользователя в Dockerfile (USER node) или через runtime.
  • Ограничивайте ресурсы контейнера (--memory, --cpus) для предотвращения вытеснения других сервисов.
  • Обновляйте базовый образ (node:*), регулярно собирайте и пересобирайте образы с патчами безопасности.
  • Сканируйте образы на уязвимости (тулзы: Trivy, Clair и т.п.).

Управление конфигурацией и секретами

  • Для разработки — .env файлы (не в репозитории).
  • Для staging/prod — менеджеры секретов (Vault), или секреты оркестратора.
  • Избегайте попадания конфиденциальных данных в переменные окружения, доступные всем процессам хоста.

Маленькие образы и оптимизация размера

  • Для уменьшения размера образа рассмотрите базовые теги node:16-alpine (меньше, но возможны несовместимости) или distroless-образы.
  • Multi-stage build: собираете в большом образе, затем копируете только артефакты в минимальный runtime-образ.

Пример (концептуально):

# билд-стадия
FROM node:16 AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build

# рантайм
FROM node:16-alpine
WORKDIR /app
COPY --from=build /app/dist ./
CMD ["node", "index.js"]

Совместимость и версии

  • Указывайте версию Node (например, node:16) в Dockerfile, чтобы собрать предсказуемый образ.
  • Планируйте миграцию версий Node заранее: тесты, проверка несовместимостей зависимостей.
  • Используйте semantic versioning в тегах образов (v1.2.3) и дополнительно храните тег с commit SHA.

Локальные альтернативы и распространённые ошибки в русскоговорящих командах

  • Частая ошибка: не актуальный package-lock.json в репозитории — это ломает детерминированность установки.
  • Копирование локального node_modules в образ — увеличивает контекст и часто приводит к ошибкам со сборкой нативных модулей.
  • Привычка запускать контейнеры без управления конфигурацией — приводит к незадокументированным флагам при повторном развёртывании.

Мониторинг и логирование

  • Логи контейнера доступны через docker logs и должны агрегироваться в центральную систему (ELK, Loki, Datadog и т. п.).
  • Метрики приложения (Prometheus) и health-эндпоинты повышают наблюдаемость.
  • Для Kubernetes используйте readiness/liveness probes.

Деплой в registry и CI/CD (общая последовательность)

  1. CI собирает образ и помечает тэгом (например, registry.company.com/project/node-app:sha-abc123).
  2. CI пушит образ в приватный registry.
  3. CD берёт новый образ и обновляет staging/production конфигурацию (Compose/K8s), выполняя контрольный прогон smoke-тестов.
  4. Если тесты пройдены — переключение трафика (canary/blue-green) или масштабирование новой версии.

Примеры отказов и что делать

Симптом: контейнер стартует, но приложение не отвечает.

  • Проверьте логи: docker logs .
  • Убедитесь, что внутренняя портовая конфигурация совпадает с пробросом портов.
  • Убедитесь, что NODE_ENV и другие переменные заданы корректно.

Если причина — зависимость, верните предыдущий образ и исправьте конфигурацию/код.

План миграции на Kubernetes (высокоуровнево)

  1. Определите манифесты Deployment и Service.
  2. Добавьте readiness/liveness probes.
  3. Настройте Resource requests/limits.
  4. Настройте Horizontal Pod Autoscaler (HPA) при необходимости.
  5. Настройте Ingress/Reverse proxy и TLS.

Пример readiness/liveness для Kubernetes:

readinessProbe:
  httpGet:
    path: /health
    port: 8080
  initialDelaySeconds: 5
  periodSeconds: 10

livenessProbe:
  httpGet:
    path: /health
    port: 8080
  initialDelaySeconds: 30
  periodSeconds: 30

Заключение

Контейнеризация Node.js с помощью Docker — мощный инструмент для унификации среды запуска, автоматизации перезапуска и простого развёртывания. Для локальной разработки Docker ускоряет воспроизводимость, а для продакшна — служит основой для оркестрации и масштабирования.

Ключевые практики: контролируйте версии базовых образов, отделяйте установку зависимостей от копирования исходников, не храните секреты в образах, автоматизируйте сборку и развёртывание через CI/CD. Для сложных или высоконагруженных систем переходите на Docker Compose или Kubernetes и применяйте описанные выше рекомендации по безопасности и мониторингу.

Важно: начните с простого workflow (как в этом руководстве), а затем постепенно внедряйте более сложные элементы (регистри, CI, оркестратор), соблюдая чек-листы для ролей.


Важные ссылки и команды для быстрого доступа:

  • Сборка образа: docker build -t node-app:latest .
  • Запуск: docker run -d -p 8080:8080 –name my-app –restart on-failure node-app:latest
  • Просмотр логов: docker logs -f my-app
  • Удаление: docker rm -f my-app
Поделиться: X/Twitter Facebook LinkedIn Telegram
Автор
Редакция

Похожие материалы

Несколько аккаунтов Skype: Multi Skype Launcher
Программное обеспечение

Несколько аккаунтов Skype: Multi Skype Launcher

Журнал для работы: повысить продуктивность
Productivity

Журнал для работы: повысить продуктивность

Персональные звуки уведомлений на Android
Android.

Персональные звуки уведомлений на Android

Скачивание шоу Hulu для офлайн‑просмотра
Стриминг

Скачивание шоу Hulu для офлайн‑просмотра

Microsoft Start: персонализированная новостная лента
Новости

Microsoft Start: персонализированная новостная лента

Как изменить имя в Epic Games быстро
Гайды

Как изменить имя в Epic Games быстро