Интеграция Docker

Интеграция Docker

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

Но наше приложение не всегда будет состоять из одного контейнера. Как мы уже ранее упоминали, возможно появится необходимость подключения базы данных или прокси-сервера для корректного запуска вашего ПО. В этом случае используется Docker Compose — инструмент, который описывает мультиконтейнерное приложение в одном YAML‑файле (сервисы, сети, тома) и позволяет поднять всё одной командой docker compose up. Сейчас используется Compose v2 (встроен в Docker CLI как плагин docker compose).

В прошлой версии Docker Compose v3 (не спрашивайте почему 2 идет после 3 - честно не знаю) в качестве основной команды использовался docker-compose, сейчас дефис уже не нужен.

Установка Docker

Формально есть несколько способов поставить Docker на каждую из платформ, здесь рассмотрим только самые классические и удобные.

Windows 10/11: Docker Desktop + WSL 2

  1. Скачайте и установите Docker Desktop for Windows. Во время установки выберите опцию с включением WSL 2 (Windows Subsystem For Linux). После установки включите опцию Use WSL 2 based engine в Settings → General.
  2. (При необходимости) Установите и обновите сам WSL 2 по инструкции Microsoft, затем в Docker Desktop включите интеграцию с выбранным дистрибутивом WSL (Settings → Resources → WSL Integration).
  3. Проверьте установку:
    docker --version
    docker compose version
    docker run hello-world
    Сообщение Hello from Docker! означает, что всё работает.

Если будете хранить исходники внутри файловой системы WSL 2 (например, /home/<user>/projects/...) — это немного ускорит bind‑mount (процесс монтирования) при старте. У Docker Desktop есть официальные рекомендации по производительности WSL 2. ([Docker Documentation][6])

macOS: Docker Desktop for Mac

  1. Скачайте Docker Desktop для macOS (отдельные сборки для Apple Silicon и Intel) и установите по инструкции. ([Docker Documentation][8])

  2. Проверьте:

    docker --version
    docker compose version
    docker run hello-world

    Сообщение Hello from Docker! подтверждает корректную установку.

Docker Desktop для macOS также включает Compose.

Linux: Скрипт get-docker.sh

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

curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh

После установки:

# Разрешим текущему пользователю работать с docker без sudo
sudo groupadd docker 2>/dev/null || true
sudo usermod -aG docker $USER
newgrp docker

# Проверка
docker --version
docker run hello-world

Добавление в группу docker фактически даёт root‑права к демону.

Root доступ на самом деле серьезная угроза для безопасности. В этой области достаточно часто находят CVE высокого уровня опасности, поэтому можете изучить альтернативный вариант — rootless mode. ([Docker Documentation][10]). Или рассмотреть альтернативный вариант - использовать Podman вместо docker.

Базовые команды Docker и Compose

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

  • Сборка образа: docker build -t myapp:dev . — собирает образ по Dockerfile. По умолчанию используется BuildKit/Buildx.
  • Запуск контейнера: docker run --rm -p 8080:80 myapp:dev — запускает контейнер, пробрасывая порт. По этому порту потом можно обратиться к содержимому контейнера из основной хост системы.
  • Список контейнеров: docker ps (только запущенные) или docker ps -a (все).
  • Логи/доступ внутрь: docker logs <container> и docker exec -it <container> sh — посмотреть вывод и попасть в shell конкретного контейнера.

Но как мы с вами уже и сказали, чаще всего наше взаимодействие будет е напрямую с docker, а с его плагином, который позволит объединить несколько контейнеров в единую сеть, чтобы они могли выступать в качестве компонентов / ресурсов нашей единой информационной системы.

При использовании Compose (v2) вы скорее всего будете использовать следующий набор основных команд:

  • Поднять приложение: docker compose up / в фоне -d / форс‑сборка --build.
  • Остановить и убрать всё созданное: docker compose down.
  • Состояние/логи/выполнить команду: docker compose ps, docker compose logs -f, docker compose exec.

Раньше в compose файлах еще использовался ключ version, но после перехода на Compose v2 он был помечен как depreciated.

Также для объединения несколько файлов конфигураций можно использовать концепцию объединения (override), которая описана в документации Docker Compose. (Docker Documentation ). Пример с её использованием мы рассмотрим чуть позже.

Dockerfile

Прежде чем мы что-то сможем запустить нам нужно написать инструкции каким образом наш проект в принципе должен быть запущен. Для этого используется Dockerfile — текстовый рецепт сборки образа. Строится послойно, выполняя инструкции сверху вниз. Файл должен начинаться с FROM (кроме глобальных ARG/директив парсера).

Минимальный набор инструкций, который вам необходимо узнать и познакомиться:

Инструкция Назначение / пример
FROM <image>[:tag] Базовый образ. FROM node:24-alpine
WORKDIR <path> Рабочий каталог для последующих команд. WORKDIR /app
COPY <src> <dest> Копирование файлов в образ. COPY package*.json .
ADD Как COPY, но с распаковкой архивов/URL; используйте COPY по умолчанию.
RUN <cmd> Команда на этапе сборки. RUN npm ci
ENV k v / ARG k=v Переменные окружения и аргументы сборки.
EXPOSE <port> Документация порта (для людей/инструментов).
CMD ["..."] Команда по умолчанию при запуске контейнера.
ENTRYPOINT ["..."] Исполняемый процесс PID 1, сочетается с CMD. Чаще всего используется для запуска кастомных скриптов при старте контейнера
HEALTHCHECK ... Проверка здоровья контейнера (опционально).
LABEL Метаданные образа.

Но кроме самого Dockerfile при сборке еще очень часто (почти всегда) необходим еще один файл - .dockerignore (похож на .gitignore - с ним вы уже должны быть знакомы). Он исключает папки/файлы из контекста сборки — ускоряет и снижает размер образа. Для Node‑проекта обычно игнорируют node_modules, dist, .git, *.log, .env* и т. п. (Docker Documentation )

Dockerfile для нашего проекта

В dev‑режиме Vite слушает порт 5173 и по умолчанию биндится на localhost (это вы могли заметить, когда после написания npm run dev вам необходимо перейти в браузере по маршруту http://localhost:51173, чтобы открыть и просмотреть базовую страницу). В контейнере кроме этого нам нужно будет явно включить прослушивание на всех интерфейсах (--host 0.0.0.0) и зафиксировать порт в самом Vite strictPort: true, чтобы при возникновении конфликта Vite не попробовал «перескочить» на соседние порты.

Примерная структура того что мы с вами добавим:

  • Dockerfile
  • docker-compose.dev.yml
  • .dockerignore
  • vite.config.ts
  • package.json
  • Порт зафиксируем опцией strictPort, чтобы Vite не «перепрыгивал», если 5173 занят:

    src/vite.config.ts
    import { defineConfig } from 'vite'
    import react from '@vitejs/plugin-react'
    
    export default defineConfig({
      plugins: [react()],
      server: {
        host: true,        // эквивалент "0.0.0.0"
        port: 5173,        // порт по умолчанию
        strictPort: true   // если занят — не переключаться автоматически
      }
    })

    Подробнее изучить опции server.host, –host и –strictPort можете в официальных руководствах Vite (Vite ).

    .dockerignore:

    .dockerignore
    node_modules
    dist
    .git
    .vscode
    .DS_Store
    *.log
    .env*

    Dockerfile — собираем dev‑контейнер на Node 24 Alpine (в сборках вы практически всегда будете видеть Alpine, т.к. это один из самых легковесных Linux образов и размер финального контейнера с его использованием получится минимальный):

    Dockerfile
    # Dockerfile
    # ---- базовые зависимости
    FROM node:24-alpine AS deps
    WORKDIR /app
    COPY package*.json ./
    RUN npm ci
    
    # ---- dev: Vite + HMR
    FROM node:24-alpine AS dev
    WORKDIR /app
    COPY --from=deps /app/node_modules ./node_modules
    # Копируем остальной код
    COPY . .
    # Открываем порт dev-сервера
    EXPOSE 5173
    # Запускаем Vite
    CMD ["npm","run","dev"]
    docker-compose.dev.yml
    services:
      frontend:
        build:
          context: .
          dockerfile: Dockerfile
          target: dev
        ports:
          - "5173:5173"            # хост:контейнер
        volumes:
          - .:/app                 # код хоста внутрь контейнера
          - /app/node_modules      # анонимный том, чтобы не затирать node_modules
        environment:
          - WATCHPACK_POLLING=true

    Запуск dev‑окружения:

    docker compose -f docker-compose.dev.yml up --build

    При сборке проекта под Windows можете попробовать сохранить проект внутри WSL (например, \\wsl.localhost\Ubuntu\home\.../home/...) — это ускоряет горячую перезагрузку и работу файловой системы с bind‑mount.

    Собираем в prod

    В продакшне нам нужен минимальный образ с уже собранными статическими файлами. Для этого используем multi‑stage build: отдельный этап «builder» собирает dist/, а финальный — на nginx:alpine — только раздаёт статику.

    Dockerfile (дополненный):

    Dockerfile
    # наше прошлое содержимое
    # ...
    
    # ---- build: собираем статическую дистрибуцию
    FROM node:24-alpine AS build
    WORKDIR /app
    COPY --from=deps /app/node_modules ./node_modules
    COPY . .
    RUN npm run build
    
    # ---- prod: nginx + статика
    FROM nginx:1.27-alpine AS prod
    # Замена стандартного конфига NGINX
    RUN rm -f /etc/nginx/conf.d/default.conf
    COPY ./nginx/nginx.conf /etc/nginx/conf.d/app.conf
    # Копируем результаты сборки
    COPY --from=build /app/dist/ /usr/share/nginx/html/
    EXPOSE 80

    nginx/nginx.conf — конфиг Nginx для SPA:

    nginx/nginx.conf
    server {
      listen       80;
      server_name  _;
    
      root /usr/share/nginx/html;
      index index.html;
    
      # Кэш для статических файлов
      location ~* \.(?:js|css|svg|png|jpg|jpeg|gif|ico|woff2?)$ {
        expires 7d;
        add_header Cache-Control "public, max-age=604800, immutable";
        try_files $uri =404;
      }
    
      # SPA fallback: все неизвестные пути -> index.html
      location / {
        try_files $uri $uri/ /index.html;
      }
    }

    Также не забудем создать и свой docker-compose.yml файл для prod сборки:

    docker-compose.yml
    services:
      web:
        build:
          context: .
          dockerfile: Dockerfile
          target: prod
        ports:
          - "80:80"

    Запуск теперь соответственно будет проводиться командой docker compose -f docker-compose.yml up -d.

    Как можно улучшить приведенную сверху конфигурацию? Ранее мы уже упоминали про механизм override (перезаписи), его можно применить тут, чтобы задать одну базовую конфигурацию и перезаписывать её в зависимости от активного режима (dev/prod). Создадим файл compose.base.yml:

    services:
    web:
      build:
        context: .
        dockerfile: Dockerfile

    А теперь создадим его вариации, которые будут запускаться в зависимости от активного режима.

    # compose.dev.yml
    services:
      web:
        build:
          target: dev
        ports:
          - "5173:5173"
        volumes:
          - .:/app
          - /app/node_modules
        environment:
          - WATCHPACK_POLLING=true

    И prod вариант:

    # compose.prod.yml
    services:
      web:
        build:
          target: prod
        ports:
          - "80:80"

    Теперь запуск тестовой сборки можно производить при помощи команды: docker compose -f compose.base.yml -f compose.dev.yml up -d --build, а в prod режиме имя второго файла изменится на compose.prod.yml. Основное преимущество такого подхода, что мы получаем возможность централизованно менять базовую конфигурацию в едином источнике правды, а не в каждом из файлов по отдельности.

    Еще стоит посмотреть

    • Что такое контейнеры и Docker: Docker Overview / Concepts. (Docker Documentation )
    • Compose — обзор, спецификация, установка: Compose overview, file reference, install. (Docker Documentation )
    • Установка: Docker Desktop (Windows/macOS), WSL 2 backend и best practices, convenience script для Linux. (Docker Documentation )
    • Dockerfile reference, .dockerignore, multi‑stage builds: официальные референсы. (Docker Documentation )
    • Vite server options (порт/хост): официальные опции server.port, server.host. (vitejs )
    • Nginx: статика/try_files, reverse‑proxy/proxy_pass. (NGINX Documentation )