我有使用 python 构建的单页应用程序烧瓶框架。我正在使用gunicorn作为 Web 服务器,我已经使用码头工人. 它部署在Azure Kubernetes 服务(艾克斯) 和Nginx 入口控制器。
设置
我的 Flask 应用程序如下所示:
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)
有base.html
一个导航栏,我使用 Flask 的url_for
函数分别获取主页和导入页面的链接href="{{ url_for('main.home') }}
,href="{{ url_for('main.import_page') }}
aks ingress 在以下 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
问题
在 aks 上部署后,可以通过 访问该应用example.com/myapp
。提供的页面显示导航栏的 html 包含href
和"/"
。"/import"
单击其中任何一个时,浏览器都会导航到example.com
并example.com/import
删除myapp
前缀,当然会出现 404。预期是,在浏览页面时,URL 正确构建,前缀为 ,例如example.com/myapp/import
。Kubernetes 会找到活跃度和就绪性检查(可在example.com/myapp/health/live
和 处找到example.com/myapp/health/ready
)。
我的尝试
我尝试过很多解决方案,但都不起作用。
脚本名称
经过几次搜索,我发现这篇博文这暗示了正确的解决方案。我在 dockerfile 中设置了环境变量,并在本地机器上运行了容器,果然它正常工作了:
- 主页位于
localhost/myapp
- 点击导航栏会将我带到
localhost/myapp/import
- 点击导入页面中的按钮触发
localhost/myapp/run_job
后端作业。
然而,部署到 aks 之后,所有内容都只是多了一个前缀:
- 主页现在位于
example.com/myapp/myapp
- 导航到其他页面
example.com/myapp/import
时,我被带到了example.com/myapp/myapp/import
- 类似的事情
run_job
- 此外,由于 kubernetes 也位于双前缀路径下,因此其活跃性和就绪性检查失败。
代理修复
我尝试按照建议使用 ProxyFix在这个SO答案中并在初始化应用程序后添加以下行:
app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_host=1)
但这似乎没有任何效果。我也尝试传递参数x_prefix=1
,但没有成功。
问题
我读了这么多东西,现在我感到很困惑。我开始使用“带有 aks 的 flask 路由”作为关键词来搜索答案,然后转到“wsgi 服务器”,然后是“nginx 反向代理”、“nginx 前缀”或“nginx 入口”,现在我不确定到底发生了什么。我不确定解决方案是否应该来自 gunicorn,ingress.yaml
或者是否是需要适应的 flask 应用程序。
我看到的行为是什么?我该如何解决它?
因为这个项目结构(连同 aks 基础设施)是从模板构建的,所以我想要一个可以添加到这样的模板中或作为代码的单独添加的解决方案。
答案1
您的代理配置缺少必要的X-Forwarded-Prefix
标头配置。
从文档:
X-Forwarded-Prefix 标头
要将非标准
X-Forwarded-Prefix
标头以字符串值的形式添加到上游请求中,可以使用以下注释:nginx.ingress.kubernetes.io/x-forwarded-prefix: "/path"
这需要与 Proxyfix flask 配置一起使用,x_prefix=1
当然包括参数。