The following instructions target Ubuntu 22.04 LTS and Debian 12; but, with some minor changes, should also apply to Linux distribution that uses systemd
to manage services. Python 3.10 or newer is required and the system must already have a working installation available.
This document provides instructions on how to serve the application through Gunicorn and use NGINX as a front-end HTTP server. Other options are available for serving up applications built using Flask, but those options will not be covered here.
Clone a copy of this repository to a location of your choosing by running:
git clone https://github.com/questionlp/stats.wwdt.me.git
Within the new local copy of the repository, create a new virtual environment and install the required packages by running the following commands:
python3 -m venv venv
source venv/bin/activate
pip install -U pip setuptools wheel
pip install -r requirements.txt
Next, make a copy of the config.json.dist
file and name it config.json
. Edit the config.json
file and fill in the required database connection information and any other settings that are specific to your environment.
To validate the installation, start up gunicorn
using the following command while in the application root directory and with the virtual environment activated:
gunicorn stats:app --reload
Once started, open a browser and browse to http://127.0.0.1:8000/. This should bring up the Stats Page web application.
Earlier versions of the Stats Page application included SQL queries that were written to target MySQL 5.5 and MariaDB 10.x. Some of the queries may throw errors due to violation of the sql_mode
flag ONLY_FULL_GROUP_BY
on newer versions of MySQL. These SQL queries should already be updated to resolve such errors.
In case these errors persist when running older versions of the Stats Page application, the ONLY_FULL_GROUP_BY
flag needs to be removed from the global sql_mode
variable. To verify which flags are currently set, you will first need to query the current value of sql_mode
by running:
select @@sql_mode;
In order for the flag to be set on MySQL service startup, you will need to update the mysqld.cnf
file on the server with the following configuration line and then restart the service.
sql-mode = <flags>
Gunicorn can take configuration options either as command line arguments or it can load configuration options from a gunicorn.conf.py
file located in the same directory that Gunicorn is launched from.
A template configuration file is included in the repository called gunicorn.conf.py.dist
. A copy of that file should be made and named gunicorn.conf.py
and the configuration options reviewed. The following options may need to be changed depending on the environment in which the application is being deployed:
bind
: The template defaults to using a UNIX socket file at/tmp/gunicorn-wwdtmstats.sock
as the listener. If TCP socket is preferred, change the value toIP:PORT
(replacingIP
andPORT
with the appropriate IP address of the interface and TCP port to listen to)workers
: The number of processes that are created to handle requests.accesslog
: The file that will be used to write access log entries to. Change the value from a string toNone
to disable access logging if that'll be handled by NGINX or a front-end HTTP server.errorlog
: The file that will be used to write error log entries to. Change the value from a string toNone
to disable error logging (not recommended). The directory needs to be created before running the application.
For more information on the above configuration options and other configuration options available, check out the Gunicorn documentation site.
A template systemd
service file is included in the repository named gunicorn-wwdtmstats.service.dist
. That service file provides the commands and arguments used to start a Gunicorn instance to serve up the application. A copy of that template file can be modified and installed under /etc/systemd/system
.
For this document, the service file will be installed as gunicorn-wwdtmstats.service
and the service name will be gunicorn-wwdtmstats
. The service file name, thus the service name, can be changed to meet your needs and requirements.
You will need to modify the following items in the new service file:
User
: The user which the service will run underGroup
: The group which the service will run underWorkingDirectory
: Provide the full path to the application root directory. Do not include theapp
directory in the pathEnvironment
: Add the full path to thevenv/bin
directoryExecStart
: Include the full path to thevenv/bin
directory and insert that betweenExecStart=
andgunicorn
Save the file and run the following commands to enable and start the new service:
sudo systemctl enable gunicorn-wwdtmstats
sudo systemctl start gunicorn-wwdtmstats
Verify that the service started by running the following command:
sudo systemctl status gunicorn-wwdtmstats
Once the service is up and running, NGINX can be configured to proxy requests to Gunicorn. NGINX can also be set up to cache responses and provide additional access controls that may not be feasible with Gunicorn.
Add the following NGINX configuration snippet either to your base nginx.conf
or to a virtual site configuration file. The configuration settings provides a starting point for serving up the application.
upstream gunicorn-wwdtmstats {
server unix:/tmp/gunicorn-wwdtmstats.sock fail_timeout=0;
}
server {
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://gunicorn-wwdtmstats;
}
}
NGINX can also be configured to cache rendered pages to quickly serve up pages that are commonly and frequently requested. NGINX has documentation on configuring and enable proxy caching in their ngx_http_proxy_module module documentation.
If proxy caching is used, the following configuration settings should be added to allow the random guests, hosts, locations, panelists, scorekeepers and shows routes to work correctly.
location ~* /(guests|hosts|locations|panelists|shows|scorekeepers)/random {
proxy_pass http://gunicorn-wwdtmstats$request_uri;
proxy_cache off;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Real-IP $remote_addr;
add_header Cache-Control 'no-store';
add_header Cache-Control 'no-cache';
expires 0;
}