This repository contains everything needed to run the SMS Checker system using Docker Compose.
The system consists of two services:
model-service– Python / Flask service that exposes the SMS spam detection model.app– Java / Spring Boot frontend that serves the web UI and calls the backend.
The actual source code lives in separate repositories in the doda25-team24 GitHub organization and as sibling folders in the local checkout:
../app– Spring Boot frontend../model-service– Python model backend../lib-version– Maven library used for versioning
This operation repository focuses on how to start and operate the system.
Local directory structure:
doda25-team24/
├── model-service/
├── app/
├── lib-version/
└── operation/ (current)
Repositories:
model-service: https://github.com/doda25-team24/model-serviceapp: https://github.com/doda25-team24/applib-version: https://github.com/doda25-team24/lib-versionoperation: https://github.com/doda25-team24/operation
- Docker installed
- Docker Compose available as
docker compose(ordocker-compose) - The
appandmodel-servicerepositories present as sibling folders as shown above
No manual build is required: Docker Compose will build both services using the Dockerfiles in ../app and ../model-service.
From the operation/ directory:
# Build images and start both services in the foreground
docker compose up
# Or start them in the background:
docker compose up -dOnce both services are up:
-
Open the frontend in the browser:
http://localhost:8080/sms -
The backend (model-service) is reachable inside the Docker network as:
http://model-service:8081
(for examplehttp://model-service:8081/predictorhttp://model-service:8081/apidocs)
To stop the application:
docker compose downIf you started the stack with -d, you can inspect logs with:
docker compose logs -fThe main configuration lives in docker-compose.yml.
A typical configuration (matching this project) looks like this:
version: '3.8'
services:
# Backend Service (Python AI Model)
model-service:
build:
context: ../model-service # Path to the model-service folder
dockerfile: Dockerfile
target: production # Use the production stage of the Dockerfile
container_name: sms-checker-model-service
# Port inside the container is controlled by MODEL_PORT (default from code)
environment:
- MODEL_PORT=8081
# Host port 8081 -> container port 8081
ports:
- "8081:8081"
# Persist model output outside the container
volumes:
- ../model-service/output:/model-service/output
# Frontend Service (Spring Boot App)
app:
build:
context: ../app # Path to the app folder
dockerfile: Dockerfile
container_name: sms-checker-app
# APP_PORT controls Spring Boot's server.port, MODEL_HOST points to the backend
environment:
- APP_PORT=8080
- MODEL_HOST=http://model-service:8081
# Host port 8080 -> container port 8080
ports:
- "8080:8080"
# Ensure app container is started after model-service container is created
depends_on:
- model-servicemodel-servicebuilds and runs the Python backend and exposes the spam detection API.appbuilds and runs the Spring Boot frontend and sends classification requests to the backend.
Both containers use environment variables to configure ports and connections, as required by F6: Flexible Containers.
The app service understands the following environment variables:
Port on which the Spring Boot application listens inside the container.
In application.properties this is configured as:
server.port=${APP_PORT:8080}If APP_PORT is not set, the app defaults to port 8080.
URL of the model-service as seen from the app container.
By default: http://model-service:8081, using the Docker Compose service name (model-service) and its internal port.
To run the app on a different port (e.g., 9090) you can change both the environment variable and the port mapping in docker-compose.yml.
To set up the infrastructure for this project, use Vagrant to provision the required virtual machines:
vagrant up
vagrant provisionTo execute the finalization.yml file:
ansible-playbook -u vagrant --private-key=../.vagrant/machines/ctrl/virtualbox/private_key -i 192.168.56.100, finalization.ymlThese commands will create and configure the VMs according to the specifications in the Vagrantfile, and apply the Ansible playbooks to set up the Kubernetes cluster.
The provisioning process sets up the Kubernetes Dashboard, which is accessible via a custom local domain.
To access the dashboard, you must update your local hosts file (/etc/hosts on Linux/macOS or C:\Windows\System32\drivers\etc\hosts on Windows) to map the dashboard URL to the Ingress Controller's IP:
192.168.56.110 dashboard.local
Once configured, you can access the dashboard at: https://dashboard.local
Here's an overview of the provisioning-related files and directories:
├── Vagrantfile # VM configuration and provisioning settings
├── ansible/ # Ansible playbooks and configuration
│ ├── ctrl.yaml # Control plane node configuration
│ ├── files/ # Static files for provisioning
│ ├── finalization.yml # Final setup tasks
│ ├── hosts # Ansible inventory file
│ ├── kubeconfig/ # Kubernetes configuration directory
│ │ └── admin.conf # Cluster admin credentials
│ └── node.yaml # Worker node configuration
├── docker-compose.yml # Docker Compose configuration (if needed)
├── env.yaml # Environment variables for Helm
├── general.yaml # General configuration settings
├── kubeconfig/ # Local kubeconfig directory
│ └── admin.conf # Local copy of admin credentials
└── requirements.txt # Python dependencies
Key Components:
- Vagrantfile: Defines the VMs (control plane and worker nodes)
- ansible/: Contains all Ansible playbooks that configure Kubernetes on the VMs
ctrl.yaml: Sets up the control plane nodenode.yaml: Configures worker nodesfinalization.yml: Performs final cluster setup tasks
- kubeconfig/: Stores Kubernetes cluster access credentials
- env.yaml: Contains environment-specific variables for the Helm deployment
Once the infrastructure is provisioned, deploy the application using Helm.
To deploy with helm, simply execute the setup.sh file from the operation folder:
./setup.shYou might need to run this command first: export GITHUB_TOKEN=paste_your_token_here
Using prometheus to gather metrics for model-service.
On a separate terminal, enable tunnelling
sudo minikube tunnelInteract with the app and execute some requests for prometheus to have activity to measure. For that you can port-forward the frontend
kubectl port-forward svc/sms-checker-app 8080:8080and access the app on http://127.0.0.1:8080/sms/
On a separate terminal, enable port forwarding for the new ingress
kubectl port-forward svc/ingress-nginx-controller 8000:80 -n ingress-nginxYou can also test the python backend directly with:
curl -X POST http://localhost:8081/predict -H "Content-Type: application/json" -d '{"sms":"Test message"}'expected:
{
"classifier": "decision tree",
"result": "ham",
"sms": "Test message"
}-
Metrics are exposed in Prometheus text format at
/metrics -
Custom metrics are defined by the application code
-
Framework-provided metrics (Spring Boot Actuator) are enabled and exposed explicitly
| Metric | Type | Labels | Description |
|---|---|---|---|
app_sms_spam_predictions_total |
Counter | — | Total SMS spam predictions |
app_requests_active |
Gauge | — | Active prediction requests |
app_prediction_latency_seconds |
Histogram | — | Prediction request latency |
app_prediction_latency_seconds_max |
Gauge | — | Maximum prediction latency |
| Metric | Type | Description |
|---|---|---|
sms_checks_total |
Counter | Total SMS predictions |
sms_active_requests |
Gauge | Active prediction requests |
sms_prediction_latency_seconds |
Histogram | Prediction latency |
process_cpu_seconds_total– Total CPU time consumedprocess_resident_memory_bytes– Resident memory sizeprocess_virtual_memory_bytes– Virtual memory sizepython_gc_objects_collected_total– Objects collected by garbage collectorpython_gc_collections_total– Number of garbage collection runspython_info– Python runtime information
Metrics scraping is enabled automatically during Helm installation
No manual Prometheus configuration is required
ServiceMonitor resources are created automatically for all services
After running helm install, the metrics endpoints should be available through the ingress, by running the commands:
curl http://127.0.0.1:8000/metrics/model-servicefor python backend
and
curl http://127.0.0.1:8000/metrics/sms-checker-appfor app frontend-service metrics
You can also expose prometheus and query the metrics abovementioned 1. Expose Prometheus:
minikube service myprom-kube-prometheus-sta-prometheus2. Check available targets:
- Navigate to Status → Targets
- Verify both
model-serviceandsms-checker-appare UP
To view the dashboards, port-forward using kubectl:
kubectl port-forward svc/sms-checker-grafana 3000:80Open Grafana in your browser at http://localhost:3000/
Default credentials (from kube-prometheus-stack): Username: admin Password: Obtain via
kubectl get secret --namespace default myprom-grafana -o jsonpath="{.data.admin-password}" | base64 --decode ; echoThe grafana dashboard for A3 monitoring is named 'SMS Checker Operations'
To run the continous experimentation tests, execute test_experiments.sh from the operation folder
./test_experiments.shTo view the results, follow the same procedure as in the 'Visualizing with Grafana' section. The continuous experimentation dashboard is named 'Experiment Results (Pod View)'