Ruby, Unicorn e Nginx na Amazon EC2

Se você esta lendo este artigo através do blog, então esta acessando uma estrutura igual a que iremos criar. Irei mostrar como instalar e configurar todos os elementos citados no título deste post, passo-a-passo.

Objetivo

Será mostrado como instalar o Ruby junto ao Rails para ser rodado no Unicorn e servido pelo NGINX na Amazon EC2.

Instância da Amazon EC2

Para criar a instância, siga o artigo Amazon EC2 com Java, MySQL e Tomcat, porém escolha uma instância mais recente do Ubuntu. Enquanto escrevo ou atualizo este artigo, a versão 14.04.

Setup

Após logarmos no servidor com algo do tipo:

ssh -i ~/.ssh/{{app_name}}.pem ubuntu@{{amazon_dns}}

Vamos fazer download de um arquivo do projeto Installer:

wget https://raw.githubusercontent.com/wbotelhos/installers/master/amazon/init.sh

E então, para preparar o ambiente com atualização das libs do Ubuntu e afins, vamos executar:

chmod +x init.sh
sudo ./init.sh

Git

Vamos executar a tarefa de instalação e ativação com a seguinte versão:

./git/git.sh activate 1.9.0

Configuração de Path

sudo ./amazon/path.sh

Ruby

Se você já pensou no RVM, sem problemas. Também utilizo o mesmo localmente, porém no servidor iremos compilar tudo, deixando o sistema bem enxuto. (:

Vamos executar a tarefa de instalação e ativação com a seguinte versão:

sudo ./ubuntu/ruby/ruby.sh activate 2.1.2

Vá tomar um café…

Ao fim, faça logout e reconecte-se ao servidor:

exit

Verifique se tudo deu certo:

ruby -v
# ruby 2.1.2p95 (2014-05-08 revision 45877) [x86_64-linux]

RubyGems

O RubyGems é um repositório de gems no qual iremos fazer o download automáticos das nossas gems. Para criar no home do usuário o arquivo de configuração .gemrc, execute:

./rubygems/rubygems.sh install

Bundler

O Bundler é uma ferramenta para garantir a atualização correta das nossas gems, e iremos instalá-lo:

./bundler/bundler.sh install

NGINX

Temos boas opção de servidores web para aplicações Ruby como é o caso do Apache, mas por vários motivos iremos utilizar o NGINX.

Como já estamos configurando o servidor, precisamos já decidir onde iremos armazenar o nosso site e qual o nome de usuário que será usado para acessar os arquivos do mesmo. Por padrão, o site é armazenado em /var/www e o nome de usuário é ubuntu por conta do padrão da Amazon. Você pode alterar isso na seção Configurations do arquivo ./nginx/nginx.sh.

Da mesma forma podemos adicionar ou remover módulos do NGINX, para isso, edite o arquivo já citado e altere o método configure com os módulos desejados.

Vamos executar a tarefa de instalação e ativação com a seguinte versão:

./nginx/nginx.sh activate 1.7.4

Verifique se tudo deu certo:

nginx -v
# nginx version: nginx/1.7.4

É possível ver as informações completas incluindo os módulos instalando utilizando o parâmetro V.

nginx -V

Configurations

Contendo as configurações básicas, todos se encontram na ./nginx onde alguns contêm variáveis que são substituidas de acordo com as configurações no arquivo nginx.sh como, por exemplo, pid {{pid_file}};.

Para configurar tudo de forma automática, após você definir suas configurações, execute o seguinte job:

./nginx/nginx.sh configure

Nginx (arquivo do nosso sistema)

Como podemos ter mais de um sistema rodando no mesmo NGINX, devemos criar um arquivo de configuração para cada um. Vamos criar um arquivo para o blog wbotelhos.com:

sudo vim /etc/nginx/sites-enabled/wbotelhos.conf

E colar a seguinte configuração:

upstream app {
  server 127.0.0.1:5000;
  server 127.0.0.1:5001;
  server 127.0.0.1:5002;
}

server {
  # A porta no qual o servidor esta escutando as requisições.
  listen 80;

  # IP ou domínio definido para apontar para o nosso virtual host.
  server_name ec2-x-p-t-o.sa-east-1.compute.amazonaws.com 0.0.0.0;

  # Configurar o root para a pasta "public" é muito importante quando queremos utilizar arquivos
  # estáticos sem passar pelo Rails como é o caso dos meus plugins (wbotelhos.com/raty).
  # Como o root da aplicação esta apontando para "public", logo wbotelhos.com/raty aponta
  # para uma pasta dentro de "public/raty" que é acessada diretamente fora do Rails.
  # E o melhor é que se você acessar apenas wbotelhos.com com barra no final ou não
  # o que você definiu no seu routes.rb será processado: `root to: 'articles#index'`
  root /var/www/wbotelhos/current/public;

  # O "index" é complemento da configuração "root", onde ao acessar wbotelho.com/raty seremos
  # redirecionados automáticamente para a página public/raty/index.html.
  # Precisamos dessa configuração, pois não conseguimos acessar a página html diretamente.
  index index.html;

  # Tamanho máximo permitido para requisção indicado pelo Content-Length. Default: 1M.
  client_max_body_size 5M;

  location / {
    proxy_redirect   off;
    proxy_set_header Host $http_host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Real-IP  $remote_addr;

    if ($request_uri ~* "\.(ico|css|js|gif|jpe?g|png)\?[0-9]+$") {
      access_log off;
      add_header Cache-Control public;
      expires    max;
      break;
    }

    if (-f $request_filename/index.html) {
      rewrite (.*) $1/index.html break;
    }

    if (-f $request_filename.html) {
      rewrite (.*) $1.html break;
    }

    if (!-f $request_filename) {
      proxy_pass http://app;
      break;
    }
  }

  error_page  500 502 503 504  /500.html;

  location = /500.html {
    root /var/www/wbotelhos/current/public;
  }
}

Você deve substituir o Public DNS ec2-x-p-t-o.sa-east-1.compute.amazonaws.com pelo DNS da instância que você criou:

server_name ec2-x-p-t-o.sa-east-1.compute.amazonaws.com 0.0.0.0;

E então estamos utilizando o diretório wbotelhos, no qual o root será o diretório público:

root /var/www/wbotelhos/current/public;

Nginx (Upstart)

Vamos criar um arquivo de inicialização do Nginx utilizando o Upstart:

sudo vim /etc/init/nginx.conf

O conteúdo será o seguinte:

description 'nginx webserver'

start on startup
stop on shutdown

respawn
expect fork
exec /opt/local/sbin/nginx

Vamos garantir que o nosso usuário tem acesso aos arquivos de configuração:

sudo chown ubuntu:ubuntu /var/log/nginx/error.log
sudo chown ubuntu:ubuntu /etc/nginx/nginx.conf

E então verificar se tudo esta correto:

nginx -t

Bem provável ser lançado algumas mensagens não positivas.

nginx: [emerg] open() "/var/run/nginx.pid" failed (13: Permission denied)

Ainda não há PID criado, pois não estamos rodando o NGINX, ignore e inicie o serviço:

sudo start nginx
# nginx start/running, process 23757

Certifique-se que o serviço esta rodando:

ps aux | grep nginx

Para parar use o stop:

sudo stop nginx
# nginx stop/waiting

E para reinicar use o restart:

sudo restart nginx
# nginx stop/waiting
# nginx start/running, process 23757

Agora faça o teste acessando o DNS público pelo browser:

open http://ec2-x-p-t-o.sa-east-1.compute.amazonaws.com
# 404 Not Found --- nginx/1.5.8

Unicorn

Configurar o Unicorn é bem simples. Ele é uma gem que declaramos no Gemfile do nosso projeto. Vamos criar o seu arquivo de inicialização:

sudo vim /etc/init/unicorn.conf
description 'unicorn server'

pre-start script
  mkdir -p /var/run/unicorn
  chown ubuntu:ubuntu /var/run/unicorn
  chmod 770 /var/run/unicorn

  mkdir -p /var/log/unicorn
  chown ubuntu:ubuntu /var/log/unicorn
  chmod 770 /var/log/unicorn
end script

start on startup
stop on shutdown

exec sudo -u ubuntu -g ubuntu sh -c "cd /home/ubuntu/www/wbotelhos/current && RAILS_ENV=production GEM_HOME=/opt/local/ruby/gems bundle exec unicorn_rails -c /home/ubuntu/www/wbotelhos/config/unicorn.rb"

respawn

Repare que já apontamos alguns caminhos como a pasta current que manterá a versão corrente do nosso sistema e a pasta config que além de diversas configurações terá um arquivo de configuração do Unicorn.

Ainda não temos a pasta wbotelhos e nem a pasta config, então vamos criá-las:

mkdir -p /var/www/wbotelhos/config

E então podemos criar as configurações:

vim /var/www/wbotelhos/config/unicorn.rb
worker_processes 3

listen 5000
listen 5001
listen 5002

preload_app true

timeout 30

pid               '/var/www/wbotelhos/shared/pids/unicorn.pid'
stderr_path       '/var/www/wbotelhos/shared/log/unicorn.error.log'
stdout_path       '/var/www/wbotelhos/shared/log/unicorn.out.log'
working_directory '/var/www/wbotelhos/current'

Estas configurações basicamente são os caminhos dos arquivos de log e o diretório onde o servidor irá atuar, além da quantidade de processos e portas em que ele ficará escutando.

Deste modo estamos com o Ruby, NGINX e Unicorn prontos para rodar uma aplicação. É claro que iremos precisar de uma tarefa de deploy usando por exemplo, o Capistrano, que será o tema do próximo post. (: