Всем привет.
У меня уже несколько лет работает довольно небольшой экземпляр Discourse (без каких-либо проблем): https://discuss.cubeisland.de/.
Я использовал стандартный процесс развёртывания на основе launcher на выделенной виртуальной машине (на собственном оборудовании в дата-центре). Единственное, что я менял за эти годы, — это миграция на внешнюю общую базу данных PostgreSQL.
Недавно я начал переносить приложения с выделенных виртуальных машин в Docker Swarm в качестве подготовительного шага к eventual миграции на кластер Kubernetes, в основном для экономии ресурсов и повышения «эластичности» некоторых частей инфраструктуры.
Сегодня я решил заняться этим небольшим экземпляром Discourse, так как он был одним из немногих оставшихся приложений на выделенных виртуальных машинах. «Он уже работает в Docker, насколько сложно будет развёрнуть его в Swarm?» — подумал я. И, судя по тому, что я прочитал, это действительно возможно. Я могу просто взять образ из текущего работающего экземпляра, загрузить его в наш внутренний реестр и запустить в Swarm, и всё будет работать отлично.
Я изучил файлы launcher, особенно шаблоны и примеры, и понял, что в таком развёртывании, вероятно, стоит вынести Redis отдельно. Возможно, я мог бы настроить задачу CI для сборки новых образов при добавлении плагинов или при желании обновить систему. Поэтому я клонировал репозиторий discourse_docker локально, скопировал своё существующее определение контейнера app.yml в клон и попытался выполнить ./launcher bootstrap app, чтобы собрать образ, который затем можно было бы загрузить в мой реестр, не разворачивая его сразу.
К моему удивлению, скрипт попытался подключиться к «продакшн»-серверу PostgreSQL для миграции базы данных, к счастью, у него не было доступа к нему с моей локальной рабочей станции.
Я поискал информацию здесь и, похоже, именно так всё и работает, что заставляет меня задуматься:
- Как собрать контейнер для нового экземпляра, если у меня ещё нет базы данных? Нужно ли сначала настроить продакшн-базу данных, прежде чем собирать образ?
- Предполагаю, что db:migrate выполняется только один раз. Значит, если у меня есть несколько похожих экземпляров (например, prod и test), мне нужно будет обновить один из них, чтобы собрать новый образ, но тогда я не смогу использовать тот же образ для второго экземпляра, даже если образы будут идентичными.
- Как собирать образы для экземпляров, если сервер базы данных недоступен с системы, на которой собирается образ (что, вероятно, не так уж редко встречается)?
Прочитав несколько постов (очевидно, включая этот), я прекрасно понимаю причины текущего процесса сборки и вижу его ценность для упомянутых 99% людей, которые эпизодически разворачивают Discourse на стандартных «полноценных» виртуальных машинах. Я также привык к моделям «всё в одном» в контейнерах и не против них. В конце концов, ключевая ценность Docker заключается в том, что поставщик программного обеспечения может заранее оптимизировать конфигурации и упаковать их в воспроизводимую среду выполнения, избавляя операционную команду от необходимости обладать специфическими знаниями о приложении. Поэтому я полностью поддерживаю использование предоставленных вами инструментов. Зачем мне ожидать, что кто-то другой создаст лучшие контейнеры, чем сам поставщик ПО? Зачем разделять nginx и Ruby-приложение, если от этого нет никакой выгоды, только ради более «чистой» развёртки (что бы это ни значило)?
Однако странно видеть контейнер, который изменяет состояние во время выполнения, даже не будучи запущенным. Я уже запускаю множество приложений в контейнерах и сам контейнеризировал немало из них, некоторые из которых изначально не предназначались для работы в контейнерах.
Первым примером, который приходит на ум, является GitLab. Хотя сейчас они предоставляют красивый Helm-чарт для полностью декомпозированной развёртки в Kubernetes «как должно быть», я предполагаю (без изучения статистики), что аналогичные 99% их развёрток малого и среднего размера используют Docker-образ GitLab Omnibus (или пакет ОС, что практически то же самое). У них есть аналогичный процесс инициализации, основанный на Chef внутри контейнера, который выполняется при каждом запуске и выполняет обычные действия, такие как миграции базы данных и компиляция ассетов.
Да, запуск GitLab может занимать несколько минут из-за этого, но в развёртках, с которыми я сталкивался (в том числе в крупных компаниях), это никогда не было проблемой. Особенно с современными системами оркестрации, такими как Docker Swarm и Kubernetes, которые могут выполнять rolling-обновления: старый экземпляр отключается только после того, как новый запущен и успешно прошёл проверки работоспособности и готовности. Поэтому длительный процесс развёртывания может не стать проблемой. Но даже без сложных rolling-обновлений, которые могут работать, а могут и нет, во многих ситуациях можно обойтись значительным временем простоя.
Итак: возможно ли настроить launcher так, чтобы он пропускал операции, зависящие от базы данных, во время сборки образа, а выполнял их при запуске контейнера?
Я готов потратить на это своё время, но по вечерам оно ограничено, поэтому любые подсказки будут очень кстати.
Также я открыт к совершенно другим подходам, если вы считаете, что мой вариант глупый или вообще невозможен.
Спасибо за любую обратную связь!