Last time
we set up a Django Project using Cookiecutter, managed the application
environment via Docker, and then deployed the app to Digital Ocean. In
this tutorial, we’ll shift away from Docker and detail a development to
deployment workflow of a Cookiecutter-Django Project on Fedora 23.
Development
Install cookiecutter globally and then generate a bootstrapped Django project:
This command runs cookiecutter with the cookiecutter-django repo, allowing us to enter project-specific details. (We named the project and the repo django_cookiecutter_fedora.)
NOTE: Check out the Local Setup section from the previous post for more information on this command as well as the generated project structure.
Before we can start our Django Project, we are still left with few steps…
NOTE: There may be some variation in the
above command for creating a database based upon your version of
Postgres. You can check for the correct command in Postgres’ latest
documentation found here.
Dependency Setup
Next, in order to get your Django Project in a state ready for
development, navigate to the root directory, create/activate a virtual
environment, and then install the dependencies:
Ensure all is well by navigating to http://localhost:8000/
in your browser to view the Project quick start page. Once done, kill
the development server, initialize a new Git repo, commit, and PUSH to
Github.
Deployment
With the Project setup and running locally, we can now move on to deployment where we will utilize the following tools:
Set up a quick Digital Ocean droplet, making sure to use a Fedora 23 image. For help, follow this tutorial. Make sure you set up an SSH key for secure login.
Now let’s update our server. SSH into the server as root, and then fire the update process:
$ ssh root@SERVER_IP_ADDRESS
# dnf upgrade
Non-Root User
Next, let’s set up a non-root user so that applications are not run
under administrative privileges, which makes the system more secure.
As a root user, follow these commands to set up a non-root user:
# adduser <name-of-user># passwd <name-of-user>Changing password for user <name-of-user>.
New password:
Retype new password:
passwd: all authentication tokens updated successfully.
In the above snippet we created the new, non-root user user then specified a password for said user. We now need to add this user to the administrative group so that they can run commands that require admin privileges with sudo:
# usermod <name-of-user> -a -G wheel
Exit from the server and log back in again as the non-root user. Did you notice that the shell prompt changed from a # (pound-sign) to $ (dollar-sign)? This indicates that we are logged in as a non-root user.
Required Packages
While logged in as the non-root user, download and install the following packages:
Note: Below we are giving our non-root
user root rights (recommended!). If by any chance you wish to not give
the non-root user root rights explicitly, you will have to prepend sudo keyword with every command you execute in the terminal.
With the dependencies downloaded and installed, we just need to set up our Postgres server and create a database.
Initialize Postgres and then manually start the server:
Then log in to the Postgres server by switching (su) to the postgres user:
# sudo su - postgres# psqlpostgres=#
Now create a Postgres user and database required for our project,
making sure that the username matches the name of the non-root user:
postgres=# CREATE USER <user-name> WITH PASSWORD '<password-for-user>';CREATE ROLE
postgres=# CREATE DATABASE django_cookiecutter_fedora;CREATE DATABASE
postgres=# GRANT ALL ON DATABASE django_cookiecutter_fedora TO <user-name>;GRANT
NOTE: If you need help, please follow the official guide for setting up Postgres on Fedora.
Exit psql and return to your non-root user’s shell session:
postgres=# \q$ exit
When you exit from the postgres session, you will return back to your non-root user prompt. [username@django-cookiecutter-deploy ~]#.
Note: Did you notice the #
sign at the prompt? This is now appearing because we gave our non-root
user the root rights before starting off with setting up our server.
Configure Postgres so that it starts when the server boots/reboots:
Clone the project structure from your GitHub repo to the /opt directory:
# git clone <github-repo-url> /opt/<name of the repo>
NOTE: Want to use the repo associated with this tutorial? Simply run: sudo git clone https://github.com/realpython/django_cookiecutter_fedora /opt/django_cookiecutter_fedora
Dependency Setup
Next, in order to get your Django Project in a state ready for
deployment, create and active a virtualenv inside your project’s root
directory:
# cd /opt/django_cookiecutter_fedora/# pip3 install virtualenv# pyvenv-3.4 venv
Before activating the virtualenv, give the current, non-root user admin rights (if not given):
$ sudo su
# source venv/bin/activate
Unlike with the set up of the development environment from above, before installing the dependencies we need to install all of Pillow’s external libraries. Check out this resource for more info.
Next, we need to install one more package in order to ensure that we
do not get conflicts while installing dependencies in our virtualenv.
# dnf install redhat-rpm-config
Then run:
# ./install_python_dependencies.sh
Again, this will install all the base, local, and production
requirements. This is just for a quick sanity check to ensure all is
working. Since this is technically the production environment, we will
change the environment shortly.
NOTE: Deactivate the virtualenv. Now if you issue an exit command – e.g., exit
– the non-root user will no longer have root rights. Notice the change
in prompt. That said, the user can still activate the virtualenv. Try
it!
To ensure things are working fine, just visit the server’s IP address
in your browser – e.g., <ip-address or hostname>:8000.
Gunicorn Setup
Before setting up Gunicorn, we need to make some changes to the production settings, in the /config/settings/production.py module.
Take a look at the production settings here, which are the minimal settings needed for deploying our Django Project to a production server.
To update these, open the file in VI:
$ sudo vi config/settings/production.py
First, select all and delete:
:%d
And then copy the new settings and paste them into the now empty file
by entering INSERT mode and then pasting. Make sure to update the ALLOWED_HOSTS variable as well (very, very important!) with your server’s IP address or hostname. Exit INSERT mode and then save and exit:
:wq
Once done, we need to add some environment variables to the .bashrc file, since the majority of the config settings come from environment variables within the production.py file.
Again, use VI to edit this file:
Notice how we updated the DJANGO_SETTINGS_MODULE variable to use the production settings.
It’s a good practice to change your DJANGO_SECRET_KEY to a more complex string. Do this now if you want.
Again, exit INSERT mode, and then save and exit VI.
Now just reload the .bashrc file:
$ source ~/.bashrc
Ready to test?! Inside the root directory, with the virtualenv activated, execute the gunicorn server:
$ gunicorn --bind <ip-address or hostname>:8000 config.wsgi:application
This will make our web application, again, serve on <ip-address or hostname>:8000.
Keep in mind that as soon as we log out of our server this command
will stop and hence we would no longer be able to serve our web app. So,
we have to make our gunicorn server execute as a service so that it can
be started, stopped, and monitored.
Nginx Config
Follow these steps for adding a configuration file for making our Django Project serve via Nginx:
$ cd /etc/nginx/conf.d
$ sudo vi django_cookiecutter_fedora.conf
Add the following, making sure to update the server, server_name, and location for project root:
Save and exit VI, and then restart the Nginx server:
$ sudo systemctl restart nginx.service
That’s it!
Gunicorn Start Script
Now let’s create a Gunicorn start script which will run as an
executable, making our bootstrapped Django web application run via the
Gunicorn server, routed through Nginx.
Within the project root, run:
$ sudo mkdir deploy log
$ cd deploy
$ sudo vi gunicorn_start
The contents of the gunicorn_start script can be found here.
It is divided into 3 significant parts, which are, for the most part,
self-explanatory. For any questions, please comment below.
NOTE: Make sure the USER and GROUP variables match the same user and group for the non-root user.
Paste the contents into VI, and then save and exit.
Finally, let’s make it executable:
$ sudo chmod +x gunicorn_start
Start the server:
$ ./gunicorn_start
Once again visit the your server’s ip address in the browser and you will see your Django web app running!
Did you get a 502 Bad gateway error? Just follow these steps and it will probably be enough to make your application work…
Once done, make sure to restart your server via Digital Ocean’s dashboard utility.
Systemd
To make our gunicorn_start script run as a system service so
that, even if we are no longer logged into the server, it still serves
our Django web application, we need to create a systemd service.
Just change the working directory to /etc/systemd/system, and then create a service file:
$ cd /etc/systemd/system
$ sudo vi django-bootstrap.service
Add the following:
#!/bin/sh[Unit]Description=Django Web App
After=network.target
[Service]PIDFile=/var/run/cric.pid
ExecStart=/opt/django_cookiecutter_fedora/deploy/gunicorn_start
Restart=on-abort
[Install]WantedBy=multi-user.target
Save and exit, and then start the service and enable it:
$ sudo systemctl start django-bootstrap.service
NOTE: If you encounter an error, run journalctl -xe to see more details.
Finally, enable the service so that it runs forever and restarts on arbitrary shut downs:
$ sudo systemctl enable django-bootstrap.service
Sanity Check (final!)
Check for the status of the service:
$ sudo systemctl status django-bootstrap.service
Now just visit your server’s IP address (or hostname) and you will
see a Django error page. To fix this, run the following command in your
project’s root directory (with your virtualenv activated):
$ python manage.py collectstatic
Now you are good to go, and you will see the Django web app running
on the web browser with all the static files (HTML/CSS/JS) files
working.