Deploying Python Applications with Gunicorn
In this article, we’ll show how to set up an Ubuntu 20.04 server and install and configure the components required for deploying Python applications. We’ll configure the WSGI server Gunicorn to interact with our application. Gunicorn will serve as an interface that converts client requests via the HTTP protocol into Python function calls executed by the application. Then, we will configure Nginx as a reverse proxy server for Gunicorn, which will forward requests to the Gunicorn server. Additionally, we will cover securing HTTP connections with an SSL certificate or using other features like load balancing, caching, etc. These details can be helpful when working with cloud services like those provided by Hostman.
Creating a Python Virtual Environment Copy link
To begin, we need to update all packages:
sudo apt updateUbuntu provides the latest version of the Python interpreter by default. Let’s check the installed version using the following command:
python3 --versionExample output:
Python 3.10.12We’ll set up a virtual environment to ensure that our project has its own dependencies, separate from other projects. First, install the virtualenv package, which allows you to create virtual environments:
sudo apt-get install python3-venv python3-devNext, create a folder for your project and navigate into it:
mkdir myapp
cd myappNow, create a virtual environment:
python3 -m venv venvAnd create a folder for your project:
mkdir appYour project directory should now contain two items: app and venv.
You can verify this using the standard Linux command to list directory contents:
lsExpected output:
myapp venvYou need to activate the virtual environment so that all subsequent components are installed locally for the project:
source venv/bin/activateInstalling and Configuring Gunicorn Copy link
Gunicorn (Green Unicorn) is a Python WSGI HTTP server for UNIX. It is compatible with various web frameworks, fast, easy to implement, and uses minimal server resources.
To install Gunicorn, run the following command:
pip install gunicornWSGI and Python Copy link
WSGI (Web Server Gateway Interface) is the standard interface between a Python application running on the server side and the web server itself, such as Nginx. A WSGI server interacts with the application, allowing you to run code when handling requests. Typically, the application is provided as an object named application in a Python module, which is made available to the server.
In the standard wsgi.py file, there is usually a callable application. For example, let’s create such a file using the nano text editor:
nano wsgi.pyAdd the following simple code to the file:
from aiohttp import web
async def index(request):
return web.Response(text="Welcome home!")
app = web.Application()
app.router.add_get('/', index)
In the code above, we import aiohttp, a library that provides an asynchronous HTTP client built on top of asyncio. HTTP requests are a classic example of where asynchronous handling is ideal, as they involve waiting for server responses, during which other code can execute efficiently. This library allows sequential requests to be made without waiting for the first response before sending a new one. It’s common to run aiohttp servers behind Nginx.
Running the Gunicorn Server Copy link
You can launch the server using the following command template:
gunicorn [OPTIONS] [WSGI_APP]Here, [WSGI_APP] consists of $(MODULE_NAME):$(VARIABLE_NAME) and [OPTIONS] is a set of parameters for configuring Gunicorn.
A simple command would look like this:
gunicorn wsgi:appTo restart Gunicorn, you can use:
sudo systemctl restart gunicornSystemd Integration Copy link
systemdConfiguring the Gunicorn Service and Socket
First, create the service configuration file:
sudo nano /etc/systemd/system/gunicorn.serviceAdd the following content to the file:
[Unit]
Description=gunicorn daemon
Requires=gunicorn.socket
After=network.target
[Service]
Type=notify
User=someuser
Group=someuser
RuntimeDirectory=gunicorn
WorkingDirectory=/home/someuser/myapp
ExecStart=/path/to/venv/bin/gunicorn wsgi:app
ExecReload=/bin/kill -s HUP $MAINPID
KillMode=mixed
TimeoutStopSec=5
PrivateTmp=true
[Install]
WantedBy=multi-user.target
Make sure to replace /path/to/venv/bin/gunicorn with the actual path to the Gunicorn executable within your virtual environment. It will likely look something like this: /home/someuser/myapp/venv/bin/gunicorn.
Next, create the socket configuration file:
sudo nano /etc/systemd/system/gunicorn.socketAdd the following content:
[Unit]
Description=gunicorn socket
[Socket]
ListenStream=/run/gunicorn.sock
SocketUser=www-data
[Install]
WantedBy=sockets.target
Enable and start the socket with:
systemctl enable --now gunicorn.socketConfiguring Gunicorn Copy link
Let's review some useful parameters for Gunicorn in Python 3. You can find all possible parameters in the official documentation.
Sockets
-b BIND,--bind=BIND— Specifies the server socket. You can use formats like:$(HOST),$(HOST):$(PORT).
Example:
gunicorn --bind=127.0.0.1:8080 wsgi:appThis command will run your application locally on port 8080.
Worker Processes
-w WORKERS,--workers=WORKERS— Sets the number of worker processes. Typically, this number should be between 2 to 4 per server core.
Example:
gunicorn --workers=2 wsgi:appProcess Type
-k WORKERCLASS,--worker-class=WORKERCLASS— Specifies the type of worker process to run.
By default, Gunicorn uses the sync worker type, which is a simple synchronous worker that handles one request at a time. Other worker types may require additional dependencies.
Asynchronous worker processes are available using Greenlets (via Eventlet or Gevent). Greenlets are a cooperative multitasking implementation for Python. The corresponding parameters are eventlet and gevent.
We will use an asynchronous worker type compatible with aiohttp:
gunicorn wsgi:app --bind localhost:8080 --worker-class aiohttp.GunicornWebWorkerAccess Logging
You can enable access logging using the --access-logfile flag.
Example:
gunicorn wsgi:app --access-logfile access.logError Logging
To specify an error log file, use the following command:
gunicorn wsgi:app --error-logfile error.logYou can also set the verbosity level of the error log output using the --log-level flag. Available log levels in Gunicorn are:
-
debug
-
info
-
warning
-
error
-
critical
By default, the info level is set, which omits debug-level information.
Installing and Configuring Nginx Copy link
First, install Nginx with the command:
sudo apt install nginxLet’s check if the Nginx service can connect to the socket created earlier:
sudo -u www-data curl --unix-socket /run/gunicorn.sock httpIf successful, Gunicorn will automatically start, and you'll see the HTML code from the server in the terminal.
Nginx configuration involves adding config files for virtual hosts. Each proxy configuration should be stored in the /etc/nginx/sites-available directory.
To enable each proxy server, create a symbolic link to it in /etc/nginx/sites-enabled. When Nginx starts, it automatically loads all proxy servers in this directory.
Create a new configuration file:
sudo nano /etc/nginx/sites-available/myconfig.confThen create a symbolic link with the command:
sudo ln -s /etc/nginx/sites-available/myconfig.conf /etc/nginx/sites-enabledNginx must be restarted after any changes to the configuration file to apply the new settings.
First, check the syntax of the configuration file:
nginx -tThen reload Nginx:
nginx -s reloadConclusion Copy link
Gunicorn is a robust and versatile WSGI server for deploying Python applications, offering flexibility with various worker types and integration options like Nginx for load balancing and reverse proxying. Its ease of installation and configuration, combined with detailed logging and scaling options, make it an excellent choice for production environments. By utilizing Gunicorn with frameworks like aiohttp and integrating it with Nginx, you can efficiently serve Python applications with improved performance and resource management.