У меня есть одностраничное приложение, созданное с использованием Python.Флягафреймворк. Я используюгуникорнкак веб-сервер и я контейнеризировал его с помощьюдокер. Он развернут наСлужбы 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 определен в следующем шаблоне 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 как "/"
и "/import"
. При нажатии на любой из них браузер переходит на example.com
и example.com/import
отбрасывает myapp
префикс, попадая на 404, конечно. Ожидается, что при навигации по страницам URL-адрес будет построен правильно, с префиксом, например example.com/myapp/import
. Проверки жизнеспособности и готовности (доступные по адресу example.com/myapp/health/live
и example.com/myapp/health/ready
) обнаруживаются Kubernetes.
Мои попытки
Я перепробовал несколько решений, но ни одно из них не сработало.
ИМЯ_СКРИПТА
После нескольких поисков я нашелэтот пост в блогеэто намекало на правильное решение. Я установил переменную окружения в моем 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
Я попробовал использовать ProxyFix, как было предложено.в этом SO ответи добавил следующую строку после инициализации приложения:
app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_host=1)
Однако это, похоже, не дало никакого эффекта. Я пробовал также передать x_prefix=1
параметр, но безуспешно.
Вопрос
Я прочитал так много всего, что теперь совсем запутался. Я начал искать ответы, используя «маршрутизация Flask с AKS» в качестве ключевых слов, затем перешел к «серверу WSGI», затем «обратному прокси-серверу nginx», «префиксу nginx» или «входу nginx», и теперь я не уверен, что на самом деле происходит. Я не уверен, должно ли решение исходить от ingress.yaml
gunicorn или это приложение 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
аргумент, конечно.