33 - aktualizacja kube-prometheus-stack i stabilizacja Grafany¶
Data: 2026-09-29
Środowisko końcowe: 3× HP T630, Ubuntu 24.04.5 LTS, K3s v1.36.4+k3s1, Cilium 1.20.2, Longhorn 1.12.1, Flux v2.9.5, kube-prometheus-stack chart 91.8.1, Prometheus Operator v0.94.1, Prometheus v3.15.0, Alertmanager v0.34.1, Grafana 13.2.2
Cel sesji¶
- Potwierdzić kolejny poprawny zimny start klastra po aktualizacji K3s.
- Zamknąć problem niepełnej macierzy połączeń API server → kubelet.
- Zinwentaryzować instalację kube-prometheus-stack oraz sposób aktualizacji CRD.
- Zaktualizować chart etapami z
82.10.1do91.8.1. - Walidować po każdej wersji Prometheusa, Alertmanagera, Grafanę, kube-state-metrics, node-exporter i operator.
- Usunąć przyczyny przejściowej niedostępności Grafany i alertów
CPUThrottlingHigh. - Przeprowadzić migrację uwierzytelniania ServiceMonitor wymaganą przez chart
90.x. - Potwierdzić działanie zaostrzonego RBAC Prometheus Operatora w chartcie
91.x.
1. Zimny start klastra¶
Playbook potwierdził:
- prawidłową walidację pełnego inventory;
- wysłanie WoL do wszystkich trzech hostów;
- poprawny cykl aktywacji i dezaktywacji interfejsu WoL na
logos; - dostępność SSH na
master,worker1iworker2; - łączność Ansible i działające nieinteraktywne
sudo; - aktywną usługę K3s na wszystkich nodach;
- lokalne API i embedded etcd
3/3; - dokładny skład klastra;
- wszystkie trzy nody
Ready; - macierz API server → kubelet
9/9; - brak użycia resyncu remotedialera.
K3s potrzebował kilku ponowień podczas oczekiwania na stan active. Wszystkie trzy usługi uruchomiły się w dopuszczalnym czasie, a końcowy wynik playbooka nie zawierał błędów.
Był to kolejny pełny zimny start z macierzą 9/9. Problem niepełnych tuneli remotedialera można uznać za rozwiązany przez aktualizację K3s do v1.36.4+k3s1. Skrypt resync pozostaje narzędziem diagnostycznym i nie musiał ingerować w start klastra.
2. Stan początkowy kube-prometheus-stack¶
Przed aktualizacją działający release miał stan:
Helm release: kube-prometheus-stack
namespace: monitoring
revision: 4
chart: 82.10.1
app version: v0.89.0
status: deployed
Instalacja była zarządzana przez Flux za pomocą:
Wartości obejmowały:
- Prometheus z retencją
7d; - PVC Prometheusa
10Giw StorageClasslonghorn; - jedną replikę Prometheusa i Alertmanagera;
- Grafanę z dashboardami dostarczanymi przez sidecar;
- kube-state-metrics;
- node-exporter jako DaemonSet na trzech nodach;
- monitoring kubeleta przez HTTPS;
- wyłączony kube-proxy, ponieważ jego funkcję realizuje Cilium;
- wyłączony monitoring kube-controller-manager, kube-scheduler i etcd;
- automatyczny hook aktualizujący CRD:
CRD nie były aktualizowane ręcznie. Helm uruchamiał Job kube-prometheus-stack-crds-upgrade przed każdą aktualizacją.
3. Strategia aktualizacji¶
Chart został zaktualizowany kolejno przez wszystkie główne wersje:
| Etap | Chart | Prometheus Operator | Prometheus | Alertmanager | Grafana |
|---|---|---|---|---|---|
| 0 | 82.10.1 |
v0.89.0 |
stan początkowy | stan początkowy | stan początkowy |
| 1 | 83.7.0 |
v0.90.1 |
zgodny z chartem | zgodny z chartem | zgodna z chartem |
| 2 | 84.5.0 |
v0.90.1 |
zgodny z chartem | zgodny z chartem | 13.0.1 |
| 3 | 85.4.0 |
v0.90.1 |
v3.11.3-distroless |
v0.32.1 |
13.0.1-security-01 |
| 4 | 86.3.2 |
v0.91.0 |
v3.12.0-distroless |
v0.33.0 |
13.0.2 |
| 5 | 87.21.0 |
v0.92.1 |
v3.13.1-distroless |
v0.33.1 |
13.1.1 |
| 6 | 88.6.5 |
v0.93.1 |
v3.14.0-distroless |
v0.34.0 |
13.2.0 |
| 7 | 89.2.4 |
v0.93.1 |
v3.14.0-distroless |
v0.34.0 |
13.2.1-distroless |
| 8 | 90.2.0 |
v0.93.1 |
v3.14.0-distroless |
v0.34.0 |
13.2.1-distroless |
| 9 | 91.8.1 |
v0.94.1 |
v3.15.0-distroless |
v0.34.1 |
13.2.2-distroless |
Każdy etap został:
- sprawdzony w oficjalnym
UPGRADE.md; - wyrenderowany lokalnie z wartościami z HelmRelease;
- skontrolowany pod kątem obrazów, hooków i zmian zasobów;
- wdrożony przez commit i Flux;
- zakończony sprawdzeniem rolloutów, Podów, logów, targetów i reguł.
Nie wykonywano bezpośredniego helm upgrade ani ręcznego kubectl apply na zasobach zarządzanych przez Flux.
4. Aktualizacje 83.x–86.x¶
Aktualizacje do 83.7.0, 84.5.0, 85.4.0 i 86.3.2 zakończyły się poprawnie.
Po każdym etapie potwierdzano rollout:
deployment/kube-prometheus-stack-operator
deployment/kube-prometheus-stack-grafana
deployment/kube-prometheus-stack-kube-state-metrics
daemonset/kube-prometheus-stack-prometheus-node-exporter
statefulset/prometheus-kube-prometheus-stack-prometheus
statefulset/alertmanager-kube-prometheus-stack-alertmanager
Sprawdzano także:
- stan Prometheus i Alertmanager CR;
- endpoint kube-state-metrics;
- metrykę
up{job="kube-state-metrics"}; - obecność metryki
kube_resourcequota; - stan reguł przez
/api/v1/rules; - logi Prometheusa pod kątem
many-to-manyi błędów ewaluacji; - zdrowie API Grafany;
- stan wolumenu Prometheusa w Longhornie.
Wolumen Prometheusa zachował tożsamość:
PVC: prometheus-kube-prometheus-stack-prometheus-db-prometheus-kube-prometheus-stack-prometheus-0
PV: pvc-e3b84907-a0e1-4843-b522-02da79e01a75
Longhorn raportował wolumen jako attached i healthy.
5. Przejściowe podwójne targety po rolloutach¶
Podczas kolejnych rolloutów Prometheus chwilowo widział stare i nowe Pody kube-state-metrics oraz node-exportera.
Przykładowo zapytania zwróciły:
Nie oznaczało to utworzenia dodatkowych stałych replik. W czasie rolling update stare Pody nadal istniały w TSDB i przez krótki czas pozostawały aktywnymi targetami. Po zakończeniu rolloutów EndpointSlice zawierał wyłącznie nowy gotowy endpoint, a liczba targetów wróciła do oczekiwanej wartości.
6. Niedostępność Grafany po aktualizacji 87.21.0¶
Po wdrożeniu chartu 87.21.0 Grafana okresowo zwracała przez Traefik:
Wszystkie dashboardy traciły jednocześnie dostęp, a po odświeżeniu GUI chwilowo wracały. EndpointSlice Grafany przechodził między stanem gotowym i niegotowym, mimo braku restartu kontenera.
Początkowe zasoby Grafany wynosiły:
Pomiary historyczne wykazały:
Grafana jednocześnie osiągała limit CPU i prawie cały limit pamięci. Readiness probe /api/health nie odpowiadał w wymaganym czasie, dlatego Kubernetes usuwał Pod z EndpointSlice. Traefik nie miał wtedy gotowego backendu i zwracał no available server.
Logi zawierały między innymi:
database is locked (SQLITE_BUSY)
context canceled
failed to fetch dashboards
status=500 /api/annotations
Nie była to awaria Prometheusa ani pojedynczego dashboardu. Problem dotyczył procesu Grafany i jego zasobów.
7. Zmiana zasobów Grafany¶
Zasoby zwiększono najpierw do:
CPU throttling Grafany spadł do około 3.2%, a dashboardy zaczęły działać stabilnie. Podczas dalszych aktualizacji Grafana 13.2.0 osiągała jednak ponad 1Gi pamięci:
Ostateczna konfiguracja otrzymała:
grafana:
resources:
requests:
cpu: 100m
memory: 512Mi
limits:
cpu: 500m
memory: 1280Mi
env:
GOMEMLIMIT: 900MiB
GOMEMLIMIT=900MiB wymusza wcześniejszą pracę garbage collectora Go, pozostawiając zapas między celem sterty a limitem cgroup 1280Mi.
Po zmianie:
- Grafana pozostawała
Ready; - liczba restartów wynosiła
0; - EndpointSlice pozostawał gotowy;
- dashboardy działały;
- nie pojawiało się
no available server; - nie występowały OOMKill ani trwałe timeouty.
W końcowym stanie Grafana używała około 735Mi pamięci przy limicie 1280Mi.
8. CPU throttling node-exportera¶
Po aktualizacji chartu alert CPUThrottlingHigh dotyczył wszystkich trzech Podów node-exportera.
Początkowa konfiguracja:
Prometheus raportował throttling około:
Zwiększenie limitu CPU do 250m nie usunęło alertu. Node-exporter wykonuje krótkie, gwałtowne serie pracy podczas scrape, dlatego liczba okresów ograniczonych przez CFS nadal była wysoka mimo małego średniego użycia CPU.
Limit CPU został usunięty, pozostawiając request i limit pamięci:
Po rolloutcie nowych Podów zapytanie:
zwróciło pusty wynik.
Usunięcie limitu CPU nie zmienia rezerwacji schedulera. Node-exporter nadal rezerwuje 10m, ale nie podlega sztucznemu ograniczeniu CFS podczas krótkich skoków pracy.
9. Grafana 13.2.1-distroless i read-only root filesystem¶
Chart 89.2.4 wprowadził:
Render potwierdził dodatkowy zapisywalny wolumen emptyDir zamontowany pod /tmp. Pod uruchomił się poprawnie, ale logi zawierały błędy:
Background Plugin Installer próbował aktualizować wbudowane pluginy w:
Ścieżka znajduje się w read-only root filesystem obrazu distroless. Ponieważ nie instalujemy dodatkowych pluginów w czasie startu, automatyczny instalator został wyłączony:
W końcowym renderze Grafany znajdowało się:
Po rolloutcie logi nie zawierały już błędów instalatora pluginów ani filesystemu. Grafana zachowała read-only root filesystem.
10. Migracja ServiceMonitor w chartcie 90.2.0¶
Chart 90.x usunął pola odwołujące się do plików z tokenem i CA znajdujących się w filesystemie Prometheusa. Dotychczasowa wartość:
została zastąpiona przez:
Jawnie włączono utworzenie tokenu ServiceAccount:
Chart utworzył:
Secret: kube-prometheus-stack-prometheus-token
type: kubernetes.io/service-account-token
serviceAccount: kube-prometheus-stack-prometheus
Secret zawierał token, CA oraz namespace. Wartość tokenu nie została wyświetlona ani zapisana w repozytorium. Repo zawiera wyłącznie deklarację utworzenia Secretu. Sam token został wygenerowany przez Kubernetes i jest przechowywany w Secret oraz etcd.
Wyrenderowany i żywy ServiceMonitor kubeleta używał:
authorization:
type: Bearer
credentials:
name: kube-prometheus-stack-prometheus-token
key: token
tlsConfig:
ca:
configMap:
name: kube-root-ca.crt
key: ca.crt
insecureSkipVerify: true
Po aktualizacji aktywne targety obejmowały:
Dla każdego noda kubelet udostępniał trzy pule scrape:
Wszystkie targety miały pusty lastError. Nie wystąpiły odpowiedzi 401 ani 403.
11. Aktualizacja do 91.8.1 i zaostrzony RBAC¶
Chart 91.x zaktualizował Prometheus Operator do v0.94.1. Nowy ClusterRole nie używa wildcardu "*" w verbs, lecz jawnie wymienia potrzebne operacje.
Przed wdrożeniem sprawdzono render RBAC. Operator zachował dostęp do wymaganych zasobów:
- obiektów API
monitoring.coreos.com; - ich subresource
statusifinalizers; - StatefulSetów;
- ConfigMap i Secretów;
- Podów, Service, Endpoint i EndpointSlice;
- Node, Namespace i Event;
- Ingress i StorageClass.
Hook CRD miał konfigurację:
name: kube-prometheus-stack-crds-upgrade
hook: pre-install,pre-upgrade,pre-rollback
hook-weight: 5
delete-policy: before-hook-creation,hook-succeeded
image: registry.k8s.io/kubectl:v1.36.4
Job zakończył się stanem Completed, a Flux zgłosił UpgradeSucceeded dla rewizji Helm 19.
Po rolloutcie logi operatora nie zawierały:
Zaostrzenie RBAC nie spowodowało regresji w tym klastrze.
12. Stan CRD¶
Wszystkie CRD z grupy monitoring.coreos.com miały:
Wersje przechowywane w etcd:
| CRD | storedVersions |
|---|---|
alertmanagerconfigs.monitoring.coreos.com |
v1alpha1 |
alertmanagers.monitoring.coreos.com |
v1 |
podmonitors.monitoring.coreos.com |
v1 |
probes.monitoring.coreos.com |
v1 |
prometheusagents.monitoring.coreos.com |
v1alpha1 |
prometheuses.monitoring.coreos.com |
v1 |
prometheusrules.monitoring.coreos.com |
v1 |
scrapeconfigs.monitoring.coreos.com |
v1alpha1 |
servicemonitors.monitoring.coreos.com |
v1 |
thanosrulers.monitoring.coreos.com |
v1 |
v1alpha1 dla AlertmanagerConfig, PrometheusAgent i ScrapeConfig jest prawidłowym stanem tych API. Nie oznacza niedokończonej migracji i nie wymaga ręcznej modyfikacji status.storedVersions.
13. Końcowa walidacja Prometheusa i Grafany¶
Końcowy release:
Helm revision: 19
chart: kube-prometheus-stack-91.8.1
app version: v0.94.1
status: deployed
Flux: Ready=True
Prometheus:
Alertmanager:
Pozostałe obrazy:
Prometheus Operator: v0.94.1
Grafana: 13.2.2-distroless
Grafana sidecar: 2.11.2
kube-state-metrics: v2.20.0
node-exporter: v1.12.1-distroless
Grafana:
Bezpośrednio po starcie pojawiło się kilka krótkich retry SQLite:
Nie towarzyszyły im błędy HTTP 500, utrata gotowości ani restarty. Kolejna kontrola logów z ostatnich dwóch minut zwróciła:
Globalne kontrole Prometheusa zwróciły:
14. Stan końcowy¶
K3s¶
- trzy nody
Ready; - K3s
v1.36.4+k3s1; - lokalne API i embedded etcd
3/3; - macierz API server → kubelet
9/9; - zimny start bez użycia remotedialer resync;
- problem niepełnej macierzy po zimnym starcie uznany za rozwiązany.
kube-prometheus-stack¶
- chart
91.8.1; - Helm revision
19; - Flux
Ready=True; - wszystkie workloady zakończyły rollout;
- Prometheus
v3.15.0-distroless; - Alertmanager
v0.34.1; - Prometheus Operator
v0.94.1; - Grafana
13.2.2-distroless; - kube-state-metrics
v2.20.0; - node-exporter
v1.12.1-distroless; - wszystkie targety
up; - wszystkie reguły zdrowe;
- brak błędów RBAC operatora.
Grafana¶
- stabilny EndpointSlice;
- brak
no available serverpo korekcie zasobów; - brak restartów i OOMKill;
- read-only root filesystem zachowany;
- automatyczny instalator pluginów wyłączony;
GOMEMLIMIT=900MiB;- limit pamięci
1280Mi; - przejściowe blokady SQLite ograniczone do krótkich retry podczas startu.
Storage i sekrety¶
- PVC Prometheusa pozostał na Longhornie;
- wolumen był
attachedihealthypodczas aktualizacji; - token ServiceAccount Prometheusa istnieje wyłącznie jako Kubernetes Secret;
- repozytorium zawiera tylko deklarację
createTokenSecret: true; - wartość tokenu nie trafiła do Git.
15. Wnioski¶
- Duży skok chartu należy dzielić na kolejne wersje główne, nawet gdy wersja aplikacji przez kilka etapów się nie zmienia.
HelmRelease Ready=Truenie wystarcza do zaliczenia aktualizacji systemu monitoringu. Trzeba sprawdzić targety, reguły, logi, API Grafany i storage.- Render manifestów przed wdrożeniem wykrywa zmiany obrazów, RBAC, hooków, securityContext i usuniętych wartości.
- Hook aktualizacji CRD jest konieczny, ponieważ standardowy upgrade Helm nie aktualizuje automatycznie zawartości katalogu
crds/. no available serverw Traefiku może oznaczać utratę gotowości jedynego backendu, a nie awarię samego reverse proxy.- Limit CPU
200mi pamięci512Mibył zbyt niski dla Grafany 13 w tym środowisku. GOMEMLIMITpowinien pozostawiać zapas względem limitu pamięci cgroup.- CPU limit node-exportera powodował wysoki CFS throttling mimo małego średniego użycia CPU. Usunięcie limitu było lepsze niż dalsze zwiększanie go.
- Obrazy distroless i read-only root filesystem wymagają sprawdzenia pluginów, initContainerów, poleceń shellowych i zapisywalnych mountów.
- Background Plugin Installer nie powinien modyfikować wbudowanych pluginów na read-only filesystem. Wyłączenie go usunęło trwałe błędy bez osłabiania securityContext.
- Migracja chartu
90.xzmieniła sposób uwierzytelniania ServiceMonitor. Po zmianie trzeba sprawdzić wszystkie targety pod kątem401,403i brakującego Secretu. - Długowieczny token ServiceAccount jest przechowywany w Kubernetes Secret i etcd. Do Git trafia jedynie deklaracja jego utworzenia.
storedVersions: v1alpha1nie zawsze oznacza dług techniczny. Dla części CRD Prometheus Operatora pozostaje to prawidłowa wersja storage.- Zaostrzenie RBAC należy weryfikować przez logi operatora i stan zarządzanych CR, a nie tylko przez obecność nowego ClusterRole.
- Przejściowe
SQLITE_BUSYpodczas startu nie oznacza awarii, jeśli retry kończy się powodzeniem,/api/healthzwracadatabase=ok, a kolejne logi są czyste.
16. Następna sesja¶
- Po zimnym starcie potwierdzić:
3/3 Node Ready
lokalne API i etcd 3/3
API server → kubelet 9/9
brak użycia remotedialer resync
Prometheus PVC attached/healthy
Prometheus i Alertmanager reconciled/available
wszystkie targety Prometheusa up
wszystkie reguły health=ok
Grafana bez no available server
Grafana bez trwałych SQLITE_BUSY i timeoutów
Prometheus Operator bez błędów RBAC
-
Jeżeli test przejdzie, zamknąć aktualizację kube-prometheus-stack
82.10.1 → 91.8.1. -
Następny komponent wybrać dopiero po zamknięciu testu regresji całego monitoringu.