Il progetto implementa un ambiente completo di observability su cluster Kubernetes locale utilizzando Kind (Kubernetes in Docker). L'obiettivo è monitorare un'applicazione Flask instrumentata con OpenTelemetry attraverso uno stack composto da:
- OpenTelemetry Collector: Raccolta e processamento delle metriche
- Prometheus: Storage e query engine per le metriche
- Grafana: Visualizzazione e dashboard interattive
- NGINX Ingress: Routing del traffico esterno
┌──────────────────────────────────────────────────────────────┐
│ Cluster Kind (obs-cluster) │
│ │
│ ┌──────────────┐ ┌─────────────────┐ ┌────────────┐ │
│ │ │ │ │ │ │ │
│ │ Flask App │───▶│ OpenTelemetry │◀───│ Prometheus │ │
│ │ (Port 5000) │ │ Collector │ │ │ │
│ │ │ │ (Port 4317) │ │ │ │
│ └──────────────┘ └─────────────────┘ └────────────┘ │
│ ▲ │
│ ┌─────────────────┐ │ │
│ │ Grafana │──────────┘ │
│ │ (Port 3000) │ │
│ └─────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ NGINX Ingress Controller │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌──────────────┐ │ │
│ │ │flask-app │ │prometheus │ │grafana │ │ │
│ │ │.example.com │ │.example.com │ │.example.com │ │ │
│ │ └─────────────┘ └─────────────┘ └──────────────┘ │ │
│ └─────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────┘
│
┌─────────▼──────────┐
│ Host System │
│ localhost:80/443 │
└────────────────────┘
Il cluster viene configurato attraverso kind-config.yaml:
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
name: obs-cluster
networking:
apiServerAddress: "0.0.0.0" # Ascolta su tutte le interfacce
apiServerPort: 6443
nodes:
- role: control-plane
extraPortMappings:
- containerPort: 80 # HTTP
hostPort: 80
protocol: TCP
- containerPort: 443 # HTTPS
hostPort: 443
protocol: TCP
kubeadmConfigPatches:
- |
apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
apiServer:
certSANs:
- "0.0.0.0"
- "127.0.0.1"
- "10.0.0.2" # IP interno del cluster
- role: worker- Nome:
obs-cluster - Nodi: 1 control-plane + 1 worker
- Porte esposte: 80 (HTTP) e 443 (HTTPS) mappate dall'host
- API Server: Accessibile su tutte le interfacce di rete
- Certificati: Configurati per multiple interfacce
Lo stack viene installato utilizzando Helm con il chart della community:
helm repo add prometheus-community <https://prometheus-community.github.io/helm-charts>
helm repo update
helm install prometheus prometheus-community/kube-prometheus-stack \\
--namespace monitoring --create-namespaceQuesto installa:
- Prometheus Server: Raccolta dal collector e storage metriche nel suo TSDB
- Grafana: Interroga il DB prom attraverso query PromQL e li rappresenta in dashboard
- Prometheus Operator: Gestione automatica delle configurazioni
- AlertManager: Gestione degli alert
- Node Exporter: Metriche del sistema
- Kube State Metrics: Metriche del cluster Kubernetes
| Componente | Service Name | Porta | Descrizione |
|---|---|---|---|
| Prometheus Server | prometheus-kube-prometheus-prometheus |
9090 | Query engine e storage |
| Grafana | prometheus-grafana |
80 | Dashboard UI |
| AlertManager | prometheus-kube-prometheus-alertmanager |
9093 | Gestione alert |
Il Collector viene configurato attraverso una ConfigMap che definisce:
receivers:
otlp: # Riceve metriche dall'app
protocols:
grpc:
endpoint: "0.0.0.0:4317"
http:
endpoint: "0.0.0.0:4318"
prometheus/internal: # Metriche interne del collector
config:
scrape_configs:
- job_name: 'otel-collector-internal'
static_configs:
- targets: ['0.0.0.0:8888']
exporters:
prometheus: # Esporta verso Prometheus
endpoint: "0.0.0.0:9464"
send_timestamps: true
resource_to_telemetry_conversion:
enabled: true
processors:
batch: # Raggruppa metriche per efficienza
service:
pipelines:
metrics:
receivers: [otlp, prometheus/internal]
processors: [batch]
exporters: [prometheus]| Porta | Protocollo | Descrizione |
|---|---|---|
| 4317 | gRPC | Ricezione metriche OTLP |
| 4318 | HTTP | Ricezione metriche OTLP |
| 9464 | HTTP | Esposizione metriche per Prometheus |
| 8888 | HTTP | Metriche interne (telemetry) |
| 13133 | HTTP | Health check |
Il ServiceMonitor configura Prometheus per scoprire automaticamente il Collector:
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: otel-collector
namespace: monitoring
labels:
app.kubernetes.io/name: otel-collector
app.kubernetes.io/instance: prometheus
release: prometheus # Label critica per l'operator
spec:
selector:
matchLabels:
app: otel-collector
endpoints:
- port: metrics # Porta 9464
interval: 5s
path: /metrics# Configurazione del provider di metriche
resource = Resource(attributes={SERVICE_NAME: "my-flask-app"})
otlp_endpoint = os.getenv(
"OTEL_EXPORTER_OTLP_ENDPOINT",
"<http://otel-collector.monitoring.svc.cluster.local:4317>"
)
# Export delle metriche ogni 5 secondi
metric_reader = PeriodicExportingMetricReader(
OTLPMetricExporter(endpoint=otlp_endpoint, insecure=True),
export_interval_millis=5000
)
meter_provider = MeterProvider(resource=resource, metric_readers=[metric_reader])- Counter: Contatori incrementali
app_http_requests_total: Richieste HTTP per endpointapp_button_clicks_total: Click sui pulsanti per colore
- Observable Gauge: Valori istantanei
app_cpu_usage_percent: Utilizzo CPU del processoapp_memory_usage_percent: Utilizzo memoria del processo
L'applicazione include thread in background per:
- Simulazione traffico HTTP: Genera metriche automatiche
- Monitoraggio CPU: Aggiorna regolarmente l'utilizzo CPU
- Flush periodico: Forza l'export delle metriche
L'Ingress Controller viene installato con configurazione specifica per Kind:
kubectl apply -f <https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.8.1/deploy/static/provider/kind/deploy.yaml>
kubectl label nodes obs-cluster-control-plane ingress-ready=truespec:
ingressClassName: nginx
rules:
- host: flask-app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: flask-hello-world
port:
number: 80spec:
rules:
- host: prometheus.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: prometheus-kube-prometheus-prometheus
port:
number: 9090spec:
rules:
- host: grafana.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: prometheus-grafana
port:
number: 80Flask App → OpenTelemetry SDK → OTLP Exporter
- L'app genera metriche tramite l'SDK OpenTelemetry
- Le metriche vengono serializzate in formato OTLP
- Export automatico ogni 5 secondi
OTLP Exporter → OTel Collector (gRPC:4317)
- Il Collector riceve le metriche via gRPC
- Processa le metriche attraverso il pipeline batch
- Converte le metriche in formato Prometheus
OTel Collector → Prometheus (HTTP:9464)
- Prometheus scrapes il Collector ogni 5 secondi
- ServiceMonitor automatizza la discovery
- Le metriche vengono stored nel TSDB di Prometheus
Grafana → Prometheus (HTTP:9090)
- Grafana fa delle query Prometheus per le metriche
- Dashboard real-time con refresh automatico
- Visualizzazione di trend e alerting
Browser → NGINX Ingress → Service → Pod
- Richieste HTTP verso domini specifici
- Ingress Controller routing basato su host
- Load balancing verso i pod dell'applicazione
Il processo completo è automatizzato in setup_cluster.sh:
- Pulizia: Rimuove cluster esistente
- Creazione Cluster: Applica configurazione Kind
- Configurazione Networking: Installa NGINX Ingress
- Stack Monitoring: Deploy Prometheus/Grafana via Helm
- OpenTelemetry: Deploy Collector e ServiceMonitor
- Applicazione: Deploy dell'app instrumentata
# Verifica stato dei pod
kubectl get pods -n monitoring
# Verifica ingress
kubectl get ingress -n monitoring
# Verifica metriche del collector
kubectl port-forward -n monitoring svc/otel-collector 9464:9464
curl localhost:9464/metrics
# Verifica ServiceMonitor
kubectl get servicemonitor -n monitoring- Applicazione: http://flask-app.example.com
- Prometheus: http://prometheus.example.com
- Grafana: http://grafana.example.com
- Username:
admin - Password:
prom-operator, ottenibile viakubectl get secret
- Username:
Le metriche dell'applicazione sono visibili in Prometheus con prefisso app_*:
app_http_requests_total: Counter delle richieste HTTPapp_button_clicks_total: Counter dei click sui pulsantiapp_cpu_usage_percent: Gauge dell'utilizzo CPUapp_memory_usage_percent: Gauge dell'utilizzo memoria
Per creare dashboard efficaci:
- Query Prometheus: Usa PromQL per aggregare metriche
- Pannelli: Rate, percentuali, gauge per metriche diverse
- Templating: Variabili per filtrare per servizio/endpoint
- Alerting: Soglie su CPU, memoria, error rate
Per configurare questa dashboard sono state utilizzate le 4 metriche disponibili. Attraverso delle query PromQL è stato possibile recuperare sia i semplici valori assoluti che trasformarti attraverso le varie funzioni che Prometheus mette a disposizione. Quindi, da Grafana si è creata una board con vari line chart e gauge counter del rate di queste metriche. Qui sotto il risultato:
- Verifica variabile
OTEL_EXPORTER_OTLP_ENDPOINT - Controlla che il service del Collector sia attivo
- Verifica connettività di rete tra pod
- Verifica labels del ServiceMonitor
- Controlla che le porte corrispondano
- Verifica selettori del ServiceMonitor
- Verifica che l'Ingress Controller sia running
- Controlla mapping delle porte nel Kind config
- Verifica configurazione DNS locale
- Verifica data source Prometheus in Grafana
- Controlla query PromQL nelle dashboard
- Verifica retention policy di Prometheus
Questo setup fornisce un ambiente completo di observability locale per lo sviluppo e il testing di applicazioni instrumentate. La configurazione automatizzata permette di replicare rapidamente l'ambiente, mentre la struttura modulare facilita l'estensione con nuovi componenti di monitoring.
