Sign In
Sign In

Deploying Python Applications with Gunicorn

Deploying Python Applications with Gunicorn
Hostman Team
Technical writer
Python
23.10.2024
Reading time: 7 min

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

To begin, we need to update all packages:

sudo apt update

Ubuntu provides the latest version of the Python interpreter by default. Let’s check the installed version using the following command:

python3 --version

Example output:

Python 3.10.12

We’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-dev

Next, create a folder for your project and navigate into it:

mkdir myapp
cd myapp

Now, create a virtual environment:

python3 -m venv venv

And create a folder for your project:

mkdir app

Your project directory should now contain two items: app and venv.

You can verify this using the standard Linux command to list directory contents:

ls

Expected output:

myapp venv

You need to activate the virtual environment so that all subsequent components are installed locally for the project:

source venv/bin/activate

Installing and Configuring Gunicorn

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 gunicorn

WSGI and Python

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.py

Add 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

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:app

To restart Gunicorn, you can use:

sudo systemctl restart gunicorn

Systemd Integration

systemd is a system and service manager that allows for strict control over processes, resources, and permissions. We’ll create a socket that systemd will listen to, automatically starting Gunicorn in response to traffic.

Configuring the Gunicorn Service and Socket

First, create the service configuration file:

sudo nano /etc/systemd/system/gunicorn.service

Add 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.socket

Add 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.socket

Configuring Gunicorn

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:app

This 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:app

Process 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.GunicornWebWorker

Access Logging

You can enable access logging using the --access-logfile flag.

Example:

gunicorn wsgi:app --access-logfile access.log

Error Logging

To specify an error log file, use the following command:

gunicorn wsgi:app --error-logfile error.log

You 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

First, install Nginx with the command:

sudo apt install nginx

Let’s check if the Nginx service can connect to the socket created earlier:

sudo -u www-data curl --unix-socket /run/gunicorn.sock http

If 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.conf

Then create a symbolic link with the command:

sudo ln -s /etc/nginx/sites-available/myconfig.conf /etc/nginx/sites-enabled

Nginx must be restarted after any changes to the configuration file to apply the new settings.

First, check the syntax of the configuration file:

nginx -t

Then reload Nginx:

nginx -s reload

Conclusion

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.

Python
23.10.2024
Reading time: 7 min

Do you have questions,
comments, or concerns?

Our professionals are available to assist you at any moment,
whether you need help or are just unsure of where to start
Email us