===== Publicando aplicação rails em produção usando Ubuntu + Rails 4 + Nginx + Unicorn + PostgreSQL + Capistrano ===== Entre na máquina via ssh $ ssh usuario@app_name.ifce.edu.br Obs: Caso a chave ssh do servidor tenha sido alterada, por algum motivo específico, remova o arquivo known_hosts: rm ~/.ssh/known_hosts ==== Criando usuário da aplicação ==== É recomendado criar um usuário na máquina com o mesmo nome da aplicação, caso o mesmo ainda não exista. Não é interessante utilizar um usuário genérico para configurar o deploy. Nesse tutorial iremos utilizar o usuário como exemplo: minha_app. Troque esse nome pelo nome da sua aplicação ou qualquer outro nome que achar mais conveniente. Crie o usuário sudo adduser minha_app Digite a senha e as informações necessárias até finalizar o processo Adicione o usuário ao arquivo sudoers: sudo visudo No final do arquivo adicione: minha_app ALL=(ALL) NOPASSWD:ALL Digite Ctrl + O para salvar e depois Ctrl + X para fechar o arquivo Logue com esse novo usuário su minha_app ==== Adicione sua chave ssh ao servidor para se logar sem senha: ==== Dê ssh para alguma máquina a fim de criar o diretório ~/.ssh ssh usuario@app_name.ifce.edu.br Dê um Ctrl+C ao ser solicitado pela senha. Adicione sua chave ao arquivo ~/.ssh/authorized_keys Na sua máquina local pegue a sua chave ssh se existir com o comando (Cuidado para não copiar os espaços): cat ~/.ssh/id_rsa.pub No servidor adicione a chave ao arquivo: vim ~/.ssh/authorized_keys Cole conteúdo da chave nesse arquivo Salve e teste ==== Instalando o RVM ==== **Instalando o Curl** $ sudo apt-get update $ sudo apt-get install curl Obs: caso tenha algum problema ao baixar o rvm, verifique os seus servidores DNS no arquivo: /etc/resolv.conf. Sugestão: adicione os servidores de DNS do google: 8.8.8.8 e 8.8.4.4 $ curl -L get.rvm.io | bash -s stable Caso ocorra o seguinte erro, rode o comando sugerido pelo próprio erro: GPG signature verification failed for '/home/app_name/.rvm/archives/rvm-1.27.0.tgz' - 'https://github.com/rvm/rvm/releases/download/1.27.0/1.27.0.tar.gz.asc'! try downloading the signatures: Rode: command curl -sSL https://rvm.io/mpapis.asc | gpg --import - E depois rode novamente os comandos abaixo para finalizar a instalação do rvm: $ curl -L get.rvm.io | bash -s stable Agora é preciso instalar os requisitos para instalar o ruby e instalar também a gem bundles para gerenciar as outras gems do projeto: $ source ~/.rvm/scripts/rvm $ rvm requirements $ rvm install 2.0.0 (Utilize a versão que vc estiver trabalhando no projeto atual) $ rvm use 2.0.0 --default $ rvm rubygems current $ gem install bundler ==== Instale a lib de dependência do PostgreSQL, Imagemagick e Git ==== $ sudo apt-get install libpq-dev imagemagick git-core Crie o usuário do postgresql da forma que você preferir Crie o Banco de Dados do Sistema no Postgresql ==== Instalando e configurando o Nginx ==== $ sudo apt-get install nginx $ nginx -h $ cat /etc/init.d/nginx $ /etc/init.d/nginx -h $ sudo service nginx start $ sudo service nginx status (Ele deve estar rodando agora!) Crie o arquivo nginx.conf no diretório config do seu projeto rails: upstream unicorn { server unix:/tmp/unicorn.app_name.sock fail_timeout=0; } server { listen 80 default_server deferred; root /opt/apps/app_name/current/public; (Escolha o diretório em vermelho com carinho de acordo com a partição que está montada no sistema. Sugestão: rode df -h) location ^~ /assets/ { gzip_static on; expires max; add_header Cache-Control public; } try_files $uri/index.html $uri @unicorn; location @unicorn { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_redirect off; proxy_pass http://unicorn; } error_page 500 502 503 504 /500.html; client_max_body_size 20M; keepalive_timeout 120; } Adicione a gem do Unicorn ao projeto. Adicione a seguinte linha no arquivo Gemfile: gem 'unicorn' Crie a configuração do unicorn em config/unicorn.rb root = "/opt/apps/app_name/current" working_directory root pid "#{root}/tmp/pids/unicorn.pid" stderr_path "#{root}/log/unicorn.log" stdout_path "#{root}/log/unicorn.log" worker_processes Integer(ENV['WEB_CONCURRENCY']) timeout 30 preload_app true listen '/tmp/unicorn.app_name.sock', backlog: 64 before_fork do |server, worker| Signal.trap 'TERM' do puts 'Unicorn master intercepting TERM and sending myself QUIT instead' Process.kill 'QUIT', Process.pid end defined?(ActiveRecord::Base) and ActiveRecord::Base.connection.disconnect! end after_fork do |server, worker| Signal.trap 'TERM' do puts 'Unicorn worker intercepting TERM and doing nothing. Wait for master to send QUIT' end defined?(ActiveRecord::Base) and ActiveRecord::Base.establish_connection end # Force the bundler gemfile environment variable to # reference the capistrano "current" symlink before_exec do |_| ENV['BUNDLE_GEMFILE'] = File.join(root, 'Gemfile') end Crie o arquivo config/unicorn_init.sh.erb #!/bin/sh set -e # when to timeout a request, a lower value limits the risk that # a long running process will block all connections TIMEOUT=${TIMEOUT-60} # the path to the app, dynamically populate from the capistrano # definition APP_ROOT=/opt/apps/<%= application %>/current # where we're going to store unicorns pid file, must match what is # specified in unicorn.rb.erb PID=$APP_ROOT/tmp/pids/unicorn.pid CMD="cd $APP_ROOT; bundle exec unicorn -D -c $APP_ROOT/config/unicorn.rb -E <%= rails_env %>" AS_USER=deploy set -u OLD_PIN="$PID.oldbin" sig () { test -s "$PID" && kill -$1 `cat $PID` } oldsig () { test -s $OLD_PIN && kill -$1 `cat $OLD_PIN` } workersig () { workerpid="$APP_ROOT/tmp/pids/unicorn.$2.pid" test -s "$workerpid" && kill -$1 `cat $workerpid` } run () { if [ "$(id -un)" = "$AS_USER" ]; then eval $1 else su -c "$1" - $AS_USER fi } case "$1" in start) sig 0 && echo >&2 "Already running" && exit 0 run "$CMD" ;; stop) sig QUIT && exit 0 echo >&2 "Not running" ;; force-stop) sig TERM && exit 0 echo >&2 "Not running" ;; kill_worker) workersig QUIT $2 && exit 0 echo >&2 "Worker not running" ;; restart|reload) sig USR2 && echo reloaded OK && exit 0 echo >&2 "Couldn't reload, starting '$CMD' instead" run "$CMD" ;; upgrade) if sig USR2 && sleep 2 && sig 0 && oldsig QUIT then n=$TIMEOUT while test -s $OLD_PIN && test $n -ge 0 do printf '.' && sleep 1 && n=$(( $n - 1 )) done echo if test $n -lt 0 && test -s $OLD_PIN then echo >&2 "$OLD_PIN still exists after $TIMEOUT seconds" exit 1 fi exit 0 fi echo >&2 "Couldn't upgrade, starting '$CMD' instead" run "$CMD" ;; reopen-logs) sig USR1 ;; *) echo >&2 "Usage: $0 " exit 1 ;; esac Adicione a gem do capistrano ao projeto: $ vim Gemfile group :development do gem 'capistrano-rails' gem 'capistrano-rvm' gem 'capistrano3-unicorn' end Instale a gem do Capistrano $ bundle exec cap install Atualize o arquivo do Capistrano: Capfile, na raiz do projeto: require 'capistrano/setup' require 'capistrano/deploy' require 'capistrano/rvm' require 'capistrano/bundler' require 'capistrano/rails/assets' require 'capistrano/rails/migrations' require 'capistrano3/unicorn' load 'deploy' load 'config/deploy' # remove this line to skip loading any of the default tasks # Load custom tasks from `lib/capistrano/tasks' if you have any defined Dir.glob('lib/capistrano/tasks/*.rake').each { |r| import r } Atualize o arquivo de Deploy em: config/deploy.rb set :stages, %w(production staging) set :default_stage, "staging" require 'capistrano/ext/multistage' set :whenever_command, "bundle exec whenever" set :whenever_environment, defer { stage } # require "whenever/capistrano" set :application, "app_name" set :user, "caio" set :port, 22 set :deploy_via, :remote_cache set :use_sudo, false set :keep_releases, 1 set :scm, "git" set :repository, "git@gitlab.ifce.edu.br:dgti/app_name.git" set(:deploy_to) { "/home/#{user}/web/#{rails_env}" } default_run_options[:pty] = true ssh_options[:forward_agent] = true # simple method to create a file from an erb template. Used # to generate dynamic configuration files. def template(from, to) erb = File.read(from) put ERB.new(erb).result(binding), to end **Crie os arquivos do seu(s) ambiente(s) no diretório config/deploy Observar exemplo dos arquivos de outros sistemas.** ==== Gere e Adicione a chave ssh do servidor no repositório git remoto (no caso o gitlab do ifce) Entre no diretório do ssh: ==== cd ~/.ssh && ls Verifique se o arquivo **id_rsa.pub** já existe. Caso não existe você deve criá-lo com o seguinte comando: ssh-keygen -t rsa -C "app_name@ifce.edu.br" Apenas fique apertando Enter nos prompts que serão exibidos. Não precisa digitar nada. Agora você precisa copiar o conteúdo da chave pública: **id_rsa.pub** para o repositório git (no caso o gitlab do ifce) Vá no Projeto > Settings > Deploy Keys e crie uma nova, colando o conteúdo da chave no campo Key. ==== Checagem de deploy ==== $ cap production deploy:check ==== Bootstrap de deploy (criação da estrutura de diretórios inicial) ==== cap staging deploy:setup ==== Configurações finais de deploy ==== * Adicione a configuração do ambiente correspondente (production ou staging) ao arquivo: /opt/apps/app_name/shared/config/database.yml * Dê permissões para o arquivo de inicialização do unicorn chmod +x /opt/apps/app_name/shared/config/unicorn_init.sh * Remova o site padrão do nginx sudo rm /etc/nginx/sites-enabled/default * Reinicie o nginx sudo service nginx restart * Deploy $ cap production deploy