Open-Meteo Coordinates Service
A FastAPI-based microservice that queries the Open-Meteo Geocoding API to retrieve coordinates for various cities. The service includes caching, Prometheus metrics, and Grafana dashboards for monitoring.
Features
- RESTful API for retrieving city coordinates
- Intelligent caching to reduce external API calls
- Prometheus metrics for observability
- Pre-configured Grafana dashboards with 5 panels
- Docker Compose setup for easy deployment
Prerequisites
- Docker
- Docker Compose
Quick Start
-
Clone the repository
cd open-meteo-service -
Start all services
docker compose up --build -
Access the services
- API: http://localhost:8000/docs
- Prometheus: http://localhost:9090
- Grafana: http://localhost:3000 (admin/admin)
Helm (Kubernetes)
The Helm chart in the helm folder deploys the FastAPI app and, by default, Prometheus and Grafana.
What it deploys
- App: FastAPI service with
/healthzprobes and Prometheus scrape annotations - Prometheus: Scrapes the app metrics at
/metrics - Grafana: Pre-provisioned datasource and dashboard for the app metrics
Prerequisites
- Kubernetes cluster
- Helm v3
Install
helm install open-meteo-service ./helm
Upgrade
helm upgrade open-meteo-service ./helm
Access the services
kubectl port-forward svc/open-meteo-service-open-meteo-service 8080:8000
kubectl port-forward svc/open-meteo-service-open-meteo-service-prometheus 9090:9090
kubectl port-forward svc/open-meteo-service-open-meteo-service-grafana 3000:3000
Values overview
You can customize the chart via helm/values.yaml or --set flags.
App values
image.repository,image.tag,image.pullPolicyservice.type,service.portenv.cacheFilepersistence.enabled,persistence.size,persistence.storageClassName
Prometheus values
prometheus.enabledprometheus.imageprometheus.service.type,prometheus.service.portprometheus.persistence.enabled,prometheus.persistence.size,prometheus.persistence.storageClassName
Grafana values
grafana.enabledgrafana.imagegrafana.service.type,grafana.service.portgrafana.adminUser,grafana.adminPasswordgrafana.persistence.enabled,grafana.persistence.size,grafana.persistence.storageClassName
Example: disable Grafana and Prometheus
helm install open-meteo-service ./helm \
--set grafana.enabled=false \
--set prometheus.enabled=false
Example: enable persistence for all components
helm install open-meteo-service ./helm \
--set persistence.enabled=true \
--set prometheus.persistence.enabled=true \
--set grafana.persistence.enabled=true
CI Pipeline
This project includes a GitLab CI/CD pipeline that automatically builds, deploys, and tests the application without requiring Docker-in-Docker.
Pipeline Stages
-
Build Stage (Kaniko)
- Builds Docker image using Kaniko (secure, daemonless container builder)
- Pushes to GitLab Container Registry with commit-based tags
- Tags
latestfor main branch builds - Uses layer caching for faster builds
-
Deploy Stage (kind + Helm)
- Creates a local Kubernetes cluster using kind (Kubernetes in Docker)
- Installs kubectl, kind, and Helm
- Configures image pull secrets for GitLab registry access
- Deploys app using Helm with the newly built image
- Waits for deployment readiness with automatic rollback on failure
-
Test Stage (Health Checks)
- Validates deployment with port-forwarding
- Tests
/healthzendpoint for service health - Tests
/coordinatesendpoint for data retrieval - Tests
/helpendpoint for API documentation - Provides detailed debugging info if tests fail
- Cleans up kind cluster after tests
Image Tagging Strategy
- Commit builds:
$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA - Main branch: Also tagged as
latest
Viewing Pipeline Status
- Go to your GitLab project
- Navigate to CI/CD > Pipelines
- Click on a pipeline run to see individual job logs
- Each stage shows real-time logs and can be expanded for details
Debugging Failed Pipelines
If a pipeline fails:
- Check the build job logs for Docker build errors
- Check the deploy job logs for Kubernetes/Helm issues
- Pod status and events are automatically logged
- Container logs are shown if deployment fails
- Check the test job logs for endpoint test failures
- Response bodies are printed for debugging
Running Locally
To test the pipeline behavior locally:
# Build with Kaniko (requires Docker)
docker run --rm -v $(pwd):/workspace gcr.io/kaniko-project/executor:latest \
--dockerfile /workspace/Dockerfile \
--context /workspace \
--no-push
# Test with kind
kind create cluster
kubectl create secret docker-registry gitlab-registry \
--docker-server=your-registry \
--docker-username=your-user \
--docker-password=your-token
helm install open-meteo-service ./open-meteo-service \
--set imagePullSecrets[0].name=gitlab-registry
API Documentation
Endpoints
GET /coordinates
Retrieve coordinates for all configured cities.
Response:
{
"source": "cache",
"data": {
"Tel Aviv": {
"name": "Tel Aviv",
"latitude": 32.08088,
"longitude": 34.78057,
"country": "Israel"
},
...
}
}
GET /coordinates/{city}
Retrieve coordinates for a specific city.
Parameters:
city(path) - City name (e.g., "Ashkelon", "London")
Example:
curl http://localhost:8000/coordinates/Paris
Response:
{
"source": "open-meteo",
"data": {
"name": "Paris",
"latitude": 48.85341,
"longitude": 2.3488,
"country": "France"
}
}
GET /metrics
Prometheus metrics endpoint exposing service metrics.
Example:
curl http://localhost:8000/metrics
GET /healthz
Health check endpoint.
Response:
{
"status": "ok"
}
Metrics
The service exposes the following Prometheus metrics:
HTTP Metrics
-
http_requests_total- Counter of total HTTP requests- Labels:
endpoint,method,status
- Labels:
-
http_request_duration_seconds- Histogram of request durations- Labels:
endpoint,method
- Labels:
Cache Metrics
coordinates_cache_hits_total- Counter of cache hitscoordinates_cache_misses_total- Counter of cache misses
External API Metrics
openmeteo_api_calls_total- Counter of calls to Open-Meteo Geocoding API- Labels:
city
- Labels:
Grafana Dashboard
The pre-configured dashboard includes 5 panels:
- Request Rate - Requests per second by endpoint
- Request Duration p95 - 95th percentile latency
- Cache Hits vs Misses - Cache effectiveness
- Open-Meteo Calls by City - External API usage per city
- Requests by Status - HTTP status code distribution
Access the dashboard at http://localhost:3000 after logging in with admin/admin.
Caching
The service uses a local JSON file (coordinates_cache.json) to cache city coordinates:
- Reduces external API calls
- Shared across all API endpoints
- Persists between requests (not container restarts)
- Automatically updated when new cities are queried
Development
Project Structure
.
├── app/
│ ├── main.py # FastAPI application
│ ├── service.py # Business logic & caching
│ └── metrics.py # Prometheus metrics definitions
├── grafana/
│ ├── provisioning/ # Auto-configured datasources & dashboards
│ └── dashboards/ # Dashboard JSON definitions
├── docker-compose.yml # Service orchestration
├── Dockerfile # Python app container
├── prometheus.yml # Prometheus scrape configuration
└── requirements.txt # Python dependencies
Stop Services
docker compose down
View Logs
docker compose logs -f open-meteo-service
Rebuild After Code Changes
docker compose up --build
Configuration
Environment Variables
CACHE_FILE- Path to cache file (default:coordinates_cache.json)
Scrape Interval
Edit prometheus.yml to adjust the scrape interval (default: 15s).
Testing
Generate test traffic to populate metrics:
# Test all endpoints
curl http://localhost:8000/coordinates
curl http://localhost:8000/coordinates/Paris
curl http://localhost:8000/coordinates/London
# Generate load
for i in {1..10}; do curl -s http://localhost:8000/coordinates > /dev/null; done
License
MIT