Radiofisik

my knowledge base

Настройка dev среды

Использование docker в разработке имеет ряд преимуществ, таких как удобство развертывание, работа в одной среде в проде и деве…

Workflow

Типичный процесс работы с использованием docker:

Отладка

Типовой подход

В плане отладки с контейнерами не все так просто. Visual Studio 2017 по умолчанию поддерживает отладку в контейнере, который создан через механизм интеграции с docker-compose. Работает это так. При добавлении в солюшен интеграции с docker compose создается проект docker-compose.dcproj который включает файлы docker-compose.yml и docker-compose.override.yml. При запуске проекта Visual Studio 2017 запускает

docker-compose  -f "docker-compose.yml" -f "docker-compose.override.yml" -f "docker-compose.vs.debug.g.yml" -p dockercompose1508596578922884945 --no-ansi up -d

То есть добавляет свой сгенерированный файл docker-compose.vs.debug.g.yml следующего содержания:

version: '3.4'

services:
  dockertest2019:
    image: dockertest2019:dev
    build:
      target: base
    environment:
      - NUGET_FALLBACK_PACKAGES=/root/.nuget/fallbackpackages;/root/.nuget/fallbackpackages2
    volumes:
      - C:\Users\radiofisik\source\repos\dockertest2019\dockertest2019:/app
      - C:\Users\radiofisik\vsdbg\vs2017u5:/remote_debugger:ro
      - C:\Users\radiofisik\.nuget\packages\:/root/.nuget/packages:ro
      - C:\Program Files\dotnet\sdk\NuGetFallbackFolder:/root/.nuget/fallbackpackages:ro
      - C:\ProgramData\Xamarin\NuGet\:/root/.nuget/fallbackpackages2:ro
    entrypoint: tail -f /dev/null
    labels:
      com.microsoft.visualstudio.debuggee.program: "dotnet"
      com.microsoft.visualstudio.debuggee.arguments: " --additionalProbingPath /root/.nuget/packages --additionalProbingPath /root/.nuget/fallbackpackages --additionalProbingPath /root/.nuget/fallbackpackages2  \"bin/Debug/netcoreapp2.2/dockertest2019.dll\""
      com.microsoft.visualstudio.debuggee.workingdirectory: "/app"
      com.microsoft.visualstudio.debuggee.killprogram: "/bin/sh -c \"if PID=$$(pidof dotnet); then kill $$PID; fi\""

По сути переопределяется все содержимое контейнера и запускается удаленная отладка в контейнере. Такой подход имеет ряд недостатков:

Альтернативный подход

Типовая архитектура микросервисного приложения предполагает наличие некоторого Reverse Proxy между внешним миром и микросервисами. Соответственно запрос попадает сначала на прокси, затем прокси посылает его на микросервис. В качестве прокси часто используют nginx который также можно разместить в docker контейнере. В такой среде отладку можно вести локально переопределяя не содержимое контейнера, а сам сервис контейнера - с помощью reverse proxy указывая на локально запущенный под отладкой сервис вместо того что крутится в контейнере. Такой подход позволяет создать скрипты для включения

  copy ".\Root\debug\01.someService.conf"  ".\Root\services\"
  docker restart reverse_proxy_1

и отключения

  del ".\Root\services\01.someService.conf" /q
  docker restart reverse_proxy_1

где 01.someService.conf перенаправляет запросы на http://192.168.0.52:5007

  location ~ ^/api/someService/(.*)$ {
      set $upstream_endpoint http://192.168.0.52:5007;
      rewrite ^/api/someService/(.*) /api/$1 break;
      proxy_pass $upstream_endpoint;
  }

Остается решить проблему доступа самого сервиса под отладкой ко всей инфраструктуре в контейнера, и главная проблема тут - внутри и наружи разные адреса - это так по крайней мере если используется Docker Toolbox. Проблема решается довольно легко, так как код в контейнере использует переменные конфигурации полученные по следующему пути файл .env + локальные переменные среды - docker.compose.yml (environment секция) - переменные среды контейнера, которые имеют высокий приоритет. В случае же локального запуска переменные берутся из файла appsettings.json и локальных переменных среды. Таким образом осталось обеспечить чтобы код для запуска контейнера и код запуска кода под отладкой использовали разные переменные среды. Об этом далее.

Кастомизация запуска Docker Toolbox

При запуске Docker Toolbox происходит запуск bash скрипта

C:\Program Files\Docker Toolbox\start.sh

В который можно дописать вызов своего скрипта ./initenv.sh Для кастомизации запускаемой docker-machine. Пример моего скрипта, который запускает скрипт inside.sh внутри docker-machine для ее настройки. Затем устанавливает переменные среды для запуска контейнера (для решения описанной ранее проблемы) и запускает bash в среде где установлены эти переменные среды чтобы можно было запустить тут docker-compose up

#!/bin/bash

echo "Init my project"
docker-machine ssh default "bash -s"<./inside.sh

cd /d/myProject
unset database_connectionstring
unset redis_connection
export ELASTIC_CONNECTION=http://192.168.99.100:19200

exec bash 

Скрипт inside.sh в моем случае устанавливает vm.max_map_count которая необходима для нормальной работы elastic search, настраивает доступ по протоколу http к registry для хранения докер образов.

echo "inside docker"
sudo -s
sudo sysctl -w vm.max_map_count=262144;
echo "192.168.0.2 registry" >> /etc/hosts
echo '{ "insecure-registries":["registry:5000"] }' > /etc/docker/daemon.json
cat /etc/hosts

Таким образом после запуска Docker Toolbox мы оказываемся в нужной папке с нужными переменными среды, но для запуска проекта в реальной жизни приходится использовать команды вида

docker-compose -f docker-compose.yml -f docker-compose.override-local.yml -f docker-compose.database-local.yml -f docker-compose.mailcatcher.yml up -d

и аналогичные с небольшими отличиями в конце. Набирать или копировать постоянно их не удобно и чтобы избежать этого можно использовать alias которые можно настроить и в Windows и это будет работать в docker tools и в git bash. При запуске они ищут файл

"C:\Users\username\.bashrc"

туда можно дописать что-код для загрузки alias из другого файла

if [ -f ~/.bash_aliases ]; then
    . ~/.bash_aliases
fi

в самом же .bash_aliases

alias prj='docker-compose -f docker-compose.yml -f docker-compose.database-local.yml -f docker-compose.mailcatcher.yml -f docker-compose.override-local.yml'

после этого команды становятся короткими, например:

prj up -d
prj down
prj pull
...

Полезные алиасы

Для создания небольших проектов удобно использовать

githubc=mkdir $1 && cd $1 && touch readme.md && git init && git add . && git commit -m "initial" &&curl -H "Authorization: token YOURTOKEN" -H "Content-Type: application/json" https://api.github.com/user/repos -d "{\"name\": \"$1\"}" && git remote add origin https://github.com/Radiofisik/$1.git && git push --set-upstream origin master

githubu=curl -H "Authorization: token YOURTOKEN" -H "Content-Type: application/json" https://api.github.com/user/repos -d "{\"name\": \"$1\"}" && git remote add origin https://github.com/Radiofisik/$1.git && git push --set-upstream origin master

так можно создавать репозитории не заходя на github.