Different Python versions under the same uwsgi Emperor?
I'm running an uwsgi Emperor with various Vassals that each serve a specific Python app from a different virtualenv. Since uwsgi was compiled with it's own Python 2.7 interpreter, trying to use a virtualenv with Python 3 in it produces the following error in vassal.log:
ImportError: No module named site
I believe the origin of this error is that uwsgi is using its built-in Python 2.7 interpreter, while the virtualenv directory it's running in only supports Python 3 interpreters. Indeed, when I use another uwsgi (simply by installing it with pip install uwsgi
in the same virtualenv), the error dissappears. However, I'd like one Emperor to rule over several different virtualenvs, so installing a separate uwsgi in each is not an option.
According to this answer on Stackoverflow, The Right Way to solve this is to compile uwsgi with different Python interpreters as loadable modules. Before I commit to this approach, I'd like to know how I can configure my Vassals to each use another interpreter plugin.
Right now I have one Emperor that is started from my /etc/rc.local with the following settings:
[uwsgi]
uid = www-data
gid = www-data
master = true
emperor = /etc/uwsgi/vassals
daemonize = /var/log/uwsgi/emperor.log
Then I have a bunch of Vassals with ini files like this:
[uwsgi]
master = false
single-interpreter = true
socket = /tmp/%n.sock
virtualenv = /home/user/.virtualenvs/djangoproject
chdir = /home/user/djangoproject
wsgi-file = project/wsgi.py
logto = /var/log/uwsgi/%n.log
I have no problem compiling a tweaked version of uwsgi with several interpreter plugins, but I'd like to know what I have to change in my configuration to actually use these separate interpreters. Can I just say one vassal.ini:
plugin = python3.4
and in another:
plugin = python2.7
?
Please help me figure out how to combine Python 2.7 and Python 3 virtualenvs under the same uwsgi Emperor.
Solution 1:
Well, since I wasn't exactly overwhelmed by responses, here is the solution I came up with myself:
First, I created a new virtualenv with a Python 3 interpreter:
mkvirtualenv -p /usr/bin/python3 python3env
Then I installed the stock uwsgi from Pypi, which gets compiled automatically with a Python 3 interpreter:
pip install uwsgi
I created a configuration directory /etc/uwsgi-python3
that contains the emperor.ini and a subdirectory vassals, containing vassal.ini. Finally, I added the following line to /etc/rc.local
/home/user/.virtualenvs/python3env/bin/uwsgi --ini /etc/uwsgi-python3/emperor.ini
Now there's an uwsgi Emperor running that uses the Python 3 interpreter for its vassals. It doesn't interfere with another uwsgi Emperor that was already running and uses the Python 2.7 interpreter.
I know it's not optimal, because I'm not using the pluggable interpreter architecture that's explained in the documentation (thanks roberto! I don't know how I could've overlooked that). However, it runs flawlessly and I didn't have to touch my existing uwsgi installation that's serving a bunch of production apps.
Solution 2:
Under osx i made like this. I unistalled all uwsgi on my system (from brew from pip etc).
After that i downloaded under /usr/local the source code
wget https://projects.unbit.it/downloads/uwsgi-latest.tar.gz
tar zxvf uwsgi-latest.tar.gz
after
cd uwsgi-2.0.17
make PROFILE=nolang
In this way i created an executable without plugins for python.
After that i made each plugin for each version on my system:
PYTHON=python3.6 ./uwsgi --build-plugin "plugins/python python36"
PYTHON=python2.7 ./uwsgi --build-plugin "plugins/python python27"
PYTHON=python2.6 ./uwsgi --build-plugin "plugins/python python26"
Now i have 3 plugins.
In my ini files for the emperor i specified the plugins dir and the plugin version for each file
[uwsgi]
plugins-dir = /usr/local/uwsgi-2.0.17
plugin = python36
[uwsgi]
plugins-dir = /usr/local/uwsgi-2.0.17
plugin = python27
[uwsgi]
plugins-dir = /usr/local/uwsgi-2.0.17
plugin = python26
...
I symlinked the uwsgi binary in my /usr/local folder
ln -s /usr/local/uwsgi-2.0.17/uwsgi /usr/local/bin/uwsgi
And after run the emperor
uwsgi --emperor /PATH/TO/INI/FILES/FOLDER/
And voila now i can run python26, python27 and python36 project simultaneously
Solution 3:
Another possible solution is to reuse the system-wide "emperor", and only substitute the vassal with the new version. This way you don't need to invent any new folders under /etc
nor launch new services to rc.local
.
- Install
uwsgi
viapip
into a virtualenv. -
Edit the
/etc/uwsgi/apps-enabled/your-app.ini
as follows:- Remove the
plugins=...
line (because pip-compileduwsgi
does not support plugins). -
Add the line:
unprivileged-binary-patch = /path/to/your/venv/bin/uwsgi
This will force the uWSGI emperor launch your own
uwsgi
binary as the vassal.
- Remove the
Reload your app in the emperor
service uwsgi restart your-app
.
The last step somewhy reports a failure to restart the server:
* Starting app server(s) uwsgi
...fail!
However, in reality, the new vassal starts fine as well as all the other apps. I did not find the time to debug this.