Celery e Tarefas assícronas com Python

O Celery é um aplicação, escrita em Python, que permite que você delegue tarefas a diversos workers, localizados em outros servidores.

O Celery é uma forma bastante de realizar estes jobs ou tarefas de forma assíncrona, sem ter de quebrar muito a cabeça com conceitos complicados e difíceis de serem acertados.

Primeiramente, vou explicar como o Celery funciona, em termos de arquitetura.

Temos alguns componentes principais envolvidos:

  1. Aplicação – este carinha aqui é a sua aplicação normal, a qual o usuário ou outra máquina interage;
  2. Broker – este componente é uma fila de mensagens, utilizada para transportar mensagens entre os processos que estamos descrevendo aqui. O Celery permite o uso de diversos brokers, como redis e RabbitMQ. Usamos o redis por aqui, mas é uma preferência;
  3. Worker – este é o componente que trabalha de verdade. Qualquer tarefa agendada pela aplicação no broker, será executada pelo worker;
  4. Beat – é um componente opcional, mas funciona como um agendador de tarefas, que dispara de tempos em tempos as tarefas para o broker, como uma aplicação;

Com esta arquitetura, é bastante fácil escalar a coisa toda para funcionar em várias máquinas, bastando adicionar mais workers conforme a frequência das tarefas aumentam. O Celery é muito customizável e tem opções importantes, como controle de filas (algumas tarefas só podem ser processadas uma a uma, em uma única fila, sem concorrência alguma, enquanto outras podem ser paralelizadas), aplicaçoes de monitoramento (como o Flower) e extensões de biblioteca para os desenvolvedores (como o Jobtastic).

Vamos hoje dar um exemplo bastante simples, integrado a um projeto Django. Não vamos entrar em muitos detalhes de como subir os workers ou o broker, pois vai depender muito do seu ambiente.

Imagine que sua aplicação manda emails. Quase todas as aplicações (web) mandam emails, mas algumas mandam mais emails que outras e com maior frequência. Entre o ponto em que uma requisição feita pelo usuário é processada e uma resposta é retorna, vários segundos podem se passar e não queremos deixar o usuário esperando. Então decidimos delegar o envio do email, que é mais demorado, para uma tarefa assícrona no Celery.

Como fazer isto?

Bem, primeiramente é necessário configurar sua aplicação Celery. Fazemos (aqui na SIGMA) por meio de um arquivo celery.py, localizado junto wsgi.py do projeto. Ele é mais ou menos assim:

# coding: utf-8
"""Configuração inicial do Celery"""
from __future__ import absolute_import
import os
from django.conf import settings

if not "DJANGO_SETTINGS_MODULE" in os.environ:
    os.environ["DJANGO_SETTINGS_MODULE"] = "settings.local"  # aqui na SIGMA usamos settings separados para cada ambiente

from celery import Celery
app = Celery('nome-da-app')

app.config_from_object('django.conf:settings')
app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)

Com esta configuração batuta, podemos iniciar um worker, por exemplo. Além disso, usamos o autodiscover do Celery, que percorrerá todas as INSTALLED_APPS e cadastrará todas as tarefas dentro dos módulos tasks.py.

Ok, com o Celery configurado, podemos startar um worker.

celery -A projeto worker -l info

Agora, vamos criar nossa task e chamar ela de dentro de uma view qualquer. Crie um arquivo chamado tasks.pyem uma de suas apps, por exemplo, sistema.

Para definir a task, utilizaremos um decorador:

from projeto.celery import app  # lembre do nosso arquivo celery.py?


@app.task()
 def email_async(email, assunto, template, contexto):
    template_html = get_template(template)
    template_txt = get_template(template.replace('.html', '.txt'))
    if isinstance(contexto, dict):
        contexto = Context(contexto)

    contexto['email_header_image'] = get_random_image()
    html = template_html.render(contexto)
    txt = template_txt.render(contexto)
    if isinstance(email, basestring):
        email =[email]

    send_mail(assunto,
              txt,
              'remetente',
              recipient_list=email,
              html_message=html)

Com a tarefa definida, podemos invocá-la de forma assícrona. O Celery tem muitas formas de se invocar uma tarefa. Existe um negócio chamado canvas que permite você combinar as tarefas de forma assícrona, paralelas, etc. Confira a documentação do canvas.

Vá em uma de suas views que enviam emails:

from .tasks import email_async

def view_qualquer(request):

    # faça alguma coisa útil

    email_async.delay('novo email', 'sistema/email.html', {'variavel_contexo_a': 'foo', 'b': 'novo email maroto'})

O segredo da chamada assícrona está no método delay, que irá usar o broker para agendar a tarefa e futuramente será executada por um worker.

É um resumo muito curto para mostrar todas as capacidades do Celery, mas é um projeto muito bem mantido, utilizado por muita gente.

O exemplo que demos, apesar de parecer bobo, é bastante significativo quando estamos falando de uma aplicação que envia centenas ou milhares de emails por dia, diminuindo muito o impacto nos servidores de aplicação e permitindo que você lide com uma carga bem mais alta.

Iremos fazer uma pequena série sobre o Celery. Tem jeito de consultar o status da tarefa, agendar tarefas periódicas, controlar exceções e um monte de coisa legal.

Fique ligado e obrigado!

Automação de Infraestrutura com Puppet

Let the computer do the repetitious, the mundane – it will do a better job of it than we would. We’ve got more important and more dificult things to do.
Trecho retirado do livro Programador Pragmatico.

O profissional de desenvolvimento de software conta com um grande trunfo em sua manga que é frequentemente negligenciado, um trunfo que vem sendo utilizado vastamente por outras áreas em que sua complexidade é maior do que a encontrada na área de software. Com a automação pode ser alcançado o que economia chama de lucro, pois um profissional aumenta sua produtividade e a qualidade do que é produzido. Estes dois parametros trarão uma enorme mudança em sua carreira, então a partir deste momento se torne um profissional que se vale deste trunfo e se destaca dos demais, simplesmente aplicando em seu trabalho o que de melhor fazemos: automatizar tarefas.

Durante os ultimos anos vemos as práticas DevOps se tornando cada vez mais a cultura dos desenvolvedores e administradores. Neste artigo faremos uma comparação da utilização de uma das tecnologias de escrita de infraestrutura como código criada pela empresa Puppet, o produto escolhido se chama puppet-agent e vamos escrever em uma dsl de mesmo nome, que tenta tornar este processo tão simples como listar quais software serão utilizados.

#####Aplicamos esta prática em dois cenários:

#####Windows:

Neste cenário o cliente utiliza Windows, embora esta arquitetura seja compatível com ambientes linux o cliente tinha mais experiência com Windows e o utilizava em todos ambientes do desenvolvimento à produção. Para prover serviços de mapa e hospedar a aplicação web map é utilizado IIS 7.5, Geoserver, Postgresql e Postgis. Nós escolhemos o puppet-agent e o script será aplicado em uma maquina existente preparada para receber as instalações oriundas do script puppet.

O primeiro passo é instalar o puppet para poder executar o script, que como dito é uma dsl simples

A script que criamos é composta por resources, cada resource tem funções como: instalação de pacote, execução de comandos shell, agendamento de tarefas, administração de serviços, criação de usuários, operações no sistema de arquivos e etc.

Os resources podem ser combinados de forma a determinar qual é o resource requerido para que um outro seja executado, ou assim que determinado resource for executado ele pode notificar outro para que este seja executado.

Uma dificuldade ao utilizar o puppet para instalação de sistemas é que o windows não possui um gestor de pacotes, os instaladores são individuais e distribuídos individualmente por cada fornecedor. Desta forma o processo de instalação exige que em alguns casos seja utilizado o processo de instalação headless que é disponibilizado pela ferramenta de empacotamento utilizada pela empresa que provê o instalador do software. Infelizmente por não haver uma padrão isto é o que mais dá trabalho durante a redação do script.

Após as instalações é necessário configurar as aplicações instaladas, iniciar serviços ou agendar tarefas.

Basicamente utilizamos o resources exec, que permite executar comandos shell, o file, que permite copiar arquivos e o scheduled_task para agendar tarefas.

Com o exec nós configuramos o postgres, executamos os scripts sql de criação das feições espaciais, com o file copiamos arquivos, como por exemplo o diretório data_dir do geoserver e com o scheduled_task configuramos a tarefa de execução do geoserver.

Utilizamos também o Hiera para criar um arquivo de configuração externo e permitir que o script fosse executado independente dos diretórios em que os arquivos existiriam ou do local em que desejamos instalar.

A grande desvantagem foi o tempo que levamos para montar este script. É bastante trabalhoso e em por utilizarmos o windows neste cenário, tivemos pouca documentação disponível principalmente para as instalações headless de cada instalador. Porém quando este desafio foi ultrapassado a grande vantagem foi conseguir instalar com pouquissima dificuldade quatro maquinas diferentes, uma maquina na cloud (Windows Server 2012 R2 ) e três estações de trabalho com Windows 7. O primeiro grande beneficio é que temos a certeza que todas as maquinas possuem a mesma versão e configuração e uma vantagem é que nossa equipe é distribuída e podemos contar com o apoio de outros desenvolvedores que não estejam geográficamente perto e eles conseguirão replicar o ambiente com rapidez e poderão efetivamente apoiar o desenvolvimento do software.

#####Linux:

Montar a maquina servidora de um web map escrito em python e javascript, que consulta um banco de dados geográfico.

De cara podemos dizer que só há vantagens ao utilizar vagrant e puppet para montar ambientes em Linux (dist Ubuntu) não tivemos um décimo das dificuldades apresentadas no Windows e o tempo transcorrido para redigir os scripts foi muito menor.

O maior desafio que encontramos é que algumas versões disponíveis por padrão nos repositórios do apt-get podem estar com uma grande defasagem, o que pode induzir ao erro ao usar uma versão antiga que contém bugs que já foram resolvidos, porém uma vez que você configura o repositório correto do pacote e instala as versões mais recentes o restante é muito prático.

Nós utilizamos postgres e postgis e é excelente ter um repositório de pacotes, fizemos todas as configurações, criação do database e configuração da extensão postgis.

Uma novidade foi utilizar um provider diferente ao utilizar o resource package, pois utilizamos o pip3 para instalação dos pacotes python, porém não tivemos problema algum.

Não identifiquei desvantagens ao escrever o script puppet para uma maquina linux, basta ter atenção às versões que são instaladas pelos repositórios default do apt-get e na dúvida utilize o repositório da fornecedora do software.

Para não dizer que a experiência foi livre de problemas, perdemos um tempo para instalar corretamente o puppet, pois às maquinas ubuntu disponíveis não trazem mais esta instalação por padrão.

A grande vantagem neste caso é que a replicação para outros provider do vagrant se torna fácil, permitindo que eu utilize o virtualbox, vmware, amazon aws ou digital ocean.