Каналы Django, обслуживаемые с помощью gunicorn с --preload, не могут подключиться к сокету unix
Я работаю над веб-приложением, которое использует каналы django для связи через вебсокет. На производстве я использую gunicorn с рабочими uvicorn, за Nginx, проксирование через unix socket.
Моя цель состоит в том, чтобы код выполнялся только один раз (для регистрации конфигурации и планирования заданий с помощью APScheduler), когда приложение запускается. Рассматривая этот файл asgi.py:
import os
from django.core.asgi import get_asgi_application
from .startup import run_at_startup
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'tcpy.settings')
django_asgi_app = get_asgi_application()
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
import tcpy.routing
application = ProtocolTypeRouter({
"http": django_asgi_app,
"websocket": AuthMiddlewareStack(
URLRouter(
tcpy.routing.websocket_urlpatterns
)
),
})
run_at_startup()
Я обнаружил, что использование аргумента --preload в gunicorn заставляет функцию run_at_startup запускаться только один раз за загрузку. Проблема в том, что gunicorn не может подключиться к сокету unix. Если я использую --preload, все работает нормально, но run_at_startup будет выполняться один раз на каждый рабочий процесс (я этого не хочу).
Вот мой конфигурационный файл systemd для создания сокета ( /etc/systemd/system/tr_gunicorn.socket ):
[Unit]
Description=Testrune unix-socket for http communications (nginx<>gunicorn)
[Socket]
ListenStream=/run/tr_gunicorn.socket
SocketUser=testrune
SocketGroup=www-data
[Install]
WantedBy=sockets.target
А это файл, запускающий gunicorn ( /etc/systemd/system/tr_gunicorn.service ):
[Unit]
Description=Testrune Webserver
Requires=tr_gunicorn.socket
After=network.target
[Service]
EnvironmentFile=/etc/testrune/environment.env
User=testrune
Group=www-data
WorkingDirectory=/home/testrune/tcpy_srv/testrune/tcpydev/tcpy/
ExecStart=/home/testrune/tcpyenv/bin/gunicorn tcpy.asgi:application -w 2 -k uvicorn.workers.UvicornWorker --bind unix:/run/tr_gunicorn.socket --forwarded-allow-ips='*' --preload
Restart=on-failure
[Install]
WantedBy=multi-user.target
Вот журналы единорогов:
Feb 04 09:10:28 testrune gunicorn[12267]: [2022-02-04 09:10:28 +0000] [12267] [INFO] Starting gunicorn 20.1.0
Feb 04 09:10:28 testrune gunicorn[12267]: [2022-02-04 09:10:28 +0000] [12267] [ERROR] Retrying in 1 second.
Feb 04 09:10:29 testrune gunicorn[12267]: [2022-02-04 09:10:29 +0000] [12267] [ERROR] Retrying in 1 second.
Feb 04 09:10:30 testrune gunicorn[12267]: [2022-02-04 09:10:30 +0000] [12267] [ERROR] Retrying in 1 second.
Feb 04 09:10:31 testrune gunicorn[12267]: [2022-02-04 09:10:31 +0000] [12267] [ERROR] Retrying in 1 second.
Feb 04 09:10:32 testrune gunicorn[12267]: [2022-02-04 09:10:32 +0000] [12267] [ERROR] Retrying in 1 second.
Feb 04 09:10:33 testrune gunicorn[12267]: [2022-02-04 09:10:33 +0000] [12267] [ERROR] Can't connect to /run/tr_gunicorn.socket
Feb 04 09:10:33 testrune systemd[1]: tr_gunicorn.service: Main process exited, code=exited, status=1/FAILURE
Feb 04 09:10:33 testrune systemd[1]: tr_gunicorn.service: Failed with result 'exit-code'.
Может быть, мне следует поместить следующее в другую тему, но в любом случае это здесь. Вместо аргумента --preload я также пробовал использовать крючки gunicorn. Но если я использую функции on_starting или when_ready, служба прерывается со следующей ошибкой:
Feb 04 09:15:03 testrune gunicorn[13180]: File "/home/testrune/tcpyenv/lib/python3.9/site-packages/gunicorn/arbiter.py", line 167, in start
Feb 04 09:15:03 testrune gunicorn[13180]: self.cfg.when_ready(self)
Feb 04 09:15:03 testrune gunicorn[13180]: File "/home/testrune/tcpy_srv/testrune/tcpydev/tcpy/gunicorn.conf.py", line 9, in when_ready
Feb 04 09:15:03 testrune gunicorn[13180]: run_at_startup()
Feb 04 09:15:03 testrune gunicorn[13180]: File "/home/testrune/tcpy_srv/testrune/tcpydev/tcpy/tcpy/startup.py", line 69, in run_at_startup
Feb 04 09:15:03 testrune gunicorn[13180]: from .auth_backend import do_sync_db
Feb 04 09:15:03 testrune gunicorn[13180]: File "/home/testrune/tcpy_srv/testrune/tcpydev/tcpy/tcpy/auth_backend.py", line 29, in <module>
Feb 04 09:15:03 testrune gunicorn[13180]: from django.contrib.auth.backends import ModelBackend
Feb 04 09:15:03 testrune gunicorn[13180]: File "/home/testrune/tcpyenv/lib/python3.9/site-packages/django/contrib/auth/backends.py", line 5, in <module>
Feb 04 09:15:03 testrune gunicorn[13180]: from django.contrib.auth.models import Permission
Feb 04 09:15:03 testrune gunicorn[13180]: File "/home/testrune/tcpyenv/lib/python3.9/site-packages/django/contrib/auth/models.py", line 2, in <module>
Feb 04 09:15:03 testrune gunicorn[13180]: from django.contrib.auth.base_user import AbstractBaseUser, BaseUserManager
Feb 04 09:15:03 testrune gunicorn[13180]: File "/home/testrune/tcpyenv/lib/python3.9/site-packages/django/contrib/auth/base_user.py", line 47, in <module>
Feb 04 09:15:03 testrune gunicorn[13180]: class AbstractBaseUser(models.Model):
Feb 04 09:15:03 testrune gunicorn[13180]: File "/home/testrune/tcpyenv/lib/python3.9/site-packages/django/db/models/base.py", line 107, in __new__
Feb 04 09:15:03 testrune gunicorn[13180]: app_config = apps.get_containing_app_config(module)
Feb 04 09:15:03 testrune gunicorn[13180]: File "/home/testrune/tcpyenv/lib/python3.9/site-packages/django/apps/registry.py", line 252, in get_containing_app_config
Feb 04 09:15:03 testrune gunicorn[13180]: self.check_apps_ready()
Feb 04 09:15:03 testrune gunicorn[13180]: File "/home/testrune/tcpyenv/lib/python3.9/site-packages/django/apps/registry.py", line 135, in check_apps_ready
Feb 04 09:15:03 testrune gunicorn[13180]: raise AppRegistryNotReady("Apps aren't loaded yet.")
Feb 04 09:15:03 testrune gunicorn[13180]: django.core.exceptions.AppRegistryNotReady: Apps aren't loaded yet.
Feb 04 09:15:03 testrune systemd[1]: tr_gunicorn.service: Main process exited, code=exited, status=1/FAILURE
Это работает, если я использую хуки на основе рабочих, например post_worker_init, но опять же, это заставит мой код выполняться несколько раз. Помощь будет очень признательна