No post anterior foi mostrada a importância de se colocar o projeto num servidor de controle de versões, mesmo logo após a sua criação. Algumas preocupações puderam ser rapidamente testadas e solucionadas, por exemplo o setup em máquinas de outros desenvolvedores.
Ainda nesta fase tão precoce, podemos ir mais além: testar o deploy da aplicação num ambiente production-like. Desta forma, cria-se uma cultura de, em cada iteração, testar de ponta-a-ponta o processo de desenvolvimento.
Um dos objetivos é antecipar questões e riscos que tradicionalmente só seriam atacados ao final do projeto. Caso surjam problemas de integração, ou dificuldades técnicas que possam colocar em risco a subida do sistema para produção, as causas podem ser prontamente diagnosticadas e tratadas o mais cedo possível antes que possam comprometer o sucesso do projeto.
Setup do Heroku
O Heroku é uma opção muito popular para o deploy de aplicações Rails, que consiste simplesmente em executar:
git push heroku master
ou seja, pushando o projeto para um repositório Git remoto no Heroku.
A preparação é semelhante a da criação de uma conta no Github. É necessário criar uma conta no Heroku, e fazer o upload de uma chave pública para administração da conta via terminal.
Instalação
O primeiro passo é instalar a gem do heroku:
gem install heroku
Chave pública
Em seguida, fazer o upload da chave pública:
heroku keys:add | |
Enter your Heroku credentials. | |
Email: fabio@miranti.net.br | |
Password: | |
Could not find an existing public key. | |
Would you like to generate one? [Yn] Y | |
Generating new SSH public key. | |
Uploading ssh public key /home/fabio/.ssh/id_rsa.pub |
Caso o heroku não encontre uma chave pública em ~/.ssh/id_rsa.pub, e também não exista a chave privada ~/.ssh/id_rsa correspondente, o próprio heroku é capaz de gerar estas chaves para nós (como mostrado no trecho acima).
Podemos também aproveitar a chave pública do github, bastando que ela esteja presente em ~/.ssh/id_rsa.pub para ser upada para o heroku.
Caso mais de uma chave esteja presente no diretório .ssh, o heroku pergunta qual deve ser usada:
1) heroku_test_id_rsa.pub | |
2) id_rsa.pub | |
Which would you like to use with your Heroku account? 2 | |
Uploading ssh public key /home/fabio/.ssh/id_rsa.pub |
Criação da aplicação no Heroku
Resolvida a questão das chaves, podemos criar a aplicação no heroku:
heroku create | |
Creating afternoon-robot-3025... done, stack is bamboo-mri-1.9.2 | |
http://afternoon-robot-3025.heroku.com/ | git@heroku.com:afternoon-robot-3025.git | |
Git remote heroku added |
git remote | |
heroku | |
origin |
Deploy de uma aplicação Rails no Heroku
Dispensa comentários:
git push heroku master | |
Counting objects: 67, done. | |
Compressing objects: 100% (46/46), done. | |
Writing objects: 100% (67/67), 26.27 KiB, done. | |
Total 67 (delta 5), reused 67 (delta 5) | |
-----> Heroku receiving push | |
-----> Ruby/Rails app detected | |
-----> Detected Rails is not set to serve static_assets | |
Installing rails3_serve_static_assets... done | |
-----> Configure Rails 3 to disable x-sendfile | |
Installing rails3_disable_x_sendfile... done | |
-----> Configure Rails to log to stdout | |
Installing rails_log_stdout... done | |
-----> Gemfile detected, running Bundler version 1.0.7 | |
Unresolved dependencies detected; Installing... | |
Using --without development:test | |
Fetching source index for http://rubygems.org/ | |
Installing rake (0.9.2.2) | |
Using bundler (1.0.7) | |
... | |
Installing rails (3.1.1) | |
Installing sass (3.1.10) | |
Installing sass-rails (3.1.4) | |
Installing sqlite3 (1.3.4) with native extensions | |
Installing uglifier (1.0.4) | |
Your bundle is complete! It was installed into ./.bundle/gems/ | |
-----> Compiled slug size is 6.3MB | |
-----> Launching... done, v4 | |
http://afternoon-robot-3025.heroku.com deployed to Heroku | |
To git@heroku.com:afternoon-robot-3025.git | |
* [new branch] master -> master |
Até mesmo para abrir o navegador, o heroku ajuda:
heroku open
Rackfile e Procfile
O Heroku consegue fazer o deploy de aplicações Rails devido à presença do arquivo config.ru na raiz do projeto. Trata-se de um arquivo Rack, que instrui ao Heroku a forma como a aplicação é iniciada:
# This file is used by Rack-based servers to start the application. | |
require ::File.expand_path('../config/environment', __FILE__) | |
run AppContatosRails::Application |
Procfile e Foreman
Uma alternativa ao Rack é criar um arquivo Procfile na raiz do projeto. Trata-se de um mecanismo de declarar quais comandos devem ser executados ao deployar na plataforma Heroku. Ele consiste de um conjunto de pares informando o tipo de processo e o comando para executar um determinado processo:
web: rails s | |
worker: QUEUE=* bundle exec rake resque:work | |
urgentworker: QUEUE=urgent bundle exec rake resque:work | |
clock: bundle exec clockwork clock.rb |
heroku scale
para ajustar como escalar a aplicação, baseados no Procfile.
Para executar localmente aplicações baseadas no Procfile, é necessário instalar o Foreman.
gem install foreman
Ao iniciar a aplicação com o foreman, ele inicia cada um dos processos declarados no Procfile:
foreman start | |
02:22:56 web.1 | started with pid 6323 | |
02:22:59 web.1 | => Booting WEBrick | |
02:22:59 web.1 | => Rails 3.1.1 application starting in development on http://0.0.0.0:3000 | |
02:22:59 web.1 | => Call with -d to detach | |
02:22:59 web.1 | => Ctrl-C to shutdown server | |
02:23:01 web.1 | [2011-11-15 02:23:01] INFO WEBrick 1.3.1 | |
02:23:01 web.1 | [2011-11-15 02:23:01] INFO ruby 1.9.2 (2011-07-09) [i686-linux] | |
02:23:01 web.1 | [2011-11-15 02:23:01] INFO WEBrick::HTTPServer#start: pid=6326 port=3000 |
Django e Heroku
Conforme anunciado neste post,
o Heroku também oferece suporte a Python, no stack* Cedar, baseado no Ubuntu 10.04.
* o Heroku oferece diferentes stacks - ambientes completos de deploy, incluindo o sistema operacional e bibliotecas associadas.
Para criar um ambiente no stack cedar, é necessário executar o comando:
heroku create --stack cedar
e o deploy também é feito via git push:
heroku create --stack cedar | |
Creating strong-stream-6920... done, stack is cedar | |
http://strong-stream-6920.herokuapp.com/ | git@heroku.com:strong-stream-6920.git | |
Git remote heroku added | |
git push heroku master | |
Counting objects: 39, done. | |
Compressing objects: 100% (26/26), done. | |
Writing objects: 100% (39/39), 8.29 KiB, done. | |
Total 39 (delta 14), reused 31 (delta 10) | |
-----> Heroku receiving push | |
! Heroku push rejected, no Rails or Rack app detected | |
To git@heroku.com:falling-journey-6271.git | |
! [remote rejected] master -> master (pre-receive hook declined) | |
error: failed to push some refs to 'git@heroku.com:falling-journey-6271.git' |
Porém, como se vê acima, o push é rejeitado, informando que o Heroku não consegue detectar nenhuma aplicação Rails/Rack. Alguns ajustes são necessários para conseguir realizar o deploy da aplicação Django no Heroku.
Procfile para rodar aplicações Django no Heroku
Aplicações que NÃO são baseadas no Rack podem ser deployadas no Heroku usando o Procfile. No caso de uma aplicação Django, basta criar um Procfile com o seguinte conteúdo:
web: python manage.py runserver
que a aplicação passa a ser inicializável via foreman:
foreman start | |
02:33:47 web.1 | started with pid 6483 | |
02:33:49 web.1 | Validating models... | |
02:33:49 web.1 | | |
02:33:50 web.1 | 0 errors found | |
02:33:50 web.1 | Django version 1.3.1, using settings 'App-Contatos-Django.settings' | |
02:33:50 web.1 | Development server is running at http://127.0.0.1:8000/ |
Agora que está tudo rodando localmente, basta apenas fazer o deploy e correr pro abraço, vai dar até pra sair mais cedo e pegar um cineminha:
git push heroku master | |
Counting objects: 45, done. | |
Compressing objects: 100% (30/30), done. | |
Writing objects: 100% (45/45), 8.86 KiB, done. | |
Total 45 (delta 16), reused 31 (delta 10) | |
-----> Heroku receiving push | |
-----> Python app detected | |
! Django app must be in a package subdirectory | |
! Heroku push rejected, failed to compile Python app | |
To git@heroku.com:strong-stream-6920.git | |
! [remote rejected] master -> master (pre-receive hook declined) | |
error: failed to push some refs to 'git@heroku.com:strong-stream-6920.git' |
Ainda bem que o Google e o Stack Overflow existem, e alguém já havia postado a solução para este problema: http://stackoverflow.com/questions/7974902/deploying-existing-django-app-on-heroku.
Para deployar a aplicação Django no Heroku, ela precisa estar organizada de tal forma que o Procfile e o requirements.txt fiquem na raiz do repositório, e o projeto Django deve ser um subdiretório a partir da raiz do repositório.
Com esta nova organização, é importante assegurar que o arquivo settings.py esteja com o valor correto para a propriedade ROOT_URLCONF:
ROOT_URLCONF = 'urls'
Ajustada a estrutura do projeto, aí sim, esperamos que o deploy seja bem sucedido.
git push heroku master | |
Counting objects: 49, done. | |
Compressing objects: 100% (33/33), done. | |
Writing objects: 100% (49/49), 9.42 KiB, done. | |
Total 49 (delta 17), reused 31 (delta 10) | |
-----> Heroku receiving push | |
-----> Python/Django app detected | |
-----> Preparing virtualenv version 1.6.4 | |
New python executable in ./bin/python | |
Installing setuptools............done. | |
Installing pip...............done. | |
-----> Django settings injection | |
Injecting code into App-Contatos/settings.py to read from DATABASE_URL | |
-----> Installing dependencies using pip version 1.0.2 | |
Downloading/unpacking Django==1.3.1 (from -r requirements.txt (line 1)) | |
Creating supposed download cache at /app/tmp/repo.git/.cache/pip_downloads | |
Storing download in cache at /app/tmp/repo.git/.cache/pip_downloads/http%3A%2F%2Fpypi.python.org%2Fpackages%2Fsource%2FD%2FDjango%2FDjango-1.3.1.tar.gz | |
Running setup.py egg_info for package Django | |
Requirement already satisfied (use --upgrade to upgrade): wsgiref==0.1.2 in /usr/local/lib/python2.7 (from -r requirements.txt (line 2)) | |
Installing collected packages: Django | |
Running setup.py install for Django | |
changing mode of build/scripts-2.7/django-admin.py from 600 to 755 | |
changing mode of /tmp/build_ylstetpmj562/bin/django-admin.py to 755 | |
Successfully installed Django | |
Cleaning up... | |
-----> Discovering process types | |
Procfile declares types -> web | |
-----> Compiled slug size is 7.7MB | |
-----> Launching... done, v5 | |
http://strong-stream-6920.herokuapp.com deployed to Heroku | |
To git@heroku.com:strong-stream-6920.git | |
* [new branch] master -> master |
Basta agora um heroku open
e ver se o navegador abre a página e... Ooops. Nada acontece:

A essas alturas, cineminha já era... Neste caso, nem com ajuda de Google e Stack Overflow surgiu alguma pista. Recorrendo ao comando
heroku logs
é possível visualizar o log da subida da aplicação no Heroku, tentando procurar alguma pista sobre o que está faltando:
2011-11-15T23:05:29+00:00 app[web.1]: raise ImproperlyConfigured("Error loading psycopg2 module: %s" % e) | |
2011-11-15T23:05:29+00:00 app[web.1]: django.core.exceptions.ImproperlyConfigured: Error loading psycopg2 module: No module named psycopg2 | |
2011-11-15T23:06:40+00:00 heroku[run.8]: State changed from created to starting | |
2011-11-15T23:06:45+00:00 heroku[run.8]: State changed from starting to complete | |
2011-11-15T23:06:56+00:00 heroku[run.9]: State changed from created to starting | |
2011-11-15T23:07:00+00:00 app[run.9]: Starting process with command `sudo apt-get install psycopg2` | |
2011-11-15T23:07:00+00:00 heroku[run.9]: Process exited | |
2011-11-15T23:07:01+00:00 heroku[run.9]: State changed from starting to complete | |
2011-11-15T23:07:39+00:00 heroku[run.8]: Process exited |
Googleando um pouco mais, surge este blog, com instruções simples para instalar localmente este módulo:
- Pré-requisito:
sudo apt-get install libpq-dev python-dev
- Instalação: adicionar ao requirements.txt a linha
psycopg2==2.4.2
- Executar
pip install -r requirements.txt
Após nova subida, é cruzar os dedos e... nada! Eis o novo log:
2011-11-15T23:27:42+00:00 heroku[web.1]: Starting process with command `python App-Contatos/manage.py runserver` | |
2011-11-15T23:27:42+00:00 app[web.1]: Validating models... | |
2011-11-15T23:27:42+00:00 app[web.1]: | |
2011-11-15T23:27:43+00:00 app[web.1]: 0 errors found | |
2011-11-15T23:27:43+00:00 app[web.1]: Django version 1.3.1, using settings 'App-Contatos.settings' | |
2011-11-15T23:27:43+00:00 app[web.1]: Development server is running at http://127.0.0.1:8000/ | |
2011-11-15T23:27:43+00:00 app[web.1]: Quit the server with CONTROL-C. | |
2011-11-15T23:27:43+00:00 heroku[web.1]: Error R11 (Bad bind) -> Process bound to port 8000, should be 47557 (see environment variable PORT) | |
2011-11-15T23:27:43+00:00 heroku[web.1]: Stopping process with SIGKILL | |
2011-11-15T23:27:43+00:00 heroku[web.1]: Process exited | |
2011-11-15T23:27:44+00:00 heroku[web.1]: State changed from starting to crashed |
Ao que o erro indica, o Heroku não faz o binding na porta 8000, e sugere pesquisar a variável de ambiente PORT. Um tanto enigmático. Recorrendo mais uma vez ao Google, eis uma pista melhor, nos comentários deste blog:
"Creating a Procfile and adding: "web: play run --http.port=$PORT $PLAY_OPTS" fixed the problem." (Ivar Abrahamsen)
o que nos conduz a alterar o Procfile para:
web: python manage.py runserver 0.0.0.0:$PORT
E finalmente:

Deploy de aplicação Django no Heroku - Resumo
- O projeto Django deve ser um subdiretório a partir da raiz que é comitada no Git
- Alterar o settings.py, fazendo
ROOT_URLCONF = 'url'
- Adicionar um Procfile na raiz do projeto contendo:
web: python manage.py runserver 0.0.0.0:$PORT
-
Instalar o psycopg2
- Para deployar no Heroku é suficiente adicionar ao requirements.txt:
psycopg2==2.4.2
- Para instalar localmente, é necessário:
- Instalar pré-requisitos
sudo apt-get install libpq-dev python-dev
- Invocar o PIP pra instalar o pycopg2
pip install -r requirements.txt
- Instalar pré-requisitos
- Para deployar no Heroku é suficiente adicionar ao requirements.txt:
- Deploy:
git push heroku master
Considerações Finais
As tentativas que envidamos para conseguir subir a aplicação Django no Heroku reforçam a tese da importância de testar o processo de desenvolvimento de ponta-a-ponta. Em cada iteração, é importante incluir o deploy para um ambiente production-like, para identificar dificuldades e riscos iterativamente, à medida que a aplicação é desenvolvida.