Представьте: Вы — веб-разработчик, который только недавно освоил Ruby on Rails. И тут Ваш первый проект подходит к стадии, когда его нужно выложить в интернет.
Вы, конечно, можете залить его на Heroku, но тамошние цены немного кусаются. Остается только купить
«Что может быть проще? Найду какой-нибудь гайд, да следаю всё по нему» — подумаете Вы. Вот только гайдов, которые не просто выкладывают команды, но и объясняющие, что эти команды делают, — единицы, да и те используют уже устаревшую вторую версию Capistrano.
Поэтому я решил написать свой гайд, в котором постараюсь подробно рассмотреть:
- Первичную настройку сервера
- Установку и настройку nginx (с модулем PageSpeed), postgresql, redis
- Установку rvm, rails
- Настройку гема foreman для управления процессами Вашего приложения
- Настройку сервера Unicorn
- Настройку гема Capistrano (v3.1) для автоматизации деплоя
Я надеюсь, что этот гайд будет полезен не только новичкам, но и разработчикам со стажем.
Первичная настройка сервера
Вы купили свой первый
Первым делом сменим пароль для пользователя root коммандой
passwd
Создадим нового пользователя:
adduser deployer
Разрешим ему пользоваться коммандой sudo:
visudo
и дописываем:
deployer ALL=(ALL:ALL) ALL
Изменим настройки ssh сервера (запретим логин под root, доступ по доменному имени и разрешим логин только под нашим новым пользователем). Добавляем в файл '/etc/ssh/sshd_config':
PermitRootLogin no
UseDNS no
AllowUsers deployer
Перезапустим ssh сервер коммандой:
reload ssh
Чтобы не вводить каждый раз пароль при подключении по ssh, нам надо скопировать ssh ключ с Вашей машины на сервер. Самый простой способ сделать это — выполнить на локальной машине
ssh-copy-id deployer@123.123.123.123
(На маке требуется установка ssh-copy-id, сделать можно через brew, на Windows не знаю автоматизированного средства для копирования ключей, но в интернете есть много интересного на эту тему).
Так же, пока уж мы под рутом, можно создать SWAP файл, если у Вас мало RAM. Делается это так:
dd if=/dev/zero of=/swapfile bs=1024 count=512k
mkswap /swapfile
swapon /swapfile
Далее в файле '/etc/fstab' добавляем строчку:
/swapfile none swap sw 0 0
И далее выполняем:
echo 0 > /proc/sys/vm/swappiness
sudo chown root:root /swapfile
sudo chmod 0600 /swapfile
Можно перезагрузиться и проверить наличие SWAP файла коммандой
swapon -s
Установка и настройка nginx
На этот раз логинемся под нашим новым пользователем коммандой
ssh deployer@123.123.123.123
(на локальном компьютере).
Лично я использую модуль PageSpeed, поэтому nginx собираю сам. Но сначала нам надо обновить репозитории, обновить систему, и скачать необходимые для успешной сборки пакеты:
sudo apt-get update
sudo apt-get dist-upgrade
sudo apt-get install build-essential zlib1g-dev libpcre3 libpcre3-dev unzip
Теперь собираем:
wget https://github.com/pagespeed/ngx_pagespeed/archive/v1.7.30.1-beta.zip
unzip v1.7.30.1-beta.zip
cd ngx_pagespeed-1.7.30.1-beta
wget https://dl.google.com/dl/page-speed/psol/1.7.30.1.tar.gz
tar -xzvf 1.7.30.1.tar.gz
wget http://nginx.org/download/nginx-1.4.4.tar.gz
tar -xzvf nginx-1.4.4.tar.gz
cd nginx-1.4.4
./configure --add-module=$HOME/ngx_pagespeed-1.7.30.1-beta
make
sudo make install
Для управления nginx напишем upstart скрипт. Создаём файл '/etc/init/nginx.conf' со следующим содержимым:
description "nginx http daemon"
author "George Shammas <georgyo@gmail.com>"
start on (filesystem and net-device-up IFACE=lo)
stop on runlevel [!2345]
env DAEMON=/usr/local/nginx/sbin/nginx
env PID=/var/run/nginx.pid
expect fork
respawn
respawn limit 10 5
#oom never
pre-start script
$DAEMON -t
if [ $? -ne 0 ]
then exit $?
fi
end script
exec $DAEMON
Теперь вы можете выполнять
sudo start/stop/restart/status nginx
Наш nginx.conf лежит по адресу '/usr/local/nginx/conf/nginx.conf', но пока мы его трогать не будем. Мы его зальем автоматически при первом деплое приложения.
Для наших веб приложений мы создадим нового пользователя и новую группу, добавим себя в эту группу, и создадим папку:
sudo useradd -s /sbin/nologin -r nginx
sudo groupadd web
sudo usermod -a -G web nginx
sudo usermod -a -G web deployer
sudo mkdir /var/www
sudo chgrp -R web /var/www
sudo chmod -R 775 /var/www
Для того, чтобы мы смогли писать в папку придется разлогиниться и зайти снова под нашим пользователем.
Установка и настройка PostgreSQL
В репозиториях ubuntu лежит устаревшая версия, так что мы добавим сторонний репо. В файл '/etc/apt/sources.list.d/pgdg.list' добавим:
deb http://apt.postgresql.org/pub/repos/apt/ precise-pgdg main
Затем добавляем ключ репозитория и устанавливаем PostgreSQL:
wget --quiet -O - http://apt.postgresql.org/pub/repos/apt/ACCC4CF8.asc | sudo apt-key add -
sudo apt-get update
sudo apt-get install postgresql-9.3 postgresql-server-dev-9.3
И создаем нового пользователя:
sudo -u postgres psql
create user deployer with password 'ваш пароль';
alter role deployer superuser createrole createdb replication;
q
Чтобы иметь доступ с локального компьютера в файле '/etc/postgresql/9.3/main/postgresql.conf' изменим параметр listen_addresses = 'localhost'
на listen_addresses = '*'
и добавим в файл '/etc/postgresql/9.3/main/pg_hba.conf' строчку
host all deployer ваш.внешний.ip.адрес 255.255.255.0 md5
Перезагружаем postgresql коммандой
sudo service postgresql restart
Установка и настройка Redis
Если Вы используте gem resque, то Вам нужно установить Redis. Ибо в репозитории устаревшая версия, я его собираю из исходников, к тому же это занимает немного времени:
sudo apt-get install tcl8.5
wget http://download.redis.io/redis-stable.tar.gz
tar xvzf redis-stable.tar.gz
cd redis-stable
make
make test
sudo cp src/redis-server /usr/local/bin
sudo cp src/redis-cli /usr/local/bin
Redis по умолчанию не защищен паролем и открыт всем, поэтому поставим пароль: в файле 'redis.conf' добавляем параметр requirepass
с нашим паролем. Redis легко брутфорсится, поэтому я делаю пароль не менее 100 символов. Также, чтобы потом не вылетало ошибок, меняем параметр dir
на /var/www/other
, предварительно создав такую папку (mkdir /var/www/other
).
Копируем наш конфиг командой
sudo cp redis.conf /etc/redis/redis.conf
Создаем upstart скрипт по адресу '/etc/init/redis-server.conf' со следующим содержанием:
#!upstart
description "Redis Server"
env USER=deployer
start on runlevel [2345]
stop on runlevel [016]
respawn
exec start-stop-daemon --start --make-pidfile --pidfile /var/run/redis-server.pid --chuid $USER --exec /usr/local/bin/redis-server /etc/redis/redis.conf >> /var/www/log/redis.log 2>&1
Теперь мы можем управлять Redisом коммандами
sudo start/stop/restart/status redis-server
, предварительно создав папку для логов (mkdir /var/www/log
).
Установка RVM, Ruby, Rails, Bundler
Тут совсем ничего сложного:
sudo apt-get install git curl python-software-properties
sudo add-apt-repository ppa:chris-lea/node.js
sudo apt-get update
sudo apt-get install nodejs
curl -L get.rvm.io | bash -s stable
source ~/.rvm/scripts/rvm
rvm requirements
rvm install 2.0.0
rvm use 2.0.0 --default
gem install rails --no-ri --no-rdoc
gem install bundler
Создаем репозиторий на GitHub/BitBucket
Мы будем использовать git на удаленном сервере для деплоя нашего приложения. Можно и настроить git сервер на нашем
/config/database.yml # доступ к базе данным
/Procfile # про него я еще расскажу
/config/deploy/ # файлы Capistrano
/shared/ # файлы, которых нет в репозитории, но они будут скопированы на сервер при первом деплое приложения
/public/system/ # если установлен Paperclip
Теперь можно сделать первый коммит и запушить проект в git.
git init
git remote add origin #АДРЕС РЕПО
git add -A
git commit -m 'first commit'
git push -u origin --all
Также нам надо добавить ключ нашего сервера в админку Github/BitBucket, это обязательное условие, т.к. из репозитория изменения будут загружаться на сервер. Как это сделать можно узнать в Helpе сервиса.
gem foreman
foreman — гем для управления процессами приложения. На локальной машине он позволяет запускать сразу все процессы, указанные в Procfile, одной коммандой
foreman start
и показывает их вывод.
На сервере командой
foreman export upstart
он создает upstart скрипт для легкого управления приложением с помощью команд start/stop/restart. Но об этом потом. Пока просто установим его, создадим Procfile в корне приложения, и наполним его для локального использования. У меня он выглядит так.
web: rails s
job1: bundle exec rake resque:work PIDFILE=./tmp/pids/resque2.pid QUEUES=send_email
job2: bundle exec rake resque:work PIDFILE=./tmp/pids/resque2.pid QUEUES=send_email
Production конфигурацию мы напишем потом, когда речь зайдет о Capistrano.
Устанавливаем Unicorn
Unicorn — продвинутый HTTP сервер. Установим его, добавив
group :production do
gem 'unicorn'
end
в Gemfile. (Не забудьте про bundle install
)
В папке '/config/' создаем файл unicon.rb c примерно таким содержимым:
worker_processes 2
working_directory "/var/www/apps/ИМЯ_ПРИЛОЖЕНИЯ/current" # available in 0.94.0+
# listen on both a Unix domain socket and a TCP port,
# we use a shorter backlog for quicker failover when busy
listen "/var/www/apps/ИМЯ_ПРИЛОЖЕНИЯ/socket/.unicorn.sock", :backlog => 64
listen 8080, :tcp_nopush => true
# nuke workers after 30 seconds instead of 60 seconds (the default)
timeout 30
# feel free to point this anywhere accessible on the filesystem
pid "/var/www/apps/ИМЯ_ПРИЛОЖЕНИЯ/run/unicorn.pid"
# By default, the Unicorn logger will write to stderr.
# Additionally, ome applications/frameworks log to stderr or stdout,
# so prevent them from going to /dev/null when daemonized here:
stderr_path "/var/www/apps/ИМЯ_ПРИЛОЖЕНИЯ/log/unicorn.stderr.log"
stdout_path "/var/www/apps/ИМЯ_ПРИЛОЖЕНИЯ/log/unicorn.stdout.log"
# combine Ruby 2.0.0dev or REE with "preload_app true" for memory savings
# http://rubyenterpriseedition.com/faq.html#adapt_apps_for_cow
preload_app true
GC.respond_to?(:copy_on_write_friendly=) and
GC.copy_on_write_friendly = true
# Enable this flag to have unicorn test client connections by writing the
# beginning of the HTTP headers before calling the application. This
# prevents calling the application for connections that have disconnected
# while queued. This is only guaranteed to detect clients on the same
# host unicorn runs on, and unlikely to detect disconnects even on a
# fast LAN.
check_client_connection false
before_fork do |server, worker|
# the following is highly recomended for Rails + "preload_app true"
# as there's no need for the master process to hold a connection
defined?(ActiveRecord::Base) and
ActiveRecord::Base.connection.disconnect!
# The following is only recommended for memory/DB-constrained
# installations. It is not needed if your system can house
# twice as many worker_processes as you have configured.
#
# # This allows a new master process to incrementally
# # phase out the old master process with SIGTTOU to avoid a
# # thundering herd (especially in the "preload_app false" case)
# # when doing a transparent upgrade. The last worker spawned
# # will then kill off the old master process with a SIGQUIT.
old_pid = "#{server.config[:pid]}.oldbin"
if old_pid != server.pid
begin
sig = (worker.nr + 1) >= server.worker_processes ? :QUIT : :TTOU
Process.kill(sig, File.read(old_pid).to_i)
rescue Errno::ENOENT, Errno::ESRCH
end
end
#
# Throttle the master from forking too quickly by sleeping. Due
# to the implementation of standard Unix signal handlers, this
# helps (but does not completely) prevent identical, repeated signals
# from being lost when the receiving process is busy.
# sleep 1
end
after_fork do |server, worker|
# per-process listener ports for debugging/admin/migrations
# addr = "127.0.0.1:#{9293 + worker.nr}"
# server.listen(addr, :tries => -1, :delay => 5, :tcp_nopush => true)
# the following is *required* for Rails + "preload_app true",
defined?(ActiveRecord::Base) and
ActiveRecord::Base.establish_connection
# if preload_app is true, then you may also want to check and
# restart any other shared sockets/descriptors such as Memcached,
# and Redis. TokyoCabinet file handles are safe to reuse
# between any number of forked children (assuming your kernel
# correctly implements pread()/pwrite() system calls)
end
Заменяем ИМЯ_ПРИЛОЖЕНИЯ на Ваше имя приложения, которое Вы потом зададите в настройках Capistrano.
Capistrano
Capistrano — весьма удобное средство для деплоя приложения, пусть и сначала таковым не кажется. Установим его с нужными дополнениями, добавив в Gemfile:
group :development do
gem 'capistrano'
gem 'capistrano-rails'
gem 'capistrano-bundler'
gem 'capistrano-rvm'
end
Выполняем bundle exec cap install
и добавляем в Capfile:
require 'capistrano/deploy'
require 'capistrano/rvm'
require 'capistrano/bundler'
require 'capistrano/rails'
Уже сейчас, просто указав адрес сервера, репозитория и рабочую папку, Capistrano:
- Загрузит ваше приложение из репозитория на сервер в папку
рабочая_папка/имя_приложения/releases/дата_релиза/
, не удаляя старую версию (по умолчанию он хранит 5 последних версий приложений). - Выполнит bundle install.
- Выполнит db:migrate.
- Выполнит assets:precompile.
- Создаст symlink из папки приложения в папку
рабочая_папка/имя_приложения/current
Но нам этого мало. Нам нужно реализовать следующее:
- При первом деплое выполнить настройку nginx, unicorn, загрузить некоторые файлы, которые не будут меняться, создать upstart скрипт (с помощью foreman).
- Перед каждым деплоем автоматически выполнять git add, git commit, git push (сообщение к коммиту спрашивать у пользователя). После каждого деплоя создавать симлинки и перезапускать Unicorn.
Нужные только в первый раз файлы будем хранить в папке shared (в папке проекта на локальной машине), не зря мы ее добавили в .gitignore. Сначала создадим там nginx.conf примерно с таким содержимым:
user nginx web;
pid /var/run/nginx.pid;
error_log /var/www/log/nginx.error.log;
events {
worker_connections 1024; # increase if you have lots of clients
accept_mutex off; # "on" if nginx worker_processes > 1
use epoll; # enable for Linux 2.6+
# use kqueue; # enable for FreeBSD, OSX
}
http {
# nginx will find this file in the config directory set at nginx build time
include mime.types;
types_hash_max_size 2048;
server_names_hash_bucket_size 64;
# fallback in case we can't determine a type
default_type application/octet-stream;
# click tracking!
access_log /var/www/log/nginx.access.log combined;
# you generally want to serve static files with nginx since neither
# Unicorn nor Rainbows! is optimized for it at the moment
sendfile on;
tcp_nopush on; # off may be better for *some* Comet/long-poll stuff
tcp_nodelay off; # on may be better for some Comet/long-poll stuff
# we haven't checked to see if Rack::Deflate on the app server is
# faster or not than doing compression via nginx. It's easier
# to configure it all in one place here for static files and also
# to disable gzip for clients who don't get gzip/deflate right.
# There are other gzip settings that may be needed used to deal with
# bad clients out there, see http://wiki.nginx.org/NginxHttpGzipModule
gzip on;
gzip_http_version 1.0;
gzip_proxied any;
gzip_min_length 0;
gzip_vary on;
gzip_disable "MSIE [1-6].";
gzip_proxied expired no-cache no-store private auth;
gzip_comp_level 9;
gzip_types text/plain text/xml text/css
text/comma-separated-values
text/javascript application/x-javascript
application/atom+xml;
# this can be any application server, not just Unicorn/Rainbows!
upstream app_server {
server unix:/var/www/apps/ИМЯ_ПРИЛОЖЕНИЯ/socket/.unicorn.sock fail_timeout=0;
}
server {
# PageSpeed
pagespeed on;
pagespeed FileCachePath /var/ngx_pagespeed_cache;
location ~ ".pagespeed.([a-z].)?[a-z]{2}.[^.]{10}.[^.]+" {
add_header "" "";
}
location ~ "^/ngx_pagespeed_static/" { }
location ~ "^/ngx_pagespeed_beacon$" { }
location /ngx_pagespeed_statistics {
allow 127.0.0.1; allow 5.228.169.73; deny all;
}
location /ngx_pagespeed_global_statistics {
allow 127.0.0.1; allow 5.228.169.73; deny all;
}
pagespeed MessageBufferSize 100000;
location /ngx_pagespeed_message {
allow 127.0.0.1; allow 5.228.169.73; deny all;
}
location /pagespeed_console {
allow 127.0.0.1; allow 5.228.169.73; deny all;
}
charset utf-8;
# enable one of the following if you're on Linux or FreeBSD
listen 80 default deferred; # for Linux
# listen 80 default accept_filter=httpready; # for FreeBSD
# If you have IPv6, you'll likely want to have two separate listeners.
# One on IPv4 only (the default), and another on IPv6 only instead
# of a single dual-stack listener. A dual-stack listener will make
# for ugly IPv4 addresses in $remote_addr (e.g ":ffff:10.0.0.1"
# instead of just "10.0.0.1") and potentially trigger bugs in
# some software.
# listen [::]:80 ipv6only=on; # deferred or accept_filter recommended
client_max_body_size 4G;
server_name _;
# ~2 seconds is often enough for most folks to parse HTML/CSS and
# retrieve needed images/icons/frames, connections are cheap in
# nginx so increasing this is generally safe...
keepalive_timeout 5;
# path for static files
root /var/www/apps/ИМЯ_ПРИЛОЖЕНИЯ/current/public;
# Prefer to serve static files directly from nginx to avoid unnecessary
# data copies from the application server.
#
# try_files directive appeared in in nginx 0.7.27 and has stabilized
# over time. Older versions of nginx (e.g. 0.6.x) requires
# "if (!-f $request_filename)" which was less efficient:
# http://bogomips.org/unicorn.git/tree/examples/nginx.conf?id=v3.3.1#n127
try_files $uri/index.html $uri.html $uri @app;
location ~ ^/(assets)/ {
root /var/www/apps/ИМЯ_ПРИЛОЖЕНИЯ/current/public;
expires max;
add_header Cache-Control public;
}
location @app {
# an HTTP header important enough to have its own Wikipedia entry:
# http://en.wikipedia.org/wiki/X-Forwarded-For
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# enable this if you forward HTTPS traffic to unicorn,
# this helps Rack set the proper URL scheme for doing redirects:
# proxy_set_header X-Forwarded-Proto $scheme;
# pass the Host: header from the client right along so redirects
# can be set properly within the Rack application
proxy_set_header Host $http_host;
# we don't want nginx trying to do something clever with
# redirects, we set the Host: header above already.
proxy_redirect off;
# set "proxy_buffering off" *only* for Rainbows! when doing
# Comet/long-poll/streaming. It's also safe to set if you're using
# only serving fast clients with Unicorn + nginx, but not slow
# clients. You normally want nginx to buffer responses to slow
# clients, even with Rails 3.1 streaming because otherwise a slow
# client can become a bottleneck of Unicorn.
#
# The Rack application may also set "X-Accel-Buffering (yes|no)"
# in the response headers do disable/enable buffering on a
# per-response basis.
# proxy_buffering off;
proxy_pass http://app_server;
}
# Rails error pages
error_page 500 502 503 504 /500.html;
location = /500.html {
root /var/www/apps/ИМЯ_ПРИЛОЖЕНИЯ/current/public;
}
}
}
Это мой конфиг для nginx, там необходимо заменить 'ИМЯ_ПРИЛОЖЕНИЯ' на Ваше имя приложения, указанное в первой строчке config/deploy.rb (set :application, 'ИМЯ_ПРИЛОЖЕНИЯ').
Теперь создаем там же (в /shared/) файл Procfile с таким содержанием:
web: bundle exec unicorn_rails -c /var/www/apps/ИМЯ_ПРИЛОЖЕНИЯ/current/config/unicorn.rb -E production
job1: bundle exec rake resque:work RAILS_ENV=production PIDFILE=/var/www/apps/ИМЯ_ПРИЛОЖЕНИЯ/run/resque1.pid QUEUES=*
job2: bundle exec rake resque:work RAILS_ENV=production PIDFILE=/var/www/apps/ИМЯ_ПРИЛОЖЕНИЯ/run/resque2.pid QUEUES=*
Это конфиг для приложения с двумя resque workerами. Если Вы не используете Resque, то просто оставьте только первую строчку.
Там же создаём database.yml с настройками базы данных и application.yml, если пользуетесь гемом Figaro.
Нвш Capistrano скрипт будет выполнять некоторые команды от имени суперпользователя на сервере. Чтобы разрешить ему делать это, выполняем команду
sudo visudo
на сервере и добавляем строчку:
deployer ALL=NOPASSWD: /usr/sbin/service, /bin/ln, /bin/rm, /bin/mv, /sbin/start, /sbin/stop, /sbin/restart, /sbin/status
Осталось только настроить Capistrano. В файле 'config/deploy/production' делаем изменения:
server 'IP сервера', user: 'deployer', roles: %w{web app db}
В файл 'config/deploy.rb' добавляем сверху:
set :repo_url, 'Адрес репозитория'
set :application, 'ИМЯ_ПРИЛОЖЕНИЯ'
application = 'ИМЯ_ПРИЛОЖЕНИЯ'
set :rvm_type, :user
set :rvm_ruby_version, '2.0.0-p353'
set :deploy_to, '/var/www/apps/ИМЯ_ПРИЛОЖЕНИЯ'
namespace :foreman do
desc 'Start server'
task :start do
on roles(:all) do
sudo "start #{application}"
end
end
desc 'Stop server'
task :stop do
on roles(:all) do
sudo "stop #{application}"
end
end
desc 'Restart server'
task :restart do
on roles(:all) do
sudo "restart #{application}"
end
end
desc 'Server status'
task :status do
on roles(:all) do
execute "initctl list | grep #{application}"
end
end
end
namespace :git do
desc 'Deploy'
task :deploy do
ask(:message, "Commit message?")
run_locally do
execute "git add -A"
execute "git commit -m '#{fetch(:message)}'"
execute "git push"
end
end
end
Что всё это значит? Первые строчки — конфиг. Затем мы описываем задачи. Есть задача foreman, у которой есть 4 действия: start, stop, restart status. При выполнении 'cap production foreman:start' на локальной машине на сервере будет выполнено 'sudo start ИМЯ_ПРИЛОЖЕНИЯ', но пока что это нам ничего не даст, ибо foreman еще не создал upstart скрипты. Идем дальше: есть задача git, у которой есть действие deploy. При выполнении 'cap production git:deploy' пользователя спросят комментарий к коммиту и будет выполнено:
git add -A
git commit -m 'КОММЕНТАРИЙ'
git push
Совсем не сложно, правда? Но этими командами мы не будем пользоваться сами, они будут выполняться при выполнении других скриптов. Теперь внутри 'namespace :deploy do' добавляем
desc 'Setup'
task :setup do
on roles(:all) do
execute "mkdir #{shared_path}/config/"
execute "mkdir /var/www/apps/#{application}/run/"
execute "mkdir /var/www/apps/#{application}/log/"
execute "mkdir /var/www/apps/#{application}/socket/"
execute "mkdir #{shared_path}/system"
sudo "ln -s /var/log/upstart /var/www/log/upstart"
upload!('shared/database.yml', "#{shared_path}/config/database.yml")
upload!('shared/Procfile', "#{shared_path}/Procfile")
upload!('shared/nginx.conf', "#{shared_path}/nginx.conf")
sudo 'stop nginx'
sudo "rm -f /usr/local/nginx/conf/nginx.conf"
sudo "ln -s #{shared_path}/nginx.conf /usr/local/nginx/conf/nginx.conf"
sudo 'start nginx'
within release_path do
with rails_env: fetch(:rails_env) do
execute :rake, "db:create"
end
end
end
end
desc 'Create symlink'
task :symlink do
on roles(:all) do
execute "ln -s #{shared_path}/config/database.yml #{release_path}/config/database.yml"
execute "ln -s #{shared_path}/Procfile #{release_path}/Procfile"
execute "ln -s #{shared_path}/system #{release_path}/public/system"
end
end
desc 'Foreman init'
task :foreman_init do
on roles(:all) do
foreman_temp = "/var/www/tmp/foreman"
execute "mkdir -p #{foreman_temp}"
# Создаем папку current для того, чтобы foreman создавал upstart файлы с правильными путями
execute "ln -s #{release_path} #{current_path}"
within current_path do
execute "cd #{current_path}"
execute :bundle, "exec foreman export upstart #{foreman_temp} -a #{application} -u deployer -l /var/www/apps/#{application}/log -d #{current_path}"
end
sudo "mv #{foreman_temp}/* /etc/init/"
sudo "rm -r #{foreman_temp}"
end
end
desc 'Restart application'
task :restart do
on roles(:app), in: :sequence, wait: 5 do
sudo "restart #{application}"
end
end
after :finishing, 'deploy:cleanup'
after :finishing, 'deploy:restart'
after :updating, 'deploy:symlink'
after :setup, 'deploy:foreman_init'
after :foreman_init, 'foreman:start'
before :foreman_init, 'rvm:hook'
before :setup, 'deploy:starting'
before :setup, 'deploy:updating'
before :setup, 'bundler:install'
Есть задача deploy и мы добавили 4 новых действия: setup (первичная настройка), foreman_init (создание upstart скрипта для приложения), symlink (создание символьных ссылок) и restart (перезагрузка приложения). Также мы указываем после/перед какими стадиями, что нужно выполнить.
deploy:setup выполняет первичную настройку сервера: загружает файлы из папки shared на локальном компьютере в папку shared на сервере, настраивает nginx, создает нужные папки и запускает deploy:foreman_init, который в свою очередь создает upstart скрипты через foreman и копирует их в /etc/init, после чего мы можем управлять нашим приложением командами sudo start/stop/restart/status ИМЯ_ПРИЛОЖЕНИЯ
. Перед deploy:setup выполняется три первых шага обычного деплоя приложения, а именно заливаются файлы на сервер и выполняется bundle install
. После каждого деплоя создаются новый симлинки и перезагружается Unicorn. Осталось только в конец этого файла добавить before :deploy, 'git:deploy'
и теперь перед каждым деплоем автоматически будут коммититься новые изменения.
Еще раз:
- выполняем
cap production deploy:setup
при самом первом деплое Вашего приложения. - выполняем
cap production deploy
при каждом деплое Вашего приложения.
Вот именно так я всегда деплою свои приложения на
Автор: goooseman