Can we add support for ASGI (Asynchronous Server Gateway Interface) on Toolforge, to supplement the current uWSGI services we have now? An ASGI server implementation like Uvicorn would likely be a logical choice and would open up the options for developers to more modern async frameworks like Fastapi.
Description
Related Objects
Event Timeline
Considering that FastAPI, Django, and several other prominent Python frameworks support(and sometimes require) ASGI servers this would be great. The current alternative is Cloud VPS in case one wants to stay on WMFs cloud services.
Is there a "best" or most popular ASGI server to consider for implementing this? Django's ASGI docs list three different pip installable ASGI servers.
The webservice setup for python WSGI applications is based on uWSGI with an "opinionated" default configuration which expects certain conventions from the application. The main thing this gives the container is a consistent entry point that webservice can setup without needing the tool maintainer to do much beyond putting their code in the expected directories. Ideally we would be able to define a similar convention based setup for an ASGI variant.
If someone has the energy to play around with ideas, making a custom Deployment to proof of concept things should not be horribly difficult (given a reasonable level of comfort with direct use of Kubernetes). You would need to create the Pod running the ASGI service and your app, a Service exposing the ASGI process port, and an Ingress routing traffic to the Service similar to what webservice --backend=kuberenetes python3.9 start creates. One of the useful things to test here would be if the ASGI server's package can be outside of the venv needed by the app, or if webservice would need to ensure that the ASGI package chosen is installed in $HOME/web/python/venv along with the other application dependencies.
https://asgi-test.toolforge.org/ is serving a example application using ASGI.
I copied https://fastapi.tiangolo.com/#example to ~/www/python/src/app.py, and created a virtualenv at ~/www/python/venv with fastapi and uvicorn[standard] installed using webservice python3.7 shell. I picked https://www.uvicorn.org/ because the logo is a unicorn, which is of course reason enough to pick any software.
Then I abused the fact that all the "generic" webservice images have Python installed, and started it with webservice golang111 start -- ${HOME}/www/python/venv/bin/uvicorn --host 0.0.0.0 --port 8000 --server-header --app-dir ${HOME}/www/python/src app:app. ("generic" webservices let you specify the command to start the webservice instead of being stuck with that language's standard)
Then I read through https://www.uvicorn.org/deployment/ and learned that for production deployments they recommend using gunicorn. OK, installed that into the venv and started with webservice golang111 start -- ${HOME}/www/python/venv/bin/gunicorn -w 4 -k uvicorn.workers.UvicornWorker -b 0.0.0.0:8000 --chdir ${HOME}/www/python/src app:app.
https://docs.gunicorn.org/en/stable/deploy.html#using-virtualenv says to just install gunicorn into the venv.
Thanks for this work @Legoktm - very cool way to get it working. It does indeed work and is showing the documentation here: https://asgi-test.toolforge.org/docs#/
I suppose the follow-up decision is whether we just support this "kludge" mode as-is, or whether it makes sense to formalize running ASGI in a more supported mode using gunicorn/uvicorn as the default config. Any opinions?
In the short-term I think we should add support for a intentional Python "generic" webservice that allows users to specify the command they want to be used to start a webservice. I think this would significantly reduce the hackiness since you don't need to abuse the golang image for this (we also want to remove Python from those other images too).
Then we wait a bit and see what people do with ASGI, which runner is preferred, how much demand there is, what config tweaks are needed for ideal operation.
The reason @bd808 asked if the ASGI server can be installed out of the venv is because it introduces a weird split in what is user-controlled and what is provided for by Toolforge. Currently for WSGI Python apps, Toolforge installs its preferred version of uwsgi (fixed for that Python version) as a system package and then specifies all the command-line arguments to run the app via the user-created venv. Users can customize uwsgi.ini to adjust settings/flags if they want.
If the ASGI server needs to be installed into the venv (like gunicorn recommends), then it becomes something that users control and manage the version for. If Toolforge is then supposed to provide the command-line arguments, how does it know that the correct version of gunicorn and uvicorn are installed? And when there is a breaking change, does Toolforge hold people back or force tools to upgrade?
So figuring out some resolution to that issue is also going to be needed. Skimming the gunicorn docs, it allows adjusting the PYTHONPATH, maybe that is good enough, dunno.
This is now possible using the Toolforge Build Service (warning: still in beta). If you try deploying an ASGI app using the Build Service, we would love your feedback.
Awesome news! I've left a comment on the Community Wishlist Survey proposal pointing interested parties towards this (and have noted that it's very much in beta).