Быстрое развертывание Rails 5 с PostgreSQL и Nginx на Ubuntu 16.04
С использованием Foreman и systemd
В теории, этот гайд будет работать и для Ubuntu 14.04, но лучше не рисковать и обзавестись сервером с 16.04.
Внимание! Гайд рассчитан на то, что все действия вы будете производить на чистом сервере
Что мы сделаем:
- Создадим отдельного нерутового пользователя
- Установим и настроим postgres
- Установим Ruby и зависимости
- Настроим Foreman и переменные окружения
- Настроим Nginx
- С помощью Foreman настроим systemd-сервисы
Шаг нулевой: Доставляем приложение на сервер
Я для этого использую так называемые “Deploy keys” в Gitlab/Github/Bitbucket. Они позволяют сгенерировать на сервере ssh-ключ и использовать его для клонирования проекта.
Здесь один важный момент: НИКОГДА не клонируйте проект в домашнюю папку рутового пользователя (/root). Я все свои проекты держу в /var/www (главное убедитесь, что он не светит именно этой папкой в интернеты через apache/nginx)
Шаг первый: Отдельный пользователь
Для начала, заведем специального пользователя, от чьего имени будет запускаться наше приложение:
sudo useradd -r railsuser
Флаг -r означает “системный аккаунт”: у этого пользователя не будет домашней папки и входа по паролю. Он нужен для того, чтобы не запускать приложение от root.
Имя пользователя может быть любое, но я выбрал railsuser. В дальнейшем, везде в гайде будет фигурировать именно оно.
PostgreSQL
Теперь установим и настроим постгрес:
Для начала, коль у нас чистый сервер, обновим пакеты:
sudo apt-get update && sudo apt-get upgrade
Затем, поставим постгрес и библиотеки, необходимые для рельс:
sudo apt-get install postgresql postgresql-contrib libpq-dev
Теперь, создадим пользователя в postgresql. Имя ему дадим такое же, как и у пользователя для запуска приложения:
sudo -u postgres createuser -s railsuser --createdb
Это создаст пользователя (роль в терминах postgres) railsuser с правами на создание баз данных.
Теперь разрешим ему беспарольный вход из под localhost:
Откроем файл /etc/postgresql/9.5/main/pg_hba.conf
в каком-нибудь консольном редакторе (например, nano или vim), и в разделе “put your actual configuration here” напишем следующее:
Перезапустим постгрес:
sudo systemctl restart postgresql
Проверим:
psql -U railsuser -h localhost
Если мы видим сообщение о том, что БД не существует, значит все ок. Если видим запрос пароля — значит не всё ок.
Ruby и другие нужные штуки
Если вы так же как я разворачиваете все приложения на отдельные сервера, то ruby лучше поставить через apt.
Для этого воспользуемся репозиториями Brightbox:
Здесь можно почитать о них подробнее: https://www.brightbox.com/docs/ruby/ubuntu/
Установим штуку, упрощающую подключение ppa:
sudo apt-get install software-properties-common
Подключим ppa и обновим репозитории:
sudo apt-add-repository ppa:brightbox/ruby-ng && sudo apt-get update
Теперь установим Ruby. Например, мне нужна Ruby 2.4 (второй пакет нужен для сборки зависимостей)
sudo apt-get install ruby2.4 ruby2.4-dev
Теперь установим Node.js (он нужен для сборки ассетов). Воспользуемся репо nodesource (подробнее: https://github.com/nodesource/distributions)
Ставить будем последнюю из существущих на данный момент LTS: 8.9
curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -
sudo apt-get install -y nodejs
Теперь поставим bundler:
sudo gem install bundler
Procfile, права на папку
Теперь пора внести некоторые изменения в приложение.
Для работы с переменными откружения мы будем использовать файл .env
Для начала, внесите .env в .gitignore
Затем, напишем Procfile:
Подробно о формате Procfile можно почитать на Heroku: https://devcenter.heroku.com/articles/procfile
Для Rails он будет выглядеть примерно так:
web: bundle exec rails server -p $PORT
Обратите внимание на $PORT. Оно должно выглядеть именно так, туда в дальнейшем foreman подставит номер порта.
Запушим Procfile и изменения в .gitignore в git и склонируем их на сервер.
Затем, выдадим права на папку с нашим приложением нашему пользователю:
chown -R railsuser:railsuser deploy/
Где deploy/ — это имя папки
Установка зависимостей
Теперь нам нужно установить некоторые пакеты для запуска rails:
sudo apt-get install git-core curl zlib1g-dev build-essential libssl-dev libreadline-dev libyaml-dev libsqlite3-dev sqlite3 libxml2-dev libxslt1-dev libcurl4-openssl-dev libffi-dev
Теперь установим зависимости нашего приложения:
bundle install --without development test
Настройка .env
Создадим файл .env и напишем туда нужные нам вещи:
RAILS_ENV=production
DATABASE_URL=postgres://railsuser@localhost/railsuser
RAILS_LOG_TO_STDOUT=true
SECRET_KEY_BASE=yoursecretkeyhere
SECRET_KEY_BASE мы можем сгенерировать, запустив эту команду в папке приложения:
bundle exec rails secret
Учтите, что ваше приложение должно быть настроено для работы с DATABASE_URL. Если вы пользуетесь новой версией Rails, скорее всего, оно будет работать “из коробки”.
Foreman
Установим foreman:
sudo gem install foreman
После установки, временно подредактируем наш Procfile, добавив к запуску сервера флаг -b 0.0.0.0
. Он нужен, чтобы мы могли протестировать наше приложение без nginx. Позже мы его уберем
Пробуем запустить сервер:
sudo -u railsuser foreman start
И… запустился!
Заходим на нашайпи:5000 и смотрим в логах, что ему не хватает
А не хватает ему БД и статики
БД, миграции, ассеты
Чтобы при запуске команды подхватывали наши переменные окружения, запускать их нужно через foreman run
Для начала, создадим БД:
foreman run rails db:create
Затем, запустим миграции:
foreman run rails db:migrate
Seeds:
foreman run rails db:seed
Прекомпилируем ассеты:
foreman run rails assets:precompile
Заходим на наше приложение и… все равно не видим статики. Почему?
Потому что rails в продакшн-режиме по-умолчанию настроен на то, что статику за него будет отдавать кто-нибудь другой. У нас этим “кто-нибудь другой” будет Nginx.
Nginx
Для начала, приведем Procfile к исходному виду, убрав -b 0.0.0.0
, так как нам больше не надо, чтобы приложение слушало не-локальные порты.
Теперь установим Nginx:
sudo apt-get install nginx
Затем, в папке /etc/nginx/sites-available создадим файл с именем, равным имени нашего домена (не забудьте настроить A-запись на ваш сервер!). У меня это demotest.andv.xyz
Содержимое у него должно быть примерно такое:
upstream puma {
server 127.0.0.1:5000;
}server {
listen 80;
listen [::]:80; server_name demotest.andv.xyz; root /var/www/apps/deploy/public; try_files $uri/index.html $uri @rails; location @rails {
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $http_host; proxy_redirect off; proxy_pass http://puma$request_uri;
} location ^~ /assets/ {
gzip_static on;
expires max;
add_header Cache-Control public;
} location ^~ /system/ {
gzip_static on;
expires max;
add_header Cache-Control public;
} error_page 500 502 503 504 /500.html;
error_page 404 /404.html;
error_page 422 /422.html;
client_max_body_size 10M;
keepalive_timeout 10;
}
Где:
/var/www/apps/deploy/public;
— путь к папке public вашего приложения
demotest.andv.xyz
— ваш домен
UPD 11.07.18: Добавлен X-Forwarded-Proto, чтобы не возникало вечного редиректа при включенном force_ssl
Можно запустить сервер и посмотреть, всё ли ок
Systemd
Остался последний штрих: сделать так, чтобы нам не приходилось запускать сервер в терминале, а его запуском руководил systemd.
Заведём в нашем репо папку scripts, в которой напишем один полезный скрипт по имени update-services-and-restart.sh
:
if [ ! -f Procfile ]; then
echo "Procfile not found! Run this script from the project root"
exit 1
fiapp_name="appname"
app_port="5000"
app_user="railsuser"foreman export systemd --app $app_name -p$app_port --user $app_user /etc/systemd/system
systemctl daemon-reload
systemctl restart $app_name.target
В трех переменных нам нужно указать:
app_name
— имя приложения
app_port
— порт, кратный 1000 (не меньше 2000)
app_user
— имя пользователя, от которого будет запущено приложение
Если вы ничего не меняли порт в конфиге nginx и имя пользователя, то вам нужно указать только app_name
Выдаем права на запуск:
chmod +x scripts/update-services-and-restart.sh
Коммитим, пуляем на сервере, УБИРАЕМ -b 0.0.0.0 ИЗ Procfile, запускаем (при выключенном сервере rails).
При каждом изменении Procfile вам нужно будет заново запускать этот скрипт, это важно!
Заходим на домен и видим приложение.
А если не видим — пишем journalctl -f --lines 500
и смотрим, что сломалось.
Отдельно про foreman run
В конце хочу напомнить про то, что все команды rails, которым нужно знать текущее окружение (например, rails console или rails db:migrate) теперь нужно запускать через foreman run.