Skip to content

Instantly share code, notes, and snippets.

@bendavis78
Last active September 14, 2024 21:19
Show Gist options
  • Save bendavis78/3ffb083781a70fb1bcf4d2b450c49ade to your computer and use it in GitHub Desktop.
Save bendavis78/3ffb083781a70fb1bcf4d2b450c49ade to your computer and use it in GitHub Desktop.
Building uwsgi w/ support multiple python versions

Installation

Remove all traces of uwsgi on the system, if previously installed

apt remove uwsgi uwsgi-core
rm /usr/bin/uwsgi
rm /usr/local/bin/uwsgi
rm -rf /usr/lib/uwsgi
rm -rf /usr/local/lib/uwsgi

Any config files in /etc can stay.

Building and installing uWSGI from source

Download and extract latest stable uWSGI tarball

wget https://projects.unbit.it/downloads/uwsgi-latest.tar.gz
tar xf uwsgi-latest.tar.gz

From the created directory run:

make PROFILE=nolang

this will build the uWSGI “core” binary.

We now build the plugins we want. The argument on the far right is whatever we want to name the plugin. For python, we generally want the name of the plugin to mirror the binaries installed on the system.

PYTHON=python ./uwsgi --build-plugin "plugins/python python"
PYTHON=python3 ./uwsgi --build-plugin "plugins/python python3"
PYTHON=python3.5 ./uwsgi --build-plugin "plugins/python python35"
PYTHON=python3.6 ./uwsgi --build-plugin "plugins/python python36"
PYTHON=python2 ./uwsgi --build-plugin "plugins/python python2"
PYTHON=python2.7 ./uwsgi --build-plugin "plugins/python python27"

Make sure the python binaries listed above actually exist. UWSGI will not complain if it doesn't and will likely use the default pyhton.

I also want PHP support, so...

./uwsgi --build-plugin "plugins/php"

Now we have uwsgi, python_plugin.so, python3_plugin.so, python2_plugin.so, etc..

Install uwsgi python packages

You'll need to install uwsgi for each python version in their respective site-packages dirs.

# (run from inside the uwsgi src dir)
python setup.py install
python3 setup.py install
python3.6 setup.py install

Do this for each version of python installed on your system. If you don't do this, you'll get the error ImportError: No module named 'encodings'

Install uwsgi binary and plugins to /usr/local

You could, if you want, just run it out of that folder, but it's preferrable to install it in /usr/local:

sudo mkdir /usr/local/lib/uwsgi/
sudo cp *_plugin.so /usr/local/lib/uwsgi/
sudo cp uwsgi /usr/local/bin/uwsgi

Configuration

I like to run uwsgi emperor to manage vassals for each app.

Emperor

Create /etc/systemd/system/uwsgi.service

[Unit]
Description=uWSGI Emperor
After=syslog.target

[Service]
ExecStart=/usr/local/bin/uwsgi --ini /etc/uwsgi/emperor.ini
ExecReload=/usr/bin/kill -HUP $MAINPID
Restart=always
KillSignal=SIGQUIT
Type=notify
StandardError=syslog
NotifyAccess=all

[Install]
WantedBy=multi-user.target

Create the emperor config in /etc/uwsgi/emperor.ini

[uwsgi]
emperor = /etc/uwsgi/vassals/*.ini
show-config = 1
uid = http
gid = http
emperor-stats = /run/uwsgi/emperor-stats.sock
umask = 002
logto = /var/log/uwsgi/emperor.log

Make sure any directories referenced above actually exist.

Enable and start the emperor

sudo systemctl enable uwsgi
sudo systemctl start uwsgi

Base config for python apps

I like to keep a base config for python apps which I symlink to the vassals directory.

/etc/uwsgi/python-defaults.ini:

[uwsgi]
plugins = python3

# paths
base = /srv/%n
virtualenv = %(base)
logto = %(base)/log/uwsgi.%n.log
socket = /run/uwsgi/%n.sock
touch-reload = %(base)/reload

# default process settings
buffer-size = 32768
cpu-affinity = 2
workers = 1
harikiri-verbose = 60
max-requests = 5000
limit-as = 1024
show-config = 1
enable-threads = 1

# Trying to avoid OSError: write error
# https://stackoverflow.com/questions/36156887/uwsgi-raises-oserror-write-error-during-large-request
ignore-sigpipe = true
ignore-write-errors = true
disable-write-exception = true

# default environment variables
env = LANG=en_US.utf8

# load environment variables from config file
for-readline = /srv/%n/config.env
  env = %(_)
endfor =

# load environment variables based on name in case dir is symlinked
for-readline = /srv/%n/config-%n.env
env = %(_)
endfor =

if-file = /srv/%n/uwsgi.ini
 ini = /srv/%n/uwsgi.ini
endif =

Then for each app, I can just make a symlink to enable it:

sudo ln -s /etc/uwsgi/python-defaults.ini /etc/uwsgi/vassals/my-app.ini
@novialriptide
Copy link

Thank you so much!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment