Colocar todo o seu coração no que você faz faz a diferença. Por quê? Porque, como
diz o Barão no filme The Cat
Returns , a criação ganha uma
alma.
No dia 4 de dezembro de 2014, recebemos a maravilhosa notícia de que a
Crowdcrafting foi reconhecida como uma das
empresas de tecnologia social do ano .
Ganhar este preço tem sido incrível, um reconhecimento à nossa dificuldade em tornar nosso
site de Crowdcrafting robusto, escalável e estável.
TL; DR, pois isso vai descrever nossa infraestrutura atual e como executamos o
Crowdcrafting, portanto, você está avisado!
Balanceador de carga HTTP
Hospedamos todos os nossos serviços na Rackspace. O motivo? Bem, eles têm uma
calculadora útil que nos permite estimar quanto vai nos custar rodando nossos
serviços lá, e eu adoro isso. Basicamente, porque eles não mentem e os
números se encaixam.
Um dos recursos interessantes que a Rackspace oferece é a opção de habilitar um
balanceador de carga HTTTP para seus servidores em nuvem. Isso simplifica muito nossa configuração
e nós o configuramos para equilibrar as solicitações de entrada para nossos
servidores PyBossa .
Portanto, quando um usuário solicita uma página do Crowdcrafting, o primeiro serviço
que é contatado é o balanceador de carga. O balanceador distribuirá as
solicitações aos nossos servidores PyBossa.
Nginx e uWSGI
Uma vez que a solicitação foi redirecionada para um dos servidores PyBossa, a solicitação
chega ao servidor Nginx . Nginx, verifica seus sites habilitados e direciona a solicitação
para nosso aplicativo PyBossa Flask escrito em Python. Nesse momento, o servidor
é contatado e atendido por meio do middleware uWSGI , que cuidará da movimentação
da solicitação por meio de nossa infraestrutura.
Conseqüentemente, o Nginx cuida de servir arquivos estáticos, enquanto o uWSGI cuida do
resto.
No início do Crowdcrafting, usamos Apache2 e mod_wsgi, mas
mudamos para Nginx e uWSGI porque:
Quando estávamos executando no Apache2 e mod_wsgi, o
desempenho estava abaixo do ideal (testamos com Locust.io) e pudemos ver um
claro prejuízo no número de solicitações por segundo que estávamos entregando em
comparação com a configuração atual. Por isso, procuramos novas soluções
e descobrimos que a melhor opção para nós é o Nginx + uWSGI .
Enquanto desenvolvemos o PyBossa, sempre temos em mente que o PyBossa
deve ser capaz de escalar horizontalmente sem problemas . Embora pareça fácil
de alcançar, a verdade é que existem tantas opções por aí que no
final se torna um pesadelo decidir qual delas é a melhor solução.
Por exemplo, servir avatares de N servidores diferentes deve sempre retornar a
mesma imagem de todos eles para os clientes.
No nosso caso, decidimos manter as coisas o mais simples possível (o princípio KISS).
Por esse motivo, habilitamos o suporte Rackspace CDN no PyBossa (ele pode ser
facilmente estendido para qualquer outro CDN, pois temos uma classe genérica que pode ser
herdada) para servir arquivos de um local central. Esta solução nos permite
crescer horizontalmente sem cuidar de onde os arquivos estão sendo servidos.
Além disso, se alguém não quiser habilitar o CDN, pode configurar o
PyBossa para usar o uploader local e usar um Glusterfs para distribuir os arquivos
por todos os servidores. Não gostamos dessa solução, pois ela acrescentou mais um ponto de
falha em nossos sistemas e temos que cuidar disso nós mesmos, enquanto o CDN faz
isso para nós de forma automática .
Uma vez que a solicitação está no middleware uWSGI , o servidor PyBossa provavelmente
precisará acessar os dados no banco de dados, para que possa renderizar o HTML e retornar a
resposta de volta ao cliente. A próxima seção explica como lidamos com essa parte
da solicitação.
PostgreSQL e PgBouncer
Depois que a solicitação chega ao aplicativo Flask , geralmente envolve uma consulta ao
banco de dados. Usamos PostgreSQL 9.3 e estamos realmente impressionados com a qualidade, o
desempenho e a comunidade em torno dele. Nós amamos isso! Melhor DB de todos os tempos.
Como teremos muitas conexões provenientes de diferentes servidores, queríamos
melhorar a forma como tratamos essas conexões com o banco de dados para reduzir a sobrecarga e o tempo de
estabelecimento de conexões, fechamento, etc. Para este problema, estamos usando em cada
servidor PyBossa PgBouncer para pooling as conexões a dois servidores PostgreSQL :
- Nó mestre aceitando consultas de leitura e gravação e
- Nó escravo aceitando apenas consultas de leitura.
O PyBossa estabelece duas conexões diferentes com os bancos de dados para usar
conexões somente leitura quando queremos obter apenas informações ou gravar
conexões quando precisamos escrever algo de volta no banco de dados.
Embora o PgBouncer faça um pool de conexões, ele não faz o balanceamento de carga, por isso
usamos o HAProxy para balancear a carga das consultas READ entre os
nós mestre e escravo de forma transparente. A melhor parte dessa configuração é que
tudo é feito de forma totalmente automática e transparente pelo HAProxy , então o
PyBossa não sabe nada sobre isso.
Graças a esta configuração, podemos adicionar mais nós escravos horizontalmente, dimensionando e
balanceando a carga de nossa infraestrutura facilmente.
Embora esta solução seja ótima, algumas consultas precisam ser armazenadas em cache antes de
chegarem ao banco de dados, pois levam tempo para serem processadas (isto é, estatísticas para
projetos de Crowdcrafting ). Por esse motivo, estamos usando Redis e Sentinel para armazenar quase
tudo em cache .
Redis e Sentinel
Se somos apaixonados pelo PostgreSQL, o que podemos dizer sobre Redis e Sentinel : também os
amamos 🙂
Desde o início, a PyBossa usa Redis e Sentinel para construir uma
solução de cache de alta disponibilidade com balanceamento de carga.
A configuração é muito simples: um nó mestre Redis que aceita
consultas de leitura e gravação , enquanto quase todos os outros nós em nossa infraestrutura têm um nó escravo.
Além disso, o Sentinel cuida de lidar com todos esses nós de forma
transparente e não temos que fazer nada nós mesmos. Essa solução tem
funcionado muito bem para nós, e graças a ela estamos salvando muitas consultas do
banco de dados, melhorando nosso desempenho.
Além disso , estamos usando o Redis também para trabalhos em segundo plano (ou seja, exportação de resultados,
estatísticas de computação, envio de e-mails, etc.) graças ao
Python-RQ e rq-scheduler
para executar trabalhos periódicos.
Verificamos o Celery, mas foi um exagero para o que estamos
construindo e decidimos novamente manter as coisas simples.
Python-RQ e
rq-scheduler são pequenas bibliotecas que podem ser facilmente adaptadas às nossas
necessidades, além disso, já possuímos em nossos sistemas Redis por isso foi o melhor candidato
para nós.
Resumo
Em resumo, estamos usando microestruturas para construir nosso projeto emparelhado com
uma infraestrutura muito simples que nos permite crescer
horizontalmente sem problemas e balancear a carga de nosso tráfego de entrada de forma eficiente.
A próxima imagem mostra como uma solicitação passa por nossa configuração atual:
src = “/ assets / img / blog / infrastructurediagram.png”> </p>
ATUALIZAÇÃO : Algumas pessoas perguntaram sobre nossos números. A verdade é que
a configuração atual pode servir até 2,5k rpm em menos de 200ms para 1500
usuários navegando no site ao mesmo tempo (temos 2 servidores PyBossa com 2 GB de RAM
e 2 núcleos cada, enquanto os DBs têm 4 GB de RAM e 4 núcleos – mestre e escravo).
Em agosto de 2014, conseguimos armazenar em nossos servidores mais de 1,5 datum por segundo por
dia. Naquele momento os servidores de BD possuíam apenas 1GB de RAM,
e considerando que o SO ocupa cerca de 200MB,
os BDs estavam utilizando apenas 800MB de RAM.
Implantações e Ansible
Até agora, gerenciamos toda a nossa infraestrutura manualmente. No entanto, nas
últimas semanas temos migrado nossa infraestrutura para ser totalmente
controlada pelo Ansible .
Além disso, desenvolvemos nossa própria solução interna
para implantações automáticas para toda a equipe integrada com Github Deployments
API e Slack para receber notificações em nossos próprios canais de chat de equipe. Fazer uma
implantação agora consiste em mesclar e fechar uma solicitação pull. Tão simples
quanto isso.
Usar o Ansible para tudo nos ajudou a ter playbooks semelhantes reutilizados
em diferentes clientes, permitindo-nos fazer implantações mais rápidas que são fáceis de
manter, depurar e implantar.
Por outro lado, a solução de implantações automáticas usa os mesmos manuais, de
modo que tudo funciona nas mesmas ferramentas e tecnologias.
Verificamos diferentes soluções como HUBot, mas decidimos novamente ter uma
solução muito simples para integrar todas essas ferramentas em nosso conjunto de ferramentas. O
servidor de implantações tem menos de 300 linhas de código, é 100% totalmente testado e coberto, por
isso é muito simples adaptá-lo e corrigi-lo. Além disso, ele roda nos mesmos
serviços que usamos atualmente: Nginx + uWSGI , então não precisamos adicionar
nada diferente à nossa pilha.
NOTA : Vou escrever uma postagem no blog sobre a solução de implantação 🙂
EDITAR : Você pode ler sobre a solução de implantação
aqui .
Integração contínua e qualidade do código
Levamos muito a sério a qualidade e os testes do código. No momento, temos quase 1000
testes (953 no momento da escrita) cobrindo quase todo o código-fonte (97%
coberto) e com uma qualidade de saúde de 94%.
Nossa solução de implantações usa a API Travis-CI Github Statuses para fazer as
implantações, para que possamos ter certeza de que funcionará em nossos
sistemas de produção .
Seguimos o fluxo do Github mais ou menos, pois não temos um esquema de versionamento
per se para o nosso software PyBossa. O que fazemos é que tudo o que está no
mestre seja estável, pois nosso serviço principal é executado diretamente a partir dele. Por este motivo,
levamos muito a sério a qualidade do nosso software, pois um bug ou um problema pode
prejudicar a nossa plataforma de Crowdcrafting.
Normalmente fazemos várias implantações por semana, adicionando novos recursos, correções de bugs,
etc. ao PyBossa e, portanto, Crowdcrafting, já que toda a equipe tem
direitos de implantação . Isso provou ser um recurso incrível, pois oferecemos muito rápido
seguindo o princípio RERO: Liberar o lançamento antecipadamente com frequência .
E isso é tudo! Espero que você goste. Se você tiver dúvidas, por favor, use a
seção de comentários abaixo e tentarei responder. Agora:
NOTA : esta postagem do blog foi publicada originalmente no meu blog em 10 de fevereiro de 2015.