Примерно пару лет назад я открыл для себя великолепный инструмент в арсенале разработчика под названием Docker. Вкратце, Docker — это открытая платформа для разработки, доставки и эксплуатации приложений. Сам Docker работает по принципу виртуализации, то есть каждое приложение представляет из себя виртуальный образ, который помещается в контейнер. Docker использует возможности операционной системы создавать изолированные контейнеры. Контейнер отличается от виртуальной машины тем, что контейнер не эмулирует железо и использует операционную систему хоста.
С помощью Docker, приложение будет безопасно изолированно в контейнере. Есть возможность управлять и ограничивать характеристики контейнера такие как CPU, Memory и другие, а также мониторить производительность и затрачиваемые ресурсы.
Возможности Docker достаточно обширны, однако в данной статье хотелось бы остановиться на трех:
Возможность быстро и безопасно развернуть инфраструктурные приложения (Postgres, Rabbit, Redis и другие),
Возможность мониторинга работы и использования ресурсов собственного приложения в изолированной среде,
Возможность публикации нескольких приложений, взаимодействующих между собой в виртуальной частной сети, таких как: микросервисы,
Инфраструктура в Docker.
Docker позволяет безопасно и быстро разрабатывать и отлаживать приложения на локальной машине и экспериментировать с различными компонентами или даже наборами компонент инфраструктуры.
Используя Docker нет необходимости ставить и настраивать локально базу данных, кэш или обменник. Пусть эти компоненты находятся в образах Docker. Тем более, что образы таких компонент скорее всего уже существуют на Docker Hub.
Так, например, для развертывания Postgres необходимо выполнить:
docker run --name habr-pg-13.3
-p 5432:5432
-e POSTGRES_USER=habrpguser
-e POSTGRES_PASSWORD=pgpwd4habr
-e POSTGRES_DB=habrdb
-d postgres:13.3
При выполнении команды скачается образ postgres:13.3 и запуститься на порту 5432 с пользователем habrpguser, паролем pgpwd4habr и названием базы habrdb.
Приложение в контейнере.
Если с инфраструктурой всё понятно и достаточно только найти нужный образ, скачать его и запустить в контейнере, то со своим собственным приложением всё не так просто. Чтобы собрать Docker-образ необходимо сформировать Dockerfile.
Если вы используете Visual Studio, редактор сам предоставляет такую возможность если добавить «Docker Support»:
Если выбрать, например, платформу Linux, получим такой Dockerfile:
FROM mcr.microsoft.com/dotnet/runtime:6.0 AS base
WORKDIR /app
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /src
COPY ["Dir/App.csproj", "Dir/"]
RUN dotnet restore "Dir/App.csproj"
COPY . .
WORKDIR "/src/Dir"
RUN dotnet build "App.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "App.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "App.dll"]
Здесь скачиваются образа mcr.microsoft.com/dotnet/runtime:6.0 и mcr.microsoft.com/dotnet/sdk:6.0, затем в контейнер помещается директория с проектом восстанавливаются все зависимости, используемые проектом, далее проект билдится в режиме релиза, выполняется publish, производятся копирования и объявляется точкой входа команда dotnet app.dll приложения. Помимо этого в VS студии появится профиль запуска «Docker».
После запуска в Docker Desktop будет создан контейнер с нашим приложением. Если выбрать его в перечне, то мы сможем посмотреть логи, помимо логов можно увидеть вкладки Inspect, где отображаются параметры запуска и Stats, где отображается статистика ресурсов, используемых контейнером.
Помимо вкладок есть управляющие кнопки контейнера, Stop, Restart, Delete, Open In Browser, Cli. Особый интерес здесь представляет Cli эта команда откроет терминал в контейнере. Другой способ открытия терминала: написать в командной строке
docker exec -it <имя контейнера> /bin/bash
Для различных редакторов вроде VS и VS Code есть расширения для просмотра содержимого контейнеров и работы с ними. В частности в VS Code:
Микросервисы. Docker Compose.
Запустить приложение в контейнере оказалось достаточно несложно, особенно используя VS, но как развернуть несколько связанных приложений? Конечно, можно каждый раз отдельно запускать группу приложений, по отдельности и инфраструктурные компоненты. Да, такой вариант, в целом, обеспечит нам группу сервисов, но управлять их конфигурацией будет достаточно проблематично. Как же быть? На этот случай существует Docker Compose. Docker Compose используется для одновременного управления несколькими контейнерами, входящими в состав приложения.
Для использования Docker Compose необходимо сформировать файл docker-compose.yml. В этом файле конфигурируются все контейнеры, которые мы собираем вместе:
version: '3.8'
networks:
app-net:
external: false
services:
app1:
image: app1:latest
container_name: app1
hostname: app1
depends_on:
- mongo_image
- rabbitmq_image
build:
context: "../"
dockerfile: "App1/Dockerfile"
ports:
- "63696:63696"
- "63695:63695"
environment:
- ConnectionStrings__mongo=mongodb://mongo_image:27017/app?appName=app&waitQueueMultiple=7&maxPoolSize=100
- ConnectionStrings__Queues=amqp://myuser:mypass@rabbitmq_image:5672/app
- ASPNETCORE_ENVIRONMENT=Production
- ASPNETCORE_URLS=https://*:63696;http://*:63695
- ConnectionStrings__redis=redis:6379
networks:
- app-net
app2:
image: app2:latest
container_name: app2
hostname: app2
environment:
- ASPNETCORE_URLS=http://*:65000
- ConnectionStrings__redis=redis:6379
- Cors=["https://localhost:63696","http://localhost:63695"]
- ConnectionStrings__main=Server=postgres_image;User Id=postgres;Password=123;Database=app;Port=5432
depends_on:
- postgres_image
- cache
build:
context: "../"
dockerfile: "App2/Dockerfile"
ports:
- "65000:65000"
networks:
- app-net
rabbitmq_image:
image: rabbitmq:3.8.8-management
ports:
- 5673:5672
- 15673:15672
restart: always
networks:
- app-net
environment:
RABBITMQ_DEFAULT_VHOST: app
RABBITMQ_DEFAULT_USER: myuser
RABBITMQ_DEFAULT_PASS: mypass
mongo_image:
image: mongo:4.2.8
ports:
- "27019:27017"
restart: always
environment:
MONGO_INITDB_DATABASE: app
networks:
- app-net
postgres_image:
image: postgres:11
ports:
- "5431:5432"
restart: always
environment:
POSTGRES_USER: "postgres"
POSTGRES_PASSWORD: "123"
POSTGRES_DB: "app"
networks:
- app-net
cache:
image: redis:latest
hostname: redis
restart: always
ports:
- '6379:6379'
command: redis-server --save 20 1 --loglevel warning
networks:
- app-net
app3:
image: app3:latest
container_name: app3
hostname: app3
depends_on:
- rabbitmq_image
- clickhouse_image
- cache
- app1
build:
context: "../"
dockerfile: "App3/Dockerfile"
environment:
- ConnectionStrings__Queues=amqp://myuser:mypass@rabbitmq_image:5672/app
- ConnectionStrings__ClickHouse=Host=clickhouse_image;Port=9000;Database=default
- app__url=https://app:63696
- DOTNET_ENVIRONMENT=Production
networks:
- app-net
Здесь описаны три сервиса: app1, app2, app3 и инфраструктурные компоненты: rabbitmq_image, mongo_image, postgres_image, cache через переменные секции environment обеспечивается передача параметров для связки сервисов между собой, а также инфраструктуры. Параметры секции environment будут отображаться во вкладке Inspect каждого контейнера. Все компоненты находятся в сети app-net. Чтобы развернуть все эти компоненты необходимо из директории с файлом вызвать команды:
docker-compose build
docker-compose up
Такой файл можно сделать руками, однако в VS есть возможность данный процесс автоматизировать за счет добавления оркестратора контейнеров:
После добавления поддержки оркестратора контейнеров добавится поддержка docker-compose, в котором, по показанному ранее примеру можно сконфигурировать стенд из необходимых компонент.
Для корректной работы приложений важно правильно указать строки доступа к компонентам инфраструктуры и к сервисам. Выделение контейнеров в отдельную сеть обеспечивает их изолированность от внешних приложений и дает возможность взаимодействия между собой. Обеспечив правильные параметры запуска в секции environment и параметры инфраструктуры получаем готовый стенд из нескольких приложений, который сможет развернуть любой, у кого есть Docker и доступ в интернет.
Комментарии (13)
segment
23.09.2024 07:55А для того чтобы собрать новый образ интернет соединение обязательно? Можно ли работать с докер контейнерами и отлаживать офлайн?
alex_smite Автор
23.09.2024 07:55Можно собирать как локально, так и брать образа из сетевого регистри компании, если такое есть. Кроме того, docker по слоям запоминает, так что, если слои не меняются, они и не пересобираются
zelenin
23.09.2024 07:55а также все образы и контейнеры можно восстановить с файловой системы из архива, если нет доступа к реестру
Tab761
23.09.2024 07:55+1А при чём тут C#?!
alex_smite Автор
23.09.2024 07:55Сервисы на C#, но сути вы правы из особенностей разве только некоторые особенности студии
Worgen
На новом месте работы, ребята завезли Docker для разработки.
Честно, сбежал через 3 недели из Docker`а, развернул БД и прочие сервисы на совсем сервере, и запускаю как обычное App приложение, и бед не знаю.
Решарпер почему-то очень сильно начинает тормозить приложение при дебагге, а так же неадекватно работает hotreload.
Так что, по старинке лучше
ИМХО
Lainhard
Да, к сожалению ребятам из jetbrains еще работать и работать над поддержкой девконтейнеров. Сижу на Clion и девконтейнеры это боль, приходится вместо них использовать docker тулчейны (это похоже на девконтейнеры, но отличие в том что контейнер запускается на каждый бинарь, запускаемый IDE и живет пока бинарь не завершит работу).
ryanl
А зачем дебажить приложение запущенное в Docker-е? Нельзя запустить локально инстанс и натравить конфу остальных контейнеров на него? Конечно если пытаться впихать невпихуемое, то будут сложности.
Можно сидеть на табуретке с 4-мя ножками, одна из которых сломана, и вместо сломанной подставить собственную коленку.
Однако будет очень неудобно.
ritorichesky_echpochmak
Попробуйте немного в разработку реального софта и выяснится, что там есть какая-никакая инфраструктура, а ваш кейс должны на 146% закрывать автотесты, модный TDD во все места... в теории)... на практике, ну миленько, но про всякий remote debug (который в MSVS ещё со времён классического фреймворака), всякие XDebug и прочий дебаг через SSH-туннели придумали серьёзные чуваки не потому что всё так розово и пушисто.
По статье же... ну, клёво, но на дворе уже давно .NET Core 8, который даже штатно завезли репы бубунты, который умеет в chiseled containers. `
version: '3.8'
` для docker compose уже не требуется. Почему контейнерам дают имена с _image - не понятненько.С Rider и дебагом в винде компоузов немного грустно, он стабильно падает с ошибкой
и при нажатии на "Install tools to the container" валится с
Поэтому приходится отключать всякие улучшайзеры (Don't use Docker fast mode) и добавлять в Environment variables
и тогда на хосте разработчика можно зацепиться за процесс дебаггером, а на проде через CI/CD раскатанные образы будут честно релизные, без дебага
ryanl
Хех, вот вы попробовали, и что, у вас получается дебажить контейнеры в Райдере? Если у вас действительно есть какой-то опыт разработки реального софта, то уже давно бы поняли, что если при решении задачи возникает техническая проблема толщиной в несколько бетонных стен, то не стоит разбегаться и биться головой об них (даже если вы супермен), и по пробитии кричать во всю глотку "Я это сделал!"; а стоит просто спокойно обойти, ибо эстимейты уже проставлены. А за использование всего подряд, что есть под рукой обычно голосят еще зеленые в индустрии люди (мидл минус и ниже).
Отладка с аттачем к процессу это необходимость лишь тогда, когда реализованная фича достаточно сложна и тонка, что разраб, где-то что-то не формализовал и конкретно зафакапил; при этом совершенно ничего не логируется (что в наши дни моветон).