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 [email protected]{{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. (:
Washington,
Havia um erro no meu /etc/init/nginx.conf =.=' ... na hora de copiar e colar esqueci alguns caracteres xD.
Washington,
Fiz "sudo nginx -t" e funcionou:
[email protected]:~$ sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
Mas quando uso "sudo nginx stop":
stop: Unknown job: nginx
Em seguida usei "sudo nginx -s reload":
nginx: [error] invalid PID number "" in "/var/run/nginx.pid"
Depois que fiz o "sudo nginx -t" ele cria o arquivo nginx.pid, mesmo assim o PID é inválido. Fiz "cap deploy:start" e o erro do log do nginx mudou:
2013/02/10 10:39:14 [notice] 3958#0: signal process started
2013/02/10 10:39:14 [error] 3958#0: open() "/var/run/nginx.pid" failed (2: No such file or directory)
2013/02/10 10:39:42 [notice] 3964#0: signal process started
2013/02/10 10:39:42 [error] 3964#0: invalid PID number "" in "/var/run/nginx.pid"
É o mesmo que apresenta quando faço "sudo nginx -s reload".
Você tem alguma ideia do que sejao problema do PID? Eu já apaguei o nginx.pid e recriei de novo com o "sudo nginx -s reload" e continua com o erro.
Washington,
Tentei iniciar a aplicação com deploy:start e apresentou o mesmo erro que ocorreu com o Igor e fui ver o error.log do niginx:
2013/02/08 21:57:10 [warn] 1120#0: the "user" directive makes sense only if the master process runs with super-user privileges, ignored in /etc/nginx/nginx.conf:2
2013/02/08 21:57:10 [emerg] 1120#0: open() "/var/run/nginx.pid" failed (13: Permission denied)
2013/02/08 21:59:25 [warn] 1129#0: the "user" directive makes sense only if the master process runs with super-user privileges, ignored in /etc/nginx/nginx.conf:2
2013/02/08 21:59:25 [emerg] 1129#0: open() "/var/run/nginx.pid" failed (13: Permission denied)
2013/02/08 22:00:17 [warn] 1138#0: the "user" directive makes sense only if the master process runs with super-user privileges, ignored in /etc/nginx/nginx.conf:2
2013/02/08 22:00:17 [emerg] 1138#0: mkdir() "/opt/local/nginx/1.3.9/uwsgi_temp" failed (13: Permission denied)
~
Na parte do tutorial onde você pedi para mudarmos a permissão do caminho "/var/run/nginx.pid" só consegui via:
chown -R ubuntu:ubuntu /var/run/nginx.pid
Mesmo mudando as permissões, permanece o erro. O nginx não criou o arquivo PID, mas quando faço "nginx -V" ele apresenta como se estivesse ativo/funcionando.
Quando faço "nginx -t", apresenta este erro:
[email protected]:~$ nginx -t
nginx: [warn] the "user" directive makes sense only if the master process runs with super-user privileges, ignored in /etc/nginx/nginx.conf:2
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: [emerg] mkdir() "/opt/local/nginx/1.3.9/uwsgi_temp" failed (13: Permission denied)
nginx: configuration file /etc/nginx/nginx.conf test failed
Pronto, só precisei dá reboot! =D
Washington,
Estou seguindo seus passos na integra, entretanto na parte do "ruby -v", apresenta como se não tivesse instalado:
[email protected]:~$ ruby -v
The program 'ruby' can be found in the following packages:
Isso também ocorre com o nginx. O que eu preciso fazer?
Cara, parabéns pelos posts do seu blog.
Realmente são muito bons e me ajudaram bastante.
Ótimo post
Nós também usamos nginx+unicorn, mas na nossa configuração queríamos ter a possibilidade de usar versões diferentes de ruby e, principalmente, separar os gemsets de cada aplicação para não haver problemas de dependências. Naturalmente usamos uma instância large na amazon com várias aplicações ruby e por isso vem essa necessidade. Em máquina que rodam uma única app o que você fez atende bem!
Segue o procedimento em um gist: https://gist.github.com/2408305
Espero ter ajudado, se algo não estiver muito claro ou não funcionar pode me contatar.
Abraços!
Oi Saulo Arruda,
Muito legal a sua solução. Onde trabalho não usamos o RVM, mas estamos pensando em migrar para o Unicorn. De qualquer forma é um bom tutorial para necessidades futuras.
Valeu!
Então olhando a saida do Capistrano , depois do primeiro deploy , teve o seguinte erro:
Oi Igor,
Dá uma analisada no log da aplicação (production.log), do Unicorn (unicorn.error.log e unicorn.out.log) e do NGINX (access.log, error.log).
As vezes precisamos remover o arquivo de PID do Unicorn e dar restart.