Hands-On Docker for Microservices with Python
上QQ阅读APP看书,第一时间看更新

Configuring uWSGI

The uwsgi.ini file contains the uWSGI configuration:

[uwsgi]
uid=uwsgi
chdir=/opt/code
wsgi-file=wsgi.py
master=True
pidfile=/tmp/uwsgi.pid
http=:8000
vacuum=True
processes=1
max-requests=5000
# Used to send commands to uWSGI
master-fifo=/tmp/uwsgi-fifo

Most of it is information that we have from the Dockerfile, though it needs to match so that uWSGI knows where to find the application code, the name of the WSGI file to start, the user to start it from, and so on.

Other parameters are specific to uWSGI behavior:

  • master: Creates a master process that controls the others. Recommended for uWSGI operation as it creates smoother operation.
  • http: Serves in the specified port. The HTTP mode creates a process that load balances the HTTP requests toward the workers, and it's recommended to serve HTTP outside of the container.
  • processes: The number of application workers. Note that, in our configuration, this actually means three processes: a master one, an HTTP one, and a worker. More workers can handle more requests but will use more memory. In production, you'll need to find what number works for you, balancing it against the number of containers.
  • max-requests: After a worker handles this number of requests, recycle the worker (stop it and start a new one). This reduces the probability of memory leaks.
  • vacuum: Clean the environment when exiting.
  • master-fifo: Create a Unix pipe to send commands to uWSGI. We will use this to handle graceful stops.
The uWSGI documentation ( https://uwsgi-docs.readthedocs.io/en/latest/) is quite extensive and comprehensive. It contains a lot of valuable information, both for operating uWSGI itself and understanding details about how web servers operate. I learn something new each time that I read it, but it can be a bit overwhelming at first.

It's worth investing a bit of time in running tests to discover what are the best parameters for your service in areas such as timeouts, the number of workers, and so on. However, remember that some of the options for uWSGI may be better served with your container's configuration, which simplifies things.

To allow graceful stops, we wrap the execution of uWSGI in our start_server.sh script:

#!/bin/sh

_term() {
echo "Caught SIGTERM signal! Sending graceful stop to uWSGI through the master-fifo"
# See details in the uwsgi.ini file and
# in http://uwsgi-docs.readthedocs.io/en/latest/MasterFIFO.html
# q means "graceful stop"
echo q > /tmp/uwsgi-fifo
}

trap _term SIGTERM

uwsgi --ini /opt/uwsgi/uwsgi.ini &

# We need to wait to properly catch the signal, that's why uWSGI is started
# in the background. $! is the PID of uWSGI
wait $!
# The container exits with code 143, which means "exited because SIGTERM"
# 128 + 15 (SIGTERM)
# http://www.tldp.org/LDP/abs/html/exitcodes.html
# http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_12_02.html

The core of the script is the call to uwsgi to start the service. It will then wait until it stops.

The SIGTERM signal will be captured and uWSGI will be stopped gracefully by sending the q command to the master-fifo pipe.

A graceful stop means that a request won't be interrupted when a new container version is available. We'll see later how to make rollout deployments, but one of the key elements is to interrupt existing servers when they are not serving requests, to avoid stopping in the middle of a request and leaving an inconsistent state.

Docker uses the SIGTERM signal to stop the execution of containers. After a timeout, it will kill them with SIGKILL.