Abundancia de servidores: usando CherryPy de Python para prototipos escalables rápidos en un ambiente magro

Abundancia de servidores: usando CherryPy de Python para prototipos escalables rápidos en un ambiente magro

Por 23 de febrero, 2017Ingeniería

TL; DR

Uno de los resultados de convertirse en un ingeniero más antiguo es que simplemente quiero hacer proyectos. Pasar tiempo hackeando a través de una biblioteca que no funciona, está mal implementado, o mal documentado hace que la codificación de una tarea en lugar de una alegría. Es por eso que he estado muy complacido de trabajar con CherryPy Recientemente:

  • El archivo de acoplador no es complicado
  • El contenido estático se puede servir rápidamente
  • Los guiones Python son ciudadanos de primera clase
  • Las pruebas son directas
  • Escalar mi aplicación es un trazador de líneas

¿Qué es CherryPy?

CherryPy es un framework web minimalista de Python. Nunca reemplazará un marco más grande más completo para el software empresarial, pero para conseguir algo en funcionamiento es fantástico. Tradicionalmente, usaría un SimpleHttpServer para la creación de prototipos, pero es luz de la característica-requiriendo que me actualice tan pronto como hice cualquier cosa que realmente empujó el servidor. CherryPy permite que mis aplicaciones se escalen lo suficiente como para ser útiles antes de tener que cambiar a un marco más grande (si es que lo es). Para la referencia, el océano digital hizo un agradable Comparación de Python WSGI Servidores.

Docker y CherryPy

Usamos Docker para todo en ShareThis, así que hagámoslo primero para tener un entorno de desarrollo completo con pruebas. Puedes arrancar esto simplemente clonando nuestro repositorio. Configurar esto al principio toma 10 minutos extra, pero se amortizará muchas veces en los primeros días de la codificación. Este es posiblemente el único coste que pagamos por intercambiar CherryPy en Docker por SimpleHttpServer en la línea de comandos.

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

Run.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 $@

Test.py

import unittest

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

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

Primer paso: contenido con secuencias de comandos

Supongamos que quiere empezar un experimento Lean para ver si alguien aterrizará en una página. Lo más rápido que se debe hacer es servir una página estática y visitas de registro. Añadamos el 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(), '/')

En este punto usted tiene un servidor Web escalable que puede hacer casi cualquier cosa. Utilizo esto para tableros de mandos, supervisión, y un anfitrión de herramientas internas.

En este punto, usted querrá comenzar a Mucking con la configuración. Por ejemplo, desea que su servidor Web se declare en 0.0.0.0 en lugar de 127.0.0.1 para el enrutamiento público. Usted quiere servir en el puerto 80 para el tráfico en vivo. Desea más de 10 subprocesos para múltiples conexiones en vivo (más en la siguiente). Quieres tráfico enrutado a pesar de la barra de seguimiento. Quieres un anfitrión de otras cosas a suceder.

Vamos a añadir algunas configs mediante la adición de una llamada a cherrypy.config.update. Además, vamos a permitir la carga de contenido estático y un archivo de configuración cambiando cherrypy.quickstart(Landing(), '/') para cherrypy.quickstart(Landing(), '/', "prod.conf"). No olvides añadir Prod. conf a tu 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")

Alrededor de este punto, usted probablemente necesitará activos estáticos también. Vamos a añadir el archivo de configuración con una referencia a la imagen estática! Puede agregar cualquier número de activos estáticos. Prefiero un enfoque declarativo, pero se puede servir un directorio También.

Prod. conf

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

Expandirse rápidamente

Escalar un prototipo significa deshacerse rápidamente de los bloqueadores. Discutamos algunos de los problemas más comunes que afectan a CherryPy y cómo sacarlos del camino lo más rápido posible.

Controles médicos y controladores adicionales

En ShareThis, ejecutamos nuestras aplicaciones dentro de Kubernetes en AWS. Necesitamos monitorear el estado de nuestro contenedor Docker para que los sistemas sepan si el contenedor está vivo y si el balanceador de carga está sano. Esto se puede archivar ampliamente bajo la necesidad de manipuladores adicionales. Aquí es un ejemplo simple de escribir un punto final HealthCheck simple y agregar el controlador a nuestro objeto de aterrizaje. Ahora, podemos señalar http://localhost/healthcheck para ver si el servidor está vivo.

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

Un efecto secundario de la supervisión en Kubernetes es que la red oculta cierta complejidad. Cada nodo de un clúster de Kubernetes tiene un proxy que baraja los datos en cualquier lugar en el que el contenedor de la base esté ejecutándose en ese momento. El balanceador de carga elástica (Elb) de Amazon intentará mantener las conexiones vivas a cada nodo para realizar el control de salud contra el clúster. Esto significa que para cada nodo del clúster, hay que tener en cuenta que en el número de subprocesos disponibles para el servidor. En nuestro clúster de 20 nodos, nuestra aplicación de prueba no funcionará porque el Elb está accediendo a 20 subprocesos a la vez. CherryPy tiene un valor predeterminado de 10 subprocesos, por lo que los proxies se expulsarán continuamente de la rotación. Por lo tanto, nuestro aumento a 100 subprocesos anteriores le permitirá escalar sin problemas operacionales.

Agregar pruebas

La complejidad dificulta la productividad cuando no está seguro de lo que sucederá a medida que se realizan los cambios. Agregar contratos simples a su suite de pruebas que le diga a los futuros desarrolladores (incluyéndose a usted) lo que el código está destinado a hacer, puede mantener su escala de código sin mucho overhead.

La forma en que hemos incluido los scripts y la herramienta de cobertura, usted debe ser capaz de añadir una declaración de importación para cualquier script de Python y la cobertura se encargará del resto. Es posible que desee agregar el mock paquete a su pip install Declaración para facilitar pruebas más fáciles.

Resumen

CherryPy está bien documentado y se escala bastante bien. A menudo, es todo lo que necesita para obtener una aplicación en funcionamiento en un entorno de escala. Al comenzar con una plantilla, puede tener inmediatamente un servidor Web que ejecute código Python de forma nativa. Para nosotros en ShareThis, lo hemos utilizado para el procesamiento de datos de back-end a través de una interfaz de REST, monitoreo de feeds en S3 y BQ, y rápidamente la crianza de los paneles que necesitan para conectarse a la API para los datos. Al utilizar Docker, se vuelve trivial el despliegue y mantenimiento de la aplicación en la producción.

Sobre el autor
ShareThis

ShareThis ha desbloqueado el poder del comportamiento digital global sintetizando los datos de participación social, interés e intención desde 2007. Gracias al comportamiento de los consumidores en más de tres millones de dominios globales, ShareThis observa las acciones en tiempo real de personas reales en destinos digitales reales.

Sobre nosotros

ShareThis ha desbloqueado el poder del comportamiento digital global sintetizando los datos de participación social, interés e intención desde 2007. Gracias al comportamiento de los consumidores en más de tres millones de dominios globales, ShareThis observa las acciones en tiempo real de personas reales en destinos digitales reales.