[PYTHON] Veröffentlichen Sie Ihre Website mit Responder + Gunicorn + Apache

Einführung

Es gibt ein relativ neues (Oktober 2018-) Python-Webframework namens responder. Der Autor ist derjenige, der Anfragen usw. gestellt hat, und es scheint, dass es sich um einen Rahmen handelt, der den guten Punkten von Flask und Falcon entspricht. Als ich darüber nachdachte, eine einfache Webseite mit Python zu erstellen, erfuhr ich von Responder und war neugierig darauf. Deshalb entschied ich mich, sie zu verwenden. Soweit ich sehen kann, gab es keine Erklärung für Apache, daher werde ich meine eigene Konstruktionsmethode anstelle eines Memos veröffentlichen. (Bitte lassen Sie mich wissen, wenn Sie welche haben ...)

Umgebung

Es schien, dass Nginx einfacher zu erstellen war, aber ich entschied mich dafür, es so zu machen, wie es ist, weil es ein Server war, auf dem Apache ursprünglich installiert war.

Übrigens wird nur venv für das Python-Umgebungskonstruktionswerkzeug verwendet. Wenn Sie nach dem Erstellen einer Umgebung mit venv die folgende direnv-Einstellungsdatei auf das Arbeitsverzeichnis anwenden, wird sie gleichzeitig mit "cd" auf das Arbeitsverzeichnis "aktiviert", was praktisch ist.

.envrc


source <Vollständiger Pfad der venv-Aktivierungsdatei>

SCHRITT 0: Schreiben Sie ein Programm, das eine Antwort zurückgibt

Ziel ist es, die folgenden Programme weltweit zu veröffentlichen und auszuführen. Das Programm selbst finden Sie im offiziellen Schnellstart.

main.py


import responder

api = responder.API()


@api.route("/{who}")
def greet_world(req, resp, *, who):
    resp.text = f"Hello, {who}!"


if __name__ == '__main__':
    api.run()

In diesem Fall wird beispielsweise, wenn Sie mit GET auf "/ world" zugreifen, diese als "Hello, world!" Angezeigt. Wenn Sie mit GET auf "/ testtesttest" zugreifen, wird sie als "Hello, testtest test!" Angezeigt.

SCHRITT 1: Führen Sie den integrierten Server des Responders aus

Der Responder verfügt über einen integrierten Uvicorn-Server. Versuchen Sie zunächst, mit dem Responder (+ Uvicorn) zu beginnen.

$ python main.py
INFO: Started server process [693]
INFO: Waiting for application startup.
INFO: Uvicorn running on http://127.0.0.1:5042 (Press CTRL+C to quit)
$ curl http://127.0.0.1:5042/world
Hello, world!

Sie können sehen, dass der Server automatisch startet und nur durch Ausführen des Programms verbunden werden kann. Wenn Sie den Server herunterfahren, können Sie "Strg + C" wie geschrieben verwenden.

SCHRITT 2: Bewegen Sie sich mit Gunicorn

Offizielle Seite von Uvicorn Es scheint besser zu sein, Gunicorn für die Produktionsumgebung zu verwenden. Verschieben wir es also unter Bezugnahme auf die offiziellen Einstellungen. (Es ist schon eine Weile her, seit ich versucht habe, diesen Artikel zu schreiben, also keine Sorge, der Zeitstempel ist vor ein paar Monaten.)

$ pip install gunicorn

$ gunicorn -k uvicorn.workers.UvicornWorker main:api
[2019-10-31 09:39:11 +0900] [1227] [INFO] Starting gunicorn 19.9.0
[2019-10-31 09:39:11 +0900] [1227] [INFO] Listening at: http://127.0.0.1:8000 (1227)
[2019-10-31 09:39:11 +0900] [1227] [INFO] Using worker: uvicorn.workers.UvicornWorker
[2019-10-31 09:39:11 +0900] [1230] [INFO] Booting worker with pid: 1230
[2019-10-31 09:39:12 +0900] [1230] [INFO] Started server process [1230]
[2019-10-31 09:39:12 +0900] [1230] [INFO] Waiting for application startup.
$ curl http://127.0.0.1:8000/world
Hello, world!

Die Argumente beim Start von Gunicorn lauten ungefähr wie folgt.

---k uvicorn.workers.UvicornWorker: Geben Sie Uvicorn als Arbeiterklasse an --main: api: Geben Sie das zu startende Modul an. Die Notation lautet "Modulname (Programmname): Variablenname von responder.API () "

Erstellen Sie eine Gunicorn-Konfigurationsdatei

Gunicorn-Einstellungselemente können auch aus der Einstellungsdatei gelesen werden. Es ist praktisch, später eine Einstellungsdatei zu haben, also erstellen Sie sie. Die im obigen Befehl verwendeten Argumente sind minimal. Erstellen Sie eine Einstellungsdatei, indem Sie hier den Speicherort der Protokolldatei hinzufügen.

gunicorn.py


import multiprocessing
import os

name = "gunicorn"

accesslog = "<Dateiname zum Schreiben des Zugriffsprotokolls>"
errorlog = "<Dateiname zum Schreiben des Fehlerprotokolls>"

bind = "localhost:8000"

worker_class = "uvicorn.workers.UvicornWorker"
workers = multiprocessing.cpu_count() * 2 + 1
worker_connections = 1024
backlog = 2048
max_requests = 5120
timeout = 120
keepalive = 2

user = "www-data"
group = "www-data"

debug = os.environ.get("DEBUG", "false") == "true"
reload = debug
preload_app = False
daemon = False

Siehe Offizielle Dokumente für jeden Artikel. Der Einstellwert wird von dem von [Referenzstandort](# Referenzstandort) imitiert.

Verwenden Sie den folgenden Befehl, um diese Konfigurationsdatei anzuwenden und Gunicorn zu starten.

$ gunicorn --config gunicorn.py main:api

SCHRITT 3: Führen Sie Apache als Reverse-Proxy-Server aus

Richten Sie schließlich eine Verbindung über Apache ein. Wenn Sie mit http://example.com/ verbunden sind, auf dem Apache ausgeführt wird, konfigurieren Sie Gunicorn als Proxy für das wartende http: // localhost: 8000.

Apache-Seiteneinstellungen zum Proxy für Gunicorn

Erstellen Sie zunächst eine Konfigurationsdatei für den Reverse-Proxy.

/etc/apache2/conf-available/responder.conf


ProxyRequests Off
ProxyPass "/" "http://localhost:8000/"
ProxyPassReverse "/" "http://localhost:8000/"

Aktiviert die Konfigurationsdatei und die Proxy-bezogenen Module.

$ sudo a2enconf responder
$ sudo a2enmod proxy_http proxy

Laden Sie neu, nachdem Sie bestätigt haben, dass die Einstellungsdatei korrekt beschrieben wurde.

$ sudo apache2ctl configtest
Syntax OK

$ sudo systemctl reload apache2ctl

Gunicorn Autostart-Einstellungen

Um den automatischen Start zu vereinfachen, stellen Sie den Gunicorn-Start so ein, dass er von systemd verwaltet wird. Erstellen Sie zunächst eine Einstellungsdatei.

/etc/systemd/system/webapp.service


[Unit]
Description=gunicorn - responder
After=network.target

[Service]
User=www-data
Group=www-data
WorkingDirectory=<gunicorn.py und main.Vollständiger Pfad des Verzeichnisses, in dem sich py befindet>
ExecStart=<Voller Weg des Gunicorn> --config <gunicorn.voller Weg von py> main:api
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s TERM $MAINPID
PrivateTmp=true

[Install]
WantedBy=multi-user.target

Ich werde auch die Punkte erklären, die schwer zu verstehen sind.

--Dateiname: .service ist in Ordnung. Ein Beispiel für die Verwendung in einem Befehl ist systemctl restart <set service name>.

Stellen Sie den Dienst nach dem Erstellen so ein, dass er automatisch gestartet und gestartet wird.

$ sudo systemctl start webapp.service
$ sudo systemctl enable webapp.service

Stellen Sie für alle Fälle sicher, dass es richtig funktioniert.

$ sudo systemctl status webapp.service
● webapp.service - gunicorn - responder
   Loaded: loaded (/etc/systemd/system/webapp.service; enabled; vendor preset: enable
   Active: active (running) 
(Unten weggelassen)

Sowohl der Start als auch der automatische Start waren erfolgreich.

Komplett!

Wenn die Einstellungen korrekt sind, sollten Sie in der Lage sein, eine Verbindung mit Curl oder einem Browser herzustellen.

$ curl http://example.com/world
Hello, world!

ungelöst? Problem

Aus irgendeinem Grund verschwindet der HTML-Header "Content-Type" beim Durchlaufen von Apache. (Es ist richtig angeschlossen, wenn der Server von Gunicorn aus gestartet wird. Geheimnisvolles Phänomen) Als vorläufige Maßnahme füge ich Code hinzu, der beim Zurückgeben einer Antwort "Content-Type: text / html; charset = UTF-8" erzwingt.

Bonusnote

Übrigens möchte ich die Grammatik des Antwortenden aufschreiben, die ich persönlich nützlich / gegoogelt und schwer zu finden fand.

Wie schreibe ich Routing

Es scheint mehr Routing (und Verarbeitung) zu geben als nur das Schreiben am Anfang.

Erstellen Sie eine Klasse+Richten Sie das Routing später gemeinsam ein.py


import responder

api = responder.API()


class Who:
	def on_get(self, req, resp, *, who):
		#Bei GET wird dieser Vorgang automatisch ausgeführt
    	resp.text = f"Hello, {who}!"
    
    async def on_post(self, req, resp, *, who):
    	#Zum Zeitpunkt des POST wird dieser Vorgang automatisch ausgeführt
    	data = await req.media()
    	resp.text = f"{data}"

#Routing-Einstellungen
api.add_route("/{who}", Who)

if __name__ == '__main__':
    api.run()

Ist der Schreibstil am Anfang wie Flask und der gerade geschriebene Schreibstil ist der Falcon-Stil? Ich selbst habe in "Beschreibung mit on_get usw. in der Klasse + Routing-Einstellung mit dem Dekorateur" geschrieben, aber vielleicht ist es eine schlechte Idee ...

Jinja2-Filter hinzufügen

Es kann verwendet werden, wenn Sie einen statischen Dateipfad definieren oder einen Prozess schreiben möchten, der viele Male wiederholt wird.

jinja_myfilter.py


def css_filter(path):
	return f"./static/css/{path}"


def list_filter(my_list):
	return_text = "<ul>\n"
	for l in my_list:
		return_text += f"<li> {l} </li>\n"
	return_text += "</ul>"
	return return_text

main.py


import responder
import jinja_myfilter

api = responder.API()
#Filter hinzufügen
# v1.Für x
api.jinja_env.filters.update(
	css = jinja_myfilter.css_filter, 
	html_list = jinja_myfilter.list_filter
)
# v2.Für x(2020/05/12 Nachtrag)
# (Weil es einen Unterstrich gibt_env scheint als interner Wert behandelt zu werden,
#Ich konnte keine andere Möglichkeit finden, es zu spezifizieren, indem ich mir die Quelle ansah ...)
api.templates._env.filters.update(
	css = jinja_myfilter.css_filter, 
	html_list = jinja_myfilter.list_filter
)

@api.route("/")
def greet_world(req, resp):
	param = ["Gegenstand 1", "Punkt 2"]
	resp.content = api.template("index.html", param=param)

if __name__ == '__main__':
	api.run()

index.html


<link rel="stylesheet" type="text/css" href="{{ 'form.css' | css }}">
<!--Es wird von Jinja2 verarbeitet und wird wie folgt
  <link rel="stylesheet" type="text/css" href="./static/css/form.css">
-->

{% autoescape false %}
{{ param | html_list }}
{% endautoescape %}
<!--Es wird von Jinja2 verarbeitet und wird wie folgt
  <ul>
  <li>Gegenstand 1</li>
  <li>Punkt 2</li>
  </ul>
-->

Wenn eine Zeichenfolge zurückgegeben wird, die ein HTML-Tag enthält, funktioniert die automatische Escape-Funktion, sofern sie nicht in "{% autoescape false%} to {% endautoescape%}" eingeschlossen ist. Wenn es sich bei den von Ihnen übergebenen Parametern jedoch um Benutzereingaben handelt, werden die Parameter natürlich ohne Escapezeichen ausgegeben. Seien Sie also vorsichtig. Ist es sicher, es mit html.escape () usw. im Filter zu verarbeiten?

Referenzseite

Wie man Responder mit Uvicorn oder Gunicorn bereitstellt - ich möchte über Technologie und Ausweichen sprechen Für eine Einführung in Python Responder ... Voruntersuchungen - Qiita Anwendung mit Django + Nginx + Gunicorn | WEB Curtain Call starten [[1st] Lassen Sie uns eine Webanwendung für maschinelles Lernen mit Responder und Keras erstellen. [Übersichtserstellung] - Light Code Co., Ltd.](https://rightcode.co.jp/blog/information-technology/responder- keras-make-machine-learning-web-appsz9) Jinja2s benutzerdefinierter Filter zum Konvertieren von Zeilenumbrüchen in --- Ein Tagebuch zur Deprogrammierung von Anfängern mit Google App Engine + Python

Recommended Posts

Veröffentlichen Sie Ihre Website mit Responder + Gunicorn + Apache
Arbeiten Sie mit Websites mit Python_Webbrowser
Veröffentlichen Sie Ihre eigene Python-Bibliothek auf Homebrew
Versuchen Sie, eine einfache Website mit Responder und sqlite3 zu erstellen
Visualisieren Sie Zahlenschwankungen auf Websites mit Datadog
Erste Schritte mit apache2