Jefferson 'shamanime' Venerando

Words and code

Passenger, Capistrano E Environment Variables

Capistrano

Para usar env vars com Capistrano, abra o arquivo /etc/ssh/sshd_config e adicione a seguinte linha:

1
PermitUserEnvironment yes

Não se esqueça de reiniciar o serviço:

1
$ sudo service ssh restart

Agora é só coloar suas variáveis no arquivo ~/.ssh/environment do usuário deployer (sem o export antes)!

RVM

Se você usa RVM, crie o arquivo /usr/local/my_ruby_wrapper_script com o conteúdo:

1
2
3
4
5
#!/bin/sh
export MYSQL_WEB_PROD_DB="db"
export MYSQL_WEB_PROD_USR_NAME="usuario"
export MYSQL_WEB_PROD_USR_PASS="senha"
exec "/usr/local/rvm/rubies/ruby-1.9.3-p392/bin/ruby" "$@"

Troque o parâmetro do exec para o caminho do seu ruby.

Dê as permissões para o arquivo:

1
$ sudo chmod +x /usr/local/my_ruby_wrapper_script

E troque altere a linha do nginx.conf (ou da sua config do Apache) para:

1
passenger_ruby /usr/local/my_ruby_wrapper_script;

Reinicie o Nginx e Passenger, e pronto!

Ubuntu 12.04 Post Install

Configuração inicial

Logue como root:

1
ssh [email protected]

Configure o hostname:

1
2
# echo "plato" > /etc/hostname
# hostname -F /etc/hostname

Altere o arquivo /etc/hosts:

1
2
3
127.0.0.1                       localhost.localdomain    localhost
12.34.56.78                     plato.example.com        plato
2600:3c01::a123:b456:c789:d012  plato.example.com        plato

Configure a timezone para America/Sao_Paulo:

1
# dpkg-reconfigure tzdata

Atualize o software:

1
2
# apt-get update
# apt-get upgrade --show-upgraded

Instale alguns pacotes:

1
2
3
4
5
6
# apt-get -y install linux-headers-$(uname -r) build-essential
# apt-get -y install zlib1g-dev libssl-dev libreadline-gplv2-dev
# apt-get -y install imagemagick
# apt-get -y install libmagickwand-dev
# apt-get -y install libcurl4-openssl-dev
# apt-get -y install vim

Usuáiro padrão

Crie um usuário padrão:

1
# adduser shaman

Adicione o usuário no grupo de sudo:

1
# usermod -aG sudo shaman

Configure o SSH no arquivo /etc/ssh/sshd_config:

1
PermitRootLogin no

Reinicie o serviço SSH:

1
# service ssh restart

Localmente gere seu par de chaves se ainda não tiver:

1
ssh-keygen

Copie as chaves para o host:

1
ssh-copy-id -i ~/.ssh/id_rsa.pub [email protected]

Entre na máquina sem root:

1
ssh [email protected]

RVM, MySQL e NodeJS

Instale RVM system wide:

1
$ \curl -#L https://get.rvm.io | sudo bash -s stable --autolibs=3

Adicione o usuário no grupo rvm (depois saia e conecte novamente):

1
$ sudo usermod -aG rvm shaman

Crie o arquivo /etc/gemrc:

1
2
install: --no-rdoc --no-ri
update:  --no-rdoc --no-ri

Instale o Ruby 1.9.3-p392:

1
$ rvm install ruby-1.9.3-p392

Instale o MySQL:

1
$ sudo apt-get -y install mysql-server mysql-client mysql-common libmysqlclient-dev

Instale o NodeJS:

1
2
3
4
5
$ sudo apt-get -y install software-properties-common
$ sudo apt-get -y install python-software-properties python g++ make
$ sudo add-apt-repository ppa:chris-lea/node.js
$ sudo apt-get update
$ sudo apt-get -y install nodejs

Passenger e Nginx

Instale o passenger:

1
$ gem install passenger

Baixe e extraia os módulos pra upload:

(http://wiki.nginx.org/NginxHttpUploadProgressModule)

1
2
3
$ wget -O upload-progress https://github.com/masterzen/nginx-upload-progress-module/tarball/v0.9.0
$ tar -zxf v0.9.0
$ mv masterzen-nginx-upload-progress-module-a788dea/ progress-module

Baixe e extraia o Nginx:

1
2
$ wget http://nginx.org/download/nginx-1.3.15.tar.gz
$ tar -zxf nginx-1.3.15.tar.gz

Crie um usuário para o Nginx:

1
$ sudo adduser --system --no-create-home --disabled-login --disabled-password --group nginx

Instale o Nginx com o Passenger:

1
$ rvmsudo passenger-install-nginx-module

Escolha a opção 2, e passe os seguintes argumentos na instalação:

1
--user=nginx --group=nginx --add-module=/home/shaman/tools/progress-module

Crie um script de inicialização para o Nginx:

1
2
3
4
5
$ wget -O init-deb.sh http://library.linode.com/assets/660-init-deb.sh
$ sudo mv init-deb.sh /etc/init.d/nginx
$ sudo chmod +x /etc/init.d/nginx
$ sudo /usr/sbin/update-rc.d -f nginx defaults
$ sudo /etc/init.d/nginx start

Edite o arquivo /opt/nginx/nginx.conf e coloque dentro do bloco http {}:

1
include sites-enabled/*;

Crie as pastas necessárias:

1
2
$ sudo mkdir /opt/nginx/conf/sites-available
$ sudo mkdir /opt/nginx/conf/sites-enabled

Para adicionar um site:

1
2
3
4
$ cd /opt/nginx/conf/sites-available
$ sudo touch app.com
$ sudo ln -s /opt/nginx/conf/sites-available/app.com /opt/nginx/conf/sites-enabled/
$ sudo /etc/init.d/nginx restart

Quebrando Paradigmas - Minha Palestra No MongoDB SP 2012

Tive o privilégio de ser chamado para palestrar no MongoDB São Paulo, foi o primeiro evento grande que eu participei.

Na minha apresentação eu mostrei o que são paradigmas, como eles são formados e os benefícios de sair da sua zona de conforto e abraçar uma nova tecnologia pra aumentar a produtividade e obter ótimos resultados.

O vídeo foi liberado:

Se não conseguir visualizar, clique aqui.

Aqui estão os slides:

Rails + Carrierwave + Unicorn + Nginx + Progress Module

No último post eu expliquei como instalar e configurar o nginx com upload e upload progress module. Agora vamos fazer nossa aplicação funcionar com o Carrierwave e Unicorn.

Configurando o nginx

Primeiro vamos alterar o arquivo de configuração do nginx da nossa aplicação, abra o arquivo /opt/nginx/conf/sites-available/app.com no seu editor de textos favorito e adicione o seguinte conteúdo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
upstream app_unicorn {
  server unix:/tmp/unicorn.app.com.sock fail_timeout=0;
}

upload_progress proxied 1m;

server {
  listen 80;
  server_name arquivos.app.com www.arquivos.app.com;

  if ($host = 'www.arquivos.app.com' ) {
    rewrite  ^/(.*)$  http://arquivos.arquivos.app.com/$1  permanent;
  }

  root /srv/rails/app.com/current/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://app_unicorn;
  }

  # Rota para uploads da aplicação
  location /arquivos {
    # Pra onde o nginx envia a requisição quando o upload terminar
    upload_pass @unicorn;

    # Local em que o nginx vai subir o arquivo temporario
    upload_store /srv/rails/app.com/current/public/uploads/tmp 1;

    # Permissão nos arquivos enviados
    upload_store_access user:rw group:rw all:r;

    # Campos passados no request body para o Rails
    upload_set_form_field $upload_field_name[original_filename] "$upload_file_name";
    upload_set_form_field $upload_field_name[content_type] "$upload_content_type";
    upload_set_form_field $upload_field_name[path] "$upload_tmp_path";

    upload_pass_form_field "^X-Progress-ID$|^authenticity_token$";

    upload_cleanup 400 404 499 500-505;

    track_uploads proxied 30s;
  }

  location /uploads {
    # Força download dos arquivos que estiverem dentro de uploads
    if ($request_filename !~* ^.*?\.(jpg)|(png)|(gif)) {
      add_header Content-Type "application/octet-stream";
      add_header Content-Disposition "attachment; filename=$1";
    }
  }

  location ^~ /progress {
    upload_progress_json_output;
    report_uploads proxied;
  }

  client_max_body_size 4G;
  keepalive_timeout 10;
  error_page 500 502 503 504 /500.html;
  location = /500.html {
    root /srv/rails/app.com/current/public;
  }
}

O arquivo está bem comentado e explicado, tem a configuração do Unicorn e a parte que nos interessa do upload.

A aplicação responde com o progresso do arquivo enviado na url /progress, e eu uso a url /uploads pra forçar o download dos arquivos que foram enviados.

Mas ainda é necessário criar os diretórios temporários que o nginx usará:

1
2
3
$ cd /srv/rails/app.com/current/public/uploads/tmp
$ mkdir 0 1 2 3 4 5 6 7 8 9
$ sudo chown -R nginx:nginx /srv/rails/app.com/current/public/uploads/tmp

Configurando a aplicação Rails

Agora é a hora de configurar algumas partes da nossa aplicação, primeiro vamos alterar o Model que receberá o upload:

1
2
3
4
5
6
7
8
9
10
11
# app.com/app/models/arquivo.rb
class Arquivo
  include Mongoid::Document
  # ...

  attr_accessible :arquivo, :original_name, :content_type, :path

  mount_uploader :arquivo, ArquivoUploader

  # ...
end

Agora criamos o ArquivoUploader.rb:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# app.com/app/uploaders/arquivo_uploader.rb
# encoding: utf-8
require 'carrierwave/processing/mime_types'
class ArquivoUploader < CarrierWave::Uploader::Base
  include CarrierWave::MimeTypes

  process :set_content_type

  def move_to_store
    true
  end

  # Choose what kind of storage to use for this uploader:
  storage :file

  # Override the directory where uploaded files will be stored.
  # This is a sensible default for uploaders that are meant to be mounted:
  def store_dir
    "#{Rails.root}/public/uploads"
  end
end

Criamos então uma rota para receber o formulário de arquivos:

1
2
# app.com/config/routes.rb
resources :arquivos, :only => [:new, :create]

E a ação correspondente no Controller para salvar o arquivo enviado:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# app.com/app/controllers/arquivos_controller.rb
  def create
    @arquivo = Arquivo.new(params[:arquivo])

    # Para receber o arquivo do nginx em produção
    if Rails.env != "development"
      @arquivo.arquivo = ActionDispatch::Http::UploadedFile.new(
        filename: params['arquivo']['arquivo']['original_filename'],
        tempfile: File.open(params['arquivo']['arquivo']['path'])
      )
    end

    if @arquivo.save
      respond_to do |format|
        format.html {
          render :json => @arquivo,
          :content_type => 'text/html',
          :layout => false
        }
        format.json {
          render :json => @arquivo
        }
      end
    else
      render :json => @arquivo.errors, :status => :unprocessable_entity
    end
  end

A parte mágica aqui é simular um ActionDispatch::Http::UploadedFile sendo passado para o Carrierwave. Dessa forma, a aplicação recebe o arquivo do nginx e o Carrierwave move o arquivo do diretório temporário para o store_dir configurado anteriormente.

Instalando Nginx Com Upload Module E Upload Progress

Quando o assunto é upload de arquivos a dor de cabeça é quase inevitável. Algumas semanas atrás precisei construir um sistema para upload de arquivos grandes pra um cliente, e durante o caminho aprendi alguns truques úteis.

Esse artigo faz parte de uma série onde mostrarei como instalar e configurar uma aplicação Rails + Carrierwave com nginx + upload module + upload progress em produção com Unicorn.

Por que se importar?

Sem o upload module, a aplicação Rails receberá os dados brutos do upload e analisará todo o conteúdo antes de cuidar do arquivo. Para grandes arquivos isso pode ser bem lento, já que o Ruby estará fazendo todo o trabalho.

Com o upload module, o parsing do arquivo acontece em C através do nginx antes da sua aplicação em Ruby entrar em ação. O módulo coloca o arquivo em um diretório temporário e retira todos parâmetros multipart da requisição POST, os substituindo por parâmetros que você poderá usar na aplicação para pegar o nome e localização do arquivo no disco. Quando a requisição chegar na sua aplicação, todo trabalho pesado já foi concluído e o arquivo está pronto pra ser usado.

O upload progress module informa o progresso do upload, e é um ótimo parceiro do upload module.

Instalando o nginx com os módulos

Baixe e extraia o código fonte do nginx, upload module e upload progress module.

Eu não gosto de rodar o nginx com meu usuário ou root, então eu criei um usuário só pra ele:

1
$ sudo adduser --system --no-create-home --disabled-login --disabled-password --group nginx

Agora compile o nginx com os módulos, note que eu alterei o usuário, grupo e local de instalação do nginx:

1
2
3
4
$ cd <caminho do source do nginx>
$ ./configure --prefix=/opt/nginx --user=nginx --group=nginx --with-http_ssl_module --add-module=<caminho do upload module> --add-module=<caminho do upload progress module>
$ make
$ sudo make install

Nota: Se você tiver erros por causa de algum módulo, utilize o seguinte comando para o configure:

1
$ CFLAGS=-Wno-unused-but-set-variable ./configure --prefix=/opt/nginx --user=nginx --group=nginx --with-http_ssl_module --add-module=<caminho do upload module> --add-module=<caminho do upload progress module>

Crie o arquivo de inicialização do nginx

O Linode tem um bom arquivo para iniciar o nginx:

1
2
3
4
5
$ wget -O init-deb.sh http://library.linode.com/assets/660-init-deb.sh
$ sudo mv init-deb.sh /etc/init.d/nginx
$ sudo chmod +x /etc/init.d/nginx
$ sudo /usr/sbin/update-rc.d -f nginx defaults
$ sudo /etc/init.d/nginx start

Pequenas configurações

Eu gosto de manter o nginx funcionando com a mesma configuração de sites-available e sites-enabled que o Apache usa. Pra isso, entre no local de instalação do nginx e crie os diretórios:

1
2
3
$ cd /opt/nginx/conf
$ sudo mkdir sites-enabled
$ sudo mkdir sites-available

Abra o arquivo /opt/nginx/conf/nginx.conf no seu editor de texto favoritos e altere o bloco http para ficar parecido com o meu:

nginx configuration file
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
http {
    include       mime.types;
    default_type  application/octet-stream;

    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';

    #access_log  logs/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    #gzip  on;
    gzip on;
    gzip_http_version 1.1;
    gzip_comp_level 2;
    gzip_types    text/plain text/html text/css
                  application/x-javascript text/xml
                  application/xml application/xml+rss
                  text/javascript;

    server {
        listen       80;
        server_name  localhost;

        # if no listen is specified, all IPv4 interfaces on port 80 are listened to
        # to listen on both IPv4 and IPv6 as well, listen [::] and 0.0.0.0 must be specified. 
        server_name _;
        return 444;
    }

    include sites-enabled/*;
}

Agora só nos resta criar o arquivo de configuração da nossa aplicação e criar o link simbólico para ele:

1
2
3
4
$ cd /opt/nginx/conf/sites-available
$ sudo touch app.com
$ sudo ln -s /opt/nginx/conf/sites-available/app.com /opt/nginx/conf/sites-enabled/
$ sudo /etc/init.d/nginx restart

Pronto, nginx instalado com os módulos e devidamente configurado!

TS3 Server No Ubuntu

Um pequeno tutorial sobre como instalar o TeamSpeak 3 no Ubuntu 12.04 LTS.

Crie o usuário

Você precisa criar um usuário sobre o qual o TeamSpeak 3 irá rodar:

1
$ sudo adduser --disabled-login teamspeak

Faça o download e extraia

Agora faça o download do software (64 bits no meu caso) e descompacte:

1
2
$ wget http://teamspeak.gameserver.gamed.de/ts3/releases/3.0.5/teamspeak3-server_linux-amd64-3.0.5.tar.gz
$ tar -zxf teamspeak3-server_linux-amd64-3.0.5.tar.gz

Mova os arquivos para um lugar mais apropriado, e mude as permissões da pasta:

1
2
$ sudo mv teamspeak3-server_linux-amd64 /opt/ts3
$ sudo chown -R teamspeak /opt/ts3

Adicione o script de inicialização

Se você olhar dentro da pasta /opt/ts3, irá encontrar um script para iniciar/desligar o servidor chamado ts3server_startscript.sh.

Crie o seguinte arquivo /etc/init.d/teamspeak com seu editor favorito e adicione o seguinte conteúdo:

/etc/init.d/teamspeak
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#! /bin/sh
### BEGIN INIT INFO
# Provides:          teamspeak
# Required-Start:    networking
# Required-Stop:
# Default-Start:     2 3 4 5
# Default-Stop:      S 0 1 6
# Short-Description: TeamSpeak Server Daemon
# Description:       Starts/Stops/Restarts the TeamSpeak Server Daemon
### END INIT INFO

set -e

PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
DESC="TeamSpeak Server"
NAME=teamspeak
USER=teamspeak
DIR=/opt/ts3
DAEMON=$DIR/ts3server_startscript.sh
#PIDFILE=/var/run/$NAME.pid
SCRIPTNAME=/etc/init.d/$NAME

# Gracefully exit if the package has been removed.
test -x $DAEMON || exit 0

cd $DIR
sudo -u teamspeak ./ts3server_startscript.sh $1

Modifique as permissões do arquivo e inicie o servidor:

1
2
$ sudo chmod 755 /etc/init.d/teamspeak
$ /etc/init.d/teamspeak start

Pronto! Lembre-se de anotar o token para ganhar permissões em seu primeiro acesso!

Se você possuir um arquivo license.dat é só colar na pasta /opt/ts3 e reiniciar o servidor ;-)

Instalando ImageMagick No Ubuntu

ImageMagick é um utilitário que uso em muitos projetos, e sempre foi problema pra ser instalado.

Finalmente consegui instalar sem complicações!

Instalando as dependências…

Está usando Ubuntu 12.04 desktop? Então primeiro instale o aptitude com sudo apt-get install aptitude!

1
2
3
$ sudo aptitude install libperl-dev
$ sudo aptitude install libmagickwand-dev
$ sudo aptitude install libmagickcore-dev libmagick++-dev

… e agora o ImageMagick!

1
2
3
4
5
6
7
$ wget http://www.imagemagick.org/download/ImageMagick-6.7.7-0.tar.gz
$ tar -vzxf ImageMagick-6.7.7-0.tar.gz
$ cd ImageMagick-6.7.7-0
$ ./configure
$ make
$ sudo make install
$ sudo ldconfig /usr/local/lib

Agora você pode instalar suas gemas em paz! ;-)

Como Eu Expliquei REST Pra Minha Namorada

Esse é um assunto complicado, em vários lugares há publicações sobre REST, RESTful e outros termos derivados. E do outro lado da moeda, existe o bom e velho SOAP. E até recentemente, li um artigo dizendo que um é ‘evolução’ do outro.

Mas, o que é esse tal de REST? De onde ele veio? O que faz? Como funciona? Na apresentação abaixo, você aprenderá um pouco mais sobre esse assunto e entenderá a relação do REST com o HTTP.

NoSQL E MongoDB

No que você pensa quando ouve falar em Banco de Dados? Não fique surpreso se o termo SQL for um top hit da sua lista, junto com MySQL, PostgreSQL, SQLite, Firebird e vários outros nomes e termos que se associam com o modelo relacional.

Mas outro termo está ficando cada vez mais exposto hoje em dia: NoSQL, uma classe de banco de dados que não possui interface SQL e não se preocupam muito com garantias ACID.

Na apresentação abaixo, você aprenderá onde surgiu o termo, qual o seu papel, os tipos de banco de dados não-relacionais e uma comparação entre um banco de dados relacional e o MongoDB - o banco de dados primário que eu uso em produção.