Servidores Galore: Usando Python's CherryPy Para Protótipos Rapidamente Escaláveis em um Ambiente Lean

Servidores Galore: Usando Python's CherryPy Para Protótipos Rapidamente Escaláveis em um Ambiente Lean

Por 23 de Fevereiro de 2017Engenharia

TL;DR

Um dos resultados de me tornar um engenheiro mais velho é que eu simplesmente quero ter projetos prontos. Passar tempo invadindo uma biblioteca que não funciona, é mal implementado, ou mal documentado faz da codificação uma tarefa e não uma alegria. É por isso que eu tenho tido muito prazer em trabalhar com CherryPy recentemente:

  • O arquivo Docker não é complicado
  • Conteúdo estático pode ser servido rapidamente
  • Os Python Scripts são cidadãos de primeira classe
  • Os testes são simples
  • A escala da minha aplicação é um revestimento único.

O que é CherryPy?

CherryPy é uma estrutura web Python minimalista. Ele nunca irá substituir um framework maior e mais completo para software empresarial, mas para ter algo em funcionamento é fantástico. Tradicionalmente, eu usaria um SimpleHttpServer para prototipagem, mas é uma característica leve - exigindo que eu atualize assim que eu fiz algo que realmente empurrou o servidor. CherryPy permite que minhas aplicações sejam escaladas o suficiente para serem úteis antes de ter que mudar para um framework maior (se for o caso). Para referência, a Digital Ocean fez um bom comparação de Python WSGI servidores.

Docker e CherryPy

Nós usamos o Docker para tudo no ShareThis, então vamos fazer isso primeiro para que tenhamos um ambiente de desenvolvimento completo com testes. Você pode começar isso simplesmente clonando o nosso repo. Configurar isto no início leva 10 minutos extra, mas paga-se muitas vezes dentro dos primeiros dias da codificação. Este é sem dúvida o único custo que pagamos para trocar CherryPy no Docker por SimpleHttpServer na linha de comando.

Dockerfile

FROM python
RUN pip install cherrypy coverage # cherrypy server + python 3 test coverage
ADD *.py /
ADD *.sh /
CMD python run.py

testing.sh

#!/bin/bash
coverage run -a --omit=test.py /test.py
coverage report -m

correr.sh

#!/bin/bash
# docker kill and rm prevent funny errors
docker kill cherrypy > /dev/null 2>&1
docker rm cherrypy > /dev/null 2>&1
# build, test, and run the image
docker build -t cherrypy .
docker run cherrypy /testing.sh
docker run -ti --name cherrypy -p 80:80 cherrypy $@

teste.py

import unittest

class TestMethods(unittest.TestCase):
    def test_simple(self):
        self.assertTrue(True)

if __name__ == '__main__':
    unittest.main()

Primeiro passe: Conteúdo do Script

Vamos assumir que você quer começar uma experiência lean para ver se alguém vai pousar em uma página. A coisa mais rápida a fazer é servir uma página estática e registrar visitas. Vamos adicionar o script run.py que falta:

run.py (V1)

import cherrypy

class Landing(object):
    @cherrypy.expose
    def index(self):
        return scripted landing 

if __name__ == "__main__":
    cherrypy.quickstart(Landing(), '/')

Neste ponto você tem um servidor web escalável que pode fazer quase tudo. Eu uso isso para painéis de controle, monitoramento e uma série de ferramentas internas.

Neste ponto, você vai querer começar a mexer com a configuração. Por exemplo, você quer seu servidor web declarado em 0.0.0.0 ao invés de 127.0.0.1 para roteamento público. Você quer servir na porta 80 para tráfego ao vivo. Você quer mais de 10 threads para múltiplas conexões ao vivo (mais sobre isso abaixo). Você quer tráfego roteado apesar da barra de arrasto. Você quer um conjunto de outras coisas para acontecer.

Vamos adicionar algumas configurações, adicionando uma chamada para cherrypy.config.update. Além disso, vamos permitir o carregamento de conteúdo estático e um arquivo de configuração, alterando cherrypy.quickstart(Landing(), '/') para cherrypy.quickstart(Landing(), '/', "prod.conf"). Não se esqueça de adicionar prod.conf ao seu Dockerfile!!

run.py (V2)

import cherrypy

class Landing(object):
    @cherrypy.expose
    def index(self):
        return scripted landing 

# The config.update is optional, but this will prevent scaling issues in a moment
cherrypy.config.update({
    'server.socket_host': '0.0.0.0', # 127.0.0.1 is default
    'server.socket_port': 80, # 8080 is default
    'server.thread_pool': 100, # 10 is default
    'tools.trailing_slash.on': False # True is default
})

if __name__ == "__main__":
    cherrypy.quickstart(Landing(), '/', "prod.conf")

Por volta deste ponto, você provavelmente precisará de ativos estáticos também. Vamos adicionar o arquivo de configuração com uma referência à imagem estática! Você pode adicionar qualquer número de ativos estáticos. Eu prefiro uma abordagem declarativa, mas você pode servir uma lista também.

prod.conf

[/logo.png]
tools.staticfile.on = True
tools.staticfile.filename = "/logo.png"

Expandindo rapidamente

Escalar um protótipo significa livrar-se rapidamente dos bloqueadores. Vamos discutir alguns dos problemas mais comuns que afetam a CherryPy e como tirá-los do caminho o mais rápido possível.

Controles de saúde e manipuladores adicionais

No ShareThis, nós executamos nossas aplicações dentro da Kubernetes na AWS. Precisamos de monitorizar o estado do nosso contentor Docker para que os sistemas saibam se o contentor está vivo e se o equilibrador de carga está saudável. Isto pode ser amplamente arquivado sob a necessidade de manipuladores adicionais. Aqui está um exemplo simples de como escrever um ponto final HealthCheck simples e adicionar o manipulador ao nosso objecto de aterragem. Agora, nós podemos apontar para http://localhost/healthcheck para ver se o servidor está vivo.

...
class HealthCheck(object):
    def index(self):
        return "Yay
\n" index.exposed = True class Landing(object): healthcheck = HealthCheck() ...

Um efeito secundário da monitorização em Kubernetes é que a rede esconde alguma complexidade. Cada nó num cluster Kubernetes tem um proxy que baralha os dados para onde quer que o contentor Docker esteja a correr nesse momento. O Equilibrador de Carga Elástica (ELB) da Amazon tentará manter as conexões vivas a cada nó para fazer o controle de saúde contra o cluster. Isto significa que para cada nó no cluster, você tem que contabilizar isso no número de threads disponíveis para o servidor. Em nosso cluster de 20 nós, nosso aplicativo de teste não funcionará porque o ELB está acessando 20 threads de uma só vez. CherryPy tem um padrão de 10 threads, e assim os proxies serão continuamente expulsos da rotação. Portanto, nosso aumento para 100 threads acima permitirá que ele seja escalado sem problemas operacionais.

Adicionando Testes

A complexidade dificulta a produtividade quando você não tem certeza do que vai acontecer à medida que as mudanças são feitas. Adicionar contratos simples à sua suíte de testes que dizem aos futuros desenvolvedores (incluindo você mesmo) o que o código se destina a fazer, pode manter o seu código em escala sem muita sobrecarga.

Da forma como incluímos os scripts e a ferramenta de cobertura, você deve ser capaz de adicionar uma declaração de importação para qualquer script python e a cobertura cuidará do resto. Você pode querer adicionar a ferramenta de mock pacote para o seu pip install declaração para facilitar os testes.

Sumário

A CherryPy está bem documentada e é muito bem escalada. Muitas vezes, é tudo o que você precisa para colocar uma aplicação em funcionamento em um ambiente escalonado. Ao começar com um template, você pode imediatamente ter um servidor web que executa código python nativamente. Para nós do ShareThis, nós o usamos para processamento de dados backend através de uma interface de repouso, monitoramento de feeds no S3 e BQ, e rapidamente trazendo dashboards que precisam se conectar a API's para dados. Ao usar o Docker, torna-se trivial implantar e manter a aplicação em produção.

Sobre ShareThis

ShareThis has unlocked the power of global digital behavior by synthesizing social share, interest, and intent data since 2007. Impulsionado pelo comportamento do consumidor em mais de três milhões de domínios globais, ShareThis observa acções em tempo real de pessoas reais em destinos digitais reais.

Subscreva a nossa Newsletter

Receba as últimas notícias, dicas e actualizações

Assine

Conteúdo relacionado