Docker для разработчика: базовый workflow

Если вы только начинаете погружаться в программирование или уже пишете код, но сталкивались с проблемами «на моей машине всё работает, а на сервере — нет», то Docker станет вашим главным инструментом. Это не просто технология для разворачивания приложений, а фундамент современной разработки, который позволяет изолировать окружение, упростить настройку и гарантировать стабильность работы кода на любом этапе: от локальной IDE до продакшн-сервера.

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

Почему разработчик должен знать Docker?

Многие новички спрашивают: «Зачем тратить время на Docker, если можно просто поставить Python, Node.js или Java в систему?» Ответ кроется в реальной командной работе. У одного разработчика macOS, у другого Windows, у третьего Linux — и у каждого свои версии библиотек, особенности путей и конфликты зависимостей. Без контейнеризации настройка окружения превращается в бесконечную головную боль. Docker радикально решает эту проблему.

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

Ключевые преимущества для разработчика:

  1. Изоляция окружения: Приложение работает внутри контейнера, где установлены только нужные зависимости. Никаких конфликтов с системными библиотеками и гарантия, что код не «упадёт» на сервере из-за различий в окружении.
  2. Быстрая настройка: Вместо ручной установки десятка пакетов вы запускаете готовый контейнер с уже настроенным окружением. Это особенно ценно при онбординге новых членов команды — они могут начать писать код через несколько минут после клонирования репозитория.
  3. Портативность: Проект запускается одинаково на любой машине. Достаточно передать Dockerfile и код — и коллега получит точно такое же окружение без дополнительных танцев с бубном.
  4. Интеграция с CI/CD: Современные системы GitLab CI, GitHub Actions, Jenkins используют Docker для тестирования и деплоя. Без понимания контейнеров невозможно участвовать в полноценных DevOps-процессах. Фактически Docker — это пропуск в автоматизированную сборку и доставку.

Важный нюанс: Docker не заменяет вашу IDE (VS Code, IntelliJ IDEA) или отладчик. Он работает вместе с ними, предоставляя предсказуемое окружение для запуска кода. Вы по-прежнему пишете код в привычном редакторе, а собираете и запускаете его уже внутри контейнера.

Установка Docker на разные платформы

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

Установка на Windows (10/11)

  1. Скачайте Docker Desktop for Windows с официального сайта docker.com.
  2. Запустите установщик. Если у вас Windows 10/11, он предложит включить WSL 2 (Windows Subsystem for Linux). Обязательно согласитесь — это критически важно для производительности контейнеров и корректной работы файловой системы.
  3. После установки запустите Docker Desktop из меню «Пуск».
  4. В правом верхнем углу появится индикатор: «Engine running». Зелёный цвет означает, что всё готово.

Практический совет: если установка WSL 2 завершается ошибкой, проверьте, что в BIOS/UEFI включена аппаратная виртуализация (Intel VT-x или AMD-V), а в компонентах Windows активирована «Подсистема Windows для Linux» и «Платформа виртуальной машины». Без этого Docker будет использовать старый режим Hyper-V, что значительно медленнее и нестабильнее.

Установка на macOS

  1. Скачайте Docker Desktop for Mac (версия для Intel или Apple Silicon в зависимости от процессора).
  2. Переместите приложение в папку Applications.
  3. Запустите Docker Desktop.
  4. В настройках (Preferences) обратите внимание на лимиты памяти и CPU: иногда значения по умолчанию занижены, и контейнеры работают медленно. Я обычно выделяю не менее 4 ГБ ОЗУ.

На Mac c чипами M1/M2 Docker Desktop автоматически подхватывает нативную архитектуру, но если вам нужны образы под x86, можно использовать --platform linux/amd64 при сборке или запуске.

Установка на Linux (Ubuntu/Debian)

Для разработки на Linux удобно использовать как серверную версию Docker Engine, так и Docker Desktop. Если вы предпочитаете минималистичный вариант без GUI, выполните в терминале:

sudo apt update
sudo apt install docker.io
sudo systemctl start docker
sudo systemctl enable docker
sudo usermod -aG docker $USER

После выхода и повторного входа в систему пользователь сможет управлять Docker без sudo. Проверьте установку:

docker --version

Если хотите Docker Desktop, скачайте пакет с официального сайта и следуйте инструкциям в README. В любом случае базовый workflow остаётся одинаковым.

Типовая ошибка: После установки на Windows вы можете получить Permission denied при выполнении команд. Это значит, что Docker не запущен с нужными привилегиями. Запустите Docker Desktop от имени администратора или проверьте, что ваш пользователь добавлен в группу docker-users (на Windows это делает инсталлятор, но иногда требуется перелогиниться).

Основные понятия: Контейнер, Изображение и Dockerfile

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

1. Изображение (Image)

Изображение — это готовый, неизменяемый пакет, содержащий код приложения, библиотеки, переменные окружения и конфигурационные файлы. Это как «снимок» файловой системы в определённом состоянии.

Аналогия: файл установщика программы (setup.exe или .iso-образ диска). Его нельзя изменить, только использовать как основу для запуска.

Пример: python:3.9-slim — официальное изображение с Python версии 3.9 в облегчённой поставке.

2. Контейнер (Container)

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

Аналогия: открытое окно Word, запущенное из установочного файла. Исходный файл-установщик остался прежним, а у вас уже работает приложение.

Пример: команда docker run python:3.9 создаёт контейнер на основе изображения python:3.9.

3. Dockerfile

Dockerfile — это текстовый файл с инструкциями, описывающий, как собрать изображение. Вы пишете в нём: «возьми базовый образ, скопируй мой код, установи зависимости, выполни команду при старте».

Аналогия: рецепт приготовления блюда. Изображение — готовое блюдо, которое получилось по этому рецепту.

Пример: файл Dockerfile в корне вашего проекта.

Таблица: Сравнение понятий

Понятие Что это? Изменяемость Аналогия в жизни
Изображение (Image) Пакет с кодом и окружением Неизменяемо (Read-only) Установочный файл программы
Контейнер (Container) Запущенный процесс из изображения Изменяемо (внутри) Запущенная программа (окно)
Dockerfile Инструкция для создания изображения Изменяемо (текст) Рецепт приготовления

Базовый workflow: Создание первого контейнера

Переходим к практике. Создадим простой Docker-контейнер для Python-приложения — это наглядно покажет полный цикл от написания кода до запуска.

Шаг 1: Подготовка проекта

Создайте новую директорию, например my-docker-app. Внутри неё разместите файл app.py с простым кодом:

# app.py
print("Hello from Docker!")

Я обычно добавляю в проект сразу и .dockerignore, чтобы исключить служебные файлы, но на этом этапе можно обойтись без него. Главное — файл с кодом и Dockerfile в одной папке.

Шаг 2: Создание Dockerfile

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

FROM python:3.9-slim
WORKDIR /app
COPY app.py .
CMD ["python", "app.py"]

Разбор команд:

  • FROM — выбирает базовый образ. python:3.9-slim — облегчённая версия, которая не тащит лишнего.
  • WORKDIR — создаёт и устанавливает рабочую директорию /app внутри контейнера. Если её не указать, файлы попадут в корень, что неудобно.
  • COPY — копирует app.py из текущей папки (контекста сборки) в /app контейнера. Точка в конце означает текущий WORKDIR.
  • CMD — определяет команду, которая выполнится при запуске контейнера. Здесь запускаем Python с нашим скриптом.

Шаг 3: Создание изображения (Build)

Откройте терминал в директории my-docker-app и выполните:

docker build -t my-python-app .

-t my-python-app задаёт имя (тег) образу, чтобы к нему было удобно обращаться. Точка в конце — путь к контексту сборки, где лежит Dockerfile.

Docker скачает базовый образ python:3.9-slim (если его нет локально), выполнит инструкции из Dockerfile и создаст новый образ. При первом запуске вы увидите загрузку слоёв, а в конце — сообщение об успешной сборке.

Шаг 4: Запуск контейнера (Run)

Теперь запустим контейнер из собранного образа:

docker run my-python-app

В терминале появится:

Hello from Docker!

Это значит, что вы прошли весь базовый workflow! Если вместо вывода получаете ошибку command not found: docker, проверьте, что Docker Desktop запущен, а терминал перезапущен после установки.

Управление контейнерами: Команды, которые нужно знать

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

Таблица: Основные команды Docker

Команда Описание Пример использования
docker ps Показать запущенные контейнеры docker ps
docker ps -a Показать все контейнеры (включая остановленные) docker ps -a
docker stop <ID> Остановить контейнер docker stop 1a2b3c
docker start <ID> Запустить остановленный контейнер docker start 1a2b3c
docker rm <ID> Удалить контейнер docker rm 1a2b3c
docker rmi <IMAGE> Удалить изображение docker rmi my-python-app
docker logs <ID> Показать логи (вывод) контейнера docker logs 1a2b3c
docker run -d Запустить в фоновом режиме (daemon) docker run -d my-python-app
docker exec Выполнить команду внутри контейнера docker exec -it 1a2b3c python

Практический пример: Работа с фоновым режимом

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

docker run -d --name my-web-app my-python-app

Проверим, что он запущен:

docker ps

Посмотрим, что выводит приложение:

docker logs my-web-app

Когда нужно остановить и удалить контейнер:

docker stop my-web-app
docker rm my-web-app

Изображение при этом останется, его можно переиспользовать или удалить отдельно через docker rmi.

Работа с файлами и переменными окружения

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

Передача файлов: COPY и ADD

В Dockerfile мы уже использовали COPY app.py .. Если нужно скопировать целую папку:

COPY . /app

Директива ADD делает то же самое, но дополнительно умеет распаковывать архивы (.tar.gz) и скачивать файлы по URL. Однако я рекомендую всегда использовать COPY, когда не нужна автоматическая распаковка — поведение ADD менее предсказуемо, а в CI‑пайплайнах оно может приводить к неожиданным эффектам (например, распаковке архива с вредоносным содержимым).

Переменные окружения: ENV

Приложения часто зависят от настроек (адрес базы данных, API-ключи). Их можно задать в Dockerfile через ENV:

ENV DATABASE_URL=postgres://user:pass@localhost/db
ENV API_KEY=secret

В коде Python эти переменные доступны через os.environ:

import os
db_url = os.environ.get('DATABASE_URL')

Но фиксировать секреты в Dockerfile — плохая практика. Поэтому чаще переменные передают при запуске контейнера.

Запуск с переменными из командной строки

Значения можно задать прямо в команде docker run с флагом -e:

docker run -e DATABASE_URL=postgres://user:pass@host/db -e API_KEY=prod-secret my-python-app

Такой подход позволяет использовать один и тот же образ для разработки, тестирования и продакшена, просто подставляя разные значения переменных. Я часто комбинирую это с файлами .env и флагом --env-file для удобства.

Чек-лист: Базовый workflow Docker для разработчика

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

✅ Чек-лист запуска Docker-проекта

  1. [Установка] Проверить, что Docker Desktop запущен и индикатор «Engine running» зелёный.
  2. [Проект] Создать папку проекта и файл с исходным кодом (например, app.py, index.js).
  3. [Dockerfile] Создать файл Dockerfile в корне проекта:
    • Указать FROM (базовый образ).
    • Задать WORKDIR.
    • Скопировать код через COPY.
    • Определить CMD для запуска приложения.
  4. [.dockerignore] Создать файл .dockerignore и перечислить в нём каталоги и файлы, которые не должны попасть в контейнер (node_modules, .git, временные файлы IDE). Это ускорит сборку и уменьшит размер образа.
  5. [Build] Выполнить docker build -t <имя_приложения> . в папке проекта.
  6. [Run] Запустить контейнер: docker run <имя_приложения>.
  7. [Проверка] Убедиться, что вывод программы соответствует ожидаемому.
  8. [Управление] При необходимости остановить (docker stop) и удалить (docker rm) контейнер.
  9. [Оптимизация] Убедиться, что .dockerignore исключает всё лишнее, и при необходимости пересобрать образ.

Пример содержимого .dockerignore:

node_modules
.git
*.log
.env

Такой файл предотвращает копирование чувствительных данных и мусора в контейнер, что особенно важно в командной работе.

Типовые ошибки и как их избежать

На старте работы с Docker почти каждый наступает на одни и те же грабли. Вот самые частые проблемы и способы их решения, проверенные на практике.

Ошибка 1: Permission denied или Access denied

Причина: Docker не запущен или у вашего пользователя нет прав на взаимодействие с демоном.

Решение:

  • На Windows: запустите Docker Desktop с правами администратора или добавьте пользователя в группу docker-users (иногда требуется выход из системы и повторный вход).
  • На Linux: выполните sudo usermod -aG docker $USER и перезайдите в систему.

Ошибка 2: image not found

Причина: Вы пытаетесь запустить контейнер, но образ ещё не собран.

Решение: Сначала выполните docker build -t myapp ., затем docker run myapp. Либо используйте docker run сразу с указанием сборки через --build (если поддерживается вашим оркестратором).

Ошибка 3: Контейнер останавливается сразу после запуска

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

Решение: Убедитесь, что CMD запускает долгоживущий процесс. Для Flask это может выглядеть так:

CMD ["flask", "run", "--host=0.0.0.0"]

Если вы просто вызываете print, контейнер отработает и остановится — это нормально, но для веб-приложений нужно блокирующее выполнение.

Ошибка 4: No space left on device

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

Решение: Периодически очищайте систему:

docker container prune   # удалит остановленные контейнеры
docker image prune        # удалит неиспользуемые образы
docker volume prune       # удалит неиспользуемые тома
docker system prune -a    # удалит всё неиспользуемое (осторожно!)

Я обычно выполняю docker system prune раз в неделю, если активно экспериментирую с разными образами.

Ошибка 5: Код не обновляется в контейнере

Причина: Вы изменили файл app.py на хосте, но контейнер был запущен из старого образа и не видит изменений.

Решение: Пересоберите образ и перезапустите контейнер, либо (что правильнее для разработки) используйте volume для синхронизации файлов, как описано в следующем разделе.

Продвинутый уровень: Использование Volume (Mount) для разработки

Постоянно пересобирать образ после каждого изменения кода — медленно и непродуктивно. Для локальной разработки применяют volumes (монтирование директорий), когда папка с исходным кодом на хосте напрямую связывается с контейнером. Любое изменение на хосте мгновенно отражается внутри.

Как это работает?

Вы запускаете контейнер с флагом -v, указывая путь к проекту на хосте и точку монтирования в контейнере:

docker run -v "$(pwd)":/app my-python-app

$(pwd) подставляет текущую директорию (в Linux/macOS). На Windows в Git Bash это тоже работает, но в PowerShell нужно использовать ${PWD} или полный путь к папке. Я предпочитаю работать в WSL‑терминале, чтобы не путаться с форматами.

Результат: когда вы редактируете app.py в IDE, изменения сразу доступны в контейнере. Без пересборки. Если приложение поддерживает hot‑reload (например, Flask с --reload), оно автоматически подхватит новый код.

Пример с Flask и Volume

Предположим, у вас есть Dockerfile для Flask‑приложения, собранный в образ my-flask-app. Запустим его с пробросом порта и синхронизацией кода:

docker run -p 5000:5000 -v .:/app -e FLASK_ENV=development my-flask-app
  • -p 5000:5000 — порт контейнера 5000 пробрасывается на localhost:5000, теперь можно открыть браузер.
  • -v .:/app — текущая папка монтируется в /app контейнера (рабочую директорию).
  • -e FLASK_ENV=development — включает режим разработки с автоперезагрузкой.

Теперь любые правки в коде автоматически приводят к перезагрузке Flask, и вы видите изменения в браузере без перезапуска контейнера. Это кардинально ускоряет цикл разработки.

Важный нюанс: На Linux файлы, созданные контейнером (например, логи или загруженные пользователем файлы), могут принадлежать root, и на хосте будет не хватать прав для их удаления. Чтобы избежать этого, можно запускать контейнер с флагом --user, указывая UID текущего пользователя хоста: docker run --user $(id -u):$(id -g) .... Я часто добавляю это в скрипты для разработки.

FAQ: Часто задаваемые вопросы о Docker

Ниже собраны ответы на типичные вопросы, которые возникают у разработчиков при первом знакомстве с Docker.

1. Docker это виртуальная машина (VM)?

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

2. Как удалить все контейнеры и изображения?

Для полной очистки используйте:

docker system prune -a

Флаг -a удаляет все неиспользуемые образы, не только dangling. Будьте осторожны: после выполнения останутся только образы, которые используются запущенными контейнерами, и сами контейнеры. Лучше предварительно посмотреть список docker images и docker ps -a.

3. Можно ли использовать Docker на Windows без WSL 2?

Да, но это не рекомендуется. Без WSL 2 Docker работает через устаревшую эмуляцию Hyper-V, которая медленнее и часто вызывает проблемы с производительностью и файловой системой. Включите WSL 2 в компонентах Windows, это делается за пару минут и кардинально улучшает опыт.

4. Как проверить версию Docker?

docker --version

5. Что делать, если контейнер не запускается и выдает ошибку?

Снимите логи контейнера — это лучший способ диагностики:

docker logs <имя_или_ID_контейнера>

Даже если контейнер сразу упал, логи сохранятся (пока контейнер не удалён). Чаще всего там будет понятный traceback или сообщение о недостающей переменной окружения.

6. Как запустить контейнер с интерактивным доступом (как в терминал)?

Используйте флаги -it (interactive + pseudo-TTY) и команду оболочки в конце:

docker run -it my-python-app /bin/bash

После этого вы попадёте в терминал внутри контейнера и сможете выполнять команды (ls, python и т.п.). Выйти можно через exit.

7. Можно ли использовать Docker для работы с Node.js?

Да, абсолютно. Вот типичный Dockerfile для Node.js-приложения:

FROM node:16-alpine
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install
COPY . .
CMD ["node", "server.js"]

Здесь мы сначала копируем только файлы зависимостей и устанавливаем их — это позволяет использовать кэширование слоёв при повторных сборках (если код меняется, а зависимости нет, шаг RUN npm install не перевыполняется).

8. Сколько места занимает Docker?

Базовая установка Docker Desktop занимает около 2–3 ГБ. Однако пространство быстро растёт за счёт скачанных образов, контейнеров и томов. Регулярно выполняйте docker system prune, чтобы контролировать объём. В моей практике на ноутбуке с 256 ГБ диском Docker редко выходил за 10–15 ГБ при разумной очистке.

Заключение: Docker — ваш новый стандарт разработки

Освоение базового workflow Docker — это переход на следующий уровень профессионализма. Схема «Установка → Dockerfile → Build → Run → Управление» становится фундаментом для любых современных процессов: от тестирования на CI до деплоя в облака.

Теперь вы умеете:

  • Изолировать окружение и избегать конфликтов зависимостей;
  • Быстро разворачивать проекты на любой машине;
  • Использовать volumes для эффективной разработки с синхронизацией кода;
  • Управлять контейнерами и образами через основные команды.

Следующий шаг — изучение Docker Compose (запуск связок контейнеров: приложение + база данных + кэш) и настройка CI/CD пайплайнов с Docker. Но без сегодняшнего практического фундамента эти темы будут казаться сложными.

Не бойтесь экспериментировать: создайте тестовый проект, измените код, запустите контейнер, загляните в логи. Docker быстро станет привычным и надёжным инструментом, который вы будете использовать каждый день.