Die Einrichtung

Die Einrichtung

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.htmlgibt eine Navigationsleiste, in der ich die url_forFunktion 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 hrefS als "/"und "/import". Wenn Sie auf eines von beiden klicken, navigiert der Browser zu example.comund example.com/importlässt das myappPrä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/liveund 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 unterlocalhost/myapp
  • Durch Klicken auf die Navigationsleiste gelangte ich zulocalhost/myapp/import
  • Klicken Sie auf die Schaltflächen auf der Importseite, um localhost/myapp/run_jobden Backend-Job auszulösen.

Nach der Bereitstellung in AKS hatte jedoch alles einfach ein zusätzliches Präfix:

  • Die Homepage befand sich nun unterexample.com/myapp/myapp
  • das Navigieren zu den anderen Seiten, auf die ich weitergeleitet wurde, example.com/myapp/importals die Seite jetzt beiexample.com/myapp/myapp/import
  • ähnliche Angelegenheit mit demrun_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=1Parameter 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.yamlGunicorn 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-PrefixHeader-Konfiguration.

Ausdie Dokumentation:

X-Forwarded-Prefix-Header

Um der Upstream-Anforderung den nicht standardmäßigen X-Forwarded-PrefixHeader 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=1natürlich einschließlich des Arguments.

verwandte Informationen