Широкое распространение развертывания приложений с использованием Docker стало причиной написания этой статьи. Преимущества, недостатки, сложности и прочие сравнительные аспекты широко освещаются в различных руководствах, являются причинами создания различных по сложности и наполненности курсов, обучающих материалов и т.д. и т.п. Попробуем подойти к этому вопросу с практической стороны и решить задачу без наличия каких либо специфичных знаний в этой области. В качестве исходных данных возьмем следующее: домашний ноутбук с операционной системой Mac OS Big Sur работающее приложение на Rails 7 используемую базу данных postgres Разобьем задачу на этапы: Установка Docker Перенос базы PostgreSQL в контейнер. Подключение контейнера к работающему приложению. Перенос приложения в контейнер Подключение приложения из контейнера к контейнеру с базой данных. Использование возможностей Docker для автоматизации данного процесса. Посмотрим, что получается и что можно сделать дальше. С этим пунктом все просто. Если операционная система и железо не "старое", получается быстро и буквально по инструкции. Скачиваем уже предлагаемый пакет Docker и устанавливаем его. Следуем инструкции Install and run Docker Desktop on Mac. Отличная инструкция на русском языке есть на habr Полное практическое руководство по Docker Там же приведена терминология и описаны основные моменты работы с Docker. Для доступа к существующим контейнерам потребуется учетная запись на Docker Hub Проверяем, что после установки все работает. На этом установку можно считать завершенной. Поскольку планируем работать с docker в основном из терминала с претензией на более универсальный подход, для удобства используем zsh-docker-aliases.- dk=docker- dkr='docker run'- dkIb='docker image build'- dke='docker exec'- dkIls='docker image ls'- dkpl='docker pull' Можно просто создать необходимые для часто используемых команд aliases, но так как автор никогда до этого с docker не сталкивался, определить сразу, что будет использоваться, а что нет - весьма затруднительно. Просто воспользуемся опытом других. Дальше по тексту будут использоваться alias из этого plugin Найдем необходимый нам image PostgreSQL Берем первый, он же "официальный", поскольку сейчас особых каких то требований нет и идем по пути наименьшего сопротивления. Запустим postgres instance на основе этого image в detached mode (-d) с открытием всех публичных портов со случайным mapping (-P) Зададим пароль пользователю postgres, чтобы можно было проверить работу из консоли. Можно указать опцию (--rm) для удаления контейнера после завершения работы (dk stop) Получаем информацию о запущенных контейнерах Берем назначенный порт и подключаемся к базе. Все достаточно просто и работает. Теперь зафиксируем порт для использования в настройках Rails. Остановим контейнер и запустим с определенным портом. Чтобы избежать настроек с безопасностью, возьмем, например порт 54320 Создаем базу, применяем миграции. Проверяем, что получилось. Установки locale в образе только по умолчанию. Поправить это можно двумя способами, описано вот здесь Locale Customization. взять образ на базе alpine и указать параметры locale в строке запуска дополнить существующий образ. Выбираем второй вариант, возможно потом будут еще какие то дополнения. Создаем Dockerfile Создаем image на основе этого файла. Создаем контейнер на основе этого image и проверяем установку locale Теперь у нас есть контейнер, который создается с использованием нашего Dockerfile c необходимыми нам параметрами locale Можно запустить приложение и убедиться, что оно работает с базой данных в созданном контейнере. У нас приложение Rails, для него требуется в качестве основы контейнер, который включает в себя web server и сервер приложений, умеющий работать с Rails. Поскольку в "безконтейнерном" варианте для решения данной задачи можно использовать passenger в сочетании с nginx, поищем образ, представляющий базовую конфигурацию для этого. В качестве альтернативного решения возможно использование Universal Web App Server, который тоже существует в образах docker nginx/unit 1.29.1-ruby3.1 - 343.89 MB phusion/passenger-ruby31:2.3.0 - 255.28 MB По размерам примерно одинаковые, возьмем версию с tag 2.3.0, которая использует ruby 3.1.2 по умолчанию, поскольку приложение Rails создано с использованием этой версии. Подробное описание настроек phusion / passenger-docker  В последующем можно заняться оптимизацией, поскольку полная версия кроме ruby поддерживает python, node и meteor. Сначала сделаем отдельный image для приложения. Сделаем новый файл Dockerfile.ruby, ниже объединим с созданием контейнера для СУБД PostgreSQL в один процесс с использованием docker-compose. Берем за основу предлагаемый в описании файл конфигурации Dockerfile и вносим небольшие изменения и дополнения. Немного снабдил комментариями, остальное все достаточно понятно. Создадим файл конфигурации для nginx и passenger, используем предлагаемый прототип. Единственное возникшее затруднение - потребовалось определение passenger_ruby, путь оказался несколько иным, чем в описании. В связи с этим после первой сборки passenger не запустился. Возможно это связано с тем, что был взят образ с определенным tag и процедура установки ruby через rvm была выключена из шагов настройки. В любом случае путь можно получить из контейнера командой: Осталось внести правильный путь в конфигурацию и пересоздать контейнер. -f Dockerfile.ruby - указываем файл для сборки, если он имеет иное название, чем Dockerfile -t tag - даем нашему образу название. точка в конце указывает каталог, где находится Dockerfile. Запускаем созданный образ Если все было сделано верно, то по адресу http://:32000 находится стартовая страница приложения. Ну если быть точным, то не стартовая страница, а сообщение Rails о том, что база данных не найдена и предложение ее создать. Попытка создания базы данных приведет к следующей ошибке: Соединение с базой данных установить не удалось, host не найден. Сейчас у нас должно быть два контейнера, к каждому из которых есть доступ с локального компьютера по установленным портам, но между собой они никак не связаны. Процедура подключения контейнеров к общей сети описана вот здесь Networking with standalone containers Из описания следует, что контейнеры должны были автоматически подключиться, но у меня этого не произошло. Диагностировать проблему оказалось весьма затруднительно, причина в том, что инструментарий для сетевой диагностики, включенный в выбранные образы весьма ограничен. Если для passenger образа есть хотя бы базовые команды работы с IP стеком (ip address и прочее), то в образе для postgres этого нет вообще. Нет и иных привычных утилит, например ping и т.д. В общем, это совершенно верный подход, в работающих настроенных контейнерах ничего лишнего быть не должно. Устанавливать все необходимое выходило за рамки задачи, поэтому выбран вариант создания своей сети для двух контейнеров и запуск их с указанием этой сети. Как делать сети - описано в том же руководстве, кроме того еще и вот здесь habr Полное практическое руководство по Docker Еще один момент - в конфигурации приложения Rails необходимо указать внутренний порт для доступа к базе, 5432, после чего еще раз пересоздать контейнер. Запускаем наши контейнеры, указывая в качестве параметра запуска созданную нами сеть Смотрим состояние запущенных контейнеров. Проверяем визуальную работу приложения на порту :32000 - приложение должно сообщить о необходимости создания базы данных, потом - выполнения миграций и .. Запуститься. Итак, у нас есть два контейнера, в одном находится база данных postgres, во втором - приложение Rails. Посмотрим, как можно использовать docker-compose для того, чтобы сразу создать работающее приложение, разделенное на два контейнера без тех промежуточных шагов, которые были необходимы для раздельного переноса базы и приложения в контейнер. Инструмент для этого - docker-compose Docker Compose Описание весьма внушительное и содержательное, требующее внимательного изучения, поэтому переходим сразу к разделу Try Docker Compose с надеждой на то, что все окажется не так и страшно. Опираясь на этот tutorial и используя Полное практическое руководство по Docker создаем файл docker-compose.yml В руководстве достаточно подробно описано, что есть что в этом файле, поэтому кратко о содержании. Создаем два сервиса (это и есть наши будущие контейнеры) db и webapp - соответственно база данных и приложение. hostname - указываем "человеческое" имя хоста внутри контейнера, иначе docker подберет цифровое случайное обозначение. Не обязательно, но для внешнего администрирования контейнера и просто логов - очень полезно. build - указание наших ранее созданных Dockerfile для сборки образов для контейнеров. Здесь немного поменял названия, теперь файл для сборки postgres называется Dockerfile.postgres, а для приложения - просто Dockerfile. Для стандартного Dockerfile имя указывать не обязательно. ports - определяем внешний проброс портов. Для приложения добавлена возможность подключения по ssh, которая была определена ранее. restart - поведение при ошибках и сбоях environment - переменные окружения, все, что раньше указывали в командной строке с ключом -e переносим сюда. depends_on - указываем зависимость второго контейнера от первого, с базой данных. Удаляем контейнеры, созданные ранее, освобождаем место и запускаем процесс создания с помощью docker-compose c ключами создания и запуска с последующим detach. И .. Это все. Приложение вместе с базой данных помещается в два контейнера и они запускаются. Все работает. Не надо создавать сети, все создается автоматически, контейнеры находятся в одной сети и могут взаимодействовать. Можно просматривать логи работы контейнеров В логах выводится информация по умолчанию, это можно переопределять при создании как контейнеров, так и образов. Первая сборка проходит довольно долго, в основном это связано с загрузкой начальных образов и gem пакетов для приложения, зато повторный запуск осуществляется очень быстро. Если сравнивать со временем запуска подобной конструкции в виртуальной машине - отличаются порядки. Следующий интересный шаг, который можно было бы попробовать - разместить приложение в cloud, чтобы протестировать, насколько это переносимо, но aws отключил регистрацию пользователей из России и обходить это нет никакого желания. Технология вызывает сильное уважение и восхищение - количество "не понятных" и сложных моментов - минимально. Повторное использование уже созданных контейнеров путем добавления/изменения конфигураций сборки предоставляет большие возможности. Возможности организации совместной работы контейнеров с использованием подключаемых томов Можно использовать предварительные image для последующего построения контейнеров. Клонирование контейнеров и последующее использование для масштабирования И еще достаточно много полезных и очень полезных возможностей. :-) И если добавить к этому возможность управления всем этим процессом с учетом балансировок нагрузки, предоставляемую Kubernetes - многие процессы, организация кластера для базы данных, балансировка нагрузки приложения, обеспечение отказоустойчивости и т.д. и т.п. становятся существенно проще.