Контейнеризация JavaScript: Docker для Node.js и фронтенда

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Почему ваш JavaScript-проект заслуживает Docker?

Вы когда-нибудь слышали фразу: «На моей машине всё работало!»? Если вы JavaScript-разработчик, то наверняка сталкивались с ситуацией, когда приложение отлично запускается локально, но на сервере или у коллеги — падает с загадочной ошибкой. Контейнеризация JavaScript приложений с Docker решает эту проблему раз и навсегда.

Docker позволяет упаковать ваш код, все зависимости (npm-пакеты), переменные окружения и даже версию Node.js в один легковесный «контейнер». Этот контейнер будет работать одинаково на вашем ноутбуке, на сервере в AWS и на компьютере новичка, который только что установил Git. В этой статье мы разберём, как подружить JavaScript и Docker, напишем реальный код и создадим production-ready образ для вашего следующего pet-проекта.

Основы Docker для JavaScript-разработчика

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

Dockerfile — рецепт вашего приложения

Это текстовый файл, в котором вы описываете, как собрать образ. Для Node.js приложения типичный Dockerfile выглядит так:

# Используем официальный образ Node.js
FROM node:18-alpine

# Устанавливаем рабочую директорию внутри контейнера
WORKDIR /app

# Копируем package.json и package-lock.json
COPY package*.json ./

# Устанавливаем зависимости
RUN npm ci --only=production

# Копируем исходный код
COPY . .

# Открываем порт
EXPOSE 3000

# Команда для запуска
CMD ["node", "server.js"]

Обратите внимание: мы сначала копируем package.json, а потом весь код. Это не случайность. Docker кеширует слои, и если вы не меняли зависимости, этап npm ci не будет перезапускаться при каждом изменении кода. Это сильно ускоряет сборку.

Образы и контейнеры

  • Образ (Image) — это шаблон. Вы собираете его один раз.
  • Контейнер (Container) — это запущенный экземпляр образа. Вы можете создать 10 контейнеров из одного образа.

Создаём Node.js приложение с нуля и контейнеризируем его

Давайте напишем простое Express-приложение и упакуем его в Docker. Это будет наш первый практический шаг в контейнеризации JavaScript.

Шаг 1: Пишем приложение

Создайте папку docker-node-app и внутри — файл server.js:

const express = require('express');
const app = express();
const PORT = process.env.PORT || 3000;

app.get('/', (req, res) => {
  res.json({ 
    message: 'Привет из контейнера!',
    nodeVersion: process.version,
    environment: process.env.NODE_ENV || 'development'
  });
});

app.listen(PORT, () => {
  console.log(`Сервер запущен на порту ${PORT}`);
});

Инициализируем npm и установим express:

npm init -y
npm install express

Теперь у нас есть рабочее приложение. Запустите его локально: node server.js. Откройте http://localhost:3000 — вы увидите JSON.

Шаг 2: Создаём Dockerfile

Создайте файл Dockerfile (без расширения) в корне проекта:

FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]

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

node_modules
npm-debug.log
.git
.gitignore
.env
Dockerfile

Шаг 3: Собираем и запускаем

Откройте терминал в папке проекта и выполните:

docker build -t my-node-app .
docker run -p 3000:3000 my-node-app

Флаг -p 3000:3000 пробрасывает порт: первый — это порт на вашей машине, второй — порт внутри контейнера. Теперь откройте http://localhost:3000 — ваше приложение работает в контейнере!

Продвинутые техники: многослойная сборка (Multi-stage build)

Когда вы работаете над крупным JavaScript-проектом, особенно с фронтендом, образ может «распухать». Для production-сборки нам не нужны dev-зависимости и инструменты сборки. Здесь на помощь приходит многослойная сборка.

Пример: React приложение в Docker

Представьте, что у нас есть React-приложение, собранное с помощью Vite. Мы хотим, чтобы финальный образ содержал только статические файлы, которые раздаёт Nginx.

# Этап 1: Сборка
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# Этап 2: Продакшн
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

Первый этап (builder) устанавливает все зависимости и собирает статику. Второй этап берёт только готовую папку dist и копирует её в чистый образ Nginx. Итоговый образ весит около 20 МБ вместо 500 МБ. Это лучшая практика контейнеризации фронтенда.

Docker Compose: оркеструем несколько сервисов

Редко какое приложение состоит только из Node.js. Часто есть база данных, Redis или очередь сообщений. Docker Compose позволяет описать всю инфраструктуру в одном YAML-файле и запускать одной командой.

Пример: Node.js + MongoDB

Создайте файл docker-compose.yml:

version: '3.8'

services:
  app:
    build: .
    ports:
      - "3000:3000"
    environment:
      - MONGO_URI=mongodb://mongo:27017/mydb
      - NODE_ENV=production
    depends_on:
      - mongo

  mongo:
    image: mongo:6
    volumes:
      - mongo-data:/data/db

volumes:
  mongo-data:

Теперь в коде вашего приложения можно подключиться к MongoDB по адресу mongo — Docker Compose автоматически создаёт внутреннюю сеть, где сервисы видят друг друга по имени.

Запуск одной командой:

docker-compose up -d

Флаг -d запускает контейнеры в фоне. Чтобы остановить: docker-compose down. Это идеальный инструмент для локальной разработки и тестирования.

Опти

Блоги

Рекомендаций по книгам