Любую проблему можно решить, даже в Docker Compose

Привет, друзья. Я всегда, когда работаю с каким-то популярным, публичным решением, например, как с Docker Compose, думаю, что нет нерешаемых проблем. Сегодня я встретился с таковой.

Итак, пишу до боли простое Java приложение: Spring boot + jOOQ + Flyway. Оно запускается в Docker-контейнере, который управляется с помощью Docker Compose. На хосте (то есть, не в контейнере) есть поднятая PostgreSQL, которая любезно слушает 5432 порт на localhost.

Давайте посмотрим на мой build.gradle, тут это важно:

Какие можно сделать выводы из этого build.gradle? Наверное, если вы не робот, или компилятор — то никаких. А давайте с помощью прекрасного плагина для gradle — com.dorongold.task-tree — построим Граф Задач для двух нужных нам Тасок — build (сборка проекта) и bootRun (запуск проекта).

Вот Дерево Задач для Сборки проекта:

А вот Дерево Тасок для Запуска проекта:

Какие выводы можно сделать из этих двух деревьев? Самые неутешительные! В обоих случаях нам нужно успешное соединенеие до базы данных — localhost:5432/jdbc-test. Это связано с тем, что в обоих Деревьях есть таска — compileJava, которая зависит от generateCsmartJooqSchemaSource и flywayMigrate.

Отвлечёмся немного от Gradle, и вспомним, что этот пост — про Docker и Docker-compose. Давайте посмотрим на структуру моего проекта:

То есть, всё супер стандартно. Есть корневая папка. В ней файлик docker-compose.yml, который описывает все сервисы проекта. Каждый сервис проекта содержится в отдельной папке (тут для примера показана папка jdbc-test). D этой папке находится Dockerfile — описание текущего image, а также дочерний docker-compose.yml — описание конкретного сервиса.

Например, так может выглядеть корневой docker-compose.yml:

Так может выглядить Dockerfile конкретного сервиса:

А вот так может выглядить дочерний docker-compose.yml:

И вот на этом моменте я словил 2 Особенности Докера, с которыми протрахался все выходные.

Я разрабатываю на Windows 10. Как известно, в Windows докер запускается не нативно, а в виртуалке. Поэтому, когда я попытался запустить этот проект —

То поймал неожиданную для меня ошибку:

Вот Ту Фак? — Подумал я. Ведь все контейнеры запущены в Хостовой сети — network_mode: «host», и подключение к localhost должно прекрасно работать. Я упустил один момент. Докер запускается в в виртуалке, котораяз запускается в винде. Поэтому localhost докера — видит только localhost виртуалки, но не винды. Об этом даже в документации написано:

If you are using Docker for Mac (or running Linux containers on Docker for Windows), the docker network ls command will work as described above, but the ip addr show and ifconfig commands may be present, but will give you information about the IP addresses for your local host, not Docker container networks. This is because Docker uses network interfaces running inside a thin VM, instead of on the host machine itself.

К счастью, этот баг можно пофиксить, причём даже несколькими способами. Один из них — это использовать не localhost во время разработки на Windows, а docker.for.win.localhost. В этом случае у вас всё будет работать.

Итак, шло время, разработку на Windows нужно было заканчивать, и деплой на Ubuntu уже становился неизбежностью. Ну, я же знал, что Докер — это универсальная среда. Если на винде всё работает, то, без базара, это запустится и на Линуксе. Ага. 10 раз. Запустилось, проверяй.

Делаю я, значит, свой любимый — docker-compose up -d —build на Линуксике, и… Ловлю ошибку:

Что за херня? Подумал я. Ведь это уже не Windows. Тут нет промежуточной виртуалки. А контейнеры запускаются в режиме network_mode: «host». Зуб даю — всё должно работать. Но нет.

Оказывается, что network_mode растространяется именно на запущенный контейнер. То есть, когда приложение работает, localhost будет прекрасно доступен. А вот во время сборки — build stage, это совсем не так.

Ну, я решил не отчаиваться, не я же первый, кто столкнулся с такой травиальной проблемой? Пошёл в доки, увидел кучу build options, среди которых тут же нашёл —network — Set the networking mode for the RUN instructions during build. Я уже подумал, что Задача Решена, Квест пройден, можно выйти в падик и попить пивасика.

Решил сначала собрать контейнер в одиночном режиме, без Docker Compose. Пишу, значит:

И теперь всё прекрасно работает. Ещё бы, ведь я указал, что нужно использовать Хостовую Сетку во время сбора проекта.

Теперь пришло время подружить всё это дело с Docker-Compose… И, это то самое место, которое я не смог решить. По какому-то непонятному мне стечению обстоятельств, нельзя задать произвольные опции сборки. Всё, что вы можете — это задать пару опций непосредственно для build команды:

А также в определении сервиса в docker-compose.yml:

  • context
  • dockerfile
  • args
  • cache_from
  • labels
  • shm_size

То есть, нет способа задать опцию Билда для Docker-compose. Я даже вопрос на stackoverflow задал — https://stackoverflow.com/q/48366594/1756750. Никто так и не ответил.

К слову, в Репозитории Docker-compose даже есть осбуждение на эту тему — https://github.com/docker/compose/issues/4114. Там один из участников предлагает реализовать такую форму задания опций билда:

Из всей этой большой болтовни можно вынести один простой вывод. Даже в супер популярном программном обеспечении есть вещи, которые попросту нельзя реализовать, если не пойти самому в исходый код. Но это уже совсем другая история.

Категории: Программирование