вторник, 29 июля 2014 г.

Auto Deploy Ruby On Rails Ubuntu (Capistrano + nginx + unicorn + PostgreSQL)

Предположим, что у вас есть физический или виртуальный сервер, на котором вы собираетесь разворачивать свое Ruby On Rails приложение. Данный процесс сопровождается несколькими небольшими, но скучными задачами, которые приходится выполнять каждый раз, а именно:
1) Получить доступ к серверу (Например, логин через SSH в командной строке);
2) Перейти в папку приложения;
3) Обновить приложение до последней версии;
4) Выполнить прекомпиляцию ресурсов, если необходимо;
5) Перезапустить приложение, если необходимо.

Есть средства, позволяющие автоматизировать данный процесс и радоваться жизни.

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

В данной статье приведена пошаговая инструкция настройки сервера для разворачивания (deploy) Ruby On Rails приложения и автоматизация данного процесса. В ней рассмотрено:
- Первичная настройка сервера и закрытого доступа к нему;
- Установка веб-сервера nginx и базы данных PostgreSQL;
- Установка rvm и Ruby On Rails;
- Настройка гема foreman для управления процессами приложения;
- Настройка http-сервера Unicorn;
- Настройка гема Capistrano (v3.1) для автоматизации деплоя.

Инструкция протестирована на операционных системах Ubuntu 14.04 LTS и 12.10 LTS.
Для работы с файлами на удаленном сервере я использовал текстовый редактор vim (sudo apt-get install vim).

Первичная настройка сервера


Допустим, что у Вас есть машинка с голой Ubuntu, к которой вы подключились по SSH. Для начала сменим пароль у пользователя root командой:

passwd

Создадим нового пользователя, от лица которого будет производится деплой (Выбирайте любое имя, которое вам удобно. Я выбрал deployer, пользуясь случаем):

adduser ИМЯ_ПОЛЬЗОВАТЕЛЯ_СИСТЕМЫ

Разрешим ему пользоваться командой sudo и укажем, какие команды можно выполнять без пароля. Для этого выполняем команду:

visudo

В открывшемся файле дописываем:

ИМЯ_ПОЛЬЗОВАТЕЛЯ_СИСТЕМЫ ALL=(ALL:ALL) ALL
ИМЯ_ПОЛЬЗОВАТЕЛЯ_СИСТЕМЫ ALL=NOPASSWD: /usr/sbin/service, /bin/ln, /bin/rm, /bin/mv, /sbin/start, /sbin/stop, /sbin/restart, /sbin/status

Изменим настройки ssh сервера (запретим логин под root, доступ по доменному имени и разрешим логин только под нашим новым пользователем).
Для этого добавляем в файл '/etc/ssh/sshd_config':

PermitRootLogin no
UseDNS no
AllowUsers ИМЯ_ПОЛЬЗОВАТЕЛЯ_СИСТЕМЫ

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

reload ssh

Или для Ubuntu 16:

sudo service ssh restart 

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

Оригинал статьи по установке nginx с плагином pagespeed тут.

Nginx является веб-сервером для обслуживания статических запросов и хранения статичных файлов. Он будет использоваться в связке с unicorn (сервером для динамических страниц приложения).

Необходимо установить его на сервере. Для этого логинимся под новым пользователем:

ssh ИМЯ_ПОЛЬЗОВАТЕЛЯ_СИСТЕМЫ@123.123.123.123

Обновляем репозитории, систему и скачиваем необходимые для успешной сборки пакеты:

sudo apt-get update
sudo apt-get install build-essential zlib1g-dev libpcre3 libpcre3-dev unzip libssl-dev

Загружаем плагин pagespeed:

NPS_VERSION=[check the release notes for the latest version]
cd
wget https://github.com/pagespeed/ngx_pagespeed/archive/v${NPS_VERSION}-stable.zip
unzip v${NPS_VERSION}-stable.zip
cd ngx_pagespeed-${NPS_VERSION}-stable/
psol_url=https://dl.google.com/dl/page-speed/psol/${NPS_VERSION}.tar.gz
[ -e scripts/format_binary_url.sh ] && psol_url=$(scripts/format_binary_url.sh PSOL_BINARY_URL)
wget ${psol_url}
tar -xzvf $(basename ${psol_url})  # extracts to psol/

Загружаем и устанавливаем nginx с поддержкой pagespeed:

NGINX_VERSION=[check nginx's site for the latest version]
cd
wget http://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz
tar -xvzf nginx-${NGINX_VERSION}.tar.gz
cd nginx-${NGINX_VERSION}/
./configure --add-module=$HOME/ngx_pagespeed-${NPS_VERSION}-stable --with-http_ssl_module
make sudo make install

Если подключение к серверу через HTTPS не требуется, флаг --with-http_ssl_module можно упустить.

Далее создадим upstart скрипт для управления nginx. Он позволит управлять сервером командами:

sudo start/stop/restart/status nginx

Для этого создаём файл '/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

Файл настроек nginx лежит по адресу '/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 ИМЯ_ПОЛЬЗОВАТЕЛЯ_СИСТЕМЫ
sudo mkdir /var/www
sudo chgrp -R web /var/www
sudo chmod -R 775 /var/www

После этого разлогинтесь и войдите под вашим пользователем снова, чтобы изменения вступили в силу, а так же запустите nginx:

sudo start nginx

Если upstart скрипты на ubuntu 16 не будут работать, переключитесь с режима systemd в upstart командой sudo apt-get install upstart-sysv и перезагрузите сервер.

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


PostgreSQL - свободная объектно-реляционная СУБД. Про ее достоинства и недостатки вы можете почитать отдельно.

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

И создаем нового пользователя. Для этого входим в оболочку PostgreSQL:

sudo -u postgres psql

Создаем пользователя, даем ему привилегии и выходим из оболочки (На сервере я назвал пользователя db_deployer. На клиенте создал пользователя со своим привычным именем и без пароля):

create user ИМЯ_ПОЛЬЗОВАТЕЛЯ_БД with password 'ВАШ_ПАРОЛЬ';
alter role ИМЯ_ПОЛЬЗОВАТЕЛЯ_БД superuser createrole createdb replication login;
\q

Далее разрешим удаленную работу с базой данных и предоставим доступ к базе данных от лица нового пользователя. Для этого только на сервере выполняем следующее:
1) в файле '/etc/postgresql/9.3/main/postgresql.conf' изменим параметр listen_addresses = 'localhost' на listen_addresses = '*';
2) добавим в файл '/etc/postgresql/9.3/main/pg_hba.conf' строчки:

local   all             ИМЯ_ПОЛЬЗОВАТЕЛЯ_БД                md5
host    all             ИМЯ_ПОЛЬЗОВАТЕЛЯ_БД    0.0.0.0/0   md5

После этого Перезагружаем postgresql командой:

sudo service postgresql restart

ЗАМЕЧАНИЕ: Если при работе с PostgreSQL на клиенте будут возникать ошибки, пропишите соответствующие разрешения и на локальной машине.

ПОЛЕЗНОЕ: Так же можно поставить веб-интерфейс teampostgresql. Управляется командами:

sudo teampostgresql-service start
sudo teampostgresql-service stop
sudo teampostgresql-service status

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


Устанавливаем на сервере Ruby On Rails и все необходимые для его работы примочки. Если Rails не установлены на вашем рабочем компьютере, устанавливаем и на нем. Про это здесь:
Если Вы еще не создали Rails приложение, то про это можно почитать тут:
Для продолжения работы по данной статье у Вас должно быть в наличии приложение в минимальной конфигурации, ибо что-то нужно разворачивать на сервере.

Создаем репозиторий на github


Мы будем использовать удаленный репозиторий git для разворачивания приложения. На сервере будет происходить загрузка последней версии приложения именно с него. Если у вас нет клиента git, его можно установить командой:

apt-get install git

Для удаленного хранения я использую сервис github. Регистрируемся, создаем репозиторий в сервисе, запоминаем его адрес. 
Далее, создаем репозиторий в папке проекта:

git init

Добавляем адрес репозитория (Будет указан во время его создания):

git remote add origin АДРЕС_РЕПОЗИТОРИЯ

Но не спешим заливать на него наше приложение!

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

/config/database.yml # доступ к базе данным
/Procfile # про него я еще расскажу
/config/deploy/ # файлы Capistrano
/shared/ # файлы, которых нет в репозитории, но они будут скопированы на сервер при первом деплое приложения

Теперь фиксируем изменения и производим их публикацию на github:

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

Так же нужно добавить публичный ключ сервера в настройки github репозитория. Для этого, подключившись к серверу по ssh производим следующее:

chmod 700 ~/.ssh
cd ~/.ssh
ssh-keygen

После этого в файле id_rsa.pub будет содержаться ключ, который и следует внести в настройки.

Установка и первоначальная настройка гема foreman


Данный гем позволяет управлять запуском потоков приложения. Если ваше приложение является многопоточным (например, главный поток, и поток отложенных задач), то они оба должны быть запущены со стартом приложения. Foreman можно настроить так, чтобы оба этих потока запускались всего лишь одной командой - foreman start. Стоит всего лишь прописать их в Procfile, находящийся в корне приложения. Более того, имеется возможность создать upstart скрипт (командой foreman export upstart), чтобы приложением можно было управлять командами start/stop/status/restart ИМЯ_ПРИЛОЖЕНИЯ, а так же запуск потоков происходил автоматически со стартом компьютера.

Добавляем строчку в Gemfile:

gem 'foreman', '0.63.0'

И выполняем:

bundle install
bundle update

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

В корне приложения создаем файл Procfile в котором прописываем, какие процессы необходимо запустить со стартом приложения:

web: rails s
worker: bundle exec rake jobs:work

Я использую гем delayed_job для отложенных заданий, по-этому запускаю для него дополнительный поток. Если у вас такого нет, то можете обойтись только первой строчкой, запускающей приложение.

Позже мы напишем Production конфигурацию Procfile, для запуска приложения на сервере.

Установка Unicorn


Unicorn - это продвинутый http сервер для обработки множества http запросов и работы с динамическими страницами. Если nginx занимается обработкой и хранением статических файлов, то Unicorn необходим для взаимодействия с базой данных через Rack интерфейс и работы с динамическими страницами.

Установим его, добавив строчки в Gemfile:

group :production do
    gem 'unicorn'
end

После чего не забудьте про bundle install

В папке '/config/' создаем файл unicon.rb c примерно таким содержимым (ИМЯ_ПРИЛОЖЕНИЯ заменяем на имя вашего приложения. Так будет называться папка на сервере):

# Устанавливаем количество процессов на сервере (Чем больше поток пользователей, тем больше нужно процессов)
worker_processes Integer(ENV["WEB_CONCURRENCY"] || 3)

working_directory "/var/www/apps/ИМЯ_ПРИЛОЖЕНИЯ/current" # available in 0.94.0+

# Слушаем Unix domain socket и TCP порт
listen "/var/www/apps/ИМЯ_ПРИЛОЖЕНИЯ/socket/.unicorn.sock", :backlog => 64
listen 8080, :tcp_nopush => true

# уничтожаем workers после 30 секунд вместо 60 дефолтных
timeout 30

# Устанавливаем путь к PID файлу главного процесса unicorn
pid "/var/www/apps/ИМЯ_ПРИЛОЖЕНИЯ/run/unicorn.pid"

# Направляем потоки вывода
stderr_path "/var/www/apps/ИМЯ_ПРИЛОЖЕНИЯ/log/unicorn.stderr.log"
stdout_path "/var/www/apps/ИМЯ_ПРИЛОЖЕНИЯ/log/unicorn.stdout.log"

# Подгружаем приложение в память до того, как произойдет форк процессов. (Типа для memory savings)
preload_app true
GC.respond_to?(:copy_on_write_friendly=) and
  GC.copy_on_write_friendly = true

#При включенной опции юникорн проверяет соединения клиентов, записывая начало
#хедера HTTP запроса до того, как он вызовется приложением. Это
#предотвращает вызов приложения для соединений, которые были отключены от очереди.
#Это лишь гарантирует определение клиентов на том же хосте, что и юникорн.
check_client_connection false

before_fork do |server, worker|

  # Следующее особенно рекомендовано для 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.

Capistrano скрипт будет выполнять некоторые команды от имени суперпользователя на сервере. Чтобы разрешить ему делать это, выполняем команду на сервере:

sudo visudo

и добавляем строчку:

ИМЯ_ПОЛЬЗОВАТЕЛЯ_СИСТЕМЫ ALL=NOPASSWD: /usr/sbin/service, /bin/ln, /bin/rm, /bin/mv, /sbin/start, /sbin/stop, /sbin/restart, /sbin/status

Установим Capistrano, добавив в Gemfile несколько строчек, и выполнив bundle update, bundle install:

group :development do
    gem 'capistrano', '3.1.0'
    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 и foreman при первом деплое, а так же запускать unicorn при каждом последующем.

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


Файлы настроек, которые необходимо загрузить на сервер, будем хранить в папке shared (в папке проекта на локальной машине). Для этого мы добавили ее в .gitignore.
Сначала создадим там nginx.conf примерно с таким содержимым:

user nginx web; 

# В этот файл пишем pid процесса
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. ИМЯ_ПРИЛОЖЕНИЯ смените на имя, которое вы указали в unicorn.rb

Если нужно подключение через HTTPS, воспользуйтесь Let's Encrypt и данной статьей:
https://www.digitalocean.com/community/tutorials/how-to-secure-nginx-with-let-s-encrypt-on-ubuntu-14-04

ВНИМАНИЕ! Если вы хотите, чтобы сервер работал как по HTTP, так и по HTTPS, все серверные настройки в файле nginx.conf должны находиться внутри одного блока server {}, а не в разных, как указано в статье по ссылке.

Теперь создаем Production конфигурацию Procfile в папке shared:

web: bundle exec unicorn_rails -c /var/www/apps/ИМЯ_ПРИЛОЖЕНИЯ/current/config/unicorn.rb -E production
worker: RAILS_ENV=production bundle exec rake jobs:work

Повторюсь, если вы не используете delayed_job, вам будет достаточно только первой строчки.

Там же создаём database.yml с настройками базы данных для сервера:

production:
   adapter: postgresql
   encoding: unicode
   database: ИМЯ_ПРИЛОЖЕНИЯ_production
   pool: 5
   username: ИМЯ_ПОЛЬЗОВАТЕЛЯ_БД
   password: ПАРОЛЬ_ПОЛЬЗОВАТЕЛЯ_БД

Осталось настроить Capistrano и написать скрипт для разворачивания приложения.

Настройка Capistrano


В файле 'config/deploy/production.rb' делаем изменения:
role :app, %w{ИМЯ_ПОЛЬЗОВАТЕЛЯ_СИСТЕМЫ@IP_АДРЕС_СЕРВЕРА}
role :web, %w{ИМЯ_ПОЛЬЗОВАТЕЛЯ_СИСТЕМЫ@IP_АДРЕС_СЕРВЕРА}
role :db,  %w{ИМЯ_ПОЛЬЗОВАТЕЛЯ_СИСТЕМЫ@IP_АДРЕС_СЕРВЕРА}
server 'ИМЯ_ПОЛЬЗОВАТЕЛЯ_СИСТЕМЫ@IP_АДРЕС_СЕРВЕРА', user: 'ИМЯ_ПОЛЬЗОВАТЕЛЯ_СИСТЕМЫ', password: 'ПАРОЛЬ_ПОЛЬЗОВАТЕЛЯ_СИСТЕМЫ', roles: %w{web app db}, my_property: :my_value

Это необходимо для того, чтобы Capistrano знал, на каком сервере разворачивать приложение, и как получить к нему доступ. Данный файл занесен в .gitignore, так как это конфиденциальная информация.

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

# config valid only for Capistrano 3.1
lock '3.1.0'

set :application, 'ИМЯ_ПРИЛОЖЕНИЯ'
set :deploy_to, '/var/www/apps/ИМЯ_ПРИЛОЖЕНИЯ'
set :deploy_via, :copy
set :scm, :git
set :repo_url, 'git@github.com:ПОЛЬЗОВАТЕЛЬ_ГИТХАБА/ИМЯ_ПРИЛОЖЕНИЯ.git'
set :branch, 'master'
application = 'ИМЯ_ПРИЛОЖЕНИЯ'
set :rvm_type, :user
set :rvm_ruby_version, '2.0.0-p481'
#set :port, 2222

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 origin master"
    end
  end
end

# Default branch is :master
# ask :branch, proc { `git rev-parse --abbrev-ref HEAD`.chomp }

# Default deploy_to directory is /var/www/my_app
# set :deploy_to, '/var/www/my_app'

# Default value for :scm is :git
# set :scm, :git

# Default value for :format is :pretty
# set :format, :pretty

# Default value for :log_level is :debug
# set :log_level, :debug

# Default value for :pty is false
# set :pty, true

# Default value for :linked_files is []
# set :linked_files, %w{config/database.yml}

# Default value for linked_dirs is []
# set :linked_dirs, %w{bin log tmp/pids tmp/cache tmp/sockets vendor/bundle public/system}

# Default value for default_env is {}
# set :default_env, { path: "/opt/ruby/bin:$PATH" }

# Default value for keep_releases is 5
# set :keep_releases, 5

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  /var/www/log/"
      execute "mkdir  /var/www/log/upstart/"
      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:all"
        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 ИМЯ_ПОЛЬЗОВАТЕЛЯ_СИСТЕМЫ -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'

  after :publishing, :restart

  after :restart, :clear_cache do
    on roles(:web), in: :groups, limit: 3, wait: 10 do
      # Here we can do anything such as:
      # within release_path do
      #   execute :rake, 'cache:clear'
      # end
    end
  end

end

#before :deploy, 'git:deploy'

Данный скрипт, помимо стандартных умений Capistrano, загрузит на сервер файлы из папки shared, настроит nginx, unicorn, создаст upstart скрипт foreman для легкого запуска приложения.
Можете самостоятельно разобраться в этом файле, синтаксис довольно простой. Главное замените ИМЯ_ПРИЛОЖЕНИЯ на имя вашего приложения, введите адрес репозитория, а так же ИМЯ_ПОЛЬЗОВАТЕЛЯ_СИСТЕМЫ на имя пользователя на сервере, под которым производится деплой.

И так, все готово! Не прошло и пол года)

Как производить деплой


Публикуем изменения на github.
Выполняем cap production deploy:setup при самом первом деплое Вашего приложения.
Выполняем cap production deploy при каждом деплое Вашего приложения.

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


За основу была взята статья:
http://habrahabr.ru/post/213269/

Код подсвечен сервисом http://hilite.me/

среда, 21 мая 2014 г.

Запуск демонов на Linux при старте системы

Идите сюда, если Вам нужно запустить какую-то службу (демон) при старте системы на Linux.

Принимаем во внимание то, что upstart скрипт (скрипт, позволяющий управлять запуском службы командами start, stop, status) уже написан и находится в директории /etc/init.d. В данной статье рассказывается лишь то, как настроить запуск службы с помощью этого скрипта при запуске системы.

1. Для выполнения задачи нам понадобится утилита sysv-rc-conf. Вот ссылка на официальный сайт http://sysv-rc-conf.sourceforge.net/

Установка простая. Печатаем в консоли:

  • sudo apt-get install sysv-rc-conf

Я натыкался еще на одну утилиту - rcconf. Но она показалась мне менее гибкой и дружелюбной.

2. Далее, с помощью этой программки можно легко установить службу на запуск при старте системы. Сделать это можно двумя способами:

  • печатая нудные команды в командой строке;
  • набрав команду sudo sysv-rc-conf наслаждаться работой в текстовом интерфейсе.
Я расскажу про текстовый интерфейс, а с командами Вы можете собственноручно ознакомиться на официальном сайте.

3. Итак, после ввода команды sudo sysv-rc-conf вы увидите такое окно:
Интерфейс удобный и понятный. В строках Вы видите список всех upstart скриптов в вашей системе. Но вот названия столбцов (какие-то цифры) приводят в ступор.

Немного теории. В Линуксе есть 10 уровней запуска системы, 7 из которых используются. Пронумерованы они с 0 до 6. Далее описаны действия, выполняемые на соответствующих уровнях:

  • 0 — выполняются действия по выключению системы.
  • 1 — однопользовательский режим (single user mode). Предназначен для различных административных действий по восстановлению системы. По своему смыслу аналогичен Safe Mode Windows, но полностью его не повторяет. На этом уровне выполнения система полностью сконфигурирована, но не запущен ни один сервис, а из пользователей может работать только один root.
  • 2 — не используется, но сконфигурирован как уровень выполнения 3. В RedHat и SuSE Linux сконфигурирован как уровень выполнения 3, но без поддержки сетевых файловых систем. В Ubuntu и Debian используется как многопользовательский режим.
  • 3 — многопользовательский режим (multiuser mode). Нормальный режим работы сервера.
  • 4 — В Slackware Linux используется для графического входа в систему. В RedHat и SuSE Linux не сконфигурирован.
  • 5 — В RedHat и SuSE Linux используется для графического входа в систему. В Slackware Linux не сконфигурирован.
  • 6 — выполняются действия по перезагрузке системы.


4. Все что нам нужно сделать, поставить крестики на тех уровнях, на которых мы хотим запускать наш демон. Просто перемещаемся стрелками на строку с нашим скриптом и выставляем крестики на уровнях с помощью клавиши Пробел. Для выхода нажимаем клавишу q. Все готово! После перезапуска системы демон будет запущен.

Еще немного теории. В папке /etc есть несколько директорий с названиями rcN.d, где N, это уровень запуска системы. Для того, чтобы служба запускалась при старте системы, нужно создать симлинки на upstart скрипт в этих папках, а в имени файла указать приоритет запуска. Программа sysv-rc-conf просто напросто предоставляет удобный интерфейс для этого процесса. Вот и все!)

воскресенье, 23 февраля 2014 г.

Ruby On Rails. Установка под линукс

Выполняем последовательно команды:

sudo apt-get install git curl python-software-properties software-properties-common
sudo add-apt-repository ppa:chris-lea/node.js sudo apt-get update sudo apt-get install nodejs
sudo add-apt-repository -y -r ppa:chris-lea/node.js
sudo rm -f /etc/apt/sources.list.d/chris-lea-node_js-*.list
curl -L get.rvm.io | bash -s stable source ~/.rvm/scripts/rvm rvm requirements rvm install ruby-2.1.1 rvm use ruby-2.1.1 --default gem install rails gem install bundler

Затем редактируем файл ~/.bashrc, добавив в его конец строку:
[[ -s "$HOME/.rvm/scripts/rvm" ]] && source "$HOME/.rvm/scripts/rvm" # Load RVM into a shell session *as a function*

Выполняем:
source ~/.bashrc
type rvm | head -1

После выполнения команды выше вы должны получить такой вывод -
rvm is a function

Все готово!)

среда, 12 февраля 2014 г.

Ruby on Rails. RubyMine

IDE для разработки Ruby On Rails приложений RubyMine.

http://www.jetbrains.com/ruby/

30 дней триал период.

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

/*

Правда, автоматически рельсовое приложение не создавало файлик schema.rb.
Добавил в файл Rake следующие строчки:
Rake::Task["db:migrate"].enhance do
  puts "gary #{ActiveRecord::Base.schema_format.inspect}"
  if ActiveRecord::Base.schema_format == :sql
    Rake::Task["db:schema:dump"].invoke
  end
end
И все заработало!)))

*/

пятница, 31 января 2014 г.

Android. Декомпиляция apk. Apk to Java decompilation

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

Алгоритм следующий:
1. Достать байт-код Java из apk;
2. Преобразовать байт-код в код на языке Java.

Для этого нам понадобятся:
1. Конвертер dex и apk файлов в jar;
2. Декомпилятор jar файлов;
3. Везение и боевой настрой =)

Последнюю версию конвертера можно скачать отсюда:
А отсюда можно скачать версию 0.0.9.15, если ссылка выше не работает:

Из перепробованных мной декомпиляторов самым удачным мне показался fernflower.
Качаем с интернета, или по этой ссылке:

И так, начнем:

1. Распаковываем архив с dex2jar, закидываем apk файл в папку с файлом dex2jar.sh и в командной строке, находясь в этой же директории, печатаем:

    ./dex2jar.sh имя_вашего_apk.apk

Нажимаем Enter.

Будет много текста, но вскоре в папке появится файл с названием "имя_вашего_apk_dex2jar.jar".

2. В этом файле уже содержатся java классы, но в нечитаемом двоичном виде. Для этого нужно декомпилировать их. Для этого применим fernflower.jar. При условии, что появившийся на первом шаге файл и fernflower.jar находятся в одной директории, набираем в командной строке:

    java -jar fernflower.jar имя_вашего_apk_dex2jar.jar имя_директории

Нажимаем Enter. Создастся папка с названием "имя_директории", в которой будет лежать файл с таким же именем, как и исходный. Но в этот раз все java файлы будут иметь читаемый вид. 

Пам парам, все готово) Можете изучать полученный код или восстанавливать потерянный.

Проблемы:

Первая проблема в том, что все константы, прописанные в коде, заменяются на заданные им значения в момент компиляции. А следовательно, вместо них в полученном после декомпиляции коде будут значения, а не имена.

Вторая проблема - в полученном коде не будет комментариев. Причина такая же, как и в первой проблеме. Они удаляются при сборке apk.

Третья проблема, тоже не очень приятная. Код восстанавливается из бинарников с некоторой точностью, а не в первозданном виде. И эта точность зависит от декомпилятора. Fernflower самый точный, из проверенных мной. Например, один из декомпиляторов не умел восстанавливать switch, вместо него получался бесконечный цикл с кучей break и contunue.