Stan's blog

Ruby on Rails Docker Gitlab CI/CD Linux

Сколько времени может занять перенос проекта Ruby on Rails в Docker для разворачивания через Gitlab CI/CD c нуля

29 января 2024
Kandinsky: Результат генерации по запросу "перенос проекта Ruby on Rails в Docker для разворачивания через Gitlab", стиль: anime

Задача

Организовать хостинг. Проект находится в docker. Деплой должен быть через Gitlab.

Выбранное решение

Везде Linux, а точнее Ubuntu 22.04. В качестве хоста выбрал отдельно стоящую машину с Docker. Сама машина расположена в DMZ (c роутера на эту машину проброшены порты, но с подсети этой машины попасть во внутреннюю сеть нельзя). Kubernetes рассматривается как перспектива на будущее. Gitlab расположен во внутренней сети и недоступен снаружи.

Шаг первый - Gitlab

Gitlab - сердце всего. Самый просто способ развернуть Gitlab - это docker, конечно. Выбрал машину во внутренней сети и вуаля. Но есть нюансы. Gitlab не доступен из внешней сети, но для нормальной работы нужны SSL сертификаты. Выпустил свой корневой сертификат, сертификат для Gitlab и добавил корневой сертификат на машинах, которые работают с Gitlab (то есть на свою рабочую).

Шаг второй - Gitlab Runner для сборки проекта

После того, как запушили коммит в Gitlab, то Gitlab начинает выполнять задачи, описанные в .gitlab-ci.yml. Количество задач и их содержание абсолютно на усмотрение программиста, но для себя я взял три этапа: Тестирование, Сборка продакшена, Деплой. И для тестирования и для сборки продакшена нужно собирать образ (docker image). Этим занимается Gitlab Runner. Runner устанавливается на какую-либо машину и регистрируется в Gitlab. Инициатором регистрации является Runner. Это означает, что Runner должен видеть Gitlab, то есть Runner в DMZ уже не поместить в моем случае (возможно есть способ зарегистрировать Runner со стороны Gitlab, но я так глубоко не копал за отсутствием необходимости). Выбрав самую шуструю машину (то есть свою рабочую), установил Runner типа (executor) Docker запущенный в docker (все в docker). Естественно, ему нужно передать в конфигурации открытую часть сертификата нашего Gitlab.

Некоторое время повозился с сертификатом, но настоящую проблему вызвал netscript. Эта редиска убивала бридж, поднятый docker compose после того, как контейнер завершался. Еще одна ошибка была - установить docker-desktop. Потом часть конфигурации, которая осталась после удаления мешала управлять docker от имени пользователя а не суперпользователя, когда пользователь уже в группе docker.

Шаг третий - Docker Register

Собранный docker-ом образ (image) надо куда-то поместить. Можно и нужно использовать встроенный в Gitlab Registry. Это позволяет использовать фишки, типа переменных среды, которые  Gitlab сам подсовывает. Но это не наш метод! Из-за того, что Gitlab не находится в DMZ, то при деплое нельзя будет вытянуть образ проекта из Gitlab. Можно, конечно, попробовать обойти это, но есть вторая более веская причина. Я не хочу забивать Gitlab большими файлами. Я делаю ежедневные резервные копии и пара десятков лишних гигабайт мне не нужны.

Настройка Docker Registry включила выпуск сертификата для него, прикручивание сертификата и закрытие логином-паролем. Также в моей маленькой локальной сети пока еще нет внутреннего DNS сервера, так что пришлось адреса прописать в /etc/hosts.

Шаг четвертый - Проверка этапов Тестирование и Сборка продакшен

Уже готова половина инфраструктуры и пора проверить. Для этого я взял rails-on-docker, так сказать, для опытов. Сначала добился, чтобы docker image собиралась локально. Пришлось вносить некоторые правки. Когда образ стал локально собираться и запускаться, пришло время этапов Тестирование и Сборка продакшен. Чтобы все заработало прописал вручную переменные CI_REGISTRY_USER, CI_REGISTRY_PASSWORD, CI_REGISTRY и т.д. Все завелось без проблем, можно переходить дальше.

Шаг пятый - Деплой продакшен чужого тестового проекта: еще один Gitlab Runner

Чтобы произвести деплой надо, собственно, то, куда деплоить и то, кто это будет делать. Понятно, что делать будет Gitlab, но на самом деле Gitlab запускает кусочки скрипта из .gitlab-ci.yml на каком нибудь Runner. Для деплоя я завел отдельный Runner уже не типа Docker а типа (executor) Shell.  

Шаг шестой - Деплой продакшен чужого тестового проекта: Настройка хоста

На хосте установлен docker. Добавлен пользователь для развертывания проектов. Есть два варианта: более безопасный - для каждого проекта свой пользователь и более ленивый - одни на всех. Проекты разворачиваются в разный каталоги. На самом деле, содержимое развернутого проекта это docker compose файл и пара папок с настройками, базой и другими файлами, который надо сохранять между запусками контейнеров. Поскольку я один разворачиваю проекты на свой хост, то и и решил ставить одного пользователя. Но это всегда просто исправить.
Traefik

На роутере внешние порты 80 и 443 пробрасываются на хост, но ими надо рулить. Варианты для прокси: apache, nginx и traefik. Traefik оказался не просто удобным, в очень удобным. Traefik имеет встроенный механизм для получения сертификатов Let's Encrypt, то есть никакой sertbot ставить и crond-ить не надо. Он, конечно не такой мощный как перечисленный выше сервера, но для своих целей он идеален. А цель это перенаправлять http/https запросы на соответствующий докер контейнер в соответствии с адресом запроса. Настройку для него можно прописывать в docker compose файле. Эта настройка подхватывается "на лету". То есть при разворачивании нового сервиса ничего в traefik менять не надо. Есть сбор статистки для prometheus. Отображать эту статистику можно с помощью Grafana. В общем, все было установлено в docker и настроено. Естественно контейнеры для traefik, prometheus и Grafana настроены в одном  docker compose файле для удобства. В тестовый проект, была добавлена настройки для traefik.

Шаг седьмой - Новый проект

Смыслы в том, чтобы потренироваться выводить новый проект с нуля в продакшен. Нет желания постоянно разворачивать на локальной машине новый ruby и новый rails, плясать вокруг gemset, да и база опять же локально лежащая захламляет локальную машину. С docker можно вести разработку напрямую в контейнере docker, пробросив внутрь всю папку проекта через volume. Есть свои нюансы связанные с пересборкой образа, когда изменяется Gemfile, но все решаемо достаточно просто. В итоге родился данный блог. В процессе создания возник ряд вопросов, решение которых описано в других заметках в этом самом блоге.

Шаг восьмой - Перенос существующего проекта в docker

Этапы следующие:
  1 - Настроить сборку docker image локально и запуск в development
        Основная головная боль была связана с тем, что одна из версий одного и gems (debug) была изъята, и надо было обновить Gemfile во время сборки. Сейчас уже понятно, что надо было собирать без него а потом поднять контейнер с консолью через run sh и обновить Gemfile.lock. У меня две версии Dockerfile - одна для разработки другая для сборки.
  2 - Настройка запуска тестов локально
        Основная проблема - ajax. Когда появляются новые элементы, а события еще не привязаны, Capybara нажимает на элементы и они не срабатывают. Надо искать правильное решение. На текущий момент решил через sleep 0.2, а с модальными формами через sleep 0.4. Зачем тестирование? Я как-то уже столкнулся, что после обновления пары компонентов половина админских элементов управления перестала работать. Не хочется больше сюрпризов.
  3 - Настройка автоматизированного тестирование -  добавление .gitlab-ci.yml
       
В основном, была всё та же проблема с таймаутами.
  4 - Деплой
        Прописать master.key и восстановить базу и storage из резервной копии не составило особого труда.  

Итог

Все задумывалось из за "Шага восемь". Некоторые шаги можно было, безусловно, пропустить, но их необходимость обусловлена тем, что на каждом шагу всегда встречались проблемы, решение которых позволило накопить опыт, чтобы относительно безболезненно выполнить восьмой шаг

Затраченное время

  Перенос проекта в докер - два дня. Один день ушел на борьбу с тестами.
  Новый проект (создание пустого проекта и деплой в продакшен) - один день. Значительная часть времени ушла на адаптацию Dockerfile для "разработки в докере"
  Деплой тестового проекта - три дня. Основное время ушло на настройку CI и решение проблем со сборкой docker image.
  Настройка traefik и метрик - два дня. Надо читать мануал как настраивать. Есть нюансы. Настройка метрик съела треть дня.
  Настройка Gitlab и Runner-ов - три дня. Полтора дня съел баг вызванный netscript.