Как я могу запустить Django на подпути в Google Cloud Run с балансировщиком нагрузки?
В начале отмечу, что у меня есть система, настроенная с использованием Google Cloud Run + Load Balancer + IAP для запуска ряда приложений на https://example.com/app1, https://example.com/app2 и т.д., и до сих пор я развертывал таким образом только приложения Streamlit. Балансировщик нагрузки направляет трафик на каждое приложение в Cloud Run согласно подпути (/app1, ...), и я использовал опцию --server.baseUrlPath=app2
streamlit run
без проблем.
Сейчас я пытаюсь получить приложение Django 4.1.5 'hello, world', работающее на https://example.com/directory, и я не могу получить правильные маршруты, пробуя различные предложения здесь, здесь, и здесь.
Dockerfile заканчивается на
CMD exec poetry run gunicorn --bind 0.0.0.0:${PORT} --workers 1 --threads 8 --timeout 0 example_dir.wsgi:application
Сначала каноническое решение.
Я добавил FORCE_SCRIPT_NAME = "/directory"
в settings.py
.
Вот example_dir/urls.py
:
urlpatterns = urlpatterns = [
path("admin/", admin.site.urls),
path("", include("directory.urls")),
]
и вот directory/urls.py
:
urlpatterns = [
path("", views.hello, name="hello"),
]
Посещение https://example.com/directory
возвращается
Page not found (404)
Request Method: GET
Request URL: http://example.com/directory/directory
Using the URLconf defined in example_dir.urls, Django tried these URL patterns, in this order:
1. admin/
2. [name='hello']
Этот URL запроса удивительный и странный.
Добавление либо USE_X_FORWARDED_HOST = True
, либо SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
, либо обоих (согласно ссылке на nixhive.com) не повлияло на результат.
Второе, уродливое решение.
Если я изменю example_dir/urls.py
на
urlpatterns = [
path("directory/admin/", admin.site.urls),
path("directory/", include("directory.urls")),
]
после посещения https://example.com/directory
работает правильно, но https://example.com/directory/admin
возвращается, но без стилей:
Это очевидно из-за того, что <head>
имеет hrefs как /static/admin/...
.
Замена STATIC_URL
в settings.py
на "directory/static/"
исправила hrefs, но я вижу несколько ошибок на консоли, таких как
Refused to apply style from 'https://example.com/directory/static/admin/css/base.css' because its MIME type ('text/html') is not a supported stylesheet MIME type, and strict MIME checking is enabled.
Ортогональная переменная
Я не могу сделать это в prod, но в качестве эксперимента я изменил Docker CMD на
CMD exec poetry run python manage.py runserver 0.0.0.0:${PORT}
Это не влияет на результаты решения FORCE_SCRIPT_NAME
, но исправляет проблему с некрасивым решением. В частности, исчезают ошибки типа MIME, и страница входа в систему администратора выглядит нормально.
Я не думаю, что имя скрипта имеет какое-то отношение к тому, чего вы пытаетесь достичь. Вам нужно иметь приложение WSGI, обертывающее ваше приложение Django, чтобы вы могли вводить путь перед.
На сайте Gunicorn https://github.com/benoitc/gunicorn/blob/master/examples/multiapp.py
есть пример того, как этого добиться.Вот модифицированная версия для вашего случая использования
from routes import Mapper
from example_dir.wsgi import application as app1
class Application(object):
def __init__(self):
self.map = Mapper()
self.map.connect('app1', '/directory', app=app1)
def __call__(self, environ, start_response):
match = self.map.routematch(environ=environ)
if not match:
return self.error404(environ, start_response)
return match[0]['app'](environ, start_response)
def error404(self, environ, start_response):
html = b"""\
<html>
<head>
<title>404 - Not Found</title>
</head>
<body>
<h1>404 - Not Found</h1>
</body>
</html>
"""
headers = [
('Content-Type', 'text/html'),
('Content-Length', str(len(html)))
]
start_response('404 Not Found', headers)
return [html]
app = Application()