Отладка docker visual studio

Обновлено: 05.07.2024

Начало проекта на Ruby on Rails — это время на развёртывание: установка нужной версии, гемов и их зависимостей, а также различных баз данных. Иногда процесс идёт не идеально, и приходится тратить время на поиск решения в Stackoverflow. Чтобы упростить жизнь, можно использовать инструмент для контейнеризации проекта со всем его окружением. Это позволит автоматизировать развёртывание и добавит бонусов вашему резюме.

В качестве полнофункциональной среды разработки мы будем использовать Docker-контейнеры и Visual Studio Code. По тому, как установить и пользоваться Docker есть масса статей и видео, поэтому этот увлекательный путь вы без труда пройдёте сами. А вот для Visual Studio Code нам потребуется установить специальное приложение Remote-Containers plugin, которое позволит работать внутри контейнера и использовать весь набор функций.

Шаг первый: завернуть весь проект с помощью Docker и Docker-Compose. Создать докер файл легко: вам достаточно нажать в VS Code F1 , выбрать Docker: Add Docker Files to Workspace и следовать подсказкам. Также можно создать Dockerfile в корневой папке. Пример файла для Ruby on Rails приложения:

Далее создаем файл со скриптами entrypoint.sh , который так же добавим в корневую папку:

Чтобы магия сработала, создадим docker-compose.yml :

После этого устанавливаем Remote-Containers plugin в VS Code — плагин позволит работать внутри контейнера. Затем создаем файл конфигурации: F1 >> Remote-Containers: Add Development Container Configuration Files


В папке .devcontainer вы найдете файл devcontainer.json

Внесём новые настройки и уберем лишние комментарии. Добавим все сервисы из docker-compose.yml в "runServices": ["workspace", "db", "redis"] , чтобы запустить их на старте.

Для того, чтобы VS Code останавливал контейнеры, когда окно закроется, добавим строчку "shutdownAction": "stopCompose" , а также добавим список портов внутрь контейнера, которые будут доступны локально "forwardPorts": [3000]

Пример итогового содержания файла:

Теперь подготовим базу для нашего приложения и запустим в терминале: docker-compose run workspace rake db:create. Запускаем F1 >> Remote-Containers: Rebuild and Reopen in Container и с помощью команды в терминале rails s открываем готовое приложение.


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


Поздравляю! Теперь не нужно устанавливать никакие зависимости и лишние библиотеки — вы работаете внутри контейнера.




Что должен уметь разработчик? «Программировать», — ответите вы и… Угадаете. Но если раньше список необходимых знаний на этом заканчивался, то теперь в век DevOps он только начинается. Когда мы пишем код, нам обязательно надо знать структуру сети: что с чем взаимодействует. Требуется поддержка сразу нескольких языков программирования, а разные куски кода в проекте могут быть написаны на чем угодно.


Мы должны знать, как откатить ПО, если обнаружится ошибка. Мы должны управлять конфигурациями для разных используемых в компании сред — это как минимум несколько dev-сред, тестовые и боевые среды. Ах да, надо еще разбираться в скриптах на разных серверах/операционных системах, ведь далеко не все можно сделать с помощью кода, иногда приходится и скрипты писать.

Мы должны знать требования безопасности, а они становятся все жестче и отъедают у разработчика очень много времени. Не забудем также о поддержке и развитии сопутствующего ПО: Git, Jenkins и так далее. Времени на чисто разработку в итоге у разработчика может просто не хватить.

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

Разбираемся в контейнерах


На нижнем уровне развернут Windows Nanoserver. Это мегаобрезанная выжимка из Windows Server, которая не умеет ничего, кроме сопровождения развернутой сервисной программы. Зато и объем у нее в 12 раз меньше.

Если сравнить физические и виртуальные сервера и контейнеры, то выгода последних очевидна.


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

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

Посмотрим, как в системе располагаются Linux-контейнеры.


Как видите, над хостовым сервером и ОС сразу располагаются бинарники с приложениями. Гостевая ОС не нужна, ресурсы освобождаются, лицензии на гостевые ОС не нужны.

Windows-контейнеры немного отличаются от линуксовых.


Базовые слои те же: инфраструктура, хостовая ОС (но теперь Windows). А вот дальше контейнеры могут работать напрямую с ОС или быть развернуты поверх гипервизора. В первом случае изоляция процессов и пространств есть, но они используют одно ядро с другими контейнерами, что с точки зрения безопасности — не айс. Если же использовать контейнеры через Hyper-V, то все будет изолировано.

Изучаем Docker под VS

Перейдем к самому Docker. Допустим, у вас есть Visual Studio, и вы проводите первую установку клиента Docker под Windows. При этом Docker развернет сервер Docker demon, интерфейс на Rest для доступа к нему и сам клиент — командную строку Docker. Она позволит нам управлять всем, что связано с контейнерами: сеть, образы, контейнеры, слои.


Docker очень органично сопряжен с Visual Studio. На скриншоте приведена панельная менюшка из Visual Studio 2017. Прямо в Intellisense интегрирована поддержка Docker compose, поддерживаются Dockerfile, а все артефакты работают в командной строке.


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

Как осуществляется сборка контейнеров? Основной элемент здесь – это файл dockerfile, который содержит инструкции для сборки образа. На каждый проект создается свой dockerfile. В нем указывается: откуда мы берем базовый образ, какие передаем аргументы, как называется рабочая директория с файлами, порты.


Вот этот аргумент source имеет два параметра. Второй параметр – путь, по которому в проект будет размещаться результат сборки, значение задано по умолчанию. На мой взгляд, это не очень удачный вариант. В этой папке часто много мусора, его периодически нужно вычищать, и мы при вычистке этой папки можем потереть сборку. Так что при желании ее можно поменять, она задается системным параметром Docker_build_source, который также можно забить руками.


Для отладки есть две среды: Debug и Release. Тег образа при дебаггинге помечается как dev, а релиз latest. Аргумент Source при дебаггинге лучше устанавливать на obj/Docker/empty, чтобы не путалось, а при релизе obj/Docker/publish. Здесь вы можете использовать все те же бинарники, вьюхи, wwwroot папку и все зависимости, которые есть.

Осваиваем Docker Compose

Перейдем к самому интересному – инструмент оркестрации Docker-compose. Рассмотрим пример: у вас есть какая-то бизнес-услуга, которая затрагивает 5-6 контейнеров. И вам надо как-то зафиксировать, как они должны собираться, в какой очередности. Здесь пригодится Docker-compose, который обеспечит всю сборку, запуск и масштабирование контейнеров. Управляется он просто, все собирается одной командой.


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

Рассмотрим структуру YAML-файла. Image — это образ Docker. Образом называется контейнер без слоя приложения, он неизменен.


Build указывает, как нужно производить сборку, куда надо собрать и где развернуть.
Depends_on — зависимость от каких сервисов он зависим.
Environment — здесь мы задаем среду.
Ports — маппинг портов, на каком порту будет доступен ваш контейнер.

Рассмотрим пример. У нас есть просто API без службы, по сути 3 контейнера: есть SQL.data на Линуксе, есть само приложение, оно зависит от webapi, а webapi зависит от SQL.data.


Неважно, в какой последовательности в файле записаны компоненты. Если все правильно описано, Compose автоматически правильно выстроит эту информацию на основе зависимостей в проекте. Этого файла достаточно для того, чтобы собрать все контейнеры сразу, на выходе получится готовый релиз.

Существует этакий «контейнер контейнеров», специальный контейнер docker-compose.ci.build.yml, в котором собрана вся композиция. Из командной строки Visual Studio можно запустить этот спецконтейнер, и он сможет осуществить всю сборку на build сервере, например, в Jenkins.


Заглянем внутрь файла. В примере прописана рабочая директория и откуда что берется. Он восстанавливает из GIT проект, сам делает публикацию этого солюшена, конфигурация Release и выкладывает результат. Вот и вся команда для сборки, больше ничего не нужно прописывать. Достаточно один раз это прописать, а потом запускать публикацию одной кнопкой.

На что еще стоит обратить внимание. Docker-compose для каждой среды собирает образы, для каждой конфигурации отдельный файл. Для каждой конфигурации в Visual Studio есть файл с настройками, которые вам нужны для среды.


Прямо из VS можно удаленно запускать отладку всей композиции.

Оркестраторы кластера

Напоследок коснемся такой темы, как оркестраторы кластера. Мы не должны думать о том, как контейнеры дальше существуют, какими людьми или системами управляются. Для этого есть 4 самые популярные системы управления контейнерами: Google Kubernetes, Mesos DC/OS, Docker Swarm и Azure Service Fabric. Они позволяют управлять кластеризацией и композицией контейнеров.


Полная версия выступления на Panda Meetup доступна ниже.

Тем, кто хочет глубже погрузиться в тему, советую изучить следующие материалы:

И напоследок важный совет из практики: самое сложное — запомнить, где что лежит.

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

Visual Studio обеспечивает согласованную разработку контейнеров Docker и локальную проверку приложения. Вы можете запускать и отлаживать свои приложения в контейнерах Linux или Windows, работающих на локальном рабочем столе Windows с установленным Docker. При этом вам не нужно перезапускать контейнер каждый раз, когда вы вносите изменения в код.

Если у вас уже есть проект поддерживаемого типа, Visual Studio может создать Dockerfile и настроить проект для запуска в контейнере. Ознакомьтесь со статьей Средства для контейнеров в Visual Studio.

Предварительные требования

Для отладки приложений в локальном контейнере Docker необходимо установить следующие средства:

Для запуска контейнеров Docker локально требуется локальный клиент Docker. Вы можете использовать Docker Desktop. Для этого требуется Windows 10 или более поздней версии.

Создание веб-приложения

Пропустите этот раздел, если у вас есть проект и вы добавили поддержку Docker, как описано в обзоре.

В начальном окне Visual Studio выберите Создать проект.

Введите имя нового приложения (или оставьте имя по умолчанию), укажите расположение на диске и нажмите кнопку ОК.

Создание веб-проекта: экран "Дополнительные сведения"

Установите флажок Включить поддержку Docker.

Выберите тип контейнера (Windows или Linux) и нажмите кнопку Создать.

В начальном окне Visual Studio выберите Создать проект.

Введите имя нового приложения (или оставьте имя по умолчанию), укажите расположение на диске и нажмите кнопку ОК.

Создание веб-проекта: экран "Дополнительные сведения"

Установите флажок Enable Docker (Включить Docker).

В текстовом поле Docker OS (ОС Docker) выберите тип контейнера (Windows или Linux) и нажмите кнопку Создать.

Изменение и обновление кода

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

Убедитесь, что Docker настроен для применения типа контейнера (Linux или Windows), который вы используете. На панели задач щелкните правой кнопкой мыши значок Docker и выберите пункт Switch to Linux containers (Переключиться на контейнеры Linux) или Switch to Windows containers (Переключиться на контейнеры Windows) в зависимости от ситуации.

Измените метод Startup следующим образом.

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

После сборки и запуска образа контейнера в контейнере Docker среда Visual Studio запустит веб-приложение в вашем браузере по умолчанию.

Перейдите на страницу Index (Индекс). Мы будем вносить на ней изменения.

Вернитесь в Visual Studio и откройте файл Index.cshtml.

Добавьте приведенное ниже содержимое HTML в конец файла и сохраните изменения.

Отладка с точками останова

Изменения часто требуют дальнейшей проверки. Для этого можно использовать функции отладки Visual Studio.

В Visual Studio откройте файл Index.cshtml.cs.

Замените содержимое метода OnGet следующим кодом:

Установите точку останова слева от строки кода.

Чтобы начать отладку и достичь точки останова, нажмите клавишу F5.

Переключитесь в Visual Studio, чтобы просмотреть точку останова. Проверьте значения.

Снимок экрана: часть кода для Index.cshtml.cs в Visual Studio, для которого точка останова установлена слева от строки кода, выделенной желтым цветом.

Снимок экрана: часть кода для Index.cshtml.cs в Visual Studio, для которого точка останова установлена слева от строки кода, выделенной желтым цветом.

Отладка с точками останова

В обозревателе решений откройте файл Program.cs.

Замените содержимое метода Main следующим кодом:

Установите точку останова слева от строки кода.

Чтобы начать отладку и достичь точки останова, нажмите клавишу F5.

Переключитесь в Visual Studio, чтобы просмотреть точку останова и проверить значения.

Снимок экрана: окно кода для Program.cs в Visual Studio, для которого точка останова установлена слева от строки кода, выделенной желтым цветом.

Повторное использование контейнеров

Во время цикла разработки среда Visual Studio перестраивает образы контейнеров и сам контейнер только при изменении файла Dockerfile. Если файл Dockerfile не изменяется, Visual Studio повторно использует контейнер из предыдущего запуска.

Если вы вручную изменили контейнер и хотите выполнить перезапуск с чистым образом контейнера, выберите команду Сборка > Очистить, а затем выполните сборку как обычную.

Ранее мы рассмотрели локальное использование отладчика в Goland IDE. В данной статье рассмотрим, как удаленно отладить программу, запущенную в Docker-контейнере из Visual Studio Code или Goland IDE.

При локальной отладке процессом полностью управляет IDE — компилирует программу и подключается к ней.

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

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

  • программа не может быть запущена или протестирована локально, только в специальном тестовом окружении;
  • вы отлаживаете сложно уловимый баг на удаленном окружении, например во время integration testing;

Отладчик, используемый "под капотом" Goland IDE или Visual Studio Code, — Delve.

Отладка в IDE с помощью Delve всегда работает так:

  • Delve запускается как серверное приложение, слушает подключения на определенном порту.
  • Delve запускает нашу программу (скомпилированную ранее или использует исходники на GO).
  • При подключении отладчика из Goland IDE или Visual Studio Code, Delve принимает данные об установленных брейкпоинтах.
  • При наступлении брейкпоинта, Delve останавливает программу, сообщает подключенному клиенту состояние переменных и другие отладочные данные.

Delve — утилита командной строки, полный список параметров которой описан здесь. Delve умеет как самостоятельно компилировать программу, так и запускать ранее скомпилированную программу (в зависимости от параметров).
Delve также имеет интерфейс отладки из командной строки (для отладки без IDE), однако данным функционалом мы пользоваться не будем.

Итак, нам необходимо запустить Delve c нашей программой и обеспечить удаленное подключение IDE к нему.

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

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

Подготовим Dockerfile с Delve и нашей программой:

Здесь используется двухэтапная сборка и статическая компиляция бинарных файлов для использования затем в Docker образе FROM scratch с минимальным размером.
В нашем результирующем образе мы сохраняем бинарные файлы двух программ — /go/bin/dlv — Delve; /app — наша программа.

Флаги сборки GO поддерживаются сразу многими утилитами - build , clean , get , install , list , run , test . Благодаря данной возможности, бинарник Delve, получаемый через go get , также собирается статически. По умолчанию, после сборки в окружении Alpine Linux , он зависит от двух библиотек. Проверить это можно через консольную команду ldd :

Флаги -gcflags "all=-N -l" , используемые для компиляции нашей программы, необходимы для работы Delve с ней.

Для сборки контейнера можно использовать команду docker build -f ./docker/debug/Dockerfile -t debug . , которая также реализована как команда Makefile с именем docker-build-debug , так что можно выполнить make docker-build-debug .

Запускаем собранный контейнер с помощью docker-compose , что также как и сборка доступно через Makefile — make docker-run-debug .

Содержимое файла docker-compose.yml :

Так как ранее мы установили ENTRYPOINT в образе как /dlv , то в параметре command мы передаем только параметры для Delve. Переданные параметры направлены на многократную удаленную отладку и подробное логгирование.

Вызываем docker-compose -f ./docker/debug/docker-compose.yml up и изучаем логи контейнера:

Если в логах видим то, что сервер запущен на порту 2345, то все хорошо. Давайте теперь подключимся к нему из IDE.

Visual Studio Code

В IDE мы должны открыть проект с исходниками отлаживаемой програмы.

Для добавления конфигурации удаленной отладки создадим или отредактируем в проекте файл .vscode/launch.json файл, чтобы в нем была следующая секция конфигурации:

  • Параметр "request": "attach" — позволяет нашему IDE именно подключаться удаленно, а не стартовать новую сессию отладки;
  • port , host — порт и хост запущенного удаленно Delve. Так как c помощью docker-compose мы запустили Docker-контейнер локально и пробросили порт 2345 на наш локальный компьютер, то подключаемся к 127.0.0.1 или localhost.
  • remotePath — один из критически важных параметров, влияющий на то, будут ли корректно ставиться breakpoints. Это путь к папке с исходниками нашей программы при компиляции. Мы компилировали нашу программу с помощью Dockerfile, находясь в корневой директории ( WORKDIR / ). Следовательно, наша директория компиляции — корневая, поэтому оставляем поле remotePath пустым.

Итак, перед действиями в IDE, проверим, что отлаживаемый нами контейнер работает.

Далее средствами VSCode запускаем задачу отладки "Attach":


Мы можем ставить breakpoints и они должны оставаться видимыми в IDE, при этом в debug console отображается наше сетевое взаимодействие с Delve в контейнере и в нем не должно быть ошибок.

При удаленной отладке у нас должен быть запущен наш Docker-контейнер с Delve и нашей программой, а в IDE открыт проект с исходниками программы.

Для данной IDE настройка проходит визуально. Важно — необходимо включить GO modules integration в настройках IDE:


Далее — главное меню — Run — Edit configurations, добавляем новую конфигурации откладки, выбираем "Go Remote":

Читайте также: