Server in Hülle und Fülle: Einsatz von Pythons CherryPy für schnell skalierbare Prototypen in einer schlanken Umgebung

Server in Hülle und Fülle: Einsatz von Pythons CherryPy für schnell skalierbare Prototypen in einer schlanken Umgebung

Von 23. Februar 2017Technisches

TL;DR

Eines der Ergebnisse der Berufung zum älteren Ingenieur ist, dass ich einfach nur Projekte realisieren möchte. Zeit damit zu verbringen, sich durch eine Bibliothek zu hacken, die nicht funktioniert, schlecht implementiert oder schlecht dokumentiert ist, macht die Programmierung zu einer lästigen Pflicht und nicht zu einer Freude. Deshalb habe ich mich sehr gefreut, mit Ihnen zusammenzuarbeiten. CherryPy in letzter Zeit:

  • Die Docker-Datei ist nicht kompliziert.
  • Statische Inhalte können schnell bereitgestellt werden.
  • Python-Skripte sind erstklassige Bürger.
  • Die Tests sind einfach und unkompliziert.
  • Die Skalierung meiner Anwendung ist eine Einzellinie.

Was ist CherryPy?

CherryPy ist ein minimalistisches Python Web-Framework. Es wird nie ein größeres, umfassenderes Framework für Unternehmenssoftware ersetzen, aber um etwas zum Laufen zu bringen, ist es fantastisch. Traditionell würde ich eine SimpleHttpServer für das Prototyping, aber es ist Feature Light - was mich dazu zwingt, zu aktualisieren, sobald ich etwas getan habe, was den Server wirklich beflügelt hat. CherryPy ermöglicht es meinen Anwendungen, so groß zu werden, dass sie nützlich sind, bevor sie zu einem größeren Framework wechseln müssen (wenn überhaupt). Als Referenz: Digital Ocean hat einen schönen Job gemacht. Vergleich von Python WSGI Server.

Docker und CherryPy

Wir verwenden Docker für alles bei ShareThis, also lassen Sie uns das zuerst erledigen, damit wir eine vollständige Entwicklungsumgebung mit Tests haben. Sie können damit sofort beginnen, indem Sie einfach unser Repository klonen. Das Einrichten dieser Umgebung nimmt am Anfang 10 zusätzliche Minuten in Anspruch, wird sich aber innerhalb der ersten paar Tage des Programmierens um ein Vielfaches auszahlen. Dies ist wohl der einzige Preis, den wir für den Tausch von CherryPy in Docker gegen SimpleHttpServer auf der Kommandozeile zahlen.

Dockerdatei

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()

Erster Durchgang: Geschriebene Inhalte

Nehmen wir an, Sie wollen ein Lean-Experiment starten, um zu sehen, ob jemand auf einer Seite landet. Das Schnellste, was Sie tun können, ist, eine statische Seite zu bedienen und Besuche zu protokollieren. Lassen Sie uns das fehlende run.py-Skript hinzufügen:

run.py (V1)

import cherrypy

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

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

An dieser Stelle haben Sie einen skalierbaren Webserver, der fast alles kann. Ich verwende dies für Dashboards, Überwachung und eine Vielzahl von internen Tools.

An dieser Stelle wirst du anfangen wollen, mit der Konfiguration zu experimentieren. Sie möchten beispielsweise, dass Ihr Webserver für das öffentliche Routing auf 0.0.0.0.0 anstelle von 127.0.0.0.1 deklariert wird. Du willst auf Port 80 für den Live-Verkehr dienen. Sie möchten mehr als 10 Threads für mehrere Live-Verbindungen (mehr dazu unten). Sie möchten, dass der Verkehr trotz des nachlaufenden Schrägstrichs geroutet wird. Du willst eine eine Menge anderer Dinge zu geschehen.

Lassen Sie uns einige Konfigurationen hinzufügen, indem wir einen Aufruf zu cherrypy.config.update. Zusätzlich erlauben wir das Laden von statischen Inhalten und einer Konfigurationsdatei durch Ändern von cherrypy.quickstart(Landing(), '/') zu cherrypy.quickstart(Landing(), '/', "prod.conf"). Vergiss nicht, prod.conf zu deiner Dockerdatei hinzuzufügen.

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")

Zu diesem Zeitpunkt werden Sie wahrscheinlich auch statische Anlagen benötigen. Fügen wir die Konfigurationsdatei mit einem Verweis auf das statische Bild hinzu! Sie können beliebig viele statische Objekte hinzufügen. Ich bevorzuge einen deklarativen Ansatz, aber du kannst es tun. ein Verzeichnis bedienen auch.

prod.conf

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

Schnelle Expansion

Die Skalierung eines Prototypen bedeutet, Blocker schnell loszuwerden. Lassen Sie uns einige der häufigsten Probleme, die CherryPy betreffen, besprechen und wie man sie so schnell wie möglich aus dem Weg räumt.

Gesundheitschecks und zusätzliche Handler

Auf ShareThis führen wir unsere Anwendungen innerhalb von Kubernetes aus. auf AWS. Wir müssen den Zustand unseres Docker-Containers überwachen, damit die Systeme wissen, ob der Container am Leben ist und ob der Load Balancer gesund ist. Dies kann breit gefächert werden, wenn zusätzliche Handler benötigt werden. Hier ist ein einfaches Beispiel für das Schreiben eines einfachen HealthCheck-Endpunktes und das Hinzufügen des Handlers zu unserem Landing Objekt. Nun, wir können auf Folgendes hinweisen http://localhost/healthcheck um zu sehen, ob der Server noch lebt.

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

Ein Nebeneffekt des Monitorings auf Kubernetes ist, dass das Netzwerk eine gewisse Komplexität verbirgt. Jeder Knoten in einem Kubernetes-Cluster verfügt über einen Proxy, der Daten dorthin weiterleitet, wo der Docker-Container gerade läuft. Der Elastic Load Balancer (ELB) von Amazon wird versuchen, die Verbindungen zu jedem Knoten am Leben zu erhalten, um den Health Check gegen den Cluster durchzuführen. Das bedeutet, dass Sie für jeden Knoten im Cluster die Anzahl der dem Server zur Verfügung stehenden Threads berücksichtigen müssen. In unserem 20-Knoten-Cluster wird unsere Test-App nicht funktionieren, da die ELB auf 20 Threads gleichzeitig zugreift. CherryPy hat standardmäßig 10 Threads, so dass die Proxies ständig aus der Rotation geworfen werden. Daher wird unsere Erhöhung auf 100 Threads oben es uns ermöglichen, ohne betriebliche Probleme zu skalieren.

Hinzufügen von Tests

Komplexität beeinträchtigt die Produktivität, wenn Sie sich nicht sicher sind, was bei Änderungen passieren wird. Das Hinzufügen einfacher Verträge zu Ihrer Testsuite, die zukünftigen Entwicklern (einschließlich Ihnen selbst) mitteilen, was der Code tun soll, kann die Skalierung Ihres Codes ohne großen Aufwand beibehalten.

Die Art und Weise, wie wir das Skript- und Coverage-Tool integriert haben, sollten Sie in der Lage sein, eine Importanweisung für jedes Python-Skript hinzuzufügen, und die Coverage kümmert sich um den Rest. Vielleicht möchtest du den mock Paket an Ihre pip install Anweisung, um einfachere Tests zu ermöglichen.

Zusammenfassung

CherryPy ist gut dokumentiert und skaliert ziemlich gut. Oftmals ist es alles, was Sie brauchen, um eine App in einer skalierten Umgebung zum Laufen zu bringen. Indem man mit einer Vorlage beginnt, kann man sofort einen Webserver haben, der nativ Python-Code ausführt. Wir bei ShareThis haben es für die Backend-Datenverarbeitung über eine Rest-Schnittstelle, die Überwachung von Feeds auf S3 und BQ und das schnelle Aufrufen von Dashboards verwendet, die sich mit APIs für Daten verbinden müssen. Durch die Verwendung von Docker wird es trivial, die Anwendung in der Produktion einzusetzen und zu warten.

Über den Autor
ShareThis

ShareThis erschließt seit 2007 die Macht des globalen digitalen Verhaltens durch die Synthese von Social Share-, Interessen- und Absichtsdaten. Auf der Grundlage des Verbraucherverhaltens auf mehr als drei Millionen globalen Domains beobachtet ShareThis Echtzeit-Aktionen von echten Menschen auf echten digitalen Zielen.

Über uns

ShareThis erschließt seit 2007 die Macht des globalen digitalen Verhaltens durch die Synthese von Social Share-, Interessen- und Absichtsdaten. Auf der Grundlage des Verbraucherverhaltens auf mehr als drei Millionen globalen Domains beobachtet ShareThis Echtzeit-Aktionen von echten Menschen auf echten digitalen Zielen.