Почему Kamal?
Ранее частым выбором для деплоя Rails-приложений был Capistrano. Он предоставлял удобные инструменты для автоматизации, но со временем начали проявляться его ограничения: каждый новый сервер требовал тщательной подготовки, что превращало поддержку инфраструктуры в постоянную борьбу с конфигурациями Ansible или Chef.
Для контейнеризации стали использовать Docker Swarm или Kubernetes, но эти системы усложняют процесс — Docker Swarm требует понимания согласования состояния, а Kubernetes подходит далеко не всем, хоть и является мощным инструментом сам по себе.
У Kamal же совершенно другой подход, предлагающий простое решение, объединяя преимущества Capistrano и Docker для деплоя контейнеризированных приложений. Kamal исключает ручную настройку серверов и зависимости — достаточно подключить «чистые» серверы с добавленным SSH-ключом, и остальное Kamal сделает за вас. Он установит Docker и развернёт приложение в течение нескольких минут.
Это элегантное решение позволяет разворачивать приложения с помощью Docker без дополнительных усложнений. Kamal использует базовые команды Docker, что делает его понятным даже для тех, кто только начинает осваивать контейнеризацию. Он позволяет сосредоточиться на главном — успешном развёртывании приложения.
Начало работы и особенности Kamal
В этой статье я не буду рассказывать о базовых конфигурациях, доступных в документации. Вместо этого мы погрузимся в особенности и тонкости настройки Kamal.
Конфигурация Builder
В данной конфигурации есть несколько важных моментов:
● Multistage-config — для минимизации веса Docker-образа рекомендуется использовать многоступенчатую сборку, где слой сборки приложения собирается отдельно, и в финальный образ включается только чистый код без лишних артефактов сборки.
● Dockerfile — все Docker-файлы размещены в папке .dockerdev для удобства и безопасности.
● Multiarch — позволяет указать необходимость многоархитектурнойсборки, что особенно полезно при использовании различной архитектуры на локальной машине и в продакшене. Например, при использовании M1 (aarch64) локально и amd64 на сервере. В CI multiarch: false, что делает билд быстрее.
● Args — передача переменных окружения, таких как версия Ruby, напрямую в контейнер. Чтобы не обновлять конфигурацию каждый раз, версия Ruby берется из стандартного файла: RUBY_VERSION: <%= File.read ('.ruby-version').chomp %>.
Настройка серверов
Kamal изначально не был предназначен для развёртывания приложений на одном сервере, однако в данном примере мы воспользуемся лайфхаком, указав общий хост и объединив их в одну сеть.
Для удобства и простоты конфигурации IP сервера хранится в переменной окружения. Это позволяет явно указать один и тот же хост для обоих контейнеров, чтобы они запускались на одном сервере.
● Network — для взаимодействия контейнеров, таких как приложение и база данных, они должны находиться в одной Docker-сети. Kamal отвечает за запуск контейнеров, но не за их взаимодействие, поэтому сеть необходимо создать заранее.
Чтобы создать сеть, можно подключиться к серверу и вручную выполнить команду docker network create sky-db-net, либо воспользоваться хуками Kamalдля автоматизации этого процесса.
Настройка балансировщика: Traefik
И наконец, вишенка на торте — настройка балансировщика с помощью Traefik. Любое веб-приложение нуждается в правильном взаимодействии с внешним миром. Ранее повсеместно использовался Nginx. В то же время Nginx не всегда эффективно работает с Docker-контейнерами «из коробки» и так на смену ему пришёл Traefik — прокси-сервер, который изначально задуман для интеграции с Docker и автоматизации большинства задач.
В базовом варианте Traefik обеспечивает простой роутинг с домена на сервис, однако в моём случае мне нужно было настроить более сложную логику перенаправления. Например, при переезде веб-портала с Tilda на собственную корзину покупок необходимо было сохранить все старые ссылки и настроить перенаправление с одной ссылки на основной домен. Это удалось реализовать с помощью middleware в Traefik, где все запросы с субдомена перенаправляются на основной домен, сохраняя структуру ссылок.
Ну и самое приятное — чтобы выпустить SSL-сертификат, не нужно вручную настраивать Certbot, прописывать конфигурации и настраивать cron для повторного обновления. Достаточно указать в конфигурации, что используем TLS, и выбрать certresolver: letsencrypt. Указываем домены в массиве, для которых будут получаться сертификаты, и Traefik сделает всё за нас.
Также стоит упомянуть несколько ключевых настроек контейнера с Traefik. Мы публикуем порт 443, чтобы обеспечить безопасное соединение, и используем volume для хранения сертификатов, чтобы они не терялись при перезапуске контейнеров. Кроме того, Traefik должен быть в одной сети с остальными сервисами (network: sky-db-net), чтобы он мог корректно направлять запросы.
Обязательно используем volume, чтобы наши сертификаты сохранялись на сервере, а не в контейнере, и не терялись при перезапуске. Настройка publish: 443 указывает, что только безопасное HTTPS-соединение будет доступно извне. Также важно включить Traefik в ту же Docker-сеть, что и остальные сервисы, чтобы он мог направлять запросы на нужные контейнеры.
Заключение
Kamal стал для меня настоящим открытием — он не только избавляет от рутинной настройки серверов, но и дарит гибкость, необходимую для быстрого и надёжного разворачивания приложений. Благодаря Kamal я могу сосредоточиться на разработке и улучшении своего приложения, а не тратить время на управление инфраструктурой. Быстрые развёртывания, легкие откаты и простота настройки — всё это делает Kamal незаменимым инструментом в моём арсенале.
В этой статье я постарался одновременно и базово ввести в работу Kamal (прежде MRSK), и раскрыть отдельные неочевидные лайфхаки, повышающие эффективность работы с ним. Надеюсь, это поможет вам уверенно шагнуть в мир контейнеризированного деплоя Ruby on Rails приложений. В следующем цикле статей я расскажу уже о более продвинутых конфигурациях Kamal, автоматизации бэкапов и интеграции с другими инструментами для создания полноценного CI/CD пайплайна, а детальнее о Ruby on Rails я делюсь здесь. Оставайтесь на связи, впереди ещё много интересного!