12

Deployment Basics – Git, GitHub, and AWS

It’s nice to develop an application with a functioning backend and a nice, flexible frontend on your machine. Still, if you want your application to be used publicly, you need to deploy the application to production. From this chapter to the last one, you will learn how to prepare the application we’ve built for deployment, deploy the backend on Amazon Web Services (AWS) and the frontend on Vercel, and finally, go through some security and performance optimizations.

In this chapter, we will learn deployment basics such as jargon and concepts to understand before going further. We will be learning about the following topics:

  • Basics of software deployment
  • Tools and methods of web application deployment
  • Platforms for web application deployment

Technical requirements

For this chapter, you will need to have Git installed on your machine. If you are on Linux or macOS, it will come by default. You can check its existence with the following command in the terminal:

git –version

Otherwise, feel free to download the right version at https://git-scm.com/downloads.

After the installation, let’s configure Git if not done yet. In a terminal, enter the following configuration commands to set the username (usually the username on your GitHub account) and the email address (usually the email address on your GitHub account):

git config --global user.name "username"
git config --global user.email "[email protected]"

You will also need an active GitHub account. You can register on the official website at https://github.com/. As we will also be deploying the application on a remote AWS server, you will need an AWS account that can be created at https://portal.aws.amazon.com/billing/signup. If you don’t have an AWS account, you can still use any virtual private server (VPS) or virtual private cloud (VPC) you have online. However, this chapter will also document how to create a VPC instance using AWS and how to upload the code and serve the Django API.

Basics of software deployment

Software deployment concerns all the activities that make a software system available to consumers. The term software deployment is also commonly described as application deployment. Following the best software deployment practices will ensure that all applications deployed operate smoothly and work as expected.

There are several benefits of software deployment, such as:

  • Saved time: A good software deployment process can be configured to only take a few minutes. This saves time for compiling and distribution to the users.
  • Increased security: Deploying your application in a structured manner rather than doing it manually or for individual users means you ensure the security of the application and not only the security of the application on every user’s device.
  • Better monitoring: Deploying an application on production servers helps provide more control and data on what is working from the user’s end.

With software deployment defined, we will dive deeper into the tools and methods used for web application deployment.

Tools and methods of web application deployment

Deploying a web application for production has drastically evolved over the years. From manual deployment to automated deployment techniques, web application deployment has advanced, making the process more secure, smooth, and as fast as possible. There are many tools for web application deployment, but in this book, we will focus on the automated tools and configure the Django project and the React project for automated deployments when pushes are made on the remote repository of the code.

But where will the code be pushed first? Let’s start describing and learning how to use the tools for our full stack application deployment, starting with Git and GitHub.

Using Git and GitHub

Git is a popular tool used for source code version control and collaboration. It not only helps the user keep track of changes made to the code but also allows developers to work through small or large code bases, with collaboration made easier. In the following subsections, we will initialize a Git repository in the backend project, commit the changes, and then push the changes to a remote repository on GitHub.

Creating a Git repository

Open a new terminal in the directory where you created the Django project and enter the following command:

git init

This command will create an empty .git/ directory in the current directory: this is a Git repository. This repository tracks all changes made to files in the project, helping build a history of changes made, with details on the files changed, the name of the person making the changes, and much more information.

After the initialization, we will need to ignore some files in the project. We are talking about files such as .pycache, .env, and the virtual environment directories. After all, we don’t want important information such as secret environment variables to be available in the project or useless cache files to be present in the changes.

Inside the directory of the Django API, create a new file called .gitignore. This file tells Git which files and directories to ignore when tracking changes:

.gitignore

__pycache__
venv
env
.env

These files and directories in the preceding code will be ignored. Next, we will add the change in the directory to the staging area. The staging area allows you to group related changes before committing them to the project history. As we have successfully added a .gitignore file, we can freely run the git add command:

git add .

The dot (.) at the end of the command tells Git to only look for changed files in the current directory. To have a look at the changes to be committed to the Git history, run the following command:

git status

The git status command is used to show the state of the working directory and also the staging area. Using the command, you can see changes that are tracked or not. The following figure shows an example of the output you should have:

Figure 12.1 – Running the git status command

Figure 12.1 – Running the git status command

We can now run the git commit command. A commit is an operation that writes the latest changes of the source code to the version control system history. In our case, with git commit command will save the changes to the local repository:

git commit

The preceding command will prompt you to a text editor in the terminal or an app, depending on your system. Either way, you will need to enter a message. It’s important to enter a meaningful message because this message will be shown in the history of changes made to the source code. You can enter the following line if you want:

Initialize git in API project

After saving the message, you can check the Git history with the git log command:

git log

You will have something similar to the following figure:

Figure 12.2 – Writing a commit message

Figure 12.2 – Writing a commit message

Important note

Writing meaningful commit messages is important, particularly in a team or a collaborative environment. You can read more about commit messages at https://www.conventionalcommits.org/en/v1.0.0/.

The project repository has been initialized locally; however, we want the code on GitHub. The next section will show you how to upload your code on GitHub.

Uploading code on GitHub

GitHub is a code hosting platform for collaboration and version control. It helps developers around the world work together on projects and is actually the code hosting platform for the majority of popular open source projects.

On your GitHub account dashboard, on the navigation bar, create a new repository:

Figure 12.3 – Creating a repository on GitHub

Figure 12.3 – Creating a repository on GitHub

Once it’s done, you will be redirected to a new page to enter basic information about the repository, such as the name of the repository and a description, stating if the repository is public or private, and adding a license or a .gitignore file. The repository name is required, and the other pieces of information are optional.

You can now create the repository, and you will have a similar page to this:

Figure 12.4 – Repository created

Figure 12.4 – Repository created

We have an existing repository, and we want to push it to the GitHub platform. Let’s follow the steps for …or push an existing repository from the command line. Inside the directory of your backend project, open a new terminal, and let’s enter the shell commands:

git remote add origin your_repository_git_url

The git remote command allows you to create, view, and delete connections to Git repositories hosted on the internet or another network. In the preceding command, we are adding a remote repository URL of the GitHub repository. Let’s change the name of the branch we are working on:

git branch -M main

By default, when a repository is created using Git on a local machine, the branch of work is called master. What is a branch in Git?

Well, it is just a separate version of the main repository. This allows multiple developers to work on the same project. For example, if you are working with a backend developer who wants to add support for file uploading on posts and comments, instead of working directly on the main branch, the developer can create a new branch (feature/images-post) from the main branch. After the work is done on this branch, the feature/images-post branch can be merged with the main branch.

With the main branch created, we can now push the changes to GitHub:

git push -u origin main

The git push command is used to upload local repository changes on the source code to a remote repository. In your case, the command will push the current code to your GitHub repository URL.

Reload the repository page on GitHub, and you will see something similar to this:

Figure 12.5 – Code pushed to the repository

Figure 12.5 – Code pushed to the repository

And voilà! We have the code uploaded on GitHub. But this is just the code. What if you can have this running on a remote server that you can access from anywhere?

Let’s talk about platforms for web application deployment and deploy the Django backend on AWS.

Platforms for web application deployment

With the complexity of software development increasing and more innovative and data-intensive applications evolving or being created every year, there has been an explosion of services to allow teams to deploy their products on the internet and scale them with ease. This has created a new kind of service called cloud computing: the on-demand delivery of IT resources over the internet with pay-as-you-go model pricing.

In this book, we will deploy the backend on AWS, mostly on an Elastic Compute Cloud (EC2) instance, which is just a fancy name for a VPS. Well, actually, an AWS EC2 instance is a virtual server in Amazon’s EC2 for running web applications. Let’s start by creating the AWS server.

Important note

The following steps can work for any VPS, not just for an AWS VPS. If you can’t create a VPS on AWS, you can see other solutions such as Linode, Google Cloud Platform (GCP), Azure, or IBM. They provide free credit you can use for learning about their services.

Creating an EC2 instance

Follow these steps to create an EC2 instance:

  1. Make sure to be logged in to your AWS account. On the dashboard, open the EC2 console:
Figure 12.6 – Accessing the EC2 console

Figure 12.6 – Accessing the EC2 console

  1. On the EC2 console, launch a new instance:
Figure 12.7 – Creating an EC2 instance

Figure 12.7 – Creating an EC2 instance

You will be shown a page where you will have to configure the instance.

  1. Enter the name of the instance:
Figure 12.8 – Naming the EC2 instance

Figure 12.8 – Naming the EC2 instance

  1. The next step is to choose an operating system. We will use Ubuntu Server 22.04 LTS for the Amazon Machine Image (AMI):
Figure 12.9 – Choosing an operating system on the EC2 instance

Figure 12.9 – Choosing an operating system on the EC2 instance

We are using Ubuntu here because of its security, versatility, and the policy of regular updates. However, feel free to use any other Linux distros you are familiar with.

  1. And finally, you will need to set the instance type and create a pair of keys for Secure Shell (SSH) login. After that, you can launch the instance:
Figure 12.10 – Launching the instance

Figure 12.10 – Launching the instance

  1. Wait a moment, and the instance will be created:
Figure 12.11 – Instance created

Figure 12.11 – Instance created

  1. Click on the View all instances button, and you will see the created Postagram instance.
  2. Click on the checkbox next to the name of the instance and click the Connect button:
Figure 12.12 – Connecting to an EC2 instance

Figure 12.12 – Connecting to an EC2 instance

This will redirect you to a page with the information and steps needed to connect via SSH:

Figure 12.13 – Connecting via SSH to an EC2 instance

Figure 12.13 – Connecting via SSH to an EC2 instance

  1. In your terminal, type the following command to connect via SSH:
    ssh -i path/to/your_keypair.pem ec2-user@ipaddress
  2. Once you are connected to the server, we will configure it to have a Django backend running on this machine and accessible from the internet:
    sudo apt update
    sudo apt upgrade

The preceding commands update the apt packages index of Ubuntu packages and upgrade all packages on the server.

The Django project will run on port 8000 on the machine, so we have to allow a connection to this port. By default, EC2 instances will only allow connections on ports 80 for HTTP requests, 22 for SSH connections, and—sometimes—443 for Secure Sockets Layer (SSL) connections.

You can allow connections on port 8000 directly on the Details page of the created EC2 instance to access the Security tab on the list of tabs at the bottom of the page and click on the security setting group:

Figure 12.14 – Security tab

Figure 12.14 – Security tab

On the security group setting, access the Actions menu and click on Edit inbound rules. You will have access to a page where you can add a new rule, as follows:

  • The type of connection is set to Custom TCP
  • The port range is set to 8000
  • The source is set to 0.0.0.0 to indicate that all requests should be redirected to the machine on port 8000
  • And finally, add a default description to not forget why we have added this rule

Click on Save rules to save the changes and allow the EC2 instance to accept connections on port 8000:

Figure 12.15 – Adding a new security rule

Figure 12.15 – Adding a new security rule

The server is now ready for work, and we can now run the Django backend application. Let’s see the next steps in the following sections.

Configuring the server for the Django project

The source code for the Django project is hosted on GitHub. It’s definitely possible to directly use scp to copy the code from your machine to the remote machine but let’s go with Git, as it will be an important command of our workflow. On the terminal of the remote instance, enter the following command:

git clone your_repository_git_url

In my case, I am using the following repository for this project:

git clone https://github.com/PacktPublishing/Full-stack-Django-and-React.git –branch chap12

The git clone command is used to get a copy of an existing repository from a remote machine on the internet or another network. The –branch flag is used to denote a specific branch you want to clone.

Important note

As I am working using the repository of the project in this book, the current code and actions done are on the chap12 branch. In your case, if you are using your own repository, you may not have to use the –branch flag. Also, depending on if the GitHub repository is private or public, you will only enter your GitHub credentials if the repository is private.

The git clone command will clone the content of the project in a new directory. Enter the newly created directory and let’s start configuring the project. We will follow most of the steps done in Chapter 1, Creating a Django Project, until the creation of the Django project:

  1. First of all, create a virtual environment with the following command:
    python3 -m venv venv
  2. And activate the virtual environment with the following command:
    source venv/bin/activate
  3. Let’s install the packages from the requirements.txt file:
    pip install -r requirements.txt

Great! The project is ready, but we need to configure a Postgres server to have the Django project running.

Postgres configuration and deployment

In Chapter 1 of the book, Creating a Django Project, we configured Postgres by directly installing an executable or building the source code. On the EC2 instance, we will directly use the apt tool to install the Postgres server. You can follow these steps to install the Postgres server on the EC2 machine:

  1. Enter the following command to install the Postgres server:
    sudo apt install postgresql-14
  2. Let’s connect to the psql console and create a database:
    sudo su postgres 
    psql
  3. Great! Let’s create the database with the same information on the DATABASES settings in the CoreRoot/settings.py file:

CoreRoot/settings.py

...
DATABASES = {
    'default': {
        'ENGINE':
          'django.db.backends.postgresql_psycopg2',
        'NAME': coredb,
        'USER': 'core',
        'PASSWORD': 'wCh29&HE&T83',
        'HOST': 'localhost',
        'PORT': '5342',
    }
}
...
  1. Enter the following command on the psql console to create the coredb database:
    CREATE DATABASE coredb;
  2. To connect to the database, we need a user with a password. Execute the following command:
    CREATE USER core WITH PASSWORD 'wCh29&HE&T83';
  3. And the next step is to grant access to our database to the new user:
    GRANT ALL PRIVILEGES ON DATABASE coredb TO core;
  4. And we are nearly done. We also need to make sure this user can create a database. This will be helpful when we can run tests. To run tests, Django will configure a full environment but will also use a database:
    GRANT CREATE PRIVILEGE TO core;

And we are done with the creation of the database. Next, let’s connect this database to our Django project:

  1. In the project directory, run the migrate command:
    python manage.py migrate.
  2. The migrate command should pass, and we can now start the Django server by running the following command:
    python manage.py runserver 0.0.0.0:8000
  3. With the Django server running, visit http://public_ip:8000 in your web browser to access your Django project. You will have a page similar to the following figure:
Figure 12.16 – DisallowedHost error

Figure 12.16 – DisallowedHost error

This is actually an error. This comes from the ALLOWED_HOSTS setting being empty. It is implemented by Django to prevent security vulnerabilities such as HTTP host header attacks. The ALLOWED_HOSTS setting contains a list of hostnames or domain names that Django can serve:

CoreRoot/settings.py

...
ALLOWED_HOSTS = []
...
  1. As we are running the project from the terminal, let’s modify the settings file directly on the server:
    vim CoreRoot/settings.py

Or, you can use the emacs or nano command. It’s up to you. The following line tells Django to accept requests from whatever is the hostname:

CoreRoot/settings.py

...
ALLOWED_HOSTS = ["*"]
...
  1. Save the file and launch the server again:
    python manage.py runserver 0.0.0.0:8000
  2. Then, again, visit http://public_ip:8000 in your web browser. You will see the following:
Figure 12.17 – Issues with DisallowedHost resolved

Figure 12.17 – Issues with DisallowedHost resolved

Great! The project is running fine on the internet, and you can even play with the API using an API client such as Postman or Insomnia. Congratulations! You have successfully deployed your Django application on an AWS EC2 machine.

However, we have a lot of issues (we can access debugging information directly on the internet, as in Figure 12.17), and we made some dangerous decisions such as not serving the API through HTTPS or not correctly setting allowed hosts throughout the deployment. Let’s explore these issues in the next section.

Errors made when deploying on EC2

We have successfully deployed the Django backend on AWS. However, I decided to ignore some important and best practices for deployment so that we can have the Django server running ASAP. Let’s correct this. Let’s start with the errors that Django can show us. In the terminal of the project on the remote server, run the following command:

python manage.py check –deploy

Here’s the output of the preceding command:

System check identified some issues:
WARNINGS:
?: (security.W004) You have not set a value for the SECURE_HSTS_SECONDS setting. If your entire site is served only over SSL, you may want to consider setting a value and enabling HTTP Strict Transport Security. Be sure to read the documentation first; enabling HSTS carelessly can cause serious, irreversible problems.
?: (security.W008) Your SECURE_SSL_REDIRECT setting is not set to True. Unless your site should be available over both SSL and non-SSL connections, you may want to either set this setting True or configure a load balancer or reverse-proxy server to redirect all connections to HTTPS.
?: (security.W009) Your SECRET_KEY has less than 50 characters, less than 5 unique characters, or it's prefixed with 'django-insecure-' indicating that it was generated automatically by Django. Please generate a long and random SECRET_KEY, otherwise many of Django's security-critical features will be vulnerable to attack.
?: (security.W012) SESSION_COOKIE_SECURE is not set to True. Using a secure-only session cookie makes it more difficult for network traffic sniffers to hijack user sessions.
?: (security.W016) You have 'django.middleware.csrf.CsrfViewMiddleware' in your MIDDLEWARE, but you have not set CSRF_COOKIE_SECURE to True. Using a secure-only CSRF cookie makes it more difficult for network traffic sniffers to steal the CSRF token.
?: (security.W018) You should not have DEBUG set to True in deployment.
System check identified 6 issues (0 silenced).

That’s a lot of things. As we are building an API, let’s focus on the security issues that concern our API:

  • SECRET_KEY: This is an important setting in Django. It is used for all sessions, cryptographic signings, and even PasswordReset tokens. Having an already set value for SECRET_KEY can lead to dangerous security issues such as privilege escalation and remote code execution.
  • DEBUG, which is set to True. That is basically why we were able to see the DisallowedHost error. Imagine an attacker going through your API, causing a 500 error, and then being able to read everything. That would be very bad.

Those are mostly the errors that Django has detected. In the last section, Postgres configuration and deployment, we resolved the issue of the DisallowedHost error by having Django allow whichever hostname comes in a Host header. Well, this is actually bad because it can lead to an HTTP Host header attack, a technique used for web cache poisoning, poisoning links in the email, and modification of sensitive operations such as password reset.

Important note

You can read more about HTTP Host header attacks at https://www.invicti.com/web-vulnerability-scanner/vulnerabilities/http-header-injection/.

There are also some issues concerning the developer experience. It’s true that we have seen how to use Git and GitHub to host source code online, clone it on a remote server, and then configure it for deployment. You can repeat the same process, right? But what happens when you have to update the code for features or fixes multiple times per day? It can quickly become draining, so we need a solution for automated deployment on our EC2 server.

Also, we have Postgres and, finally, the Django project running separately. Sometimes, there might come a time when you will need to add another service to the machine. This can be done manually, but it creates an issue: the production environment starts to become different from the development environment.

It is an important habit to make sure that the development environment and the production environment are as similar as possible; this can make the reproduction of bugs easier but also the development of features predictable.

All these issues will be addressed in the next chapters. You will be introduced to environment variables, Docker, NGINX, and continuous integration/continuous deployment (CI/CD) concepts with GitHub Actions.

Summary

In this chapter, we have successfully deployed a Django application on an EC2 instance. Before deploying the Django application, we used Git to create a repository on a local machine, then created a remote repository on GitHub and pushed the changes online.

We have also learned how to configure a server for deployment manually with the installation of essential and interesting tools such as the Postgres server. We also explored the errors made when deploying the application and how we will address these errors in the following chapters.

These errors will be resolved in the next chapters, but first, we’ll learn more about environment variables and Docker in the next chapter.

Questions

  1. What is the usage of a Git branch?
  2. What is the difference between Git and GitHub?
  3. What is an HTTP Host header attack?
  4. What is the use of SECRET_KEY in Django?
..................Content has been hidden....................

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