I will skip the obvious - don't run Django in production with built-in web server, don't use DEBUG=True in production... you already should have heard that many times.
There are couple of scenarios to consider, for example deploy database on different computer or use S3 as media storage or deploy static files to some CDN. But the truth is, that even the absolute minimum deployment with everything (database, application, static files) located on single computer is complex enough for beginning, why complicate it more?
So, in this guide, we will aim to move our Django based project on single remote computer. The anatomy of simplest Django application in production will look like in picture below.
Our production toolset:
- Nginx (web server)
- PostgreSQL (database)
- Django (the main protagonist)
- Gunicorn (application server)
The numbered order corresponds to the order in which we will install them. In this lesson we not going through any of those 4 points. In this lesson we will take care of something, which needs care - even before you start whole deployment adventure. We will take care to setup of our SSH right way.
SSH with Public Key Authentication
On your rented VPS you have either root access or maybe some other privileged user. You must make sure you have public key authentication instead of password based. There two reasons for this:
- It is more secure
- It saves you time on typing password every time you login
Also you may consider to configure your ssh to alias your server (I will explain below what I mean); boths of these steps are necessary because you will be SSHing a lot into remote machine, typing one character less will make a big difference. So, instead of:
$ ssh email@example.com # and then password $ ssh firstname.lastname@example.org # and then password
You will type:
$ ssh u # ! right, just letter 'u' and no password; it will use public key authentication instead
At this point you are root user logged into remote machine. I will skip morale talk why you must not run commands as root. Instead, create another user and add it to sudo group.
root@localhost:~$ useradd -m -s /bin/bash -G sudo john # add user john, with home directory and with bash as default bash and include him in sudo group. root@localhost:~$ passwd john # set password for john user
Double check that user john was created and he belongs to sudo group:
root@localhost:~$ cat /etc/passwd # user john will be listed at the bottom of the file root@localhost:~$ groups john # should list two groups: john and sudo
Even though command prompt above says "root@localhost" commands are executed on remote virtual private server (VPS). It is "localhost" because server was just created and host name was not configured yet. We will configure host name little bit later.
So far so good. But we still don't use public key authentication for user john. Let's solve that first. The idea is very simple, we create on your local computer a pair of files, one is secret file (private key), and the other one you can share with everybody (public key). We will copy public key file to remote host (using ssh-copy-id command) thus telling remote host to allow log in of any user with private key which corresponds to this public key.
$ ssh-keygen -f ~/.ssh/johnk # generate public/private keys named identified as johnk, to ~/.ssh folder $ Generating public/private rsa key pair. $ Enter passphrase (empty for no passphrase): # leave passphrase empty .... $ ssh-copy-id -i ~/.ssh/johnk john@<remote-ip> # copy public key identified with johnk to remote machine john@<remote-ip>
You will be asked password for user john from remote host. And then you will be able to ssh john@<remote-ip> without typing password (ssh will authenticate your account based on your private key).
And last step, which is extremely important - is to disable password based authentication on the remote server. Edit /etc/ssh/sshd_config. Change following settings:
PubkeyAuthentication yes PermitRootLogin no # Disable password based authentication PasswordAuthentication no ChallengeResponseAuthentication no MaxAuthTries 2
Restart you sshd server with sudo systemctl restart sshd for changes to take effect on the server.
If you mess up your remote ssh server settings you might not be able to connect to it anymore. This is why is always a good practice to secure your ssh connection with public key authentication, play with ssh daemon settings at very very beginning of the deployment adventure, because if you mess your sshd settings you lose just little bit of work.
Type Less with SSH Host Aliasing
And finally, create a file ~/.ssh/config on your local computer with following content (i.e configure your local ssh client):
Host jo ForwardAgent yes Hostname <remote-ip> # replace with actual IP User john IdentityFile /home/<whatever-user-your-local-machine-has>/.ssh/johnk
Above ssh configuration is really cool, it enables you to just type ssh jo and ssh will login you into remote host john@<remote-ip> without asking password! Obviously instead of jo can be any name you like. Even more, if you use more remote machines, you can configure something like this:
Host * IdentityFile /home/<whatever-user-your-local-machine-has>/.ssh/johnk ForwardAgent yes Host jo Hostname <remote-ip> # replace with actual IP User john Host db Hostname <ip-or-dns-of-db-server> User johndb Host ap1 Hostname <ip-or-dns-of-some-app-server> User appuser
Didn't we plan to deploy a Django application ? Sure! This is our goal, but don't rush. There are many steps in between and each step needs to be executed properly. We just accomplished one step, but extremely important one - we configured our local ssh client. Now, let's give remote machine a proper name. We will name remote machine - john-systems. To accomplish this, use systemd's command hostnamectl
$ sudo hostnamectl set-hostname john-systems
If you log out and log in again (or alternatively enter a new bash by typing... bash), you will notice that prompt changed to: john@john-systems.
Classical way to change hostname on Linux systems is to edit file /etc/hostname. I am not sure which way is better. In the end both methods (write hostname directly in /etc/hostname or hostnamectl command) accomplish the same job.
The hostname john-systems is not same as DNS record. Hostname john-systems is just name of the computer for your own record. At this point john-systems machine is accessible from outside only by remote IP address.
Now, let's take care of computer DNS record. Instead of referring to it by IP we will give it a DNS name. For that, you need to have a domain registered with registrars like namecheap. I will use as example my own domain dglte.net. You need to add a so called A Record (via web interface of your registrar). In my example I will map production-demo.dglte.net to external IP address of my VPS server . When done, you will need to way up to 24 hours for DNS server to update. At any point in time, you can use dig command to check if A record maps to your IP address:
$ dig production-demo.dglte.net -t A
When DNS records were updated, dig command's response will be something like:
- Lesson #18
- Feb 15, 2020