Deployment de aplicações Python com Fabric

Uma coisa é fato: subir uma nova versão de uma aplicação é um procedimento que, se feito manualmente, costuma demandar tempo precioso, independente do tamanho da aplicação e dos frameworks utilizados no projeto. Não há o que discutir.

Na verdade, ninguém parecia se importar em automatizar tarefas “mecânicas” como o deployment, coisa que mudou de figura ao passo que metodologias ágeis — juntamente com técnicas como Integração Contínua — passaram a se tornar populares. Desde então, algum esforço vem sendo investido para criar ferramentas para automatização de muitas dessas tarefas.

Atualização: Trechos de código adaptados para as versões mais recentes do Fabric (22/06/2010).

Fabric: ferramenta Python para deployment

Apesar de já ter utilizado algumas dessas ferramentas no desenvolvimento de projetos em Java, plataforma na qual eu possuo uma experiência maior, eu ainda não conhecia nenhuma ferramenta em Python que me permitisse automatizar o deployment de uma aplicação.

Como eu estava começando a ficar de saco cheio de ter que atualizar os arquivos de uma aplicação Django via SCP, eu resolvi que já era hora de ir atrás de alguma ferramenta que me ajudasse a automatizar esse processo. Finalmente, depois de muita procura, eu encontrei Fabric, uma ferramenta mais ou menos parecida com um Capistrano “capado”.

A instalação do Fabric é bem simples para quem costuma usar o easy_install:

$ sudo easy_install Fabric

Basta executar esse comando para que os fontes da última versão do Fabric (0.0.9 no momento da publicação deste texto) sejam baixados e instalados.

Atenção: Quem utiliza máquinas Windows, provavelmente precisará do Cygwin, já que o Fabric assume o uso de ambientes Unix.

Fabric a 3.000 pés

Neste momento você já deve conseguir executar o comando fab no terminal. Se tudo correu bem até aqui, estamos prontos continuar.

Vamos começar criando o arquivo fabfile.py no diretório-raíz da aplicação. Coloque o seguinte conteúdo neste arquivo:

from fabric.api import *
 
env.project = 'project_name',
env.package = '$(project).zip',
 
# Remote servers
env.user  = 'usuario_ssh',
env.hosts = ['servidor1.com', 'servidor2.com'],
 
 
def deploy():
    "Deploys the application to the production servers."
    build()
 
def build():
    "Builds the application."
    prepare()
 
def prepare():
    "Prepares the build directory."
    clean()
 
def clean():
    "Removes the build directory."
    pass

Cada método definido no arquivo é equivalente a uma task do Capistrano. No início temos as variáveis env.* necessárias para o script. Destaque para a variável env.hosts, que indica os servidores nos quais os comandos remotos devem ser executados.

Ok, tente executar o comando fab -l no terminal, no mesmo diretório onde se encontra o arquivo fabfile.py. Se tudo correr bem, você receberá a seguinte saída:

$  fab -l
Available commands:
   build      Build the application.
   clean      Remove the build directory.
   deploy     Deploy the application to the production servers.
   help       Display Fabric usage help, or help for a given command.
   license    Display the Fabric distribution license text.
   list       Display a list of commands with descriptions.
   prepare    Prepare the build directory.
   set        Set a Fabric variable.
   shell      Start an interactive shell connection to the specified hosts.
   warranty   Display warranty information for the Fabric software.

Veja que as tasks que criamos estão disponíveis para serem executadas via linha de comando. Para executar as tasks clean e deploy, por exemplo, utilizaríamos o seguinte comando:

$  fab clean deploy

Simples, não é? Antes de vermos um exemplo de script completo, vamos conhecer os três principais métodos utilizados em scripts Fabric: local, run e put:

  • local: Serve para executar comandos na máquina local. Ex: local('mkdir build');
  • run: Igual ao local, mas executa os comandos nas máquinas remotas (indicadas na variável env.hosts);
  • put: Envia um arquivo para as máquinas remotas. Ex: put('arquivo.zip', '/home/user/app.zip').

Finalmente, o script completo!

Antes de mostrar o script que eu venho utilizando para fazer o deployment de uma aplicação Django, veja como seus arquivos e diretórios estão organizados: (isso deve ajudar a entender melhor o script)

Estrutura do projeto

Estrutura do projeto

E, finalmente, o script:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
 
"""Build script.
"""
 
from fabric.api import *
 
 
env.project = 'destaqueweb'
 
# Build output directory
env.build_path = 'build'
env.package    = '%s.zip' % env.project
env.local_path = '%s/%s' % (env.build_path, env.package)
 
# Media
env.media_src   = 'media'
env.media_root  = 'public_html'
env.media_path  = '%s/%s/%s' % (env.media_root, env.project, env.media_src)
env.media_build = '%s/%s' % (env.build_path, env.media_path)
 
# Application code
env.app_root  = 'wsgi_apps'
env.app_path  = '%s/%s' % (env.app_root, env.project)
env.app_build = '%s/%s' % (env.build_path, env.app_path)
 
# Remote servers
env.hosts       = ['destaquenet.com']
env.destination = '/home/destaquenet'
 
 
def clean():
    """Removes the build directory.
    """
    local('rm -fR %s' % env.build_path)
 
def prepare():
    """Prepares the build directory.
    """
    clean()
    local('mkdir -p %s' % env.media_build)
    local('mkdir -p %s' % env.app_build)
 
def build():
    """Builds the application.
    """
    local('cp -R %s/* %s/%s' % (env.project, env.build_path, env.app_path))
    local('cp -R %s/* %s/%s' % (env.media_src, env.build_path, env.media_path))
    local('find %s -name *.pyc -delete' % env.build_path)
    local('find %s -path *upload* -delete' % env.build_path)
    local('find %s -path *fixtures* -delete' % env.build_path)
    local('cd %s;zip -r %s %s %s' % (env.build_path, env.package, env.media_root, env.app_root))
 
def reload_app():
    """Reloads the app.
    """
    run('touch %s/%s/%s/index.wsgi' % (env.destination, env.media_root, env.project))
    run('python %s/%s/manage.py ping_google /sitemap.xml' % (env.destination, env.app_path))
 
def deploy():
    """Deploys the application to the production server.
    """
    build()
    put(env.local_path, env.destination)
    run('cd %s; unzip -uo %s; rm %s' % (env.destination, env.package, env.package))
    reload_app()

Este código deve ser facilmente entendido por quem tem trabalha com uma certa facilidade no terminal.

Basicamente o que o código faz é organizar os arquivos da aplicação para que estes fiquem dispostos da mesma forma utilizada pelo servidor de produção, enviar os arquivos dentro de um pacote ZIP para os servidores e descompactá-los no local correto, além de executar um touch no script WSGI para que o Apache recarregue a aplicação.

Tudo pronto. Agora basta executar fab deploy no terminal para subir a aplicação para o(s) servidor(es) desejado(s).

Fabric e ambiente de homologação

Uma coisa que vem se tornando comum é o uso de diferentes ambientes onde uma aplicação costuma ser implantada (ex: ambiente de homologação). Se sua empresa costuma trabalhar dessa forma, o Fabric ainda pode te ajudar.

Por exemplo, vamos supor que sua empresa possua um servidor de produção e um servidor de homologação. Podemos, então, adequar o script para que ele possa trabalhar com ambos os ambientes:

# ...
env.user  = 'production',
env.hosts = ['app.com']
 
def staging():
    # deploy to staging servers
    env.user  = 'staging'
    env.hosts = ['staging.app.com']
 
# other tasks...

Fazendo as mudanças mostradas acima, para executar o deployment no ambiente de homologação basta rodar o comando fab staging deploy; o deployment em ambiente de produção continua disponível através do comando fab deploy.

Isso é possível pois podemos substituir o valor de uma variável (ou configurar novas variáveis) a qualquer momento. Então, quando executamos o comando fab staging deploy, o Fabric executa a task staging, substituindo o valor das variáveis env.user e env.hosts, fazendo com que os comandos contidos na task deploy sejam executados nos servidores de homologação em vez de nos servidores de produção.

Sobre Daniel Martins

Fundador da Destaquenet, ele é graduado em Sistemas de Informação e desenvolve softwares como hobby e profissão desde 2000. Especializado na plataforma Java, ele utiliza a tecnologia há vários anos, sendo programador e desenvolvedor web certificado pela Sun Microsystems, recentemente adquirida pela Oracle. Também se interessa por assuntos ligados à cultura open source, metodologias ágeis, engenharia de software, frameworks e linguagens dinâmicas tais como Python, Ruby e Smalltalk.
Esta entrada foi publicada em Português, Programação, Tutoriais e marcada com a tag , , , , , , , . Adicione o link permanente aos seus favoritos.

7 respostas a Deployment de aplicações Python com Fabric

  1. Na verdade dá para automatizar muitas das tarefas que possam ser feitas através da execução comandos de terminal (locais e remotos).

  2. Lucas Fais disse:

    Muito bom!

    Pelo visto, dá pra fazer deploy de qualquer aplicação, mesmo que não seja Python, não é?

  3. Pingback: Destaquenet blog » Blog Archive » Integração Contínua de aplicações Python com Hudson

  4. Pingback: Fabric + VirtualEnv: uma combinação explosiva (no bom sentido) « Destaquenet blog

  5. Ruhan disse:

    Muito bom o artigo, vou estudar um pouco mais tanto o Fabric como o Capistrano pois estou exatamente com o problema que você relatou no início do artigo.

    Vai ser de grande ajuda, obrigado!

  6. Valeu Ruhan!

    Várias coisas mudaram no Fabric desde a publicação deste texto. Assim que possível, eu darei um jeito de atualizar o post.

    []s!

  7. Gilson Filho disse:

    Desse eu gostei. Ele é mais “Pythônico”. Fora que segue um pouco o ANT na automação de processos.

Deixe um Comentário

O seu endereço de email não será publicado Campos obrigatórios são marcados *

*

Você pode usar estas tags e atributos de HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="" highlight="">