В век победившего DevOps разработчики просто обязаны знать про Docker контейнеры, зачем они нужны и как с ними работать. Это многократно облегчает работу. Причем всю силу контейнеризации могут почувствовать даже те, кто работает с .Net Core в среде разработки Visual Studio 2017. О доступных инструментах и настройке Docker под VS рассказал Павел Скиба, начальник отдела разработки серверных приложений, на митапе Panda-Meetup C# .Net.
Что должен уметь разработчик? «Программировать», — ответите вы и… Угадаете. Но если раньше список необходимых знаний на этом заканчивался, то теперь в век DevOps он только начинается. Когда мы пишем код, нам обязательно надо знать структуру сети: что с чем взаимодействует. Требуется поддержка сразу нескольких языков программирования, а разные куски кода в проекте могут быть написаны на чем угодно.
Мы должны знать, как откатить ПО, если обнаружится ошибка. Мы должны управлять конфигурациями для разных используемых в компании сред — это как минимум несколько dev-сред, тестовые и боевые среды. Ах да, надо еще разбираться в скриптах на разных серверах/операционных системах, ведь далеко не все можно сделать с помощью кода, иногда приходится и скрипты писать.
Мы должны знать требования безопасности, а они становятся все жестче и отъедают у разработчика очень много времени. Не забудем также о поддержке и развитии сопутствующего ПО: Git, Jenkins и так далее. Времени на чисто разработку в итоге у разработчика может просто не хватить.
Что же делать? Выход есть, и кроется он в Docker-контейнерах и системе их управления. Стоит один раз развернуть всю эту сложную махину, и вы, как в старые добрые времена, опять будете писать только код. Всем остальным будут управлять либо другие люди, либо сама система.
Что такое Docker-контейнер? Это конструкция, состоящая из нескольких слоев. Верхний слой – это бинарный слой вашего приложения. Второй и третий слои сейчас в .Net Core объединены, контейнер уже идет SDK-шный. Следующий слой — в зависимости от операционной системы, на которой развернут контейнер. И самый нижний слой — сама операционная система.
На нижнем уровне развернут Windows Nanoserver. Это мегаобрезанная выжимка из Windows Server, которая не умеет ничего, кроме сопровождения развернутой сервисной программы. Зато и объем у нее в 12 раз меньше.
Если сравнить физические и виртуальные сервера и контейнеры, то выгода последних очевидна.
Когда все работало на физических серверах, мы сталкивались с кучей проблем. Не было изоляции в кодах библиотек, одни приложения могли мешать друг другу. Например: одно приложение работало на .Net 1.1, а другое на .Net 2.0. Чаще всего это приводило к трагедии. Через какое-то время появились виртуальные сервера, была решена проблема изоляции, не стало общих библиотек. Правда, при этом стало очень дорого по ресурсам и трудозатратно: нужно было все время отслеживать, сколько виртуалок крутится на одной виртуалке, на Hyper- V и на железке.
Контейнеры призваны были стать недорогим и удобным решением, минимально зависимым от ОС. Посмотрим, чем они отличаются. Виртуальные серверы внутри системы располагаются примерно так.
Нижний слой – это хостовый сервер. Он может быть как физическим, так и виртуальным. Следующий слой – любая ОС с виртуализацией, выше – гипервизор. Сверху идут виртуальные серверы, которые можно разделить на гостевую ОС и приложения. То есть под каждый виртуальный сервер поверх ОС развернута гостевая ОС, а это лишняя трата ресурсов.
Посмотрим, как в системе располагаются Linux-контейнеры.
Как видите, над хостовым сервером и ОС сразу располагаются бинарники с приложениями. Гостевая ОС не нужна, ресурсы освобождаются, лицензии на гостевые ОС не нужны.
Windows-контейнеры немного отличаются от линуксовых.
Базовые слои те же: инфраструктура, хостовая ОС (но теперь Windows). А вот дальше контейнеры могут работать напрямую с ОС или быть развернуты поверх гипервизора. В первом случае изоляция процессов и пространств есть, но они используют одно ядро с другими контейнерами, что с точки зрения безопасности — не айс. Если же использовать контейнеры через Hyper-V, то все будет изолировано.
Перейдем к самому Docker. Допустим, у вас есть Visual Studio, и вы проводите первую установку клиента Docker под Windows. При этом Docker развернет сервер Docker demon, интерфейс на Rest для доступа к нему и сам клиент — командную строку Docker. Она позволит нам управлять всем, что связано с контейнерами: сеть, образы, контейнеры, слои.
На слайде показаны самые простые команды: вытянуть Docker-контейнер, запустить его, собрать, закоммитить, отправить обратно.
Docker очень органично сопряжен с Visual Studio. На скриншоте приведена панельная менюшка из Visual Studio 2017. Прямо в Intellisense интегрирована поддержка Docker compose, поддерживаются Dockerfile, а все артефакты работают в командной строке.
Интересно, что мы можем Docker`ом отлаживать контейнеры прямо в режиме реального времени. И если у вас контейнеры связаны друг с другом, то они непосредственно дебажатся все сразу, и запускать несколько сред не потребуется.
Как осуществляется сборка контейнеров? Основной элемент здесь – это файл dockerfile, который содержит инструкции для сборки образа. На каждый проект создается свой dockerfile. В нем указывается: откуда мы берем базовый образ, какие передаем аргументы, как называется рабочая директория с файлами, порты.
Вот этот аргумент source имеет два параметра. Второй параметр – путь, по которому в проект будет размещаться результат сборки, значение задано по умолчанию. На мой взгляд, это не очень удачный вариант. В этой папке часто много мусора, его периодически нужно вычищать, и мы при вычистке этой папки можем потереть сборку. Так что при желании ее можно поменять, она задается системным параметром Docker_build_source, который также можно забить руками.
Инструкция Entrypoint позволяет настроить контейнер в качестве исполняемого файла. Эта строка нужна для .Net Core, чтобы после успешного запуска контейнера он отправил в командную строку сообщение «Ваше приложение запущено».
Теперь про отладку контейнеров. Здесь все похоже на обычный .Net, вы практически не заметите разницы. Чаще всего я запускаю .Net Core как self-hosted под dotnet.exe. Он использует CLRDBG дебаггер, кэш NuGet пакетов и исходники.
ASP.Net 4.5+ размещен под управлением IIS или IIS Express, использует Microsoft Visual Studio Debugger и исходники корня сайта в IIS.
Для отладки есть две среды: Debug и Release. Тег образа при дебаггинге помечается как dev, а релиз latest. Аргумент Source при дебаггинге лучше устанавливать на obj/Docker/empty, чтобы не путалось, а при релизе obj/Docker/publish. Здесь вы можете использовать все те же бинарники, вьюхи, wwwroot папку и все зависимости, которые есть.
Перейдем к самому интересному – инструмент оркестрации 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 доступна ниже.
Тем, кто хочет глубже погрузиться в тему, советую изучить следующие материалы:
Http://dot.net
Http://docs.docker.com
Http://hub.docker.com/microsoft
Http://docs.microsoft.com
Http://visualstudio.com
И напоследок важный совет из практики: самое сложное — запомнить, где что лежит.
Документирование при работе с docker-контейнерами ляжет на ваши плечи. Без документации вы забудете, где в каком контейнере что с чем связано и что с чем работает. Чем больше сервисов, тем больше итоговая паутина связей.
Что должен уметь разработчик? «Программировать», — ответите вы и… Угадаете. Но если раньше список необходимых знаний на этом заканчивался, то теперь в век DevOps он только начинается. Когда мы пишем код, нам обязательно надо знать структуру сети: что с чем взаимодействует. Требуется поддержка сразу нескольких языков программирования, а разные куски кода в проекте могут быть написаны на чем угодно.
Мы должны знать, как откатить ПО, если обнаружится ошибка. Мы должны управлять конфигурациями для разных используемых в компании сред — это как минимум несколько dev-сред, тестовые и боевые среды. Ах да, надо еще разбираться в скриптах на разных серверах/операционных системах, ведь далеко не все можно сделать с помощью кода, иногда приходится и скрипты писать.
Мы должны знать требования безопасности, а они становятся все жестче и отъедают у разработчика очень много времени. Не забудем также о поддержке и развитии сопутствующего ПО: Git, Jenkins и так далее. Времени на чисто разработку в итоге у разработчика может просто не хватить.
Что же делать? Выход есть, и кроется он в Docker-контейнерах и системе их управления. Стоит один раз развернуть всю эту сложную махину, и вы, как в старые добрые времена, опять будете писать только код. Всем остальным будут управлять либо другие люди, либо сама система.
Разбираемся в контейнерах
Что такое Docker-контейнер? Это конструкция, состоящая из нескольких слоев. Верхний слой – это бинарный слой вашего приложения. Второй и третий слои сейчас в .Net Core объединены, контейнер уже идет SDK-шный. Следующий слой — в зависимости от операционной системы, на которой развернут контейнер. И самый нижний слой — сама операционная система.
На нижнем уровне развернут Windows Nanoserver. Это мегаобрезанная выжимка из Windows Server, которая не умеет ничего, кроме сопровождения развернутой сервисной программы. Зато и объем у нее в 12 раз меньше.
Если сравнить физические и виртуальные сервера и контейнеры, то выгода последних очевидна.
Когда все работало на физических серверах, мы сталкивались с кучей проблем. Не было изоляции в кодах библиотек, одни приложения могли мешать друг другу. Например: одно приложение работало на .Net 1.1, а другое на .Net 2.0. Чаще всего это приводило к трагедии. Через какое-то время появились виртуальные сервера, была решена проблема изоляции, не стало общих библиотек. Правда, при этом стало очень дорого по ресурсам и трудозатратно: нужно было все время отслеживать, сколько виртуалок крутится на одной виртуалке, на Hyper- V и на железке.
Контейнеры призваны были стать недорогим и удобным решением, минимально зависимым от ОС. Посмотрим, чем они отличаются. Виртуальные серверы внутри системы располагаются примерно так.
Нижний слой – это хостовый сервер. Он может быть как физическим, так и виртуальным. Следующий слой – любая ОС с виртуализацией, выше – гипервизор. Сверху идут виртуальные серверы, которые можно разделить на гостевую ОС и приложения. То есть под каждый виртуальный сервер поверх ОС развернута гостевая ОС, а это лишняя трата ресурсов.
Посмотрим, как в системе располагаются Linux-контейнеры.
Как видите, над хостовым сервером и ОС сразу располагаются бинарники с приложениями. Гостевая ОС не нужна, ресурсы освобождаются, лицензии на гостевые ОС не нужны.
Windows-контейнеры немного отличаются от линуксовых.
Базовые слои те же: инфраструктура, хостовая ОС (но теперь Windows). А вот дальше контейнеры могут работать напрямую с ОС или быть развернуты поверх гипервизора. В первом случае изоляция процессов и пространств есть, но они используют одно ядро с другими контейнерами, что с точки зрения безопасности — не айс. Если же использовать контейнеры через Hyper-V, то все будет изолировано.
Изучаем Docker под VS
Перейдем к самому Docker. Допустим, у вас есть Visual Studio, и вы проводите первую установку клиента Docker под Windows. При этом Docker развернет сервер Docker demon, интерфейс на Rest для доступа к нему и сам клиент — командную строку Docker. Она позволит нам управлять всем, что связано с контейнерами: сеть, образы, контейнеры, слои.
На слайде показаны самые простые команды: вытянуть Docker-контейнер, запустить его, собрать, закоммитить, отправить обратно.
Docker очень органично сопряжен с Visual Studio. На скриншоте приведена панельная менюшка из Visual Studio 2017. Прямо в Intellisense интегрирована поддержка Docker compose, поддерживаются Dockerfile, а все артефакты работают в командной строке.
Интересно, что мы можем Docker`ом отлаживать контейнеры прямо в режиме реального времени. И если у вас контейнеры связаны друг с другом, то они непосредственно дебажатся все сразу, и запускать несколько сред не потребуется.
Как осуществляется сборка контейнеров? Основной элемент здесь – это файл dockerfile, который содержит инструкции для сборки образа. На каждый проект создается свой dockerfile. В нем указывается: откуда мы берем базовый образ, какие передаем аргументы, как называется рабочая директория с файлами, порты.
Вот этот аргумент source имеет два параметра. Второй параметр – путь, по которому в проект будет размещаться результат сборки, значение задано по умолчанию. На мой взгляд, это не очень удачный вариант. В этой папке часто много мусора, его периодически нужно вычищать, и мы при вычистке этой папки можем потереть сборку. Так что при желании ее можно поменять, она задается системным параметром Docker_build_source, который также можно забить руками.
Инструкция Entrypoint позволяет настроить контейнер в качестве исполняемого файла. Эта строка нужна для .Net Core, чтобы после успешного запуска контейнера он отправил в командную строку сообщение «Ваше приложение запущено».
Теперь про отладку контейнеров. Здесь все похоже на обычный .Net, вы практически не заметите разницы. Чаще всего я запускаю .Net Core как self-hosted под dotnet.exe. Он использует CLRDBG дебаггер, кэш NuGet пакетов и исходники.
ASP.Net 4.5+ размещен под управлением IIS или IIS Express, использует Microsoft Visual Studio Debugger и исходники корня сайта в IIS.
Для отладки есть две среды: 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 доступна ниже.
Тем, кто хочет глубже погрузиться в тему, советую изучить следующие материалы:
Http://dot.net
Http://docs.docker.com
Http://hub.docker.com/microsoft
Http://docs.microsoft.com
Http://visualstudio.com
И напоследок важный совет из практики: самое сложное — запомнить, где что лежит.
Документирование при работе с docker-контейнерами ляжет на ваши плечи. Без документации вы забудете, где в каком контейнере что с чем связано и что с чем работает. Чем больше сервисов, тем больше итоговая паутина связей.