Auto deploy Ruby On Rails on Ubuntu

Capistrano + Nginx + PostgreSQL. Compatible with RoR 5 and 6

Peter Bazov
9 min readJun 27, 2018

Процесс разворачивания Ruby On Rails приложения на удаленном сервере состоит из множества шагов, выполнять которые вручную утомительно. Более того, нужно настроить сервер для безопасного и удобного использования.

Данная инструкция поможет вам автоматизировать процесс, а так же сделать его удобным и настраиваемым под любые нужды.

Внимание! Инструкция периодически обновляется. Список изменений ведется в конце инструкции.

Последовательность действий

Для разворачивания Rails приложения на удаленном сервере вам понадобится:

  • Загрузить обновленный исходный код на сервер;
  • Скомпилировать ресурсы, такие как .js, .css;
  • Запустить миграции базы данных;
  • Перезапустить приложение.

Более того, RoR приложение должно запускаться при включении сервера, загружать переменные окружения при старте, запускать процессы для обработки фоновых операций и много чего другого.

В рамках данной инструкции эти действия будут автоматизированы.

Настройка Ubuntu

Инструкция подразумевает, что у вас есть настроенный сервер под управлением Ubuntu с доступом по SSH через не root пользователя с правами администратора.

Некоторые провайдеры облачных платформ, такие как Yandex Cloud, уже делают это автоматически.

Если ваш сервер не настроен, то можете сделать это по данной инструкции: ссылка.

Для дополнительной безопасности смените порт SSH со стандартного порта 22 на случайный. Инструкция здесь: ссылка.

Настройте файрвол по инструкции: ссылка. Необходимо разрешить порты: SSH, 80 для доступа по http, 443 для доступа по https.

Создайте swap-файл, если на сервере мало оперативной памяти: ссылка.

Установка зависимостей

Как правило, для полноценного функционирования Rails приложений нужно заранее установить некоторые зависимости.

Составьте список зависимостей исходя из функциональности вашего приложения и установите их на сервере.

Ниже список популярных зависимостей:

  • ImageMagick или libvips— утилиты для обработки изображений.
  • Redis — хранилище данных ключ-значение, используемое в Rails для кеширования данных, отправки сообщений через веб-сокеты и организации очередей обработки данных.

Установка и настройка nginx

Для функционирования Ruby On Rails приложения вам потребуется веб-сервер nginx, который принимает HTTP запросы и перенаправляет в RoR приложение.

Установите nginx и произведите первоначальную настройку по инструкции:

Создайте файл ‘/etc/nginx/sites-available/DOMAIN_NAME’ с содержанием, указанным ниже. Замените ‘DOMAIN_NAME’ в пути к файлу и в файле на домен вашего приложения. Если домена нет, то можете использовать IP-адрес. Замените ‘APPLICATION_NAME’ в файле на имя вашего Rails приложения.

В созданном вами файле указаны настройки, как перенаправлять http-запросы из nginx в Rails-приложение, которое развёрнуто на unix-сокете.

Чтобы nginx увидел новые настройки, создайте ссылку на новый файл в папке /etc/nginx/sites-enabled:

sudo ln -s /etc/nginx/sites-available/DOMAIN_NAME /etc/nginx/sites-enabled/DOMAIN_NAME

Проверьте настройки на предмет ошибок:

sudo nginx -t

В случае успеха вы должны увидеть сообщения:

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Перезапустите nginx командой:

sudo systemctl restart nginx

Дополнительные настройки nginx

Если у вас подключен домен, настройте HTTPS соединение через Let’s Encrypt:

Активируйте поддержку HTTP2:

server {
listen 443 ssl http2;

ssl_certificate server.crt;
ssl_certificate_key server.key;
}

Если Web приложение при сборке сжимает ресурсы алгоритмом Brotli, настройте его поддержку для статических ресурсов:

Установка и настройка PostgreSQL

PostgreSQL — свободная объектно-реляционная СУБД. Она будет использоваться для хранения данных на сервере.

В репозиториях ubuntu лежит устаревшая версия, так что нужно добавить сторонний репозиторий.

sudo sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt/ `lsb_release -cs`-pgdg main" >> /etc/apt/sources.list.d/pgdg.list'

Затем добавьте ключ репозитория и установите PostgreSQL:

wget -q https://www.postgresql.org/media/keys/ACCC4CF8.asc -O - | sudo apt-key add -
sudo apt-get update
sudo apt-get install postgresql postgresql-contrib

Создайте пользователя базы данных для RubyOnRails с необходимыми привилегиями. Для этого необходимо зайти в интерактивную оболочку PostgreSQL:

sudo -u postgres psql

И выполнить команды:

create user DATABASE_USER with password 'DATABASE_PASSWORD';
alter role DATABASE_USER createrole createdb replication login;
\q

Команда \q произведет выход из интерактивной оболочки.

Предоставьте доступ к базе данных от лица нового пользователя. Для этого нужно отредактировать файл /etc/postgresql/VERSION/main/pg_hba.conf, добавив в него соответствующую строку:

local   all             DATABASE_USER                md5

Обратите внимание, что порядок строк в файле имеет значение, и строку нужно добавлять сразу же после первой строки: ‘local all postgres peer’.

Чтобы изменения вступили в силу, перезагрузите PostgreSQL:

sudo systemctl restart postgresql

Теперь приложение Ruby On Rails, развернутое на одном сервере с базой данных, сможет получить к ней доступ. Если вам нужен удаленный доступ к базе данных, измените в файле /etc/postgresql/VERSION/main/postgresql.conf значение параметра listen_addresses с ‘localhost’ на ‘*’, а в файл pg_hba.conf допишите еще одну строчку:

host    all             DATABASE_USER    0.0.0.0/0   md5

Символ ‘*’ в listen_address говорит базе данных прослушивать любые адреса для подключения к ней, а значение 0.0.0.0/0 в файле postgresql.conf разрешает проводить проверку пароля для любого IP адреса. Если вы точно знаете IP адреса, с которых будет производиться подключение к БД, лучше указать именно их, а не разрешать доступ всем подряд.

Дополнительно выполните следующую команду, без чего не сможет установиться библиотека базы данных для Rails:

sudo apt-get install libpq-dev

Установка Ruby, Rails, Bundler

Для работы Ruby On Rails приложения под Ubuntu вам понадобятся:

  • Менеджер версий ruby — rvm или rbenv,
  • Установленный язык ruby,
  • Gem Ruby On Rails,
  • Gem Bundler.

Если вы используете Webpacker, то вам так же потребуется установить актуальную версию NodeJS или Yarn.

Установить все зависимости можно по следующей инструкции:

Установка и настройка dotenv-rails

При разработке сервера часто возникает необходимость работать с приватными данными, такими как ключи API, логины и пароли базы данных и т.д. Такие данные нельзя добавлять в репозиторий. Более того, в зависимости от окружения, эти данные могут иметь разные значения.

На помощь приходят переменные окружения, к которым можно обращаться через ассоциативный массив ENV. Для удобства обращения с ними установите гем dotenv-rails.

Допишите в Gemfile следующую строчку и выполните bundle install:

gem 'dotenv-rails'

В корне приложения создайте файл .env со следующим содержимым:

RAILS_ENV=development

Если подключение к локальной базе данных у вас производится по логину и паролю, то добавьте в .env файл следующие строки

DATABASE_USER=db_user
DATABASE_PASSWORD=db_password

Сверьте названия ключей, отвечающих за подключение к базе данных, с файлом /config/database.yml, а так же задайте актуальные значения для локального сервера.

Сервер приложений Puma

Puma — это легковесный сервер приложений с асинхронной обработкой запросов. Он позволяет создавать системы, живущие под высокой нагрузкой, а так же поддерживает функционал постоянных соединений, необходимых для веб-сокетов.

Начиная с версии RoR 5 он включен в Gemfile по умолчанию. Если же его там нет, допишите:

gem 'puma'

И запустите команду:

bundle install

Запуск Puma можно осуществлять командой ‘rails s’, но в таком случае нельзя указывать множество дополнительных параметров запуска. Так что я рекомендую воспользоваться специальной командой:

bundle exec puma

Параметром -C можно указывать путь к файлу настроек. Если данный параметр не задан, то по умолчанию будет использоваться файл config/puma.rb. Если при запуске указать окружение через параметр -e, то будет использоваться файл config/puma/environment_name.rb

Создайте файл config/puma/production.rb со следующим содержанием:

Замените в этом файле ‘APPLICATION_NAME’ на имя вашего Rails приложения.

Команда Dotenv.load загружает все переменные из файла .env в ассоциативный массив ENV. Команда ‘workers’ настраивает количество процессов, запускаемых пумой. Команда ‘threads’ задает минимальное и максимальное количества одновременных потоков в рамках одного процесса. Каждое соединение с сервером будет обрабатываться в отдельном потоке. ‘preload_app!’ загружает всё приложение в оперативную память до разделения на процессы. ‘ActiveRecord::Base.establish_connection’ настраивает подключение к базе данных, что делается для каждого процесса в отдельности.

Development версия сервера разворачивается напрямую на порту 5000, а production версия работает в связке с nginx, с которым общается через unix сокеты.

Установка и настройка гема Foreman

Данный гем управляет запуском процессов приложения. Если в вашем приложении должны выполняться фоновые операции, то для них нужно запустить дополнительный процесс. Гем Foreman позволяет описать в одном файле список процессов приложения, которые будут запускаться одной командой, а так же автоматически при старте сервера.

Как на локальном, так и на удаленном сервере установите гем указанной ниже командой:

gem install foreman

Важно не добавлять гем в Gemfile. Работа с гемом производится из командной строки, а при подключении его в проект могут начаться конфликты с зависимостями других гемов.

В корне приложения создайте файл с названием Procfile и запишите в него следующее:

web: bundle exec puma

Здесь web, это название процесса, а всё, что идет после двоеточия — запускаемая команда. Например, если вы используете гем sidekiq для фоновых операций, включите следующую строку в Procfile:

worker: bundle exec sidekiq -C config/sidekiq.yml

Для запуска webpack-dev-server можно дописать в Procfile:

webpacker: NODE_ENV=development ./bin/webpack-dev-server

Теперь, выполнив команду ‘foreman start’ из корня вашего приложения, вы одновременно запустите все процессы, указанные в файле Procfile.

Для разворачивания приложения на сервере будет создан отдельный Procfile со специфическими для него командами. На его основе гем Foreman создаст systemd скрипты, которые будут автоматически запускать RoR приложение при включении сервера.

Создание облачного Git репозитория

Для разворачивания приложения будет использоваться удаленный Git репозиторий. Со стороны сервера будет происходить скачивание последних изменений из указанной вами ветки.

Создайте облачный репозиторий Git, например на ресурсе Github, и укажите ссылку на него в вашем локальном репозитории:

git remote add origin REPO_URL

Доработайте стандартный файл .gitignore в корне приложения до следующего содержания:

Указанные в .gitignore файлы и папки не будут отслеживаться через git, и, следовательно, не будут отправлены на удаленный репозиторий. К ним относятся файлы с конфиденциальной информацией, временные файлы, специфические для окружения файлы и папки и т.д.

Зафиксируйте изменения и опубликуйте их в репозиторий:

git add -A
git commit -m 'first commit'
git push -u origin master

Установка гема Capistrano

Capistrano — средство, позволяющее одной командой выполнить множество операций на удаленном сервере, связанных с разворачиванием приложения. Необходимо лишь прописать их в скрипте Capistrano.

Установите Capistrano. Для этого добавьте следующие строки в :development блок файла Gemfile:

Оставьте один из двух Gem’ов, capistrano-rvm или capistrano-rbenv, в зависимости от того, какой менеджер версий ruby вы используете.

Установите Gem’ы:

bundle install

Выполните команду:

bundle exec cap install

В созданном файле Capfile добавьте следующие строки, так же выбрав используемый вами менеджер версий ruby:

Теперь, если указать в настройках Capistrano данные для подключения к серверу, адрес репозитория и папку для приложения, то он одной командой:

  • Подключится по SSH
  • Загрузит исходный код приложения из репозитория в папку /deploy_to/releases/current_date/ на сервере, храня 5 последних версий;
  • Выполнит bundle install;
  • Выполнит db:migrate;
  • Выполнит assets:precompile;
  • Создаст symlink deploy_to/current на папку /deploy_to/releases/current_date

Файлы для настроек сервера

При разворачивании приложения нужно загрузить на удаленный сервер некоторые файлы, которые добавлены в .gitignore, но необходимы для работы приложения. Их будем хранить в папке shared на локальной машине, которая тоже добавлена в .gitignore. Исключение составляют файлы, которые обязательно должны находиться в структуре проекта, такие как ключи для Credentials — их не нужно переносить в папку shared. Все эти файлы будут автоматически загружены на сервер через Capistrano.

Так же нужно иметь возможность разворачивать приложение в разных окружениях. Для каждого окружения нужно создавать свою папку внутри папки shared. Названия папок будут соответствовать веткам, из которых производится разворачивание приложения.

Создайте production конфигурацию Procfile в папке shared/production:

Создайте production версию файла .env в папке shared/production:

Замените значения ключей ‘DATABASE_USER’ и ‘DATABASE_PASSWORD’ на актуальные для production сервера.

Настройка Capistrano

Доработайте файл config/deploy/production.rb до следующего содержания:

В корне приложения создайте новый файл .deploy.production.env для переменных окружения, в котором будут храниться данные для подключения к серверу:

Это необходимо для того, чтобы Capistrano мог получить доступ к production серверу по SSH.

Если доступ к SSH осуществляется без пароля по SSH-ключу, то удалите упоминание пароля из файлов config/deploy/production.rb и .deploy.production.env.

Доработайте файл config/deploy.rb. Это скрипт, который будет выполняться при разворачивании приложения. Скопируйте в него контент файла, который приведен ниже.

Доработайте контент файла для своего приложения:

  • Укажите имя приложения в инструкции set :application.
  • Укажите ссылку на репозиторий с вашим приложением в инструкции set :repo_url.
  • Укажите имя вашего менеджера версий ruby в переменной ruby_version_manager.

Если вы используете rbenv, то создайте в корне приложения файл .ruby-version с вашим номером версии ruby:

3.0.0

Если вы используете rvm, то после инструкции lock добавьте следующие строчки в файл config/deploy.rb, заменив версию ruby на актуальную для вашего приложения:

set :rvm_type, :user
set :rvm_ruby_version, '3.0.0'
set :rvm_ruby_string, fetch(:rvm_ruby_version)

Если вы не пользуетесь Credentials, нужно удалить упоминание master.key из файла.

Если вы не используете Active Storage, то удалите упоминание storage из файла.

Данный скрипт, помимо стандартных умений Capistrano:

  • Создает начальную структуру папок приложения.
  • Загружает на сервер файлы, которые находятся в .gitignore, но необходимы для работы приложения.
  • Создает systemd скрипт через gem foreman для автоматического запуска приложения.

Как производить Deploy

Перед первым разворачиванием приложения нужно создать базу данных на сервере. Подключитесь к серверу по SSH, войдите в оболочку postgresql под пользователем для Rails-приложения и выполните скрипт:

CREATE DATABASE "database_name" ENCODING = 'unicode';

Для каждого разворачивания опубликуйте код в ветку master и выполните команду:

cap production deploy

Во время работы Capistrano может запросить пароль от учетной записи, под именем которой вы подключаетесь к серверу, так как некоторые команды выполняются от имени sudo.

Deploy без пароля

Для выполнения некоторых sudo-команд от имени Capistrano необходимо ввести пароль пользователя ОС. Чтобы исключить ввод пароля, есть 2 способа:

  1. (Предпочтительно) Настроить аутентификацию пользователей по SSH через модуль PAM. Это разрешает пользователям выполнять sudo-команды без ввода пароля, если они аутентифицировались по SSH без пароля по ключу.
  2. Разрешить пользователю на сервере выполнять некоторые команды от имени sudo без запроса пароля.

Чтобы разрешить выполнять команды без ввода пароля, откройте для редактирования файл /etc/sudoers и допишите следующие строчки, заменив ‘APPLICATION_NAME’ и ‘DEPLOY_USER’ на актуальные значения:

Внимание! Разрешайте выполнять без пароля только минимально необходимый список команд, указывая весь список аргументов, иначе вы можете создать проблемы, связанные с безопасностью!

Спасибо за внимание!

UDP 01 (14.04.2021)

Файлы config/deploy/<ENVIRONMENT>.rb и config/deploy.rb добавлены в репозиторий.

Данные для подключения к серверу вынесены из основного .env файла в специальный файл .deploy.<ENVIRONMENT>.env

UPD 02 (28.04.2021)

Инструкция доработана для rbenv.

UPD 03 (23.11.2022)

Обновлена версия Capistrano.

Упрощён deploy при использовании rbenv — вместо добавления переменной PATH в .env, создаётся символическая ссылка для выполнения команды bundle.

Убрана секция setup из deploy-скрипта. Создание базы данных теперь нужно осуществить вручную перед первым разворачиванием приложения.

Настройка nginx производится в отдельных файлах для каждого домена вместо редактирования единого файла default.

--

--