Live Cricket Scores: Flask App Setup & Deployment via Jenkins and Kubernetes


π Introduction
In this tutorial, we'll develop a real-time cricket score tracker named CricketLive. This application fetches live scores and match schedules using the Cricbuzz API, displays them through a Flask web interface, containerizes the application with Docker, and deploys it on a Kubernetes cluster. We'll also set up a CI/CD pipeline using Jenkins for continuous integration and Argo CD for continuous deployment.
1. Build the Flask App
Start by creating a simple web application using Flask (a Python web framework).
Use the Cricbuzz API to get live cricket scores and upcoming match schedules.
Design a clean and attractive HTML page to show this data, updating in real-time for the best user experience.
2. Package the App with Docker
Once your app is working, package it into a Docker image so it runs the same way everywhere.
Create a
Dockerfile
that includes everything your Flask app needs (Python, dependencies, source code, etc.).This makes it easy to deploy your app anywhere, whether on your local machine or the cloud.
3. Deploy to Kubernetes
Set up a Kubernetes cluster (locally using Minikube or on cloud providers like AWS or GCP).
Install ArgoCD in the cluster to help automate application deployments.
Write Kubernetes YAML files (like Deployment, Service, and optionally Ingress) to describe how your app should run in the cluster.
4. Automate CI with Jenkins
Use Jenkins to automate the building and testing of your app whenever you make changes.
Create a Jenkins pipeline that:
Pulls the latest code from GitHub.
Builds a new Docker image for your Flask app.
Pushes the image to a container registry (like Docker Hub or AWS ECR).
Updates the Kubernetes deployment YAML with the new image tag.
Pushes this updated YAML file to your GitHub repo (which ArgoCD watches).
5. Set Up ArgoCD for Continuous Delivery
Create an ArgoCD application using a configuration file (YAML).
This file should point to your Kubernetes cluster, namespace, and the Git repo with your Kubernetes manifests.
ArgoCD keeps watching the Git repo, and whenever it sees a change (like a new image tag), it automatically deploys the updated version to your cluster.
π§° Prerequisites
Before we begin, ensure you have the following:
Python 3.8+ is installed on your development machine.
Docker is installed and running.
Access to a Kubernetes cluster (e.g., Minikube, AWS EKS, GCP GKE).
Jenkins is installed and configured.
Argo CD is installed on your Kubernetes cluster.
Accounts for:
RapidAPI to access the Cricbuzz API.
Docker Hub for container image storage.
GitHub for version control and repository hosting.
π Step 1: Create a GitHub Repository π & Take Cricbuzz API π
- Goto rapidapi.com
Search Crickbuzz official API
Copy the URL & headers part from both the types and paste it in the app.py code, like this
π οΈ Step 2: Develop the Flask Application
2.1. Set Up the Project Structure
Create the following directory structure:
cricketlive/
βββ app.py
βββ requirements.txt
βββ templates/
βββ index.html
2.2. Implement app.py
from flask import Flask, render_template
import requests
import json
from tabulate import tabulate
import os
app = Flask(__name__)
def fetch_cricket_scores():
url = 'https://crickbuzz-official-apis.p.rapidapi.com/matches/list'
headers = {
'x-rapidapi-key': '831af6d415msh83780108e78df43p127955jsn56ed552814e7',
'x-rapidapi-host': 'crickbuzz-official-apis.p.rapidapi.com'
}
response = requests.get(url, headers=headers)
matches_data = []
if response.status_code == 200:
try:
data = response.json()
type_matches = data.get("typeMatches", [])
for match_type in type_matches:
for series in match_type.get("seriesMatches", []):
wrapper = series.get("seriesAdWrapper", {})
for match in wrapper.get("matches", []):
# same processing logic
...
except Exception as e:
print("Error while processing match data:", e)
else:
print("Failed to fetch. Status code:", response.status_code)
return matches_data
def fetch_upcoming_matches():
url = 'https://crickbuzz-official-apis.p.rapidapi.com/schedules/international'
headers = {
'x-rapidapi-key': '831af6d415msh83780108e78df43p127955jsn56ed552814e7',
'x-rapidapi-host': 'crickbuzz-official-apis.p.rapidapi.com'
}
response = requests.get(url, headers=headers)
upcoming_matches = []
if response.status_code == 200:
try:
data = response.json()
match_schedules = data.get('matchScheduleMap', [])
for schedule in match_schedules:
if 'scheduleAdWrapper' in schedule:
date = schedule['scheduleAdWrapper']['date']
matches = schedule['scheduleAdWrapper']['matchScheduleList']
for match_info in matches:
for match in match_info['matchInfo']:
description = match['matchDesc']
team1 = match['team1']['teamName']
team2 = match['team2']['teamName']
match_data = {
'Date': date,
'Description': description,
'Teams': f"{team1} vs {team2}"
}
upcoming_matches.append(match_data)
else:
print("No match schedule found for this entry.")
except json.JSONDecodeError as e:
print("Error parsing JSON:", e)
except KeyError as e:
print("Key error:", e)
else:
print("Failed to fetch upcoming matches. Status code:", response.status_code)
return upcoming_matches
@app.route('/')
def index():
cricket_scores = fetch_cricket_scores()
upcoming_matches = fetch_upcoming_matches()
return render_template('index.html', cricket_scores=cricket_scores, upcoming_matches=upcoming_matches)
if __name__ == '__main__':
app.run(port=int(os.environ.get("PORT", 8080)), host='0.0.0.0', debug=True)
Replace the code of fetch_cricket_scores() & fetch_upcoming_matches() with your API code
2.3. Create requirements.txt
, we will use it in Dockerfile
flask==2.2.5 #replace the flask version if required
tabulate
requests
2.4. Design templates/index.html
<!DOCTYPE html>
<html>
<head>
<title>Cricket Scores</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
/* Custom styles */
.navbar-brand {
background-color: #d2dfd9; /* Change the background color */
color: white; /* Change the text color */
padding: 10px;
font-size: 34px;
}
.top-bar {
background-color: rgb(25, 105, 94); /* Change the background color */
color: #e7f1f4; /* Change the text color */
padding: 8px;
font-size: 24px;
font-family: Arial, sans-serif; /* Change the font family */
}
body {
background-color: #030f13; /* Change the overall background color */
color: #ece0e0; /* Change the overall text color */
}
.custom-card {
width: 500px; /* Change the width */
height: 250px; /* Change the height */
/* Add any other styling properties as needed */
}
.custom-upcoming-card {
width: 500px; /* Change the width */
height: 150px; /* Change the height */
/* Add any other styling properties as needed */
}
.navbar-brand {
font-size: 24px; /* Change the font size as needed */
}
</style>
</head>
<body>
<div class="top-bar">
<div class="container">
<img src="https://storage.googleapis.com/bkt-static-tt/logo.png" width="50" height="50" class="d-inline-block align-top" alt="">
CricketScore Pro : By Piyush Kabra
</div>
</div>
<!-- <nav class="navbar navbar-fixed-top navbar-expand-lg navbar-dark bg-primary">
<a class="navbar-brand" href="#">
<img src="https://storage.googleapis.com/bkt-static-tt/logo.png" width="50" height="50" class="d-inline-block align-top" alt="">
TechTrapture Cricket Score App
</a>
</nav> -->
<div class="container mt-4">
<h2>Recent Matches</h2>
<div class="row row-cols-1 row-cols-md-2">
{% for score in cricket_scores %}
<div class="col mb-4">
<div class="card custom-card">
<div class="card-body">
{{ score | safe }}
</div>
</div>
</div>
{% endfor %}
</div>
<h2>Upcoming Matches</h2>
<div class="row row-cols-1 row-cols-md-2">
{% for match in upcoming_matches %}
<div class="col mb-4">
<div class="card custom-upcoming-card">
<div class="card-body">
<ul class="list-group">
<li class="list-group-item">
<strong>Date:</strong> {{ match['Date'] }}<br>
<strong>Description:</strong> {{ match['Description'] }}<br>
<strong>Teams:</strong> {{ match['Teams'] }}
</li>
</ul>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
</body>
</html>
Now, run the app.py file locally and check whether the API is working or not.
Locally, two files are there: app.py & templates/index.html, and running app.py will run index.html & app.py
Now, see, it is running successfully
π³ Step 3: Containerize the Application with Docker
3.1. Create a Dockerfile
FROM python:latest
WORKDIR /app
COPY . ./
RUN pip install -r requirements.txt
EXPOSE 8080
CMD ["python", "app.py"]
3.2. Build and Run the Docker Image
#Docker file and app.py should by in same folder
docker build -t crickflaskapp . # to create a docker image
docker run -dit --name flask-app -p 80:8080 crickflaskapp # to run the container
3.3 πNow, Check the Website:-
Now Docker container is exposed, we can access the website using the βPublic IP of the machineβ:80 ππΌ
βΈοΈ Step 4: Deploy to Kubernetes
4.1. Create Kubernetes Deployment and Service Manifests
Create flask-deploy.yml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: cricket-flask-app
name: cricket-flask-app
spec:
replicas: 3
selector:
matchLabels:
app: cricket-flask-app
strategy: {}
template:
metadata:
labels:
app: cricket-flask-app
spec:
containers:
- image: kabrajii/crickflaskapp:latest
name: crickflaskapp
& Create flask-svc.yml
apiVersion: v1
kind: Service
metadata:
creationTimestamp: null
labels:
app: cricket-flask-app
name: cricket-flask-app
spec:
ports:
- port: 8080
protocol: TCP
targetPort: 8080
selector:
app: cricket-flask-app
type: NodePort
status:
loadBalancer: {}
4.2. Apply the Manifests:-
kubectl apply -f flask-deploy.yml
kubectl apply -f flask-svc.yml
π Step 5: Set Up Jenkins for Continuous Integration
5.1. Configure Jenkins Pipeline
Create a Jenkinsfile
in your repository:
pipeline{
agent any
environment{
DOCKER_USERNAME = "kabrajii"
APP_NAME = "flask-app"
IMAGE_TAG = "${BUILD_NUMBER}"
IMAGE_NAME = "${DOCKER_USERNAME}/${APP_NAME}"
}
stages{
stage('clean the workspace'){
steps{
script{
cleanWs()
}
}
}
stage('checkout git scm'){
steps{
git branch: 'main', url: 'https://github.com/KabraJiii/flask_app_CICD.git'
}
}
stage('build docker image'){
steps{
script{
sh "sudo docker build -t ${IMAGE_NAME}:${IMAGE_TAG} ."
}
}
}
stage('Push the Image to DockerHub') {
steps {
withCredentials([usernamePassword(credentialsId: 'Docker', passwordVariable: 'password', usernameVariable: 'user')]) {
// some block
sh """
echo ${password} | docker login -u ${user} --password-stdin
sudo docker push ${IMAGE_NAME}:"${IMAGE_TAG}"
"""
}
}
}
stage('Delete Image Locally') {
steps {
sh """
sudo docker rmi -f ${IMAGE_NAME}:${IMAGE_TAG}
"""
}
}
stage('Update the deployment file in CD') {
steps {
script{
withCredentials([usernamePassword(credentialsId: 'GitHub', usernameVariable: 'user', passwordVariable: 'pass')]) {
sh """
git clone -b main https://${user}:${pass}@github.com/KabraJiii/flask_app_CICD.git
cd flask_app_CICD
cat flask-deploy.yml
cd /var/lib/jenkins/workspace/CricketAPI-Pipeline/flask_app_CICD
cat flask-deploy.yml
echo "Changing tag to ${BUILD_NUMBER}"
sed -i 's|image: kabrajii/flask-app:.*|image: ${IMAGE_NAME}:${BUILD_NUMBER}|g' flask-deploy.yml
echo "changed tag"
cat flask-deploy.yml
echo "hello" >> hi.txt
git add .
git commit . -m "Updated the tag to ${BUILD_NUMBER}"
git push origin main
"""
}
}
}
}
}
}
Now save this Jenkinsfile and push it to GitHub, & then create a pipeline ππΌππΌ
Now run the job & see this output ππΌππΌ
π Step 6: Configure Argo CD for Continuous Deployment
6.1. Install Argo CD
kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
6.2. Access Argo CD UI & API Server
# Service Type Load Balancer
kubectl patch svc argocd-server -n argocd -p '{"spec": {"type": "LoadBalancer"}}'
# Port Forwarding
kubectl port-forward svc/argocd-server -n argocd 8080:443
Take password from this command :-
argocd admin initial-password -n argocd
Now create the argo application file argocd-application.yml
:-
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: cricket-score-app
namespace: argocd
spec:
project: default
source: #github url from where it will take code
repoURL: 'https://github.com/KabraJiii/flask_app_CICD.git'
targetRevision: HEAD
path: .
destination:
server: 'https://kubernetes.default.svc'
namespace: default
syncPolicy:
automated:
prune: true
selfHeal: true
And apply it :-
kubectl apply -f argocd-application.yml
Now, Go on βMinikube IPβ:port of svc β> http://192.168.233.131:31698/
β Final Output
Once everything is set up:
Access your application via the external IP provided by the Kubernetes LoadBalancer service.
The Flask application will display real-time cricket scores and upcoming matches fetched from the Cricbuzz API.
Any changes pushed to your GitHub repository will trigger the Jenkins pipeline, which builds and pushes a new Docker image, updates the Kubernetes manifests, and Argo CD will automatically deploy the updated application.
π GitHub Repo
Click here
Subscribe to my newsletter
Read articles from Piyush Kabra directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
