NGINX Unit is a fully dynamic application server that can serve multiple languages as well as multiple versions of each language. It’s dynamic in the sense that you use the RESTful JSON API to make changes to its configuration in memory, without service disruption or configuration reloads.
In my presentation at NGINX Conf 2018 in October, I showed how to configure a new application in an existing production environment. Specifically, with WordPress running on PHP, I deployed a Python application that uses the Django framework. I also showed show how you can load configuration both from a file and as specified with an argument to an API call.
This blog includes all of the commands and configuration code I used in the demo, to make it easier for you to adapt to your own deployment.
Prerequisites
For the demo at NGINX Conf, I had the following software installed:
- Ubuntu 16.04
- NGINX Plus, but you can use NGINX Open Source except as noted
- NGINX Unit with all language modules installed
- Python 3
- Django (not configured – that’s what the demo and this blog are about)
rootprivilege, or equivalent access viasudo(which we use where necessary)
I also had PHP and WordPress installed as the existing application.
Creating the Django Project
- Change into the directory where we’re creating our Django project:
- Use the
django-adminstartprojectcommand to initialize the new project. We’re calling it djapp. - Change into the project directory:
- Use the
manage.pyscript to migrate the database for the project, which is necessary for a newly created project. Django uses SQLite by default, and I accept the default in the demo, but you can use any database that meets your project’s needs. Themanage.pyscript is installed by thedjango-admincommand we ran in Step 2; it performs the same commands and accepts the same arguments asdjango-admin, but automatically derives and uses some project‑specific settings, which is helpful. For details, see the Django documentation. - Although it’s not strictly necessary for a sample project like this one, we recommend that you create a Django superuser identity:
- Change into the subdirectory that contains the settings.py file, which was created by the
django-adminstartprojectcommand in Step 2. - Using your preferred text editor, open settings.py. Here we’re using
nano:Find theALLOWED_HOSTSline and add in the domain name, hostname, or IP address for the application: Also add the following line at the end of the file, to name the directory that stores all static content served by the application (see Step 9). - Change back to the main project directory (where manage.py resides).
- Run the
manage.pycollectstaticcommand to collect all static files located in the Django project and put them into theSTATIC_ROOTlocation defined in Step 7.
Configuring NGINX
By default, Django itself serves the static content for a project, but NGINX Open Source and NGINX Plus offer superior performance. Here we configure NGINX Plus, but you can use NGINX Open Source except for one feature noted below.
- Change directory to /etc/nginx/conf.d, the conventional location for function‑specific (or in our case, application‑specific) HTTP configuration files:
- Create a file called django.conf (again, we’re using
nano):Insert the following configuration, which enables caching. The configuration also includes two features that are exclusive to NGINX Plus. Uncomment the relevant directives if you are using NGINX Plus and want to take advantage of the features: One thing to note is that in the demo at NGINX Conf I specified the IP address of my local machine as the second argument to theproxy_set_headerdirective. In a production environment, it makes more sense to use the$hostvariable as shown below.- Extended metrics collection, enabled for this virtual server by the
status_zonedirective. I’m assuming that the NGINX Plus API is enabled elsewhere in the configuration. - Active health checks, enabled by the
health_checkdirective.
- Extended metrics collection, enabled for this virtual server by the
- Check the configuration for syntactic validity:
- After fixing any errors, reload the configuration:
Configuring NGINX Unit
To finish up, we need to configure NGINX Unit to serve the requests to the application.
- Run this
curlcommand to display the current NGINX Unit configuration, which is for WordPress running on PHP. I don’t show the output here, but the WordPress configuration appears in Step 6 below, along with the Python application’s configuration, which we’re about to add.Note that I usesudofor thecurlcommand, which you may not need to do for mostcurlcommands. Here it’s necessary because to access the UNIX socket we need the read‑write permission thatroothas on it. - Change to the directory for NGINX Unit configuration files. Keep in mind that these files are optional and just a convenient way to load collections of configuration without typing all the data as an argument to a call to the NGINX Unit API. Because the content of the files is uploaded through the API (like all configuration data), NGINX Unit does not know about file locations and cannot automatically read files as it starts (unlike NGINX Open Source and NGINX Plus). Instead, NGINX Unit saves its runtime state in a separate directory.
- Create a file called django.config (again, we’re using
nano):Add the following JSON, which represents our Python application. - Run this
curlcommand to load the JSON contained in django.config as a new application object to be managed by NGINX Unit, called djapp:In this command:- The HTTP
PUTmethod creates a new NGINX Unit configuration object at the location named by the final argument (the URL). See the final bullet below. - The
--data-binaryargument tellscurlto load the contents of django.config exactly as provided, preserving newlines and carriage returns, and not doing processing of any kind. - The
--unix-socketargument defines where the NGINX Unit API is listening. (We use thesudocommand because we’re using the default owner of the socket,root.) - The final argument locates and names the new application object to populate with the JSON‑formatted configuration data in django.config:
configis the top‑level NGINX Unit configuration object,applicationsthe parent for application objects, anddjappthe name of the new application object.
- The HTTP
- Define the listener object for the application. Rather than loading a file of configuration data as in Step 4, we define the data directly on the
curlcommand line, specifying that thedjappapplication listens on port 8000. - Repeat the
curlcommand from Step 1 to display the NGINX Unit configuration, which now includes our Python application, djapp, highlighted in orange:
Summary
In this post we started with NGINX Unit running PHP applications for WordPress in production, and added a Python application. In the demo, I use the NGINX Plus dashboard to show that there is no disruption to the existing applications when a new application is added, but you can use any system‑monitoring tool, such as the ps command, for that purpose. The dynamic nature of NGINX Unit configuration saves resources for your running applications, and ensures zero downtime on new deployments and smooth transition between application versions.
To learn more, visit unit.nginx.org.
About the Author
Related Blog Posts
Secure Your API Gateway with NGINX App Protect WAF
As monoliths move to microservices, applications are developed faster than ever. Speed is necessary to stay competitive and APIs sit at the front of these rapid modernization efforts. But the popularity of APIs for application modernization has significant implications for app security.
How Do I Choose? API Gateway vs. Ingress Controller vs. Service Mesh
When you need an API gateway in Kubernetes, how do you choose among API gateway vs. Ingress controller vs. service mesh? We guide you through the decision, with sample scenarios for north-south and east-west API traffic, plus use cases where an API gateway is the right tool.
Deploying NGINX as an API Gateway, Part 2: Protecting Backend Services
In the second post in our API gateway series, Liam shows you how to batten down the hatches on your API services. You can use rate limiting, access restrictions, request size limits, and request body validation to frustrate illegitimate or overly burdensome requests.
New Joomla Exploit CVE-2015-8562
Read about the new zero day exploit in Joomla and see the NGINX configuration for how to apply a fix in NGINX or NGINX Plus.
Why Do I See “Welcome to nginx!” on My Favorite Website?
The ‘Welcome to NGINX!’ page is presented when NGINX web server software is installed on a computer but has not finished configuring