Chapter 5. Introducing Mezzanine: Our Test Application

Chapter 2 covered the basics of writing playbooks. But real life is always messier than the introductory chapters of programming books, so in this chapter we’re going to work through a complete example of deploying a nontrivial application.

Our example application is an open-source content management system (CMS) called Mezzanine (http://mezzanine.jupo.org), which is similar in spirit to WordPress. Mezzanine is built on top of Django, the free Python-based framework for writing web applications.

Why Is Deploying to Production Complicated?

Let’s take a little detour and talk about the differences between running software in development mode on your laptop versus running the software in production. Mezzanine is a great example of an application that is much easier to run in development mode than it is to deploy. Example 5-1 shows a provisioning script to get Mezzanine running on Ubuntu Focal/64.1

Example 5-1. Running Mezzanine in development mode
sudo apt-get install -y python3-venv
python3 -m venv venv
source venv/bin/activate
pip3 install wheel
pip3 install mezzanine
mezzanine-project myproject
cd myproject
sed -i 's/ALLOWED_HOSTS = []/ALLOWED_HOSTS = ["*"]/' myproject/settings.py
python manage.py migrate
python manage.py runserver 0.0.0.0:8000 

You should eventually see output on the terminal that looks like this:

              .....
          _d^^^^^^^^^b_
       .d''           ``b.
     .p'                `q.
    .d'                   `b.
   .d'                     `b.   * Mezzanine 4.3.1
   ::                       ::   * Django 1.11.29
  ::    M E Z Z A N I N E    ::  * Python 3.8.5
   ::                       ::   * SQLite 3.31.1
   `p.                     .q'   * Linux 5.4.0-74-generic
    `p.                   .q'
     `b.                 .d'
       `q..          ..p'
          ^q........p^
              ''''
Performing system checks...
System check identified no issues (0 silenced).
June 15, 2021 - 19:24:35
Django version 1.11.29, using settings 'myproject.settings'
Starting development server at http://0.0.0.0:8000/
Quit the server with CONTROL-C.

If you point your browser to http://127.0.0.1:8000/, you should see a web page that looks like Figure 5-1.

Figure 5-1. Mezzanine after a fresh install

Deploying this application to production is another matter. When you run the mezzanine-project command, Mezzanine will generate a Fabric (http://www.fabfile.org) deployment script at myproject/fabfile.py that you can use to deploy your project to a production server. (Fabric is a Python-based tool that helps automate running tasks via SSH.) The script is almost 700 lines long, and that’s not counting the included configuration files that are also involved in deployment.

Why is deploying to production so much more complex? I’m glad you asked. When run in development, Mezzanine provides the following simplifications (see Figure 5-2):

  • The system uses SQLite as the backend database and will create the database file if it doesn’t exist.

  • The development HTTP server serves up both the static content (images, .css files, JavaScript) as well as the dynamically generated HTML.

  • The development HTTP server uses the (insecure) HTTP, not (secure) HTTPS.

  • The development HTTP server process runs in the foreground, taking over your terminal window.

  • The hostname for the HTTP server is always 127.0.0.1 (localhost).

Figure 5-2. Django app in development mode

Now, let’s look at what happens when you deploy to production.

PostgreSQL: The Database

SQLite is a serverless database. In production, you want to run a server-based database, because those have better support for multiple, concurrent requests, and server-based databases allow us to run multiple HTTP servers for load balancing. This means you need to deploy a database management system, such as MySQL or PostgreSQL (aka Postgres). Setting up one of these database servers requires more work. You’ll need to do the following:

  1. Install the database software.

  2. Ensure the database service is running.

  3. Create the database inside the database management system.

  4. Create a database user who has the appropriate permissions for the database system.

  5. Configure the Mezzanine application with the database user credentials and connection information.

Gunicorn: The Application Server

Because Mezzanine is a Django-based application, you can run it using Django’s HTTP server, referred to as the development server in the Django documentation. Here’s what the Django 1.11 docs have to say about the development server:

Don’t use this server in anything resembling a production environment. It’s intended only for use while developing. (We’re in the business of making Web frameworks, not Web servers.)

Django implements the standard Web Server Gateway Interface (WSGI),2 so any Python HTTP server that supports WSGI is suitable for running a Django application such as Mezzanine. We’ll use Gunicorn, one of the most popular HTTP WSGI servers, which is what the Mezzanine deploy script uses. Also note that Mezzanine uses an insecure version of Django that is no longer supported.

Nginx: The Web Server

Gunicorn will execute our Django application, just like the development server does. However, Gunicorn won’t serve any of the static assets associated with the application. Static assets are files such as images, .css files, and JavaScript files. They are called static because they never change, in contrast with the dynamically generated web pages that Gunicorn serves up.

Although Gunicorn can handle TLS encryption, it’s common to configure Nginx to handle the encryption.3

We’re going to use Nginx as our web server for serving static assets and for handling the TLS encryption, as shown in Figure 5-3.

Figure 5-3. Nginx as a reverse proxy

We need to configure Nginx as a reverse proxy for Gunicorn. If the request is for a static asset, such as a .css file, Nginx will serve that file directly from the local filesystem. Otherwise, Nginx will proxy the request to Gunicorn, by making an HTTP request against the Gunicorn service that is running on the local machine. Nginx uses the URL to determine whether to serve a local file or proxy the request to Gunicorn.

Note that requests to Nginx will be (encrypted) HTTPS, and all requests that Nginx proxies to Gunicorn will be (unencrypted) HTTP.

Supervisor: The Process Manager

When we run in development mode, we run the application server in the foreground of our terminal. If we were to close our terminal, the program would terminate. For a server application, we need it to run as a background process, so it doesn’t terminate, even if we close the terminal session we used to start the process.

The colloquial terms for such a process are daemon or service. We need to run Gunicorn as a daemon and we’d like to be able to stop it and restart it easily. Numerous service managers can do this job. We’re going to use Supervisor because that’s what the Mezzanine deployment scripts use.

At this point, you should have a sense of the steps involved in deploying a web application to production. We’ll go over how to implement this deployment with Ansible in Chapter 6.

1 This installs the Python packages into a virtualenv; the online example provisions a Vagrant VM automatically.

2 The WSGI protocol is documented in Python Enhancement Proposal (PEP) 3333 (https://www.python.org/dev/peps/pep-3333).

3 Gunicorn 0.17 added support for TLS encryption. Before that, you had to use a separate application such as Nginx to handle the encryption.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.216.32.116