## Zadanie 1 [3p] ✅ ![image](https://hackmd.io/_uploads/H1CFP7Wr-l.png) ### `Ło japidi` czyli początki bywają najtrudniejsze... Potrzebujemy najpierw mieć grafanę zainstalowaną + prometheusza!! ```bash # Debian/Ubuntu: # https://grafana.com/docs/grafana/latest/setup-grafana/installation/debian/ # sudo systemctl start grafana-server.service # sudo systemctl status grafana-server.service sudo pacman -S grafana sudo systemctl status grafana sudo systemctl start grafana ``` jeśli wszystko dobrze pójdzie to pod http://localhost:3000 zobaczymy login page'a. (Login i hasło to: `admin`) potem po lewej stronie szukamy data source'y i dodajemy nowe connection. ![image](https://hackmd.io/_uploads/S1WSWDbHbe.png) Wybieramy opcję prometheus i wpisujemy odpowiedni adres ![image](https://hackmd.io/_uploads/S1qPZP-rWx.png) Klikamy na dole `save` i powinno być git. Dodajemy sobie nowy dashboard i pamiętamy wybrać w source prometheusza (i mamy cacy setup) ![image](https://hackmd.io/_uploads/Bke9WDZH-l.png) Dobra teraz pora na gówno Robimy wykresiki a) Przygotuj w Grafanie tablicę rozdzielczą z podstawowymi informacjami o komputerze, na którym pracujesz: - Uśrednione zużycie procesora w oknie 1 min. Wybieramy wykresik TimeSeries i wpisujemy jako query ``` avg(rate(node_cpu_seconds_total{mode!="idle"}[1m])) * 100 ``` ![image](https://hackmd.io/_uploads/HkVGKvbBbl.png) tak to powinno wyglądać, po lewej możemy też zaznaczyć by było to w procentach ![image](https://hackmd.io/_uploads/BJ9mFD-BWg.png) - Wskaźnik aktualnego zużycia pamięci RAM. Wybieramy Gauge ``` (1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) * 100 ``` I tyle wsm ![image](https://hackmd.io/_uploads/SyIPFD-S-x.png) - Diagram kołowy zajętej i wolnej przestrzeni dyskowej. Bierzemy Pie chart i wklepujemy te Querki ``` node_filesystem_size_bytes{mountpoint="/"} node_filesystem_free_bytes{mountpoint="/"} ``` ![image](https://hackmd.io/_uploads/HyeiKD-rWe.png) Można się pobawić w legendę by zwracała to co chcemy - Ruch przychodzący i wychodzący z karty sieciowej w postaci liczby wyświetlanej z jednostkami. Bierzemy Stat Zgodnie ze wskazówkę wklejamy ``` rate(node_network_receive_bytes_total[1m]) rate(node_network_transmit_bytes_total[1m]) ``` ![image](https://hackmd.io/_uploads/rJwM5w-SZg.png) Warto pamiętać by zmienić typ wartości na bytes/sec ![image](https://hackmd.io/_uploads/HJIXcPWrbe.png) Koniec końców tak wygląda mój dashboard :DD ![image](https://hackmd.io/_uploads/SJKYcPWHbg.png) :::spoiler ```json { "annotations": { "list": [ { "builtIn": 1, "datasource": { "type": "grafana", "uid": "-- Grafana --" }, "enable": true, "hide": true, "iconColor": "rgba(0, 211, 255, 1)", "name": "Annotations & Alerts", "type": "dashboard" } ] }, "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, "id": 0, "links": [], "panels": [ { "datasource": { "type": "prometheus", "uid": "bf9vgku47670ge" }, "fieldConfig": { "defaults": { "color": { "mode": "thresholds" }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": 0 }, { "color": "red", "value": 80 } ] }, "unit": "decbytes" }, "overrides": [] }, "gridPos": { "h": 9, "w": 16, "x": 0, "y": 0 }, "id": 4, "options": { "colorMode": "value", "graphMode": "area", "justifyMode": "auto", "orientation": "auto", "percentChangeColorMode": "standard", "reduceOptions": { "calcs": [ "lastNotNull" ], "fields": "", "values": false }, "showPercentChange": false, "textMode": "auto", "wideLayout": true }, "pluginVersion": "12.3.1", "targets": [ { "editorMode": "code", "expr": "rate(node_network_receive_bytes_total[1m])", "legendFormat": "__auto", "range": true, "refId": "A" }, { "datasource": { "type": "prometheus", "uid": "bf9vgku47670ge" }, "editorMode": "code", "expr": "rate(node_network_transmit_bytes_total[1m])", "hide": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "B" } ], "title": "Network", "type": "stat" }, { "datasource": { "type": "prometheus", "uid": "bf9vgku47670ge" }, "fieldConfig": { "defaults": { "color": { "mode": "thresholds" }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": 0 }, { "color": "red", "value": 80 } ] }, "unit": "percent" }, "overrides": [] }, "gridPos": { "h": 8, "w": 5, "x": 0, "y": 9 }, "id": 1, "options": { "minVizHeight": 75, "minVizWidth": 75, "orientation": "auto", "reduceOptions": { "calcs": [ "lastNotNull" ], "fields": "", "values": false }, "showThresholdLabels": false, "showThresholdMarkers": true, "sizing": "auto" }, "pluginVersion": "12.3.1", "targets": [ { "datasource": { "type": "prometheus", "uid": "bf9vgku47670ge" }, "editorMode": "code", "expr": "avg(rate(node_cpu_seconds_total{mode!=\"idle\"}[1m])) * 100", "legendFormat": "__auto", "range": true, "refId": "A" } ], "title": "Avg. CPU usage", "type": "gauge" }, { "datasource": { "type": "prometheus", "uid": "bf9vgku47670ge" }, "fieldConfig": { "defaults": { "color": { "mode": "thresholds" }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": 0 }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 6, "x": 5, "y": 9 }, "id": 2, "options": { "minVizHeight": 75, "minVizWidth": 75, "orientation": "auto", "reduceOptions": { "calcs": [ "lastNotNull" ], "fields": "", "values": false }, "showThresholdLabels": false, "showThresholdMarkers": true, "sizing": "auto" }, "pluginVersion": "12.3.1", "targets": [ { "editorMode": "code", "expr": "(1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) * 100", "legendFormat": "__auto", "range": true, "refId": "A" } ], "title": "RAM usage", "type": "gauge" }, { "datasource": { "type": "prometheus", "uid": "bf9vgku47670ge" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "hideFrom": { "legend": false, "tooltip": false, "viz": false } }, "mappings": [] }, "overrides": [] }, "gridPos": { "h": 8, "w": 5, "x": 11, "y": 9 }, "id": 3, "options": { "displayLabels": [], "legend": { "displayMode": "list", "placement": "bottom", "showLegend": true }, "pieType": "pie", "reduceOptions": { "calcs": [ "lastNotNull" ], "fields": "", "values": false }, "sort": "desc", "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.3.1", "targets": [ { "editorMode": "code", "expr": "node_filesystem_free_bytes{mountpoint=\"/\"}\n", "legendFormat": "Free space", "range": true, "refId": "A" }, { "datasource": { "type": "prometheus", "uid": "bf9vgku47670ge" }, "editorMode": "code", "expr": "node_filesystem_size_bytes{mountpoint=\"/\"} ", "hide": false, "instant": false, "legendFormat": "Used space", "range": true, "refId": "B" } ], "title": "Memory", "type": "piechart" } ], "preload": false, "schemaVersion": 42, "tags": [], "templating": { "list": [] }, "time": { "from": "now-5m", "to": "now" }, "timepicker": {}, "timezone": "browser", "title": "Zadanie 1", "uid": "ad4898h", "version": 9 } ``` ::: b) Zademonstruj jak wyeksportować i następnie zaimportować tablicę nawigacyjną Grafany w postaci tekstowej. EEEE nie wiem czy o to chodzi alee wybierając dashboard -> settings -> JSON model mamy nasz dashboard ![image](https://hackmd.io/_uploads/ryKQoD-rbx.png) Jak go skopiujemy i wkleimy jako nowy dashboard to będziemy mieli to samo :DD Można też po wyjściu z trybu edycji kliknąć jebitny przycisk eksport ale to by było za łatwe... I potem się klika dashboard import i podaje plik/kod. c) Pokaż, w jaki sposób bez kopiuj-wklej, w łatwy sposób, zmieniać okno uśredniania zużycia procesora z 1min na 5min i 10min Tworzymy nowe Variable w settings (najlepiej typ custom) ![image](https://hackmd.io/_uploads/rk8znPbrbe.png) i w naszym wykresie podmieniamy (albo najlepiej dodać) nowy source (korzystanie ze zmiennych wsm jak w bashu) ![image](https://hackmd.io/_uploads/S1CnnDZB-g.png) I teraz w lewym górnym rogu możemy sterować jak duże ma być okienko :DD ![image](https://hackmd.io/_uploads/rJ3yavWHZe.png) ## Zadanie 2 [1p] ✅ ![image](https://hackmd.io/_uploads/HkCsDmWS-g.png) Okej to zacznę od `dashboard link`. W skrócie pozwala on przechowywać przedział czasowy oraz zmienne jak skaczemy między dashboardami (da się również ustawić jakbyśmy korzystali z linku). Tworzymy go prosto, w dashboardzie klikamy settings a potem links ![image](https://hackmd.io/_uploads/rkanADbBZx.png) ![image](https://hackmd.io/_uploads/r1JJ1dWr-l.png) Możemy ustawiać tagi itp jakie dashboardy mamy widzieć ale głównie obchodzą nas opcje: - `Include current time range` - `Include current template variable values` Jak to zapiszemy to tak wygląda nasz dashboard ![image](https://hackmd.io/_uploads/S1wX1uZSbe.png) łatwo przetestować, że zmieniając range'a albo zmienną po kliknięciu mamy te same wartości :DD Teraz pora na `panel link`. W panelu po lewej mamy opcje dodania linku ![image](https://hackmd.io/_uploads/SkHJl_WSZl.png) ![image](https://hackmd.io/_uploads/rkyxgdWr-x.png) Wsm podajemy nazwę, link i to ma pozwolić np użytkownikom zostawić jakieś linki jak sobie poradzić z poszczególnymi problemami. ![image](https://hackmd.io/_uploads/HyRWxObSWg.png) Tutaj można je kliknąć Różnice to ee nawigacja między dashboardami vs helper dla użytkownika w jednym z wykresów (tutaj można dać linki do dashboardów innych ale still) ## Zadanie 3 [1p] ✅ ![image](https://hackmd.io/_uploads/rJU2DXWH-l.png) Skrypcioor ```python import os import time import random from prometheus_client import start_http_server, Histogram WRITE_TIME = Histogram( "file_write_duration_seconds", "Czas zapisu pliku na dysk", buckets=(0.001, 0.005, 0.01, 0.05, 0.1, 0.2, 0.5, 1, 2, 5) ) DATA_DIR = "/tmp" os.makedirs(DATA_DIR, exist_ok=True) if __name__ == "__main__": start_http_server(9101) print("Exporter działa na http://localhost:9101/metrics") while True: size = random.randint(1024, 10 * 1024 * 1024) # 1KB – 10MB payload = os.urandom(size) filename = f"{DATA_DIR}/blob_{int(time.time()*1000)}.bin" start = time.perf_counter() with open(filename, "wb") as f: f.write(payload) f.flush() os.fsync(f.fileno()) duration = time.perf_counter() - start WRITE_TIME.observe(duration) time.sleep(0.3) ``` Nie ma co tłumaczyć bo już to robiliśmy. Jedyne co to pamiętajcie dodać do `prometheus.yml` ``` - job_name: "fileWriter" static_configs: - targets: ['localhost:9101'] ``` W grafanie wybieramy heatmape ![image](https://hackmd.io/_uploads/rk5DQdbrZe.png) I chyba trzeba zaznaczyć Calculate from data (wtedy lepiej wygląda imo) Ja bym taką komendę dał, bo ktoś uciął.... `sum(rate(file_write_duration_seconds_bucket[1m])) by (le)` ## Zadanie 4 [2p] ✅ ![image](https://hackmd.io/_uploads/SJAhwXZBWx.png) Ponoć można wymusić na dockerze wystawianie metryk (sic!) dodajemy do `/etc/docker/daemon.json` ``` { "metrics-addr": "localhost:9323" } ``` Szybki restart `sudo systemctl restart docker` i powinniśmy widzieć metryki na `curl http://localhost:9323/metrics` Pamiętajcie dodać do `prometheus.yml` ``` - job_name: "docker_engine" static_configs: - targets: ["localhost:9323"] ``` Jaki dashboard zrobić? Zapytajmy chata!! ![image](https://hackmd.io/_uploads/rJ8ApOZSbl.png) Takie coś razem z nim zrobiłem mega proste rzeczy :pepopray: że nie będzie trzeba tego pokazywać tutaj wszystkie querki jakie mam - `up{job="docker_engine", instance="localhost:9323"}` - `increase(builder_builds_triggered_total{instance="localhost:9323"}[15m])` - `sum by (action) (rate(engine_daemon_container_actions_seconds_count[1m]))` - `sum by (action) (rate(engine_daemon_container_actions_seconds_sum[5m])) / sum by (action) (rate(engine_daemon_container_actions_seconds_count[5m]))` Tutaj jakieś op rzeczy do generowania sztucznie danych `for i in {1..50}; do docker run --rm alpine true; done` ## Zadanie 5 [2p] ![image](https://hackmd.io/_uploads/rk56PQbrZl.png) ![image](https://hackmd.io/_uploads/B1zAwQWrZg.png) Holy shit logi działają!!! Dobra japidi one last time potrzebujemy takie rzeczy jak: loki, promtail, grafana ```bash! sudo pacman -S loki # promtail powininen przyjść z prometheuszem ``` Teraz trzeba zrobić setup lokiego oraz promtaila ```yaml #/etc/loki/loki.yaml auth_enabled: false limits_config: allow_structured_metadata: false reject_old_samples: false server: http_listen_port: 3100 log_level: info common: instance_addr: 127.0.0.1 path_prefix: /tmp/loki replication_factor: 1 ring: kvstore: store: inmemory storage: filesystem: chunks_directory: /tmp/loki/chunks rules_directory: /tmp/loki/rules schema_config: configs: - from: 1995-01-01 store: boltdb-shipper object_store: filesystem schema: v11 index: prefix: index_ period: 24h ``` ```yaml #/etc/loki/promtail.yaml server: http_listen_port: 9080 grpc_listen_port: 0 positions: filename: /tmp/promtail_positions.yaml clients: - url: http://localhost:3100/loki/api/v1/push scrape_configs: - job_name: nasa_http static_configs: - targets: [localhost] labels: job: nasa_http __path__: /var/log/http_logs/*.log pipeline_stages: - regex: expression: '^(?P<ip>\S+) \S+ \S+ \[(?P<ts>[^\]]+)\] "(?P<method>\S+) (?P<path>\S+) \S+" (?P<status>\d{3}) (?P<bytes>\S+)' - timestamp: source: ts format: '02/Jan/2006:15:04:05 -0700' - labels: ip: status: method: ``` Teraz wsm trzeba pobrać logi i wrzucić je do `/var/log/http_logs/*.log` Teraz standardowy setup ```bash! sudo systemctl start loki sudo systemctl start promtail sudo systemctl start grafana ``` Dodajemy jako source loki w grafanie i sanity check to w zakładce explore sprawdźmy czy działa ![image](https://hackmd.io/_uploads/HynIOk4HWx.png) Teraz j*bane dashboardy Pamiętajcie zmienić datę na 1995!! (polecam przedział 01/08/1995 -> 15/08/1995) a) ``` sum(rate({job="nasa_http"}[1m])) ``` b) ``` sum by (status) ( rate({job="nasa_http"}[5m]) ) ``` ## Zadanie 6 [2p] ![image](https://hackmd.io/_uploads/SJ0Aw7ZS-l.png) ehhh nie chce mi się już :C Kodzik w pythonie robiący regresję ```py import time import numpy as np import requests from prometheus_client import start_http_server, Gauge PROM_URL = "http://localhost:9090" STEP_SECONDS = 15 # co ile próbkujemy dane z query_range HISTORY_MINUTES = 30 # ile historii bierzemy do modelu HORIZON_MINUTES = 5 # ile minut do przodu prognozujemy UPDATE_EVERY_SECONDS = 30 # co ile odświeżamy predykcję # Metryka wynikowa (predykcja) pred_cpu = Gauge( "predicted_cpu_percent", "Predykcja zuzycia CPU w procentach (regresja liniowa)", ["horizon"] ) # Ten query ma zwracać JEDNĄ serię (jak masz wiele instancji, wybierz konkretną etykietą instance) CPU_QUERY = '100 * (1 - avg(rate(node_cpu_seconds_total{mode="idle"}[1m])))' def query_range(promql: str, start_ts: float, end_ts: float, step_seconds: int): r = requests.get( f"{PROM_URL}/api/v1/query_range", params={ "query": promql, "start": start_ts, "end": end_ts, "step": step_seconds }, timeout=10 ) r.raise_for_status() data = r.json() if data.get("status") != "success": raise RuntimeError(f"Prometheus status != success: {data}") result = data["data"]["result"] if not result: raise RuntimeError("Brak danych z query_range (result pusty).") # bierzemy pierwszą serię values = result[0]["values"] # [[ts, "value"], ...] ts = np.array([float(v[0]) for v in values], dtype=float) y = np.array([float(v[1]) for v in values], dtype=float) return ts, y def linear_forecast(ts: np.ndarray, y: np.ndarray, horizon_minutes: int): # regresja po czasie; stabilniej centrować czas t0 = ts[0] x = ts - t0 # sekundy od startu okna # dopasuj linię y = a*x + b a, b = np.polyfit(x, y, 1) future_x = (ts[-1] - t0) + horizon_minutes * 60.0 y_hat = a * future_x + b return float(y_hat) if __name__ == "__main__": start_http_server(9102) print("Predictor-exporter działa na http://localhost:9102/metrics") while True: try: end_ts = time.time() start_ts = end_ts - HISTORY_MINUTES * 60.0 ts, y = query_range(CPU_QUERY, start_ts, end_ts, STEP_SECONDS) print(f"ts: {ts}, y:{y}") # jak masz dużo szumu: można uśrednić krocząco (opcjonalnie) # y = np.convolve(y, np.ones(5)/5, mode="same") y_hat = linear_forecast(ts, y, HORIZON_MINUTES) # przytnij do sensownych granic 0..100 (CPU%) y_hat = max(0.0, min(100.0, y_hat)) print(f"y_hat:{y_hat}") pred_cpu.labels(horizon=f"{HORIZON_MINUTES}m").set(y_hat) except Exception as e: # w razie błędu nie wywalaj procesu – tylko log print("Błąd predykcji:", e) time.sleep(UPDATE_EVERY_SECONDS) ``` Nic nie ma ciekawego wsm Dodajemy takie fiku miku do prometheusza ``` - job_name: "predictor" static_configs: - targets: ["localhost:9102"] ``` `100 * (1 - avg(rate(node_cpu_seconds_total{mode="idle"}[1m])))` `predicted_cpu_percent{horizon="5m"} offset 5m` Nowy keyword! `offset x` pozwala nam cofnąć czas o `x` jednostek!! ## Zadanie 7 [3p]✅ ![image](https://hackmd.io/_uploads/H1Lydm-H-l.png) japidi Zacznijmy od tego, że warto pobrać i zainstalować toole: 1. https://github.com/MacroPower/prometheus_video_renderer 2. https://github.com/MacroPower/grafana-image-renderer-sdk-go Oraz pobrać jakiś filmik np. https://www.youtube.com/watch?v=fC7oUOUEEi4 Teraz w `prometheus_video_renderer` ```bash! mkdir -p frames/stick ffmpeg -i 'stick.mp4' -vf 'scale=180:100' -vsync 0 'frames/stick/out%06d.png' mkdir -p metrics/stick export DATA=$(date +%s000) go run ./cmd/prometheus_video_renderer --mode=rgb --project=stick --frames-location=frames --metrics-location=metrics --frames-per-file=120 --frame-duration=5m --scrape-interval=1 --start-time=$DATA bash scripts/load.sh stick sudo chmod 777 data docker compose up -d ``` Teraz w `grafana_image_renderer_sdk_go` ```bash! export FRAME_COUNT=$(ls ../prometheus_video_renderer/frames/stick | wc -l) export OUT_DIR=../prometheus_video_renderer/frames_capture/stick mkdir -p "$OUT_DIR" go run ./cmd/grafana-image-renderer-cli sequence \ --api-url=http://localhost:3000 \ --api-key-or-basic-auth=admin:admin \ --dashboard=pvr-dash-8-rgb \ --panel=2 \ --start-time=$DATA \ --frame-interval=5m \ --frames=1-$FRAME_COUNT \ --width=1800 --height=1000 \ --out-directory="$OUT_DIR" ffmpeg -framerate 30 -i "$OUT_DIR/%06d.png" -c:v libx264 -pix_fmt yuv420p stick-render.mp4 ``` I w ten sposób powstaje nam plik `stick-render.mp4`, który ma nagranie naszego filmiku z grafany:D Jano niby mówił, że można animować w grafanie, ja nic takiego nie znalazłem poza tym wyżej (przynajmniej dla danych). Można niby zrobić tak, że po tym exporcie pokazać, że te dane faktycznie są lokalnie na grafanie w stylu od `2026-01-11 22:46:00` do jakiegoś czasu co 5min, ale kurde ja nie widzę łatwej metody na pokazanie tego filmiku inaczej:c