What is a three-tier architecture?
A three-tier architecture is a software design pattern used to structure applications or systems into three distinct layers or tiers. Each tier serves a specific purpose and communicates with the other tiers in a well-defined manner. This architecture is commonly used in web-based applications but can be applied to various types of software systems.
Presentation Tier (Frontend):
The presentation tier is the topmost layer and is responsible for interacting directly with users or clients.
It handles user interface components and presents data to users in a human-readable format.
This tier typically includes web browsers, mobile apps, or other client-side interfaces.
Application Tier (Middle Tier):
The application tier, also known as the business logic layer or middle tier, contains the application's core logic and processing.
It processes user requests received from the presentation tier and performs computations or business-specific operations.
This layer acts as an intermediary between the presentation tier and the data tier.
Data Tier (Backend):
The data tier, also referred to as the data access layer or backend, deals with data storage and retrieval.
It manages the storage, retrieval, and manipulation of data from various data sources, such as databases or external APIs.
The application tier communicates with the data tier to fetch and store data required for processing user requests.
Advantages of the three-tier architecture include:
Modularity: Each tier can be developed and maintained independently, promoting code reusability and easier scalability.
Scalability: The separation of concerns allows each tier to be scaled individually, improving the overall performance and efficiency of the system.
Security: By isolating the data tier from direct access by clients, the architecture enhances data security and reduces the risk of unauthorized access.
Flexibility: Different technologies can be used in each tier, enabling developers to choose the most suitable tools for specific tasks.
How are we building our three-tier application?
Our infrastructure will consist of two Virtual Machines: One running the backend code along with the database, the other running the nginx frontend. We will try to set up a local DNS as well for the user [your computer].
As a result, we want that our app will be accessible to the user via nginx only. We don't want the user to directly access the backend machine (that will be done by configuring a firewall on the backend machine).
Setting up the backend machine
Our backend machine is running CentOS. Before launching the code on the backend machine, we have to set up the development environment on it. Follow these steps:
sudo yum install python3 python3-devel git
sudo yum groupinstall 'Development Tools'
Now as the development environment is ready, we need to get the code on the machine. I already have a GitHub repo for the code. Here is the link:
https://github.com/wiz4host/StaticContents/tree/main/todo-app-api
We will put the code under the folder: /var/www/app/todo/
Make the folder using the command:
mkdir -p /var/www/app/todo/
Now let's clone the repo by:
wget -O todo-app-api.zip https://raw.githubusercontent.com/wiz4host/StaticContents/main/todo-app-api/todo-app-api.zip
# If you don't have unzip present, install it using sudo yum install unzip
uzip todo-app-api.zip
cd todo-app-api/
Now the code must be present in todo-app-api
folder.
Before running the app, we need to install various dependencies as well. You may see in this folder that we have a file named requirements.txt
. We will use this file to install all the required dependencies.
Make sure that you are in ToDo-App-API/
folder and run the following command:
pip3 install -r requirements.txt
Woah, our app is ready to run........ not yet! Let's do the changes in the code.
Open up the api.py file, with whatever text editor you want to use. I'll be using nano ๐
sudo nano api.py
Now look out for the line:
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///path/to/todo.db'
You have to replace the path/to/todo.db
with the actual path to todo.db
. Now if you're following me right now, the todo.db
path must be /var/www/app/todo/ToDo-App-API/todo.db
. So change that line to:
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////var/www/app/todo/ToDo-App-API/todo.db'
One more thing to do in the code. Scroll down to the bottom of the code so that you can see the following line:
app.run(host='0.0.0.0', port=5000)
You will have to change this 0.0.0.0
to your machine's IP Address. In my case it is 10.1.1.103
, so I'll change it to:
app.run(host='10.1.1.103', port=5000)
But why would we do that??
A Virtual Machine, or any machine in general, has many network interfaces. If we put 0.0.0.0
in the host variable, then our app will run on all the interfaces and we don't want this to happen. We only want it to run on the 10.1.1.103
IP, that's why we changed it.
Now your code is ready to run! Let's run it by:
python3 api.py
And you should see something like this:
Let's ignore the warnings as of now. The main thing is in the last line, our Application is running on the PORT 5000.
Let's save the firewall part for later.
Setting up the frontend machine
Our frontend machine is running Ubuntu, and we need to have Nginx installed on this machine. You can install it using:
sudo apt update
sudo apt install nginx
After it is installed, check for its status:
systemctl status nginx
The output of this command should look like this:
Output:
nginx.service - A high performance web server and a reverse proxy server
Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled)
Active: active (running) since Fri 2023-08-5 16:08:19 UTC; 1 days ago
Docs: man:nginx(8)
Main PID: 2369 (nginx)
Tasks: 2 (limit: 1153)
Memory: 3.5M
Now NGINX is up and running on this machine. Let's create the folder for saving logs of our ToDo application:
mkdir -p /var/www/todo.app/logs/
Let's add a configuration file for the ToDo Application:
cd /etc/nginx/sites-enabled/
sudo nano todo.app.conf
Type this server block inside the todo.app.conf
file:
server
{
# It is going to listen on the port 80
listen 80;
# This server will be accessible only to those, who are
# accessing it via this DNS: todo.app
server_name todo.app;
# These are the log files for our server
access_log /var/www/todo.app/logs/access.log
error_log /var/www/todo.app/logs/error.log
# Now let's define the endpoints for the ToDo API
# Currently we have two endpoints available for this API
# 1. /login
# 2. /users
location /login {
proxy_pass http://10.1.1.103:5000/login;
}
location /users {
proxy_pass http://10.1.1.103:5000/users;
}
}
After saving this file, you have to reboot nginx:
sudo nginx -t
sudo nginx -s reload
If everything with your configuration file is okay, then you should see this:
If you see some error here try to resolve it. There might be some syntax issue with your configuration file.
Setting up DNS on your base machine
As you can see in the todo.app.conf
configuration file, the server will only respond to the requests if they are requested on the todo.app
DNS. Let's create a DNS now!
Locate your
hosts
file. It is present in this directory:C:\Windows\System32\drivers\etc
Open up a command prompt as administrator, and:
cd C:\Windows\System32\drivers\etc notepad hosts
It should open up the
hosts
file in notepad. Now add the following line in the end of thehost
file:10.1.1.102 todo.app
And we are pretty much done with the infrastructure! Try to reach out to todo.app/login
from your browser, or any API tool you use (such as Postman).
You should see something like this:
Status: 200 OK
This is all we need to hear as a developer!
Wait, we are not done yet!
If you try to reach out to http://10.1.1.103:5000/login
, you can see that it is also reachable:
We don't want this to happen, that is, we don't want anyone else to access your App Server instead of our frontend machine [10.1.1.102]. So let's setup a firewall on the CentOS Backend machine.
Setting up a firewall on the backend
Setting up a firewall is simple. Follow along with these steps:
Installing the firewalld
utility:
sudo yum install firewalld
Enable the firewall service:
sudo systemctl enable firewalld
sudo reboot
We can then verify the state of firewall using:
sudo firewall-cmd --state
# OR
sudo systemctl status firewalld
Add this rich rule which will allow traffic on port 5000 from our frontend IP 10.1.1.103
only:
sudo firewall-cmd --permanent --zone=public --add-rich-rule='rule family="ipv4" source address="10.1.1.102" port protocol="tcp" port="5000" accept'
sudo firewall-cmd --reload
sudo firewall-cmd --zone=public --list-all
You should see your rule there.
Now restart your Python application on your server.
Now let's try to reach out to the server:
When we try to reach out to
http://todo.app/login
we should see the sameStatus: 200 OK
But when we try to reach out to
http://10.1.1.103:5000/login
we are not able to make the request there.
That means our firewall is working fine!
And with that our three-tier application infrastructure is ready!
Conclusion
If you find this blog informative, then please like and comment on it. It means a lot!
Connect with us:
๐ LinkedIn
๐ YouTube
๐ GitHub