introduction
In this section, we’re going to learn how to set up a CI/CD pipeline for a single server deployment . So, what does that mean? Basically, we have one server where our application will live and run. This server could be anything – a virtual machine like a Linode instance or an EC2 instance on AWS ☁️, or even a physical server you might have on-site 🖥️.
The cool part is that once we get everything set up, we’ll have our Python Flask application running smoothly on that server. But what we’re really focusing on here is the Continuous Deployment (CD) part of the CI/CD pipeline. After we write and test our app 📝, the big question is: how do we make sure the latest version of our code gets to the server without us having to do it manually every time?
Let’s dive into how to automatically deploy the newest version of our app to the server after it passes all tests ✅. This way, your server will always stay up-to-date with the latest changes, running smoothly without any hassle or mistakes 🔄.
By the end, you’ll have a fully automated pipeline in place, keeping your Flask app fresh, updated, and ready to shine ✨.
Now that we've got an overview of what we're going to achieve🌸, let's dive into the practical steps!!!!
Step 1: Launching Your EC2 instance on AWS ☁️
The first step in setting up our CI/CD pipeline is to create an EC2 instance on AWS. EC2 (Elastic Compute Cloud) is a virtual server in the cloud where our application will run. You can think of it as the host that will serve your app to the world 🌍.
Here’s a simple breakdown of what we’ll do:
Log into your AWS account.
Launch an EC2 instance using a suitable Amazon Machine Image (AMI) (like an Ubuntu or Amazon Linux AMI).
Choose an instance type (a small one like
t2.micro
should work for most simple apps ).Configure security groups to allow necessary access (like SSH on port 22 for connecting and HTTP/HTTPS for web access).here we open a port(443,22,80 and 5000 for our python flask app)
Download the SSH key to securely connect to your EC2 instance.
Step 2: Connect to the EC2 Instance via SSH 🔐
Navigate to the directory where you saved your
.pem
key fileSet proper permissions for your key file
Connect to your EC2 instance using SSH
4.Now you should see the Amazon Linux welcome message and command prompt, indicating a successful connection!!!😊
After successfully connecting to your EC2 instance via SSH, you're ready to start configuring your server. In this step, we'll set up the application environment, ensuring everything is ready to run your Flask app smoothly. Let's get started! ☺️
Step 3: Set Up Application Environment ⚙️
Create a Directory for Our Application 🗂️
mkdir /home/ec2-user/app
Move into the Project Directory 🔄
cd /home/ec2-user/app
Create a Python Virtual Environment 🐍
python3 -m venv venv
We use a Python virtual environment 🐍 to create an isolated space for our project, ensuring dependencies don't conflict with system-wide packages.
Step 4: Configuring and Start Flask App Service ⚙️
Create a Systemd Unit File 🔧
We create this file to manage and automate the startup, stopping, and restarting of our Flask app as a background service, ensuring it runs smoothly on the server.
vi /etc/systemd/system/flaskapp.service
Now, let's write a unit file by adding the following content:🎉
"Here is the explanation of what each line tells in this unit file: 📄✨"
[Unit]
Defines metadata and dependencies for the service.Description=Flask app
A human-readable description of the service (this is used for logging and status).After=
network.target
Specifies that the service should start after the network is up (i.e., after networking is available).[Service]
Defines the properties related to how the service itself should run.User=ec2-user
Specifies that the service should run as theec2-user
user.Group=ec2-user
Specifies that the service should run under theec2-user
group.WorkingDirectory=/home/ec2-user/app
Sets the working directory for the service to/home/ec2-user/app
, where the app files are located.Environment="PATH=/home/ec2-user/app/venv/bin"
Sets thePATH
environment variable to thebin
directory of a Python virtual environment located in/home/ec2-user/app/venv
. This ensures the app uses the virtual environment’s Python and dependencies.ExecStart=/home/ec2-user/app/venv/bin/python3 /home/ec2-user/app/
app.py
Specifies the command to start the Flask application, using the Python interpreter from the virtual environment to runapp.py
.[Install]
Defines installation details for the service, i.e., when and how it should be enabled.WantedBy=
multi-user.target
Tells systemd to start the service when the system reaches themulti-user.target
(a state where the system is ready for multiple users and services). This makes the service start on boot."Next steps to implement and activate the service. Here's how you can proceed: But wait! Before proceeding, don’t forget to save the file 😊."
Reload the System Manager Configuration 🔄
Run the following command to make systemd aware of the new or updated unit file:
sudo systemctl daemon-reload
- Enable Service to Start on Boot and Start the Service 🧑💻
sudo systemctl enable flaskapp.service
sudo systemctl start flaskapp.service
"Alright! We've got our machine all set up 🎉. Now, let’s Jump into the next exciting part—creating a Jenkins pipeline for Continuous Delivery! "
"Spin up your Jenkins server and let's get started! 🤖"
"So, we need to give Jenkins the same SSH key to connect securely. After that, we’ll need to provide the production server’s IP address. But instead of hardcoding it into the Jenkins file (because it might change in the future), I’d prefer to set it up as a credential. That way, we can easily pull it into the Jenkins file whenever we need it."
Configuring Credentials for SSH Key and Production Server IP 🔑
1. For Production Server ip:
Go to Manage Jenkins > Manage Credentials > Global credentials.
Click Add Credentials.
Set Scope to Global, Kind to Secret, and enter the production server IP in Secret.
Give it a unique ID (e.g.,
prod-server-ip
), add an optional description, and click Save.
2. For SSH Key:
Go to Manage Jenkins > Manage Credentials > Global credentials.
Click Add Credentials.
Set Scope to Global, Kind to SSH Username with private key.
Enter the Username (e.g.,
ec2-user
) and Private Key (paste your SSH key or choose "Enter directly").Give it a unique ID (e.g.,
ssh-key
), add an optional description, and click Save."Now that our credentials are set up, let’s move on to setting up our pipeline! 🚀
Setting Up the Pipeline:
Go to + New Item > Enter the item name as single-server-pipeline and select Pipeline, then click OK.
Under Build Triggers, tick GitHub hook trigger for GITScm polling.
For Pipeline Definition, choose Pipeline Script from SCM.
Select SCM as Git and enter your Repository URL:
https://github.com/Tezen96/Single-Server-Deployment. (If it's a private repo, you would need credentials, but for this case, no credentials are required).Set Branch Specifier to
*/main
.Set the Script Path to
jenkinsfile
."Everything is ready! 🎉 Now, let’s push our Jenkins file to the repository. Once we commit it on GitHub, the webhook will trigger and start our pipeline automatically. 🚀 Every time we push or commit code, it will automatically trigger, keeping your server always up-to-date with the latest changes!"
Here's How Our Pipeline is Set Up!
- "Let’s open VSCode and start writing our Jenkinsfile."
pipeline {
agent any
environment {
SERVER_IP = credentials('prod-server-ip')
}
stages {
stage('Setup') {
steps {
sh '''
cd 02-single-server-deployment
python3 -m pip install -r requirements.txt
'''
}
}
stage('Testings') {
steps {
sh '''
cd 02-single-server-deployment
python3 -m pytest
'''
}
}
stage('Packaging codes') {
steps {
sh '''
cd 02-single-server-deployment
zip -r myapp.zip ./* -x '*.git*'
ls -lart
'''
}
}
stage('Deploy to prod server') {
steps {
withCredentials([sshUserPrivateKey(credentialsId: 'ssh-key', keyFileVariable: 'MY_SSH_KEY', usernameVariable: 'username')]) {
sh '''
cd 02-single-server-deployment
scp -i $MY_SSH_KEY -o StrictHostKeyChecking=no myapp.zip ${username}@${SERVER_IP}:/home/ec2-user/
ssh -i $MY_SSH_KEY -o StrictHostKeyChecking=no ${username}@${SERVER_IP} << 'EOF'
sudo unzip -o /home/ec2-user/myapp.zip -d /home/ec2-user/app/
source /home/ec2-user/app/venv/bin/activate
cd /home/ec2-user/app/
python3 -m pip install -r requirements.txt
sudo systemctl restart flaskapp.service
EOF
'''
}
}
}
}
}
This Jenkins file defines a pipeline with four main stages:
Setup: It installs the required Python dependencies by running
pip install -r requirements.txt
in the project directory.Testing: It runs tests using
pytest
to ensure the code is working properly.Packaging: It zips the project files (excluding
.git
files) intomyapp.zip
.Deploy to Production: It securely transfers the zipped application to the production server using
scp
, unzips it, installs dependencies in a virtual environment, and restarts theflaskapp.service
on the server.
It also uses credentials for SSH and the production server's IP.
"Now, let’s push our Jenkinsfile to the repository! Once it’s there, we’re all set to let the magic happen with our automated pipeline. 🎉
The pipeline has been started! 🎉
The pipeline has finished successfully! 🎉🎉🎉🎆🎆🎆
"Now it’s time to access your application and see everything in action! 🌐🚀 Go ahead and check out the live version!"
“Thanks for reading! 🙏 I hope you found this guide helpful. I’m super excited to share more tips and tricks with you in the next post. 💁♂️Catch you in the next exciting article—stay tuned for more!😊”