This commit is contained in:
alessandro barucci
2026-08-09 17:03:02 +02:00
parent 5997a56cb3
commit 1a2cca2cd2
11 changed files with 1334 additions and 4 deletions

View File

@@ -0,0 +1,90 @@
kubectl get cluster.postgresql.cnpg.io -n <namespace>
kubectl get backup -n <namespace>
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: <pgclustername>-restored
spec:
instances: <instancenumber>
storage:
size: <dbstorage>
storageClass: csi-rbdfs-sc
walStorage:
storageClass: csi-rbdfs-sc
size: <dbstorage>
bootstrap:
recovery:
source: <pgclustername>-origin
backup:
name: sporteams-backup-20260718020000 # nome dell'oggetto Backup da kubectl get backup -n <namespace>
recoveryTarget: # opzionale in assenza applica tutto il journal (wal) a partire dal backup
targetTime: "2026-07-18 15:30:00.000000+00" # timestamp in cui si ferma rebuild dal WAL
externalClusters:
- name: <pgclustername>-origin
barmanObjectStore:
endpointURL: http://minio.minio.svc.cluster.local:9000
destinationPath: s3://cnpgbackup2/<pgclustername>
s3Credentials:
accessKeyId:
name: minio-backup-creds
key: ACCESS_KEY_ID
secretAccessKey:
name: minio-backup-creds
key: ACCESS_SECRET_KEY
wal:
compression: gzip
maxParallel: 2
data:
compression: gzip
immediateCheckpoint: false
jobs: 2
# Dopo il restore, il cluster ripristinato può avere un backup indipendente:
backup:
barmanObjectStore:
endpointURL: http://minio.minio.svc.cluster.local:9000
destinationPath: s3://cnpgbackup2/<pgclustername>-restored
s3Credentials:
accessKeyId:
name: minio-backup-creds
key: ACCESS_KEY_ID
secretAccessKey:
name: minio-backup-creds
key: ACCESS_SECRET_KEY
wal:
compression: gzip
maxParallel: 2
data:
compression: gzip
immediateCheckpoint: false
jobs: 2
retentionPolicy: "30d"
postgresql:
parameters:
max_connections: "300"
shared_buffers: "1GB"
resources:
requests:
cpu: "500m"
memory: "1Gi"
limits:
cpu: "2"
memory: "2Gi"
### Utility varie ##########
kubectl get cluster.postgresql.cnpg.io -n stsharedservices-dev
kubectl -n minio exec -it minio-0 -- sh
mc alias set local http://localhost:9000 minioadmin KAYQE1QA7uwUZ8uI
mc ls local cnpgbackup2

View File

@@ -0,0 +1,173 @@
# =====================================================================
# values.yaml per kube-prometheus-stack su RKE2 (installazione diretta,
# senza Rancher Manager)
#
# Repo: https://prometheus-community.github.io/helm-charts
# Chart: kube-prometheus-stack
#
# Install:
# helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
# helm repo update
# helm install kube-prometheus-stack prometheus-community/kube-prometheus-stack \
# -n monitoring --create-namespace \
# -f 01-kube-prometheus-stack-values.yaml
# =====================================================================
# ---------------------------------------------------------------------
# Prometheus
# ---------------------------------------------------------------------
prometheus:
prometheusSpec:
retention: 15d
retentionSize: "10GB"
# Adatta alla storageClass disponibile nel tuo cluster RKE2
# (es. longhorn, local-path-provisioner, ceph-rbd, ecc.)
storageSpec:
volumeClaimTemplate:
spec:
storageClassName: longhorn # <-- CAMBIA con la tua storageClass
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 50Gi
resources:
requests:
cpu: 250m
memory: 1Gi
limits:
memory: 2Gi
# Permette a Prometheus di scoprire ServiceMonitor/PodMonitor
# creati in QUALSIASI namespace (importante per uno stack "generico"
# dove le verticali applicative, es. CNPG, vivono in namespace propri)
serviceMonitorSelectorNilUsesHelmValues: false
podMonitorSelectorNilUsesHelmValues: false
ruleSelectorNilUsesHelmValues: false
# Su RKE2 il control-plane espone metriche ma va abilitato esplicitamente
# lo scraping (vedi sezioni kubeEtcd/kubeControllerManager/kubeScheduler
# più sotto). Se il tuo cluster è "hardened" e non espone questi endpoint
# sui nodi worker, valuta di disabilitare le relative sezioni.
# ---------------------------------------------------------------------
# Control plane RKE2 — di default questi componenti su RKE2 girano come
# pod statici raggiungibili sul nodo server; verificare le porte reali
# con `kubectl get pods -n kube-system` e `netstat` sul nodo se lo
# scraping non trova i target.
# ---------------------------------------------------------------------
kubeEtcd:
enabled: true
service:
port: 2381
targetPort: 2381
kubeControllerManager:
enabled: true
service:
port: 10257
targetPort: 10257
# RKE2 espone il controller-manager in HTTPS con cert self-signed:
# potrebbe servire scrapeConfig con tlsConfig.insecureSkipVerify: true
# (vedi kube-prometheus-stack docs, sezione "kubeControllerManager").
kubeScheduler:
enabled: true
service:
port: 10259
targetPort: 10259
# Traefik è deployato di default su RKE2 ed è considerato componente
# interno. Se lo usi come ingress controller, aggiungi un ServiceMonitor
# dedicato puntato al suo endpoint /metrics (di solito porta 9100).
kubeProxy:
enabled: true
# ---------------------------------------------------------------------
# Alertmanager
# ---------------------------------------------------------------------
alertmanager:
alertmanagerSpec:
retention: 120h
storage:
volumeClaimTemplate:
spec:
storageClassName: longhorn # <-- CAMBIA con la tua storageClass
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 5Gi
resources:
requests:
cpu: 50m
memory: 128Mi
limits:
memory: 256Mi
# Config di base; per la config completa vedi il file
# 04-alertmanager-config.yaml (AlertmanagerConfig CRD, più gestibile
# in modo dichiarativo/GitOps rispetto a alertmanager.config qui).
# ---------------------------------------------------------------------
# Grafana (incluso nel chart)
# ---------------------------------------------------------------------
grafana:
enabled: true
defaultDashboardsTimezone: Europe/Rome
persistence:
enabled: true
storageClassName: longhorn # <-- CAMBIA con la tua storageClass
size: 5Gi
# Cambia in produzione: usa un Secret invece di plaintext
adminPassword: "CHANGE_ME"
# Loki come datasource aggiuntivo (vedi file 03-loki-alloy)
additionalDataSources:
- name: Loki
type: loki
access: proxy
url: http://loki.monitoring.svc.cluster.local:3100
isDefault: false
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
memory: 256Mi
# Import automatico dashboard CNPG (ID 20417) via sidecar dashboards
# già incluso nel chart: basta creare un ConfigMap con label
# grafana_dashboard=1 (vedi file 02-cnpg-monitoring.yaml in fondo,
# sezione dashboard, oppure importa manualmente da Grafana UI).
# ---------------------------------------------------------------------
# node-exporter
# ---------------------------------------------------------------------
nodeExporter:
enabled: true
prometheus-node-exporter:
resources:
requests:
cpu: 50m
memory: 30Mi
limits:
memory: 50Mi
# ---------------------------------------------------------------------
# kube-state-metrics
# ---------------------------------------------------------------------
kubeStateMetrics:
enabled: true
kube-state-metrics:
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
memory: 128Mi

View File

@@ -0,0 +1,96 @@
# =====================================================================
# Monitoring per CloudNativePG (CNPG)
#
# Applica con: kubectl apply -f 02-cnpg-monitoring.yaml
#
# Prerequisiti:
# - CNPG operator già installato (namespace tipico: cnpg-system)
# - kube-prometheus-stack già installato nel namespace "monitoring"
# con podMonitorSelectorNilUsesHelmValues: false (vedi file 01),
# così Prometheus scopre PodMonitor in TUTTI i namespace.
# =====================================================================
# ---------------------------------------------------------------------
# 1) PodMonitor per l'operatore CNPG stesso
# (metriche interne: reconcile loop, controller-runtime, porta 8080)
# ---------------------------------------------------------------------
apiVersion: monitoring.coreos.com/v1
kind: PodMonitor
metadata:
name: cnpg-operator
namespace: cnpg-system
labels:
release: kube-prometheus-stack
spec:
selector:
matchLabels:
app.kubernetes.io/name: cloudnative-pg
podMetricsEndpoints:
- port: metrics
interval: 30s
---
# ---------------------------------------------------------------------
# 2) PodMonitor generico per TUTTI i cluster Postgres gestiti da CNPG
#
# Nota: se preferisci il metodo "automatico" (enablePodMonitor: true
# nella risorsa Cluster), questo PodMonitor manuale NON serve per
# quel cluster specifico — ma il manuale è consigliato dalla doc
# ufficiale CNPG perché dà controllo esplicito sul lifecycle,
# indipendente dal singolo Cluster CR.
#
# Il selettore usa la label standard "cnpg.io/cluster" che CNPG
# applica automaticamente a tutti i pod di ogni cluster gestito,
# quindi UN SOLO PodMonitor copre TUTTI i cluster Postgres presenti
# nel namespace target (adatta matchExpressions se i tuoi cluster
# vivono in namespace diversi: serve un PodMonitor per namespace,
# oppure passa a un selector cross-namespace via namespaceSelector).
# ---------------------------------------------------------------------
apiVersion: monitoring.coreos.com/v1
kind: PodMonitor
metadata:
name: cnpg-clusters
namespace: monitoring
labels:
release: kube-prometheus-stack
spec:
namespaceSelector:
any: true # scropa i pod CNPG in QUALSIASI namespace
selector:
matchExpressions:
- key: cnpg.io/cluster
operator: Exists
podMetricsEndpoints:
- port: metrics # exporter per-istanza, porta 9187
interval: 30s
scrapeTimeout: 10s
path: /metrics
---
# ---------------------------------------------------------------------
# 3) Import automatico dashboard CNPG ufficiale (grafana.com ID 20417)
# tramite sidecar dashboards di Grafana (già incluso nel chart
# kube-prometheus-stack). Basta un ConfigMap con la label
# "grafana_dashboard: '1'" nel namespace di Grafana.
#
# In alternativa più semplice: Grafana UI -> Dashboards -> Import
# -> inserisci ID 20417 -> seleziona datasource Prometheus.
# ---------------------------------------------------------------------
apiVersion: v1
kind: ConfigMap
metadata:
name: cnpg-grafana-dashboard
namespace: monitoring
labels:
grafana_dashboard: "1"
data:
cnpg-dashboard.json: |
{
"annotations": {"list": []},
"title": "CloudNativePG (import via ID 20417)",
"__inputs": [],
"__requires": [],
"schemaVersion": 39,
"panels": [],
"_comment": "Placeholder: sostituisci con l'export JSON completo scaricato da https://grafana.com/grafana/dashboards/20417 per avere la dashboard reale, oppure importa manualmente da Grafana UI usando l'ID."
}

View File

@@ -0,0 +1,205 @@
# =====================================================================
# PrometheusRule: alert infrastrutturali + CNPG
# Applica con: kubectl apply -f 03-alert-rules.yaml
# =====================================================================
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: infra-alerts
namespace: monitoring
labels:
release: kube-prometheus-stack
spec:
groups:
- name: node-health
rules:
- alert: NodeDown
expr: up{job="node-exporter"} == 0
for: 5m
labels:
severity: critical
annotations:
summary: "Nodo {{ $labels.instance }} irraggiungibile"
description: "node-exporter non risponde da 5 minuti su {{ $labels.instance }}."
- alert: NodeHighCPU
expr: 100 - (avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 85
for: 10m
labels:
severity: warning
annotations:
summary: "CPU alta su {{ $labels.instance }}"
description: "Uso CPU sopra 85% da 10 minuti."
- alert: NodeHighMemory
expr: (1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100 > 90
for: 10m
labels:
severity: warning
annotations:
summary: "Memoria alta su {{ $labels.instance }}"
description: "Uso memoria sopra 90% da 10 minuti."
- alert: NodeDiskSpaceLow
expr: (node_filesystem_avail_bytes{fstype!~"tmpfs|overlay"} / node_filesystem_size_bytes{fstype!~"tmpfs|overlay"}) * 100 < 15
for: 10m
labels:
severity: warning
annotations:
summary: "Spazio disco basso su {{ $labels.instance }} ({{ $labels.mountpoint }})"
description: "Meno del 15% di spazio libero da 10 minuti."
- alert: NodeDiskSpaceCritical
expr: (node_filesystem_avail_bytes{fstype!~"tmpfs|overlay"} / node_filesystem_size_bytes{fstype!~"tmpfs|overlay"}) * 100 < 5
for: 5m
labels:
severity: critical
annotations:
summary: "Spazio disco CRITICO su {{ $labels.instance }} ({{ $labels.mountpoint }})"
description: "Meno del 5% di spazio libero da 5 minuti."
- name: kubernetes-health
rules:
- alert: KubePodCrashLooping
expr: increase(kube_pod_container_status_restarts_total[15m]) > 3
for: 5m
labels:
severity: warning
annotations:
summary: "Pod {{ $labels.namespace }}/{{ $labels.pod }} in crash loop"
description: "Più di 3 restart negli ultimi 15 minuti."
- alert: KubePodNotReady
expr: sum by (namespace, pod) (kube_pod_status_phase{phase=~"Pending|Unknown"}) > 0
for: 15m
labels:
severity: warning
annotations:
summary: "Pod {{ $labels.namespace }}/{{ $labels.pod }} non Ready da 15 minuti"
- alert: KubeDeploymentReplicasMismatch
expr: kube_deployment_spec_replicas != kube_deployment_status_replicas_available
for: 15m
labels:
severity: warning
annotations:
summary: "Deployment {{ $labels.namespace }}/{{ $labels.deployment }} con repliche non allineate"
- alert: KubePersistentVolumeFillingUp
expr: (kubelet_volume_stats_available_bytes / kubelet_volume_stats_capacity_bytes) * 100 < 10
for: 10m
labels:
severity: warning
annotations:
summary: "PVC {{ $labels.namespace }}/{{ $labels.persistentvolumeclaim }} quasi pieno"
description: "Meno del 10% di spazio libero sul volume."
- alert: EtcdInsufficientMembers
expr: sum(up{job="kube-etcd"} == 1) < ((count(up{job="kube-etcd"}) + 1) / 2)
for: 5m
labels:
severity: critical
annotations:
summary: "Quorum etcd a rischio"
description: "Meno della maggioranza dei membri etcd è raggiungibile."
- name: tls-certificates
rules:
# Richiede blackbox_exporter con probe HTTPS configurato sui tuoi
# endpoint esterni; vedi nota nel file README.
- alert: TLSCertExpiringSoon
expr: probe_ssl_earliest_cert_expiry - time() < 86400 * 15
for: 1h
labels:
severity: warning
annotations:
summary: "Certificato TLS per {{ $labels.instance }} in scadenza"
description: "Meno di 15 giorni alla scadenza del certificato."
- alert: TLSCertExpired
expr: probe_ssl_earliest_cert_expiry - time() < 0
for: 5m
labels:
severity: critical
annotations:
summary: "Certificato TLS per {{ $labels.instance }} SCADUTO"
---
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: cnpg-alerts
namespace: monitoring
labels:
release: kube-prometheus-stack
spec:
groups:
- name: cnpg-health
rules:
- alert: CNPGClusterNotHealthy
expr: cnpg_collector_up == 0
for: 5m
labels:
severity: critical
annotations:
summary: "Cluster CNPG {{ $labels.namespace }}/{{ $labels.pod }} non raggiungibile"
description: "L'exporter CNPG non risponde da 5 minuti."
- alert: CNPGInstanceInUnexpectedRecovery
expr: cnpg_pg_replication_in_recovery == 1 and on(pod) cnpg_pg_replication_is_wal_receiver_up == 0
for: 5m
labels:
severity: warning
annotations:
summary: "Istanza {{ $labels.pod }} in recovery inatteso"
description: "Verifica se il ruolo primary/standby è quello atteso."
- alert: CNPGReplicationLagHigh
expr: cnpg_pg_replication_lag > 300
for: 5m
labels:
severity: warning
annotations:
summary: "Replication lag alto su {{ $labels.pod }}"
description: "Lag di replica superiore a 300 secondi."
- alert: CNPGWALArchivingFailing
expr: increase(cnpg_pg_stat_archiver_failed_count[15m]) > 0
for: 5m
labels:
severity: critical
annotations:
summary: "Archiviazione WAL fallita su {{ $labels.pod }}"
description: "Almeno un fallimento di archiviazione WAL negli ultimi 15 minuti — rischio per i backup e il point-in-time recovery."
- alert: CNPGBackupFailed
expr: cnpg_collector_last_failed_backup_timestamp > cnpg_collector_last_available_backup_timestamp
for: 5m
labels:
severity: critical
annotations:
summary: "Ultimo backup fallito per {{ $labels.namespace }}/{{ $labels.pod }}"
- alert: CNPGNoRecentBackup
expr: (time() - cnpg_collector_last_available_backup_timestamp) > 86400
for: 10m
labels:
severity: warning
annotations:
summary: "Nessun backup riuscito nelle ultime 24h per {{ $labels.namespace }}/{{ $labels.pod }}"
- alert: CNPGConnectionsNearLimit
expr: (cnpg_backends_total / cnpg_pg_settings_setting{name="max_connections"}) * 100 > 85
for: 10m
labels:
severity: warning
annotations:
summary: "Connessioni Postgres vicine al limite su {{ $labels.pod }}"
description: "Oltre l'85% di max_connections in uso."
# Nota: i nomi esatti delle metriche cnpg_* possono variare leggermente
# tra versioni dell'operatore CNPG. Verifica sempre su
# http://localhost:9090/graph con `{__name__=~"cnpg_.+"}` dopo il
# port-forward a Prometheus, e allinea le espressioni se necessario:
# kubectl port-forward -n monitoring svc/kube-prometheus-stack-prometheus 9090

View File

@@ -0,0 +1,105 @@
# =====================================================================
# Loki + Grafana Alloy — log aggregation
#
# Loki:
# helm repo add grafana https://grafana.github.io/helm-charts
# helm repo update
# helm install loki grafana/loki -n monitoring -f loki-values.yaml
#
# Alloy (raccolta log, DaemonSet su ogni nodo RKE2):
# helm install alloy grafana/alloy -n monitoring -f alloy-values.yaml
#
# Questo file contiene ENTRAMBI i set di values, separati da "---";
# salvali in due file distinti se preferisci due comandi helm separati.
# =====================================================================
# ---------------------------------------------------------------------
# loki-values.yaml (modalità single-binary, adatta a partire in piccolo;
# per HA/produzione a lungo termine valutare la modalità "simple scalable"
# con backend object storage S3-compatibile, es. MinIO)
# ---------------------------------------------------------------------
deploymentMode: SingleBinary
loki:
auth_enabled: false
commonConfig:
replication_factor: 1
storage:
type: filesystem
singleBinary:
replicas: 1
persistence:
enabled: true
storageClass: longhorn # <-- CAMBIA con la tua storageClass
size: 50Gi
resources:
requests:
cpu: 200m
memory: 512Mi
limits:
memory: 1Gi
# Disabilita i componenti "scalable" non necessari in modalità SingleBinary
read:
replicas: 0
write:
replicas: 0
backend:
replicas: 0
gateway:
enabled: false
test:
enabled: false
---
# ---------------------------------------------------------------------
# alloy-values.yaml (DaemonSet che raccoglie log da tutti i nodi/pod
# e li invia a Loki)
# ---------------------------------------------------------------------
alloy:
configMap:
content: |
discovery.kubernetes "pods" {
role = "pod"
}
discovery.relabel "pods" {
targets = discovery.kubernetes.pods.targets
rule {
source_labels = ["__meta_kubernetes_namespace"]
target_label = "namespace"
}
rule {
source_labels = ["__meta_kubernetes_pod_name"]
target_label = "pod"
}
rule {
source_labels = ["__meta_kubernetes_pod_container_name"]
target_label = "container"
}
}
loki.source.kubernetes "pods" {
targets = discovery.relabel.pods.output
forward_to = [loki.write.default.receiver]
}
loki.write "default" {
endpoint {
url = "http://loki.monitoring.svc.cluster.local:3100/loki/api/v1/push"
}
}
controller:
type: daemonset
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
memory: 256Mi

View File

@@ -0,0 +1,62 @@
# =====================================================================
# AlertmanagerConfig — routing alert per severità
# Applica con: kubectl apply -f 05-alertmanager-config.yaml
#
# Richiede i Secret con le credenziali (webhook Slack, integration key
# PagerDuty) creati separatamente, es.:
#
# kubectl create secret generic alertmanager-slack \
# -n monitoring --from-literal=url='https://hooks.slack.com/services/XXX/YYY/ZZZ'
#
# kubectl create secret generic alertmanager-pagerduty \
# -n monitoring --from-literal=serviceKey='YOUR_PAGERDUTY_INTEGRATION_KEY'
# =====================================================================
apiVersion: monitoring.coreos.com/v1alpha1
kind: AlertmanagerConfig
metadata:
name: routing-config
namespace: monitoring
labels:
# deve matchare alertmanagerConfigSelector del chart
# (di default kube-prometheus-stack seleziona per release label)
release: kube-prometheus-stack
spec:
route:
groupBy: ["alertname", "namespace"]
groupWait: 30s
groupInterval: 5m
repeatInterval: 4h
receiver: "slack-default"
routes:
- matchers:
- name: severity
value: critical
matchType: "="
receiver: "pagerduty-critical"
continue: true # invia ANCHE a slack-default per visibilità
- matchers:
- name: severity
value: warning
matchType: "="
receiver: "slack-default"
receivers:
- name: "slack-default"
slackConfigs:
- apiURL:
name: alertmanager-slack
key: url
channel: "#alerts-k8s"
sendResolved: true
title: '{{ "{{" }} .CommonAnnotations.summary {{ "}}" }}'
text: '{{ "{{" }} .CommonAnnotations.description {{ "}}" }}'
- name: "pagerduty-critical"
pagerdutyConfigs:
- serviceKey:
name: alertmanager-pagerduty
key: serviceKey
severity: "critical"
description: '{{ "{{" }} .CommonAnnotations.summary {{ "}}" }}'
sendResolved: true

View File

@@ -0,0 +1,121 @@
# Stack di monitoring/alerting per RKE2 — CNPG come verticale iniziale
Stack generico, installato senza passare da Rancher Manager: kube-prometheus-stack
(Prometheus Operator + Prometheus + Alertmanager + Grafana + node-exporter +
kube-state-metrics) + Loki/Alloy per i log + monitoring dedicato per CloudNativePG.
## Ordine di installazione
1. **Namespace + kube-prometheus-stack**
```bash
kubectl create namespace monitoring
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo add grafana https://grafana.github.io/helm-charts
helm repo update
helm install kube-prometheus-stack prometheus-community/kube-prometheus-stack \
-n monitoring \
-f 01-kube-prometheus-stack-values.yaml
```
kube-prometheus-stack has been installed. Check its status by running:
kubectl --namespace monitoring get pods -l "release=kube-prometheus-stack"
Get Grafana 'admin' user password by running:
kubectl --namespace monitoring get secrets kube-prometheus-stack-grafana -o jsonpath="{.data.admin-password}" | base64 -d ; echo
Access Grafana local instance:
export POD_NAME=$(kubectl --namespace monitoring get pod -l "app.kubernetes.io/name=grafana,app.kubernetes.io/instance=kube-prometheus-stack" -oname)
kubectl --namespace monitoring port-forward $POD_NAME 3000
Get your grafana admin user password by running:
kubectl get secret --namespace monitoring -l app.kubernetes.io/component=admin-secret -o jsonpath="{.items[0].data.admin-password}" | base64 --decode ; echo
Visit https://github.com/prometheus-operator/kube-prometheus for instructions on how to create & configure Alertmanager and Prometheus instances using the Operator.
kubectl run -it --rm debug --image=curlimages/curl -n monitoring --restart=Never -- curl -v http://kube-prometheus-stack-grafana.monitoring.svc.cluster.local:80/
2. **Monitoring CNPG** (dopo che l'operatore CNPG è già installato)
```bash
kubectl apply -f 02-cnpg-monitoring.yaml
```
3. **Alert rules** (infra + CNPG)
```bash
kubectl apply -f 03-alert-rules.yaml
```
4. **Loki + Alloy** (log aggregation) — dividi il file in due se preferisci
comandi helm separati (`loki-values.yaml` / `alloy-values.yaml`)
```bash
helm install loki grafana/loki -n monitoring -f 04-loki-alloy-values.yaml
helm install alloy grafana/alloy -n monitoring -f 04-loki-alloy-values.yaml
```
5. **Alertmanager routing** — crea prima i Secret con le credenziali reali,
poi applica la config
```bash
kubectl create secret generic alertmanager-slack \
-n monitoring --from-literal=url='<SLACK_WEBHOOK_URL>'
kubectl create secret generic alertmanager-pagerduty \
-n monitoring --from-literal=serviceKey='<PAGERDUTY_KEY>'
kubectl apply -f 05-alertmanager-config.yaml
```
## Cose da personalizzare prima di applicare in un cluster reale
- **storageClassName** in tutti i file (`longhorn` è solo un placeholder —
sostituisci con la storageClass effettivamente disponibile sul tuo RKE2,
verificabile con `kubectl get storageclass`).
- **adminPassword** di Grafana nel file 01 — spostalo in un Secret,
non lasciarlo in chiaro nei values in produzione.
- **Nomi metriche CNPG** — verifica che corrispondano alla versione esatta
dell'operatore CNPG installata (vedi nota in fondo al file 03).
- **Dashboard CNPG** — il ConfigMap nel file 02 è un placeholder; scarica
il JSON reale da https://grafana.com/grafana/dashboards/20417 e
sostituiscilo, oppure importa manualmente da Grafana UI (Dashboards →
Import → ID 20417).
- **Control-plane RKE2** (kubeEtcd/kubeControllerManager/kubeScheduler nel
file 01) — porte ed endpoint possono variare in base a come RKE2 è
configurato (hardening CIS, profili custom). Verifica con
`kubectl get pods -n kube-system` quali pod statici girano e su quali
porte, e correggi se necessario.
- **AlertmanagerConfig selector** — deve combaciare con
`alertmanagerConfigSelector` impostato dal chart (di default seleziona
per label `release: <helm-release-name>`); se hai chiamato la release
diversamente da `kube-prometheus-stack`, aggiorna la label.
## Verifiche post-installazione
```bash
# Prometheus targets (verifica che CNPG, node-exporter, ecc. siano UP)
kubectl port-forward -n monitoring svc/kube-prometheus-stack-prometheus 9090
# -> http://localhost:9090/targets
# Grafana
kubectl port-forward -n monitoring svc/kube-prometheus-stack-grafana 3000:80
# -> http://localhost:3000 (utente: admin, password: quella impostata)
# Alertmanager
kubectl port-forward -n monitoring svc/kube-prometheus-stack-alertmanager 9093
# -> http://localhost:9093
```
## Estensioni future (non incluse in questa prima fase)
- **blackbox_exporter** — probe HTTP/TCP/TLS per endpoint applicativi
esterni e monitoraggio scadenza certificati (le regole `TLSCert*` nel
file 03 presuppongono questo exporter già installato).
- **Tempo + OpenTelemetry Collector** — tracing distribuito quando ci
saranno più verticali applicative da correlare.
- **Thanos/Mimir** — retention lunga e vista multi-cluster, se in futuro
servirà storicizzare oltre i 15gg locali o aggregare più cluster RKE2.

View File

@@ -0,0 +1,38 @@
apiVersion: v1
kind: Service
metadata:
name: kube-prometheus-stack-grafana
namespace: monitoring
labels:
app.kubernetes.io/instance: kube-prometheus-stack
app.kubernetes.io/name: grafana
spec:
type: ClusterIP
selector:
app.kubernetes.io/instance: kube-prometheus-stack
app.kubernetes.io/name: grafana
ports:
- name: http
protocol: TCP
port: 80
targetPort: grafana
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: grafana
namespace: monitoring
spec:
hostnames:
- grafana.internal
parentRefs:
- name: main-gateway
namespace: nginx-gateway
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: kube-prometheus-stack-grafana
port: 80

View File