Ich habe eine Single-Page-Anwendung mit Python erstelltFlascheRahmen. Ich verwendewaffenrockals Webserver und habe es in einen Container gepackt mitDockerEs wird eingesetzt aufAzure Kubernetes-Dienste(aks) mitNginx Ingress Controller.
Die Einrichtung
Meine Flask-App sieht folgendermaßen aus:
src/main.py
from flask import Flask
from src.routes import main_bp
app = Flask(__name__)
app.register_blueprint(main_bp)
@app.route('/health/live')
def healthLiveMsg():
return 'Healthy'
@app.route('/health/ready')
def healthReadyMsg():
return 'Healthy'
if __name__ == '__main__':
app.run(host='0.0.0.0', port=80)
src/main_bp.py
from flask import Blueprint, render_template
main_bp = Blueprint('main', __name__)
# home page
@main_bp.route('/')
def home():
return render_template('index.html')
# some other page
@main_bp.route('/import')
def import_page():
# some code...
return renter_template('import.html')
# some backend job trigger
@main_bp.route('/run_job', methods=['POST'])
def run_job():
# some code...
def register_blueprints(app):
app.register_blueprint(main_bp)
Es base.html
gibt eine Navigationsleiste, in der ich die url_for
Funktion von Flask verwende, um den Link zur Startseite bzw. zur Importseite zu erhalten, href="{{ url_for('main.home') }}
undhref="{{ url_for('main.import_page') }}
Der AKS-Ingress ist in der folgenden YAML-Vorlage definiert:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: __AksIngress__-ingress
namespace: __AksNamespace__
annotations:
nginx.ingress.kubernetes.io/proxy-buffer-size: 16k
nginx.ingress.kubernetes.io/proxy-body-size: "0"
nginx.ingress.kubernetes.io/server-alias: __AksNamespace__.__AksDnsZone__.__AksDomainName__
nginx.ingress.kubernetes.io/rewrite-target: /$1
nginx.ingress.kubernetes.io/proxy-connect-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
nginx.ingress.kubernetes.io/server-snippet: keepalive_timeout 3600s;client_body_timeout 3600s;client_header_timeout 3600s;
spec:
tls:
- hosts:
- __AksNamespace__.__AksDnsZone__.__AksDomainName__
secretName: __AksIngress__-tls
ingressClassName: nginx
rules:
- host: __AksNamespace__.__AksDnsZone__.__AksDomainName__
http:
paths:
- path: /myapp/?(.*)
pathType: Prefix
backend:
service:
name: myapp-service
port:
number: 80
Das Problem
Bei Bereitstellung auf AKS ist die App unter erreichbar example.com/myapp
. Die bereitgestellte Seite zeigt das HTML der Navigationsleiste mit href
S als "/"
und "/import"
. Wenn Sie auf eines von beiden klicken, navigiert der Browser zu example.com
und example.com/import
lässt das myapp
Präfix weg, was natürlich zu einer 404-Fehlermeldung führt. Es wird erwartet, dass beim Navigieren auf den Seiten die URL korrekt mit dem Präfix erstellt wird, z. B. example.com/myapp/import
. Die Überprüfungen auf Aktivität und Bereitschaft (verfügbar unter example.com/myapp/health/live
und example.com/myapp/health/ready
) werden von Kubernetes gefunden.
Meine Versuche
Ich habe mehrere Lösungen ausprobiert, aber keine hat funktioniert.
SCRIPT_NAME
Nach ein paar Suchen fand ichdieser Blog-Beitragdas deutete auf die richtige Lösung hin. Ich habe die Umgebungsvariable in meiner Docker-Datei festgelegt und den Container auf meinem lokalen Computer ausgeführt, und ja, es hat funktioniert:
- Die Homepage befand sich unter
localhost/myapp
- Durch Klicken auf die Navigationsleiste gelangte ich zu
localhost/myapp/import
- Klicken Sie auf die Schaltflächen auf der Importseite, um
localhost/myapp/run_job
den Backend-Job auszulösen.
Nach der Bereitstellung in AKS hatte jedoch alles einfach ein zusätzliches Präfix:
- Die Homepage befand sich nun unter
example.com/myapp/myapp
- das Navigieren zu den anderen Seiten, auf die ich weitergeleitet wurde,
example.com/myapp/import
als die Seite jetzt beiexample.com/myapp/myapp/import
- ähnliche Angelegenheit mit dem
run_job
- Darüber hinaus schlugen die Überprüfungen auf Aktivität und Bereitschaft für Kubernetes fehl, da sie sich ebenfalls unter dem Pfad mit doppeltem Präfix befanden.
ProxyFix
Ich habe versucht, den ProxyFix wie vorgeschlagen zu verwendenin dieser SO-Antwortund habe nach der Initialisierung der App die folgende Zeile hinzugefügt:
app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_host=1)
Dies schien jedoch keinerlei Wirkung gehabt zu haben. Ich habe versucht, auch den x_prefix=1
Parameter zu übergeben, ohne Erfolg.
Die Frage
Ich habe so viel gelesen, dass ich jetzt ziemlich verwirrt bin. Ich begann mit der Suche nach Antworten mit den Schlüsselwörtern „Flask-Routing mit AKS“, wechselte dann zu „WSGI-Server“, dann zu „Nginx-Reverse-Proxy“, „Nginx-Präfix“ oder „Nginx-Ingress“ und bin mir jetzt nicht sicher, was eigentlich passiert. Ich bin mir nicht sicher, ob die Lösung vom ingress.yaml
Gunicorn kommen sollte oder ob die Flask-App angepasst werden muss.
Welches Verhalten sehe ich und wie kann ich es lösen?
Da diese Projektstruktur (zusammen mit der AKS-Infrastruktur) aus einer Vorlage erstellt wird, hätte ich gerne eine Lösung, die einer solchen Vorlage hinzugefügt werden kann oder als einzelne Ergänzung zum Code erforderlich ist.
Antwort1
In Ihrer Proxy-Konfiguration fehlt die erforderliche X-Forwarded-Prefix
Header-Konfiguration.
X-Forwarded-Prefix-Header
Um der Upstream-Anforderung den nicht standardmäßigen
X-Forwarded-Prefix
Header mit einem Zeichenfolgenwert hinzuzufügen, kann die folgende Annotation verwendet werden:nginx.ingress.kubernetes.io/x-forwarded-prefix: "/path"
Dies muss zusammen mit der Proxyfix-Flask-Konfiguration verwendet werden, x_prefix=1
natürlich einschließlich des Arguments.