Контейнеры и Docker: одна и та же коробка везде

Зачем:«У меня работает» перестаёт быть оправданием, когда программа едет в коробке со всем нужным. После урока вы различаете образ, контейнер и том, читаете и пишете Dockerfile и compose-файл для сайта своего проекта, запускаете контейнер в симуляторе и честно решаете, нужен ли он вам вообще.

30 минут

Озвучено синтезом речи (Qwen3-TTS), голос не принадлежит человеку.

Docker упаковывает программу со всем нужным ей в образ и запускает его контейнером одинаково везде. Образ задаёт Dockerfile, набор контейнеров — compose.yaml.

«У меня работает»

У каждого, кто хоть раз переносил программу на другой компьютер, есть эта история. На ноутбуке сайт работает, а на сервере нет: там другая версия языка, не хватает библиотеки, по-другому настроен веб-сервер. Раньше эту разницу устраняли инструкциями на десять страниц («поставьте то, потом это, потом поправьте вот тот файл»), и инструкции устаревали.

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

Образ, контейнер, том

Это три слова, которые надо различать.

  • Образ (image) это готовая неизменяемая «коробка»: система в урезанном виде, ваша программа, её библиотеки и настройки. Образ состоит из слоёв, каждый из которых добавляет что-то к предыдущему.
  • Контейнер это запущенный экземпляр образа: процесс, которому кажется, что у него своя файловая система и своя сеть. Из одного образа можно запустить сколько угодно контейнеров. Образ это рецепт торта, а контейнер сам торт на столе.
  • Том (volume) это место для данных, которое живёт отдельно от контейнера. Всё, что контейнер записал внутрь себя, пропадает вместе с ним. Поэтому то, что жалко потерять (заявки, загруженные файлы, база), хранят на томе или в отдельной базе.

Есть ещё реестр: хранилище образов, откуда их скачивают (Docker Hub, у крупных облаков и у GitHub свои реестры). Из реестра берут чужие образы (например, готовый nginx), в реестр выкладывают свои.

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

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

Они пропадут вместе с контейнером. Для данных, которые нужно сохранить, нужен том или отдельная база.

Dockerfile: рецепт образа

Образ описывают в файле по имени Dockerfile: короткие строки-инструкции, каждая делает один слой.

FROM nginx:alpine
COPY dist/ /usr/share/nginx/html/
EXPOSE 80

Прочтите это как рецепт. FROM говорит, с какого готового образа начать (здесь веб-сервер nginx в лёгкой сборке alpine). COPY кладёт в образ собранные файлы вашего сайта из папки dist (её даёт сборка, о которой вы узнали в частях про JavaScript и генераторы). EXPOSE предупреждает, что внутри слушают порт 80. Всё остальное, чтобы раздавать файлы, уже есть в nginx.

Когда сайт нужно сначала собрать, на помощь приходит многостадийная сборка: в первой стадии Node.js собирает сайт, а во второй в чистый nginx кладётся только готовый результат, без Node и без исходников.

FROM node:lts-alpine AS build
WORKDIR /app
COPY package.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM nginx:alpine
COPY --from=build /app/dist /usr/share/nginx/html
EXPOSE 80

Две особенности, которые экономят время и нервы. Первая: Docker запоминает слои, и если инструкция и всё до неё не изменились, берёт слой из кэша. Поэтому сначала копируют редко меняющееся (package.json), потом часто меняющееся (код). Вторая, и главная: секреты в образ не кладут. Строка ENV API_KEY=... записывает ключ в слой навсегда: образы выкладывают, копируют, и ключ уезжает вместе с ними. Секреты передают при запуске. О мелочи: файл .dockerignore не даёт ненужному (например .env или папке node_modules) попасть в образ.

Compose: несколько контейнеров как один проект

У проекта обычно несколько частей: сайт, программа, которая принимает заявки, иногда база. Запускать каждую командой на пять флагов неудобно, поэтому есть Compose: один файл compose.yaml описывает все контейнеры, порты, тома и связи между ними.

services:
  site:
    build: .
    ports:
      - "8080:80"
    restart: unless-stopped
  receiver:
    build: ./receiver
    env_file: .env
    volumes:
      - requests:/data
    restart: unless-stopped
volumes:
  requests:

Здесь ports: "8080:80" соединяет порт 8080 вашего компьютера с портом 80 внутри контейнера (слева «снаружи», справа «внутри»). env_file: .env берёт секреты из файла, который не попадает ни в образ, ни в репозиторий. requests:/data подключает том requests к папке /data приёмника, и объявлен он в разделе volumes внизу. Запуск всего проекта: docker compose up -d; состояние: docker compose ps; остановка: docker compose down (тома при этом остаются).

Попробуйте в симуляторе

Настоящего Docker в браузере нет, поэтому это симулятор: страница сама разбирает команды. Но порядок и сообщения настоящие. Выберите Dockerfile (в третьем варианте ключ записан в образ, посмотрите, что скажет сборка), соберите образ, запустите контейнер, откройте сайт командой curl, остановите и удалите контейнер.

Docker в браузере: Dockerfile, образ, контейнер
Образы (слои)

Образов нет. Соберите: docker build -t site .

Контейнеры

Контейнеров нет.

Это учебный симулятор: настоящего Docker здесь нет. Введите help или нажмите на команду ниже.

Что стоит заметить. Если запустить контейнер без -p, он работает, но снаружи не открывается. Если порт уже занят другим контейнером, будет ошибка. Повторная сборка берёт неизменённые слои из кэша. А сайт, который отдаёт curl, подписан вашим проектом.

Если захотите попробовать по-настоящему, поставьте Docker Desktop (для личного пользования и небольших компаний бесплатный; для крупных нужна подписка) или его бесплатную замену Podman, и повторите те же команды в терминале. Из России образы с Docker Hub иногда бывают недоступны или медленны: проверьте и учтите, что у российских облаков есть собственные реестры образов.

Проверьте себя: что значит -p 8080:80 в команде запуска?

Порт 8080 вашего компьютера соединяется с портом 80 внутри контейнера. Без этого контейнер работает, но снаружи не открывается.

Честно: когда Docker лишний

У контейнеров есть цена: их надо изучить, обновлять базовые образы (в них тоже находят уязвимости), следить за диском и логами. Поэтому честный вопрос: что они решают у вас?

  • Сайт из статических файлов. Докер не нужен совсем: файлы кладут на статический хостинг, и это дешевле и надёжнее. Контейнер с nginx для одной страницы это сложный способ сделать простое.
  • Одна машина, среда не меняется. Выигрыша от «одинаковой коробки» нет.
  • Несколько программ, которые надо запускать одинаково у вас, у коллеги и на сервере (сайт, приёмник заявок, база): здесь Docker и Compose окупаются.
  • Проект, которому для главного действия хватает готового сервиса (приём форм, запись, оплата): упаковывать нечего.

Сравните с первым уроком части. Ступени хостинга тоже были лестницей, и Docker это ещё одна ступенька, а не необходимость.

Практика

Перед вами сайт вашего проекта, а под ним два файла в рамках: Dockerfile и compose.yaml. Допишите их по образцу из урока. Потом откройте симулятор, соберите образ и запустите контейнер с портом, и вставьте строку из docker ps в заметку. Скажите, что лежит на томе (через главное действие вашего проекта), и ответьте на последний вопрос: нужен ли Docker вам.

Проверка читает тексты в рамках. Запускать Docker в песочнице нельзя, так что запуск в симуляторе остаётся вашим делом, а в заметке видно, что он удался.

Что было не так раньше

В 2010 году единицей переноса был сервер: админ настраивал машину руками, и через год никто не помнил, как именно. Такие серверы называли «снежинками»: каждый неповторим, и копию не сделать. Переезд означал неделю ручной работы и несколько «а, ещё нужно поставить…». Контейнер превратил настройку в файл, который лежит в репозитории, читается и воспроизводится. Но, как всегда с удобными вещами, главное остаётся: контейнер упаковывает не знания, а лишь их запись, и разбираться в том, что внутри, всё равно придётся вам.

Практика

Готовлю вашу страницу…

Тест

Вопрос 1 из 5

Соедините понятие Docker с тем, что это

Подберите пару к каждому пункту.

Карточки

Эти карточки попадут в повторение, когда вы завершите урок.

  • Чем образ отличается от контейнера?

    Ответ

    Образ это неизменяемый шаблон (слои: система, программа, настройки). Контейнер это запущенный экземпляр образа. Из одного образа можно запустить много контейнеров.

  • Зачем нужен том?

    Ответ

    Всё, что контейнер пишет внутрь себя, пропадает вместе с ним. Том хранит данные отдельно: контейнер можно удалить и создать заново, данные останутся.

  • Основные инструкции Dockerfile

    Ответ

    FROM (базовый образ), WORKDIR, COPY (положить файлы), RUN (команда при сборке), ENV, EXPOSE (какой порт слушает), CMD (что запускать). Каждая инструкция делает слой, неизменённые слои берутся из кэша.

  • Как обращаться с секретами в Docker?

    Ответ

    Не записывать их ни в Dockerfile (ENV, ARG), ни в compose, который хранится в репозитории. Передавать при запуске: переменные окружения, env_file с файлом .env вне репозитория, менеджер секретов.

  • Когда Docker не нужен?

    Ответ

    Для сайта из статических файлов (хватит статического хостинга) и когда среда одна и не меняется. Он полезен, когда несколько программ нужно запускать одинаково в разных местах.

Источники

Видео

Введение в Docker: контейнеры, изоляция и первые шаги (OTUS)

Видео загрузится с youtube-nocookie.com только после нажатия.

Открыть на сайте

Уроки Docker для начинающих / #1 (Гоша Дударь)

Видео загрузится с youtube-nocookie.com только после нажатия.

Открыть на сайте

Дальше

Теперь вы знаете: контейнеры и Docker: образ, контейнер, том, Dockerfile и Compose.