Dev Containers и devcontainer.json
Стандартизация dev-окружений через контейнеры: как это работает
Проблема «у меня всё работает»
Этот разговор знает каждый инженер. Кто-то открывает пулл-реквест, CI падает, автор разводит руками: «у меня локально всё работает». Дальше выясняется, что у него Node.js 18, а в CI — Node.js 20. Или что в системе стоит libvips определённой версии, поставленный полгода назад под другой проект и благополучно забытый. Расхождение между локальным окружением разработчика и средой, в которой код запускается, стара как сама профессия, и контейнеры должны были её закрыть.
Docker закрыл её для продакшена: образ описывается в Dockerfile, собирается и работает одинаково везде. Но для разработки Docker в чистом виде подходит плохо. Разработчику нужны вещи, которых продакшен-контейнеру не требуется: редактор с автодополнением и навигацией по коду, дебаггер, линтер, git-хуки, SSH-ключи, шрифты для терминала, расширения IDE. Как только каждый начинает лепить свою обвязку вокруг Docker Compose — с маунтами томов, пробросом портов, кастомными скриптами запуска — идея стандартизации рассыпается.
Что такое Dev Containers
Dev Containers — открытая спецификация, описывающая, как использовать контейнер в качестве полноценного окружения для разработки. Изначально её создал Microsoft для VS Code, но к 2025 году спецификацию поддерживают JetBrains IDE, GitHub Codespaces, DevPod, Gitpod и другие инструменты.
Ключевой файл — devcontainer.json, который кладётся в .devcontainer в корне репозитория. В нём описывается всё, что нужно для работы: базовый образ, установленные инструменты, расширения редактора, переменные окружения, проброшенные порты, скрипты инициализации. Когда разработчик открывает репозиторий в поддерживающем инструменте, тот читает devcontainer.json и создаёт контейнер с полностью настроенным окружением — без ручной установки зависимостей, без README с двадцатью шагами, без «а у тебя какая версия Python?».
Анатомия devcontainer.json
Минимальный devcontainer.json выглядит так.
{
"name": "My Project",
"image": "mcr.microsoft.com/devcontainers/typescript-node:20",
"customizations": {
"vscode": {
"extensions": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode"]
}
},
"postCreateCommand": "npm install",
"forwardPorts": [3000]
}
Пять строк конфигурации, и каждый новый разработчик получает одинаковое окружение: Node.js 20, TypeScript, ESLint, Prettier, установленные зависимости и проброшенный порт для dev-сервера. Вместо image можно указать dockerFile или dockerComposeFile под более сложные сценарии — когда рядом с основным контейнером нужна база данных, Redis или другие сервисы.
В спецификации описаны lifecycle commands — хуки на разных этапах жизни контейнера: initializeCommand (до создания, на хосте), onCreateCommand (после создания), postCreateCommand (после создания и клонирования репозитория), postStartCommand (при каждом запуске), postAttachCommand (при каждом подключении). Через них автоматизируется вся настройка: от миграции базы данных до генерации SSL-сертификатов для локального HTTPS.
Features: модульность вместо монолитных образов
Одно из удачных решений спецификации — механизм Features. Это переиспользуемые модули, каждый из которых добавляет к контейнеру один инструмент или возможность. Вместо монолитного Dockerfile с десятками RUN apt-get install нужные features перечисляются в devcontainer.json:
{
"image": "mcr.microsoft.com/devcontainers/base:ubuntu",
"features": {
"ghcr.io/devcontainers/features/node:1": { "version": "20" },
"ghcr.io/devcontainers/features/python:1": { "version": "3.12" },
"ghcr.io/devcontainers/features/docker-in-docker:2": {},
"ghcr.io/devcontainers/features/github-cli:1": {}
}
}
Каждый feature — это OCI-артефакт с установочным скриптом и метаданными. Microsoft поддерживает коллекцию из десятков готовых features, а свои можно создавать и публиковать в container registry компании. Это закрывает типичную боль больших организаций: набор внутренних инструментов (CLI для деплоя, конфигурация VPN, корпоративные сертификаты) оформляется как feature и подключается ко всем репозиториям.
Поддержка IDE
Главная цель спецификации с самого начала — интеграция с VS Code, и она работает лучше всего. VS Code обнаруживает devcontainer.json в репозитории, предлагает открыть проект в контейнере, и через пару минут под рукой полноценная IDE, где все расширения, линтеры и дебаггеры настроены и подключены к контейнерному окружению. Редактор при этом работает на хосте (или в браузере), а процессы — внутри контейнера.
JetBrains добавил поддержку Dev Containers в 2024 году. Реализация пока менее зрелая, чем у VS Code (есть ограничения по поддержке некоторых свойств спецификации и features), но активно развивается и для большинства сценариев уже работает.
Trade-offs
Воспроизводимость окружения Dev Containers решают, но приносят свои сложности.
Производительность файловой системы — главная боль на macOS. Docker на Mac использует виртуализацию, и маунт файловой системы хоста в контейнер работает заметно медленнее нативного доступа. Для проектов с тысячами файлов (типичный node_modules) это превращает npm install и запуск тестов в мучение. Частичные решения существуют — named volumes для node_modules, VirtioFS в новых версиях Docker Desktop — но проблема до конца не ушла.
Кривая обучения. Тем, кто никогда не работал с Docker, придётся разбираться с новыми концепциями: образы, контейнеры, volumes, сеть. Для команды, которая и так живёт в контейнерах, проблемы нет. Для фронтенд-команды, до этого разрабатывавшей на голом macOS, — серьёзный порог входа.
Отладка проблем. Когда что-то ломается внутри dev container (а ломается всегда что-нибудь), дебаг становится сложнее: проблема может быть в образе, в features, в конфигурации маунтов, в сети контейнера. Нужны знания, которых у среднего разработчика может и не быть.
Рекомендации
Внедрение стоит начинать с одного репозитория — желательно того, где процесс настройки окружения самый сложный (обычно он и есть главный кандидат). Создайте devcontainer.json, протестируйте с новым сотрудником или с коллегой из другой команды, соберите обратную связь. Покрывать все edge cases сразу не нужно — стартуйте с базового образа и добавляйте features и скрипты по мере обнаружения проблем. Серебряной пулей Dev Containers не являются, но для команд, где настройка окружения регулярно съедает дни инженерного времени, это один из самых эффективных инструментов стандартизации.