Tengo una aplicación de una sola página compilada usando Python.Matrazestructura. Estoy usandogunicorniocomo servidor web y lo he contenedorizado usandoestibador. Está desplegado enServicios de Azure Kubernetes(pregunta) conControlador de ingreso Nginx.
La puesta en marcha
Mi aplicación Flask se ve así:
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)
Tiene base.html
una barra de navegación donde uso la url_for
función de Flask para obtener el enlace a la página de inicio y la página de importación, respectivamente href="{{ url_for('main.home') }}
yhref="{{ url_for('main.import_page') }}
La entrada de aks se define en la siguiente plantilla yaml:
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
El problema
Cuando se implementa en aks, se puede acceder a la aplicación en example.com/myapp
. La página servida muestra el html de la barra de navegación con href
s como "/"
y "/import"
. Al hacer clic en cualquiera de ellos, el navegador navega example.com
y example.com/import
suelta el myapp
prefijo, alcanzando un 404, por supuesto. La expectativa es que al navegar por las páginas, la URL se cree correctamente, con el prefijo, por ejemplo example.com/myapp/import
, . Kubernetes encuentra las comprobaciones de actividad y preparación (disponibles en example.com/myapp/health/live
y ).example.com/myapp/health/ready
Mis intentos
Probé varias soluciones, pero ninguna funcionó.
SCRIPT_NOMBRE
Después de algunas búsquedas encontréesta publicación de blogeso aludía a la solución correcta. Configuré la variable de entorno en mi dockerfile y ejecuté el contenedor en mi máquina local y sí, estaba funcionando:
- la página de inicio estaba en
localhost/myapp
- Al hacer clic en la barra de navegación me envió a
localhost/myapp/import
- Al hacer clic en los botones en la página de importación publicada para
localhost/myapp/run_job
activar el trabajo de backend.
Sin embargo, después de implementar en aks, todo simplemente tenía un prefijo adicional:
- la página de inicio ahora estaba en
example.com/myapp/myapp
- navegar a las otras páginas a las que me envió
example.com/myapp/import
cuando la página estaba ahora enexample.com/myapp/myapp/import
- asunto similar con el
run_job
- Además, las comprobaciones de actividad y preparación fallaron para Kubernetes, ya que también estaban bajo la ruta de doble prefijo.
ProxyFix
Intenté usar ProxyFix como se sugiereen esta respuesta SOy agregó la siguiente línea después de inicializar la aplicación:
app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_host=1)
Sin embargo, esto no pareció haber tenido efecto alguno. Intenté pasar también el x_prefix=1
parámetro, sin éxito.
La pregunta
He leído tantas cosas que ahora me he confundido bastante. Comencé a buscar respuestas usando "enrutamiento de matraces con aks" como palabras clave, luego pasé a "servidor wsgi", luego a "proxy inverso nginx", "prefijo nginx" o "ingreso nginx", y ahora no estoy seguro de qué es realmente sucediendo. No estoy seguro de si la solución debería venir de ingress.yaml
gunicorn o si es la aplicación flask la que necesita adaptarse.
¿Cuál es el comportamiento que estoy viendo y cómo lo soluciono?
Debido a que la estructura de este proyecto (junto con la infraestructura de aks) se construye a partir de una plantilla, me gustaría una solución que pueda agregarse a dicha plantilla o que sea una única adición al código.
Respuesta1
A su configuración de proxy le falta la X-Forwarded-Prefix
configuración de encabezado necesaria.
Encabezado de prefijo reenviado X
Para agregar el encabezado no estándar
X-Forwarded-Prefix
a la solicitud ascendente con un valor de cadena, se puede utilizar la siguiente anotación:nginx.ingress.kubernetes.io/x-forwarded-prefix: "/path"
Esto debe usarse junto con la configuración del matraz Proxyfix, incluido el x_prefix=1
argumento, por supuesto.