open-meteo-service/README.md

263 lines
6.3 KiB
Markdown

# 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
1. **Clone the repository**
```bash
cd open-meteo-service
```
2. **Start all services**
```bash
docker compose up --build
```
3. **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 `/healthz` probes 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
```bash
helm install open-meteo-service ./helm
```
### Upgrade
```bash
helm upgrade open-meteo-service ./helm
```
### Access the services
```bash
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.pullPolicy`
- `service.type`, `service.port`
- `env.cacheFile`
- `persistence.enabled`, `persistence.size`, `persistence.storageClassName`
**Prometheus values**
- `prometheus.enabled`
- `prometheus.image`
- `prometheus.service.type`, `prometheus.service.port`
- `prometheus.persistence.enabled`, `prometheus.persistence.size`, `prometheus.persistence.storageClassName`
**Grafana values**
- `grafana.enabled`
- `grafana.image`
- `grafana.service.type`, `grafana.service.port`
- `grafana.adminUser`, `grafana.adminPassword`
- `grafana.persistence.enabled`, `grafana.persistence.size`, `grafana.persistence.storageClassName`
### Example: disable Grafana and Prometheus
```bash
helm install open-meteo-service ./helm \
--set grafana.enabled=false \
--set prometheus.enabled=false
```
### Example: enable persistence for all components
```bash
helm install open-meteo-service ./helm \
--set persistence.enabled=true \
--set prometheus.persistence.enabled=true \
--set grafana.persistence.enabled=true
```
## API Documentation
### Endpoints
#### `GET /coordinates`
Retrieve coordinates for all configured cities.
**Response:**
```json
{
"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:**
```bash
curl http://localhost:8000/coordinates/Paris
```
**Response:**
```json
{
"source": "open-meteo",
"data": {
"name": "Paris",
"latitude": 48.85341,
"longitude": 2.3488,
"country": "France"
}
}
```
#### `GET /metrics`
Prometheus metrics endpoint exposing service metrics.
**Example:**
```bash
curl http://localhost:8000/metrics
```
#### `GET /healthz`
Health check endpoint.
**Response:**
```json
{
"status": "ok"
}
```
## Metrics
The service exposes the following Prometheus metrics:
### HTTP Metrics
- **`http_requests_total`** - Counter of total HTTP requests
- Labels: `endpoint`, `method`, `status`
- **`http_request_duration_seconds`** - Histogram of request durations
- Labels: `endpoint`, `method`
### Cache Metrics
- **`coordinates_cache_hits_total`** - Counter of cache hits
- **`coordinates_cache_misses_total`** - Counter of cache misses
### External API Metrics
- **`openmeteo_api_calls_total`** - Counter of calls to Open-Meteo Geocoding API
- Labels: `city`
## Grafana Dashboard
The pre-configured dashboard includes 5 panels:
1. **Request Rate** - Requests per second by endpoint
2. **Request Duration p95** - 95th percentile latency
3. **Cache Hits vs Misses** - Cache effectiveness
4. **Open-Meteo Calls by City** - External API usage per city
5. **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
```bash
docker compose down
```
### View Logs
```bash
docker compose logs -f open-meteo-service
```
### Rebuild After Code Changes
```bash
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:
```bash
# 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