Eu tenho um aplicativo de página única construído usando pythonFrascoestrutura. estou a usargunicórniocomo o servidor web e eu o coloquei em contêiner usandojanela de encaixe. Ele é implantado emServiços Azure Kubernetes(perguntas) comControlador de entrada Nginx.
A configuração
Meu aplicativo Flask é assim:
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)
O base.html
possui uma barra de navegação onde utilizo a url_for
função do Flask para obter o link para a página inicial e a página de importação, respectivamente href="{{ url_for('main.home') }}
ehref="{{ url_for('main.import_page') }}
A entrada aks é definida no seguinte modelo 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
O problema
Quando implantado no aks, o aplicativo pode ser acessado em example.com/myapp
. A página servida mostra o html da barra de navegação com href
s as "/"
e "/import"
. Ao clicar em qualquer um deles o navegador navega example.com
e example.com/import
descarta o myapp
prefixo, atingindo 404, é claro. A expectativa é que ao navegar pelas páginas a URL seja construída corretamente, com o prefixo, por exemplo example.com/myapp/import
. As verificações de atividade e prontidão (disponíveis em example.com/myapp/health/live
e example.com/myapp/health/ready
) são encontradas pelo Kubernetes.
Minhas tentativas
Eu tentei várias soluções, mas nenhuma delas funcionou.
SCRIPT_NAME
Depois de algumas pesquisas encontreiesta postagem do blogque aludiu à solução certa. Defino a variável de ambiente em meu dockerfile e executo o contêiner em minha máquina local e sim, estava funcionando:
- a página inicial estava em
localhost/myapp
- clicar na barra de navegação me enviou para
localhost/myapp/import
- clicando nos botões na página de importação postada para
localhost/myapp/run_job
acionar o trabalho de back-end.
No entanto, após a implantação no aks, tudo simplesmente teve um prefixo extra:
- a página inicial estava agora em
example.com/myapp/myapp
- navegando para as outras páginas que me enviaram
example.com/myapp/import
quando a página estava agora emexample.com/myapp/myapp/import
- assunto semelhante com o
run_job
- além disso, as verificações de atividade e prontidão falharam no kubernetes, pois também estavam no caminho de prefixo duplo.
ProxyFix
Tentei usar o ProxyFix conforme sugeridonesta resposta SOe adicionei a linha abaixo após inicializar o aplicativo:
app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_host=1)
Isso, no entanto, parecia não ter surtido efeito algum. Tentei passar também o x_prefix=1
parâmetro, sem sucesso.
A questão
Eu li tantas coisas que fiquei bastante confuso agora. Comecei a procurar respostas usando "roteamento de flask com aks" como palavras-chave, depois mudei para "servidor wsgi", depois "proxy reverso nginx" "prefixo nginx" ou "entrada nginx" e agora não tenho certeza do que é realmente acontecendo. Não tenho certeza se a solução deve vir do ingress.yaml
gunicorn ou se é o aplicativo flask que precisa se adaptar.
Qual é o comportamento que estou vendo e como resolvo isso?
Como esta estrutura de projeto (juntamente com a infraestrutura aks) é construída a partir de um modelo, gostaria de uma solução que pudesse ser adicionada a esse modelo ou que fosse uma única adição ao código.
Responder1
Sua configuração de proxy não possui a X-Forwarded-Prefix
configuração de cabeçalho necessária.
Cabeçalho X-Forwarded-Prefix
Para adicionar o cabeçalho não padrão
X-Forwarded-Prefix
à solicitação upstream com um valor de string, a seguinte anotação pode ser usada:nginx.ingress.kubernetes.io/x-forwarded-prefix: "/path"
Isso precisa ser usado junto com a configuração do frasco do Proxyfix, incluindo o x_prefix=1
argumento, é claro.