我使用 python Flask框架构建单页应用程序。我使用Gunicorn作为 Web 服务器,并使用docker将其容器化。它通过Nginx Ingress Controller部署在Azure Kubernetes Services ( aks )上。
设置
我的 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 入口在以下 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
s as"/"
和"/import"
。当点击其中任何一个时,浏览器会导航到example.com
并example.com/import
删除myapp
前缀,当然会出现 404 错误。我们期望在页面导航时正确构建 URL,并带有前缀,例如example.com/myapp/import
。活性和就绪性检查(可在example.com/myapp/health/live
和获取example.com/myapp/health/ready
)由 Kubernetes 找到。
我的尝试
我尝试了多种解决方案,但没有一个有效。
SCRIPT_NAME
经过几次搜索后,我发现这篇博客文章提到了正确的解决方案。我在 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 的活性和就绪性检查失败,因为它们也在双前缀路径下。
代理修复
我尝试按照此 SO 答案中的建议使用 ProxyFix ,并在初始化应用程序后添加以下行:
app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_host=1)
然而,这似乎并没有产生任何效果。我也尝试过传递x_prefix=1
参数,但没有成功。
问题
我读了很多东西,现在我已经很困惑了。我开始使用“flaskrouting with aks”作为关键词来搜索答案,然后转移到“wsgi服务器”,然后“nginx反向代理”“nginx前缀”或“nginx入口”,现在我不确定实际上是什么正在发生。我不确定解决方案是否应该来自ingress.yaml
gunicorn,或者是否是需要适应的flask应用程序。
我看到的行为是什么以及如何解决它?
由于此项目结构(与 aks 基础设施一起)是根据模板构建的,因此我想要一个可以添加到此类模板中或者可以单独添加到代码中的解决方案。
您的代理配置缺少必要的
X-Forwarded-Prefix
标头配置。从文档中:
这需要与 Proxyfix Flask 配置一起使用,
x_prefix=1
当然包括参数。