An end-to-end MLOps pipeline that predicts whether a customer is likely to respond positively to a vehicle insurance cross-sell offer β from raw data in MongoDB to a fully automated, containerized deployment on AWS.
π Overview Β· ποΈ Architecture Β· βοΈ Installation Β· βοΈ AWS Setup Β· π CI/CD Β· π‘ App Routes
This project goes beyond a one-off notebook model and simulates a production-grade MLOps workflow end to end:
- π¦ Modular, installable Python package (
src) viasetup.py/pyproject.toml - π MongoDB Atlas as the source data store
- πͺ΅ Custom logging and exception-handling modules
- π A fully modular training pipeline β Data Ingestion β Data Validation β Data Transformation β Model Trainer β Model Evaluation β Model Pusher
- πͺ£ AWS S3 used as a model registry (push/pull trained models)
- β‘ A FastAPI web app to trigger training and serve real-time predictions
- π Fully automated CI/CD with GitHub Actions, Docker, Amazon ECR, and a self-hosted EC2 runner
| Feature | Description | |
|---|---|---|
| π | Cloud Data Source | Raw data ingested directly from MongoDB Atlas |
| π§ͺ | Schema-Based Validation | Automated data validation against config/schema.yaml |
| π οΈ | Feature Engineering Pipeline | Reusable preprocessing objects persisted as .pkl artifacts |
| π€ | Model Training & Evaluation | Scikit-learn based training with threshold-based model acceptance |
| πͺ£ | S3 Model Registry | Trained models versioned and pushed/pulled from AWS S3 |
| β‘ | FastAPI Backend | REST endpoints to trigger training and serve predictions |
| π³ | Containerized Deployment | Dockerized app shipped via Amazon ECR |
| π | Full CI/CD Automation | GitHub Actions + self-hosted EC2 runner auto-deploys on every push |
| ποΈ | Timestamped Artifacts | Every pipeline run stores versioned artifacts for full traceability |
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β MongoDB Atlas (Raw Data) β
βββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β TRAINING PIPELINE (src/) β
β β
β Data Ingestion β Data Validation β Data Transformation β
β β β
β βΌ β
β Model Trainer β
β β β
β βΌ β
β Model Evaluation ββββββββΊ AWS S3 β
β β (Model Registry) β
β βΌ β
β Model Pusher β
βββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β FASTAPI APPLICATION (main.py) β
β β
β ββββββββββββββ ββββββββββββββββββ βββββββββββββββββββ β
β β pages.py β β prediction.py β β training.py β β
β ββββββββββββββ ββββββββββββββββββ βββββββββββββββββββ β
βββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Docker Image ββΊ Amazon ECR ββΊ EC2 (self-hosted β
β runner) ββΊ Live App (port 5000) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
MongoDB Collection
β
βΌ
Data Ingestion βββΊ feature_store/data.csv βββΊ ingested/train.csv, test.csv
β
βΌ
Data Validation βββΊ report.yaml (schema drift & completeness checks)
β
βΌ
Data Transformation βββΊ transformed/train.npy, test.npy
β transformed_object/preprocessing.pkl
βΌ
Model Trainer βββΊ trained_model/model.pkl
β
βΌ
Model Evaluation βββΊ compares against current production model in S3
β
βΌ
Model Pusher βββΊ pushes accepted model to S3 (my-model-mlopsproj)
vehicle-insurance-mlops/
β
βββ .github/workflows/
β βββ aws.yaml # CI/CD pipeline (build β push β deploy)
β
βββ app/ # FastAPI application layer
β βββ config.py # App settings (host, port, CORS, log level)
β βββ schemas.py # Pydantic request/response models
β βββ routers/
β βββ pages.py # Home / UI routes
β βββ prediction.py # /predict route
β βββ training.py # /training route
β βββ __init__.py
β
βββ artifact/ # Auto-generated, timestamped pipeline runs
β βββ <run_timestamp>/
β βββ data_ingestion/
β β βββ feature_store/data.csv
β β βββ ingested/{train,test}.csv
β βββ data_validation/report.yaml
β βββ data_transformation/
β β βββ transformed/{train,test}.npy
β β βββ transformed_object/preprocessing.pkl
β βββ model_trainer/trained_model/model.pkl
β
βββ config/
β βββ model.yaml # Model hyperparameters / selection config
β βββ schema.yaml # Expected dataset schema for validation
β
βββ logs/ # Auto-generated log files
β
βββ notebook/
β βββ data.csv
β βββ exp-notebook.ipynb # EDA & feature engineering
β βββ mongo_DB_demo.ipynb # Push raw dataset to MongoDB
β
βββ src/
β βββ cloud_storage/ # AWS S3 read/write utilities
β βββ components/ # Ingestion, validation, transformation, trainer, evaluation, pusher
β βββ configuration/ # MongoDB & AWS connection setup
β βββ constants/ # Project-wide constants
β βββ data_access/ # MongoDB β DataFrame access layer
β βββ entity/ # Config / artifact / estimator entities
β βββ exception/ # Custom exception handling
β βββ logger/ # Custom logging setup
β βββ pipeline/ # Training & prediction pipelines
β βββ utils/ # Shared utility functions
β
βββ static/
β βββ css/style.css
β βββ js/script.js
β
βββ templates/
β βββ index.html # Jinja2 UI template
β
βββ demo.py # Quick script to test logger/exception/pipeline
βββ Dockerfile
βββ main.py # FastAPI app entry point
βββ requirements.txt
βββ setup.py
βββ pyproject.toml
βββ template.py # Scaffolds the initial project structure
- Python 3.10+
- A free MongoDB Atlas account
- An AWS account (for S3 model registry & deployment)
git clone https://github.com/sharif-abusad/vehicle-insurance-mlops.git
cd vehicle-insurance-mlops# (Optional) Regenerate the project skeleton
python template.py
# Create and activate a virtual environment
conda create -n vehicle python=3.10 -y
conda activate vehicle
# Install dependencies (installs local "src" package via -e .)
pip install -r requirements.txt
# Verify local package installation
pip list- Sign up on MongoDB Atlas and create a new project.
- Create a cluster β select the M0 (free tier) β keep defaults β Create Deployment.
- Create a database user with a username and password.
- Under Network Access, add
0.0.0.0/0to allow access from anywhere. - Go to Get Connection String β Drivers β select Python, version 3.6+ β copy the connection string and replace
<password>. - Add your dataset to
notebook/and push it to MongoDB viamongo_DB_demo.ipynb. - Verify the upload under Atlas β Database β Browse Collections.
cp .env.example .envBash
export MONGODB_URL="mongodb+srv://<username>:<password>@<cluster-url>/?appName=Cluster0"
export AWS_ACCESS_KEY_ID="<your-aws-access-key-id>"
export AWS_SECRET_ACCESS_KEY="<your-aws-secret-access-key>"
export AWS_DEFAULT_REGION="us-east-1"PowerShell
$env:MONGODB_URL = "mongodb+srv://<username>:<password>@<cluster-url>/?appName=Cluster0"
$env:AWS_ACCESS_KEY_ID = "<your-aws-access-key-id>"
$env:AWS_SECRET_ACCESS_KEY = "<your-aws-secret-access-key>"
$env:AWS_DEFAULT_REGION = "us-east-1"
β οΈ Security note: Never commit.envfiles or real credentials. Ensure.env,_env, andartifact/are listed in.gitignore, and rotate any keys that may have been exposed.
python demo.pypython demo.pyEach run creates a timestamped folder under artifact/ containing intermediate outputs (ingested data, transformed arrays, preprocessing object, trained model, validation report).
python main.py| Resource | URL |
|---|---|
| App | http://localhost:5000 |
| Trigger Training | http://localhost:5000/training |
| Prediction UI | http://localhost:5000/predict |
- Log in to the AWS Console, keep the region set to
us-east-1. - Go to IAM β Create User (e.g.,
firstproj) β attach the AdministratorAccess policy. - Under the user's Security Credentials β Access Keys, create a new CLI access key and download the CSV.
- Export the access key/secret as environment variables (see above).
- Add the following to
src/constants/__init__.py:MODEL_EVALUATION_CHANGED_THRESHOLD_SCORE: float = 0.02 MODEL_BUCKET_NAME = "my-model-mlopsproj" MODEL_PUSHER_S3_KEY = "model-registry"
- Create an S3 bucket (
my-model-mlopsproj) inus-east-1, unchecking "Block all public access" (acknowledge the warning). src/configuration/aws_connection.pyandsrc/cloud_storage/aws_storage.pyhandle the AWS session and S3 push/pull logic, whilesrc/entity/s3_estimator.pywraps the pull/push operations used by Model Evaluation and Model Pusher.
The pipeline is defined in .github/workflows/aws.yaml and runs in two stages:
βββββββββββββββββββββββββββββββ ββββββββββββββββββββββββββββββββββββ
β Continuous Integration β β Continuous Deployment β
β (runs-on: ubuntu-latest) β ββββΊ β (runs-on: self-hosted / EC2) β
β β β β
β β’ Checkout code β β β’ Pull latest image from ECR β
β β’ Configure AWS creds β β β’ Stop & remove old container β
β β’ Login to Amazon ECR β β β’ Run new container with env β
β β’ Build & push Docker imageβ β vars injected as secrets β
βββββββββββββββββββββββββββββββ ββββββββββββββββββββββββββββββββββββ
-
ECR Repository β AWS Console β ECR β create repository
vehicleprojinus-east-1. -
EC2 Instance β Launch an Ubuntu Server 24.04 (free-tier eligible) instance (e.g.,
vehicledata-machine), allow HTTP/HTTPS traffic, attach a new key pair, and connect via EC2 Instance Connect. -
Install Docker on EC2:
sudo apt-get update -y sudo apt-get upgrade curl -fsSL https://get.docker.com -o get-docker.sh sudo sh get-docker.sh sudo usermod -aG docker ubuntu newgrp docker
-
Self-hosted GitHub Runner β In your GitHub repo, go to Settings β Actions β Runners β New self-hosted runner, select Linux, and run the provided download/configure commands on the EC2 instance. Start it with
./run.sh. -
GitHub Secrets β Under Settings β Secrets and variables β Actions, add:
Secret Description AWS_ACCESS_KEY_IDIAM user access key AWS_SECRET_ACCESS_KEYIAM user secret key AWS_DEFAULT_REGIONe.g. us-east-1ECR_REPOECR repository name ( vehicleproj)MONGODB_URLMongoDB Atlas connection string -
Open the App Port β In the EC2 instance's Security Group, add an inbound rule for the app port (e.g.,
5000) with source0.0.0.0/0. -
Push to
mainβ the workflow triggers automatically: builds, pushes to ECR, and deploys the container on EC2. -
Access the app at
http://<EC2-public-ip>:<port>.
| Method | Route | Description |
|---|---|---|
GET |
/ |
Home page (served via Jinja2 templates) |
POST/GET |
/training |
Triggers the model training pipeline |
POST |
/predict |
Serves predictions from the trained model |
| Component | Responsibility |
|---|---|
| Data Ingestion | Pulls data from MongoDB, converts to DataFrame, splits into train/test |
| Data Validation | Validates schema/data drift against config/schema.yaml |
| Data Transformation | Feature engineering, preprocessing pipeline, saves transformed arrays |
| Model Trainer | Trains the ML model and saves the trained estimator |
| Model Evaluation | Compares the new model against the production model in S3 |
| Model Pusher | Pushes the accepted model to the S3 model registry |
Each component is driven by strongly-typed config and artifact entities defined in src/entity/config_entity.py and src/entity/artifact_entity.py, keeping the pipeline modular and easy to extend.
- Pandas / NumPy β data manipulation
- Scikit-learn β modeling & preprocessing
- imbalanced-learn β handling class imbalance
- Matplotlib / Seaborn / Plotly β EDA & visualization
- PyMongo β MongoDB Atlas connectivity
- boto3 β AWS S3 integration
- AWS S3 / IAM / ECR / EC2 β model registry & deployment infrastructure
- Docker β containerization
- GitHub Actions β CI/CD automation (self-hosted EC2 runner)
- Add automated data drift monitoring
- Add unit and integration tests (pytest)
- Add model explainability (SHAP)
- Add experiment tracking (MLflow / DVC)
- Add batch prediction endpoint
- Add structured logging and monitoring (Sentry / CloudWatch)
- Blue-green or canary deployment strategy on EC2
Contributions are welcome and appreciated.
# 1. Fork the repository
# 2. Create a feature branch
git checkout -b feature/your-feature-name
# 3. Commit your changes
git commit -m "feat: add your feature"
# 4. Push and open a Pull Request
git push origin feature/your-feature-namePlease follow Conventional Commits for commit messages and open an issue before starting large changes.
This project is licensed under the MIT License β see the LICENSE file for details.
Sharif Abusad π§ abusharif866@gmail.com
If you found this project useful, consider giving it a β on GitHub β it helps a lot!