November 16, 2016 · Rails Deploy SQLite

Deploy de aplicação Rails com Capistrano, Rbenv, Puma, Figaro e SQLite no Ubuntu

Para fins de testes e aplicação em um sistema de pequeno porte (que está atendendo muito bem, diga-se se passagem), adotei dessa vez o Capistrano em vez do Mina, Rbenv no lugar do RVM, Puma no lugar do Unicorn e SQLite3 no lugar do Postgres.

Vou considerar que você já tenha um projeto Rails rodando em sua máquina, exemplo:

rails new frajola
bundle install

Caso não saiba como configurar seu ambiente dev, use esse tutorial aqui que é bem legal e funciona.

Adotando nesse pequeno tutorial o projeto fictício chamado frajola, inicie criando um usuario de deploy da qual sua app rodará:

sudo adduser deploy

Crie uma senha, confirme ela, sai dando ENTER em todo o resto e dê as permissões mágicas:

sudo adduser deploy sudo

DICA: use o ssh-copy-id para não ficar tendo que digitar sua senha toda vez no deploy.

Acesse ele:

su deploy

ATENÇÃO: À partir daqui os comandos abaixo serão quase tudo com sudo.

Atualize tudo no servidor:

sudo apt-get update && sudo apt-get upgrade

Instale o NGINX no seu server:

sudo apt-get install nginx

Instale as outras libs essenciais:

sudo apt-get install nodejs git-core curl zlib1g-dev build-essential libssl-dev libreadline-dev libyaml-dev libsqlite3-dev sqlite3 libxml2-dev libxslt1-dev libcurl4-openssl-dev python-software-properties libffi-dev

Repare que na lista já está o SQLite3, Git e Nodejs (para compilar o assets). Para configurar o git no seu server pode seguir esse link do próprio github.

ATENÇÃO: não esqueça de colocar /config/application.yml no seu arquivo .gitignore, ficando assim:

# Ignore bundler config.
/.bundle

# Ignore the default SQLite database.
/db/*.sqlite3
/db/*.sqlite3-journal

# Ignore all logfiles and tempfiles.
/log/*
/tmp/*
!/log/.keep
!/tmp/.keep

/.idea

# Ignore Byebug command history file.
.byebug_history

# Config
/config/application.yml
/public/uploads/*
/public/files/*

Logo mais explico por quê ele aí nessa lista.

Rbenv

Use o Rbenv para instalar a última versão do Ruby no servidor:

cd
git clone https://github.com/rbenv/rbenv.git ~/.rbenv
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
echo 'eval "$(rbenv init -)"' >> ~/.bashrc
exec $SHELL

git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build
echo 'export PATH="$HOME/.rbenv/plugins/ruby-build/bin:$PATH"' >> ~/.bashrc
exec $SHELL

rbenv install 2.3.1
rbenv global 2.3.1
ruby -v

logo após, instale o Bundler:

gem install bundler

e dê um rehash no rbenv:

rbenv rehash

Adicione o arquivo .ruby-version na raíz do seu projeto com o conteúdo: 2.3.1. Essa é a versão do Ruby (a mesma que tu instalou anteriormente) que ele vai usar no deploy.

Dica retirada daqui: https://gorails.com/deploy/ubuntu/14.04

Configurando o SQLite3

Por padrão no Rails, se você não usar nenhuma flag de informação para especificar que banco de dados será utilizado na hora de criar seu projeto rails new frajola, ele usará o SQLite3 para todos os ambientes (dev, test e producao). Então na config do seu banco config/database.yml altere para:

default: &default
  adapter: sqlite3
  pool: 5
  timeout: 5000

development:
  <<: *default
  database: db/frajola_dev.sqlite3

test:
  <<: *default
  database: db/frajola_test.sqlite3

production:
  <<: *default
  database: /home/deploy/apps/frajola/frajola.sqlite3

ATENÇÃO: O caminho da production está diferente e apontado para a raiz do projeto. Dessa forma não corremos o risco de perder todo o banco de dados após um deploy (o Capistrano, assim como o Mina, gera 'releases' da sua aplicação, limpando toda a app atual, e baixando uma nova).

Configurando a aplicação Rails

Adicione as gems no seu arquivo Gemfile:

gem 'figaro'

group :development do
  gem 'capistrano'
  gem 'capistrano-rails'
  gem 'capistrano-rbenv', '~> 2.0'
  gem 'capistrano-bundler'
  gem 'capistrano3-puma'
end

Dê o bundle:

bundle install

Coloquei as gems relacionadas ao capistrano no grupo development, já que vou rodar direto da minha máquina o deploy.

A gem figaro é um facilitador de configuração ENV do seu projeto. Crie um arquivo chamado application.yml dentro da pasta config da sua aplicação (app/config/) e coloque as informações que serão lidas pelo o Capistrano quando este se conectar com o server:

development:
  SECRET_KEY_BASE: 'sua-chave-grandona-dev'
  DADO: 'valor_dev'

production:
  SECRET_KEY_BASE: 'sua-chave-grandona-prod'
  DADO: 'valor_prod'

Você pode ler a documentação do figaro, mas resumo da ópera: uma vez instalado e com o arquivo application.yml lá na pasta app/config/, você poderá acessar essas informações dentro do código ruby através da chamada ENV['SECRET_KEY_BASE'], ENV['DADO'] etc. que ele vai usar de acordo com o ENVironment (ambiente) os dados lá setados.

Retornando ao Capistrano, dentro da sua pasta de aplicação, rode o scaffold dos arquivos base para o deploy:

bundle exec cap install

Ele vai gerar as seguintes pastas e arquivos abaixo no seu projeto:

config/deploy
config/deploy.rb
config/deploy/staging.rb
config/deploy/production.rb
lib/capistrano/tasks
Capfile

Vamos configurar os arquivos deploy.rb, production.rb e Capfile, que é que nos interessa:

No arquivo deploy.rb, coloque as informações abaixo. Vou colocar apenas o que alterei/adicionei do arquivo original:

# Nome da aplicação
set :application, 'frajola'

# Repositório para baixar e usar
set :repo_url, 'git@github.com:marlosirapuan/frajola.git'

# Definições padrões do Rbenv
set :rbenv_ruby, File.read('.ruby-version').strip
set :rbenv_prefix, "RBENV_ROOT=#{fetch(:rbenv_path)} RBENV_VERSION=#{fetch(:rbenv_ruby)} #{fetch(:rbenv_path)}/bin/rbenv exec"
set :rbenv_roles, :all

# Repare nesse caminho, eh o mesmo onde ficara o banco de dados da produção
set :deploy_to, '/home/deploy/apps/frajola'

# Linka arquivos e pastas
append :linked_files, 'config/database.yml', 'config/secrets.yml', 'config/application.yml'

append :linked_dirs, 'log', 'tmp/pids', 'tmp/cache', 'tmp/sockets', 'public/system'

# Define a quantidade de releases no server
set :keep_releases, 1

# Definições da task upload dentro do namespace figaro
namespace :figaro do
  task :upload do    
    on roles(:all) do
      # cria a pasta shared/config antes de mandar os arquivos pra lá
      execute "mkdir -p #{shared_path}/config"

      upload! 'config/secrets.yml', "#{shared_path}/config/secrets.yml"
      upload! 'config/database.yml', "#{shared_path}/config/database.yml"
      upload! 'config/application.yml', "#{shared_path}/config/application.yml"
    end
  end
end

# Rodar antes do check deploy a task upload (logo acima)
before 'deploy:check', 'figaro:upload'

A idéia aqui é a seguinte: no deploy será feito o upload dos arquivos secrets.yml, database.yml e application.yml pro seu servidor, que serão compartilhados na pasta shared e acessados por sua aplicação.

O arquivo application.yml está no seu .gitignore, ou seja, não foi e não será commitado (nele teoricamente estão dados sigilosos que voce nao quer que fique no repositorio, correto?), mas você pode e deve usá-lo em desenvolvimento.

No arquivo production.rb coloque as informações abaixo e altere de acordo com seus dados:

server 'ip-do-seu-servidor', user: 'deploy', roles: %w{app db web}

No arquivo Capfile, mantenha tudo que estiver lá, e adicione/descomente esses requires:

require 'capistrano/rbenv'
require 'capistrano/rails'
require 'capistrano/puma'

OBS: capistrano/rails já contem nele os requires:

require 'capistrano/bundler'
require 'capistrano/rails/assets'
require 'capistrano/rails/migrations'

Faça o commit dessas novas mudanças no seu repositório e rode o deploy direto da sua máquina:

bundle exec cap production deploy --trace

O --trace (opcional) é para verificar tudo que esta acontecendo, task a task.

Ao término, se não souber o caminho do processo de onde esta rodando o puma, dê uma ps aux no seu server, copie ele e jogue lá na configuração do seu NGINX:

# /etc/nginx/sites-enabled/frajola
upstream puma_frajola {
  # unix:///home.. é o mesmo caminho que esta no processo, visto via: ps aux
  server unix:///home/deploy/apps/frajola/shared/tmp/sockets/puma.sock fail_timeout=0;
}

server {
  listen 80;

  client_max_body_size 4G;
  keepalive_timeout 10;

  error_page 500 502 504 /500.html;
  error_page 503 @503;

  server_name www.frajola.com.br frajola.com.br;
  root /home/deploy/apps/frajola/current/public/;
  
  try_files $uri/index.html $uri @puma_frajola;

  location @puma_frajola {
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_redirect off;
    proxy_pass http://puma_frajola;

    access_log /var/log/nginx/frajola.access.log;
    error_log /var/log/nginx/frajola.error.log;
  }

  location ^~ /assets/ {
    gzip_static on;
    expires max;
    add_header Cache-Control public;
  }

  location = /50x.html {
    root html;
  }

  location = /404.html {
    root html;
  }

  location @503 {
    error_page 405 = /system/maintenance.html;
    if (-f $document_root/system/maintenance.html) {
      rewrite ^(.*)$ /system/maintenance.html break;
    }
    rewrite ^(.*)$ /503.html break;
  }

  if ($request_method !~ ^(GET|HEAD|PUT|PATCH|POST|DELETE|OPTIONS)$ ){
    return 405;
  }

  if (-f $document_root/system/maintenance.html) {
    return 503;
  }

  location ~ \.(php|html)$ {
    return 405;
  }
}

Reinicie o NGINX, abra uma cerveja e um abraço:

sudo service nginx restart

Parece complicado, mas após ajustar tudo e rodar vai ver que é uma mão na roda.

Cheers.

Comments powered by Disqus