Microframework contra “Baterias Incluídas”

Python é uma linguagem de programação que tem a fama existir mais frameworks web que palavras reservadas, isso se reflete em uma diversidade de opções para os mais diversos gostos. Talvez isso seja um reflexo da diversidade de pessoas que utilizam o Python para as mais diversas finalidades.

Quando iniciei no desenvolvimento web, comecei com PHP, da forma mais simples possível, montando cada página em arquivos separados com alguns includes para não repetir muito o código. Quando migrei para Python, o primeiro impacto foi ter que utilizar algum framework e não seguir a forma de cada página num arquivo, até poderia manter os arquivos utilizando CGI, mas não teria desempenho, ou escrever o WSGI diretamente, mas acabaria criando outro framework para facilitar o processo.

Comecei a aprender o Django, achei legal, porém foi complicado por ser muito diferente do que eu estava acostumado e passava mais tempo consultando a documentação que programando efetivamente. Com a filosofia de “baterias incluídas”, o Django tem incorporado bibliotecas para as funcionalidades mais comuns de uma página ou aplicação web pode precisar, como formulário, acesso a banco de dados relacionais, paginação, internacionalização…

Outra opção que temos é utilizar um microframework, que auxilie apenas no WSGI e utilizar outras bibliotecas para montar a base da aplicação, como no caso do Flask. Ele vem por padrão com outras bibliotecas, como o Jinja 2 para auxiliar a escrever o html das páginas e caso precise de banco de dados ou formulários, basta instalar alguma biblioteca como o SQLAlchemy e o WTForms.

A primeira coisa que pode ser notada ao comparar esses dois modelos é a complexidade, com certeza um microframework é mais simples e fácil de aprender, uma vez que você está focado apenas em como interagir com o servidor ou protocolo HTTP, não tem que se preocupar com banco de dados por exemplo.

O primeiro ponto contra o microframework é a necessidade do programador conheçer ou procurar outras bibliotecas para partes específicas da aplicação, como persistência de dados. Muitas vezes isso pode levar ao desenvolvimento de algo que já está pronto e disponível por desconhecimento. Porém o programador não fica restrito ao que o framework suporta, podendo adotar qualquer tipo de biblioteca, diferente do Django que por exemplo não suporta oficialmente nenhum banco NoSQL, é possível utilizá-los, porém você não conseguirá integrá-los nativamente com os models e forms. Além de que utilizar algum framework específico pode aproveitar melhor as funcionalidades de um banco de dados, em vez de funções genéricas suportada por todos.

Por outro lado, uma vantagem de você ter um framework que define as bibliotecas é a possibilidade de integração das mesmas, como no caso do Django, com um model escrito, é extremamente fácil criar um form baseado no mesmo, com validação, ou fazer a migração do esquema da tabela no banco sem precisar escrever tudo na mão ou duplicar o código e lógicas. Também não é necessário sair procurando bibliotecas na internet, e você terá tudo em apenas uma documentação, que na hora de buscar ajuda evita respostas do tipo com a biblioteca tal funciona ou ter que conhecer mais de uma biblioteca que fazem a mesma tarefa para decidir qual das duas utilizar.

Microframeworks e “baterias incluídas” são abordagens opostas, cada uma pode se sair melhor que a outra de acordo com o caso. Se você tiver que desenvolver um sistema que necessite de bibliotecas que o Django oferece e se encaixe na forma dele de resolver os problemas, com certeza é uma ótima opção, uma vez que você terá as bibliotecas integradas e tudo pronto para utilizar. Caso o sistema seja mais simples, não necessitando de todas as bibliotecas oferecidas pelo Django, ou tenha necessidades mais específicas, o Flask começa a ganhar vantagens, uma vez que o fato de ser reduzido pode deixá-lo mais leve ou até ter uma configuração inicial mais rápida.

Com certeza tem o conhecimento das duas abordagens é importante na hora da decisão do framework, nada pior que durante o desenvolvimento o framework ficar atrapalhando, por ele não ter foco para um determinado fim, ou ser tornar burocrático demais para coisas simples. Para quem estiver conhecendo um framework como o Django e acha que algumas coisas seriam mais práticas fazer na mão, tente visualizar todo o processo, que em algum ponto será facilitado por ser desta forma ou mais prático, porém vai necessitar de algum tempo para acostumar.

Anúncios

Docker

Uma das novidades da versão 3.8 do kernel Linux é o superte ao LXC (Linux Containers), apesar de ser possível utilizar esse recurso diretamente, existem algumas alternativas que facilitam seu uso como o Docker.

Os Linux Containers nada mais são que uma evolução do chroot, que antigamente era muito utilizado para rodar o bind de forma isolada do sistema por questões de segurança (procedimento na wiki do Debian). O chroot simplesmente troca o root do sistema para aquela aplicação de forma que todos os caminhos absolutos comecem de um determinado diretório e não seja possível acessar os arquivos que não estiverem de baixo do mesmo. Está técnica também é conhecida por enjaulamento (chroot jail).

Apesar do isolamento na parte de sistema de arquivos pelo chroot, nem tudo do processo fica isolado, como a pilha de rede, que por exemplo ainda é compartilhada. O Linux Container veio para melhor isso e o Docker para facilitar seu uso.

Primeira coisa para se entender do Docker é que ele só funciona no kernel Linux para executar containers Linux, apesar de existir versão no site oficial para Windows e Mac, por trás é utilizado uma VM com o Linux para executá-los. A segunda que diferente de máquinas virtuais, que recriam todo o ambiente de um computador para executar o sistema operacional, ele reutiliza o kernel em execução, então a versão do kernel dentro do container será a mesma do sistema hospedeiro, então se tiver que carregar algum módulo do kernel para alguma aplicação específica, deve ser feito no hospedeiro.

Agora para entender como a mágica é feita basta voltar no gerenciador de boot. Um dos parâmetros do Linux é o root=, onde é dito para o kernel que ponto dele deve utilizar como root para o sistema assim que for iniciá-lo. No Docker é como se criássemos um processo que voltaria neste ponto e diria que o root= é outro, apenas para um grupo de processo, porém sem iniciar um novo kernel, mas com seus próprio espaço de sistema de arquivos, pila de rede, controle de processos, CPU e alocação de memória, fazendo-o parecer muito com uma VM, se não fosse pela questão do kernel ter que ser o mesmo.

Uma vez na máquina hospedeira será possível listar todos os processos dos containers com um simples ps aux, já que tudo está sobre o mesmo kenel, porém dentro do container não é possível acessar o hospedeiro diretamente devido ao isolamento feito pelo kernel, bem como outros containers.

Agora mais específico para o Docker. Ele trabalha com grupos de imagens e diferenças entre elas, você tem uma imagem básica do sistema, que pode ser o que você quiser como CentOS, Debian ou até apenas um simples BusyBox, que é possível devido ao fraco acoplamento dos programas com o kernel (sendo possível um kernel Debian e imagem CentOS), e várias pilhas de imagens até chegar ao sistema de arquivos do seu container. O funcionamento desse sistema de arquivos é devido ao Aufs, que é um sistema de arquivos voltado a mudanças, ou seja, só serão gravadas as mudanças efetuadas, assim como num repositório Git (que é um dos requisitos no Debian pelo menos). Você tem a imagem do sistema operacional básico, uma camada aonde você instalou o SSH, outra o Apache e na última sua aplicação, qualquer mudança será feita na camada da sua aplicação, porém para acessar os arquivos binários do apache, como ele não está na camada a aplicação, vai descendo até chegar nos arquivos, para acessar o bash, por exemplo terá que ir até a camada do sistema base. Se ainda ficou um pouco confuso basta lembrar dos snapshots das VMs, aonde você pode voltar para um momento específico do sistema de arquivos e normalmente só é gravado no HD a diferença do último snapshot.

Agora para começar a parte mais prática, tendo o Docker instalado conforme a documentação, precisamos de uma imagem base, por exemplo a do CentOS, que pode ser baixada do repositório oficial do Docker com o seguinte comando docker pull centos. Para iniciá-la um container com esta imagem basta executar docker run -t -i centos /bin/bash. E assim que o bash for fechado, este container parará de executar.

Para subir um WordPress por exemplo, podemos utilizar os containers das aplicações já prontas, primeiro um container com o MySQL:
docker run --name blog-db -e MYSQL_ROOT_PASSWORD=senhanovadoroot -d mysql

E depois o WordPress:
docker run --name blog-wp --link blog-db:mysql -d wordpress

Vale notar que caso você não tenha as imagens, a mesma será baixada automaticamente do repositório oficial do Docker e que executar o banco em um container diferente permite simplesmente trocar o containder do WordPress para atualizar e manter o mesmo banco, assim como escalar para várias instâncias do WordPress conectando no mesmo banco.

O Docker também tem uma vantagem para quem faz aplicações, você pode pegá-la e gerar um container, com o sistema que mais desejar, assim como as versões específicas das bibliotecas, e sempre rodará sem problema em qualquer distribuição, atualizada ou não. Há algum tempo no meu GitHub, fiz um exemplo/tutorial de como criar um container e executar uma aplicação WSGI dentro do Docker (https://github.com/eduardoklosowski/Exemplo-Docker-para-WSGI). Para visualizar melhor as camadas do sistema de arquivos basta olhar o arquivo Dockerfile, cada comando presente no arquivo gera uma camada, e caso tenha algum erro nesse processo não é necessário executar novamente os comandos que não tiveram erro, apenas os demais.

Deixo o vídeo do Allisson Azevedo (https://www.youtube.com/watch?v=vb2k8jOlHkA) para quer quiser saber mais ou ter outra explicação para comparar, além de aconselhar a leitura da documentação oficial.