# Observability (ClickStack / HyperDX) — ohmu **Loodud 2026-09-25.** ClickStack = HyperDX UI + (väline) ClickHouse + kaasas olev OTel-kollektor. Kogu grupp on **always-on** (ei ole enam `experimental`). ## Ligipääs - UI: **http://100.87.1.24:8124/clickstack/search** (läbi `clickstack-proxy`) - UI otse: http://100.87.1.24:18080 - Kasutaja: `admin@odamus.com` / `Dev_clickhouse_2026!` - ClickHouse (väline, `clickhouse` konteiner): HTTP 8125, native 9004 (Tailscale-only) ## Arhitektuur (andmevoog) ``` node-exporter ─┐ cadvisor ──────┼─► otel-collector (scrape 60s) ─► clickstack:4317 (kaasas kollektor) ─► ClickHouse (default.otel_*) llama.cpp ─────┘ (main_ai_network) (opampsupervisor) ``` - `otel-collector` (contrib) skrape'ib node-exporter, cadvisor ja **llama.cpp** (`host.docker.internal:8070/metrics`). - Kaasas olev kollektor (clickstack konteineris) võtab configu API-lt (opamp, port 4320) ja kirjutab ClickHouse'i `otel_*` tabelitesse. - **Kriitiline:** `clickstack-entry.sh` peab käivitama `/otel-entrypoint.sh /usr/local/bin/opampsupervisor` — muidu OTLP andmed ei püsi. ## ClickHouse tabelid (default) `otel_logs`, `otel_traces`, `otel_metrics_gauge|sum|histogram|exponential_histogram|summary` + rollupid. Skeem luuakse automaatselt schema-seed'iga (goose). ## Konfiguratsioon | Fail | Sisu | |---|---| | `dev/docker-compose.yml` | `clickstack`, `clickstack-proxy`, `node-exporter`, `cadvisor`, `otel-collector` (always-on) | | `dev/config/otel-collector/config.yaml` | prometheus receiver (node/cadvisor/llamacpp) + regexp-filter + OTLP eksport | | `dev/clickstack-entry.sh` | mongod + **OTel-kollektor** + HyperDX app | | `dev/.env` | `HYPERDX_INGEST_KEY` (gitignore'itud) — kollektori OTLP auth | ### ClickStack env (olulised) - `CLICKHOUSE_ENDPOINT=tcp://clickhouse:9000?dial_timeout=10s` - `CLICKHOUSE_USER=default`, `CLICKHOUSE_PASSWORD=dev_clickhouse_2026` - `HYPERDX_OTEL_EXPORTER_CLICKHOUSE_DATABASE=default` - `DEFAULT_CONNECTIONS=[{"name":"Local ClickHouse","host":"http://clickhouse:8123","username":"default","password":"dev_clickhouse_2026"}]` ## ClickHouse ühendus (HyperDX Connections) — ÕIGE seadistus Ühendusel peavad olema **eraldi väljad** `username`/`password` ja `host` **ilma userinfo'ta** (`http://clickhouse:8123`). Siis lisab CH-proxy `X-ClickHouse-User`/`Key` päised. > ❗ **Local mode peab olema VÄLJAS:** `NEXT_PUBLIC_IS_LOCAL_MODE=false`. > Koos välise (mitte-image'sisese) ClickHouse'iga põhjustab `true` selle, et brauser > kasutab lokaalset ühendust (sessionStorage/`__ENV.js`) ja saadab ise `Authorization`/ > `user=` → proxy lisab omakorda `X-ClickHouse-*` → **Code 516** või `UNKNOWN_USER`. > Väärtus injekteeritakse **runtime'is** `public/__ENV.js` kaudu → piisab > `docker compose up -d --force-recreate clickstack` (kontroll: `cat .../public/__ENV.js`). > ❗ Varasem soovitus hoida `username`/`password` tühjad ja panna tunnused > host-URL-i (`http://default:parool@clickhouse:8123`) **ei tööta**: `http-proxy-middleware` > ei edasta host-URL-i parooli → CH saab kasutaja `default` tühja parooliga → > „Authentication failed: password is incorrect". (Kinnitatud `tcpdump -i any 'tcp port 8123'`-ga.) Mongo otsene parandus: ```javascript db.connections.updateOne({_id: ObjectId("")}, {$set:{host:"http://clickhouse:8123", username:"default", password:"dev_clickhouse_2026"}}); ``` > Pärast otsest mongo-muudatust **taaskäivita `clickstack`** (muidu jääb vahemällu): > `docker restart clickstack`. **Code 516 hoiatus:** kui ühendusel on `username` JA päring sisaldab `user=` query-param, tuleb `Code: 516 ... X-ClickHouse HTTP headers and authentication via parameters simultaneously`. Seetõttu ei tohi ühendust anda browseri CH-kliendile koos username'iga; HyperDX metadataklient (`W({queryTimeout})`) ehitab kliendi **ilma** username'ita → OK. ## Logide kogumine (filelog) `otel-collector` loeb Dockeri konteineriloge: - `receivers.filelog`: `include: /var/lib/docker/containers/*/*-json.log`, `container` parser, `start_at: end` - `service.pipelines.logs`: `[filelog] → [memory_limiter, batch] → [otlp]` - Compose: `otel-collector` vajab **mounti** `/var/lib/docker/containers:/var/lib/docker/containers:ro` **ja `user: "0:0"`** (mitte-root kasutaja → „open .: permission denied"). - Kontroll: `SELECT count(), max(Timestamp) FROM default.otel_logs`. ## Metrikad (praegu) - **Host:** RAM (available/total), swap, load, CPU, failisüsteem, võrk (node-exporter). - **Konteinerid:** CPU, mälu (working set/usage), võrk (cadvisor). - **Mudel:** llama.cpp — prompt/ predicted tokens, requests_processing, kv_cache (`--metrics`). ## Dashboardid (retseptid) HyperDX UI-s: **Dashboards → New dashboard → Add tile**. Kasuta allolevaid metrikaid (Metrics source) või "SQL" diagrammi. Ajatelje jaoks sobib `TimeUnix`. | Paneel | Mõõdik / SQL | |---|---| | RAM vaba | `node_memory_MemAvailable_bytes` (gauge) | | Swap vaba | `node_memory_SwapFree_bytes` (gauge) | | Load | `node_load1` (gauge) | | Konteineri mälu | `container_memory_working_set_bytes` (group: `Attributes['name']`) | | Konteineri CPU | `container_cpu_usage_seconds_total` (rate) | | **Llama tokeneid/s** | `rate(llamacpp:tokens_predicted_total)` või pika aja trend | | **Llama päringud** | `llamacpp:requests_processing` (gauge), `llamacpp:tokens_predicted_total` (sum) | | **Llama KV-cache** | `llamacpp:kv_cache_usage_ratio` (gauge) | | **Teenus üleval** | `ohmu_service_up` (group: `Attributes['service']`) | | **Restardid** | `ohmu_container_restarts` (group: `Attributes['name']`) | Näidis-SQL (ClickHouse): ```sql -- Teenuste seis viimase 10 min jooksul SELECT toStartOfMinute(TimeUnix) AS t, Attributes['service'] AS service, avg(Value) AS up FROM default.otel_metrics_gauge WHERE MetricName = 'ohmu_service_up' AND TimeUnix > now() - INTERVAL 10 MINUTE GROUP BY t, service ORDER BY t; ``` ## Teadaolevad UI-nõksud ### Metrika-paneeli `select` skeem (HyperDX 2.37) Metrika-diagramm ja alert nõuavad, et `select[]` elemendil oleks: `{metricName, metricType, aggFn, isDelta, filters: [], valueExpression: "Value"}`, kus `metricType` ∈ `gauge|sum|histogram|summary|exponential histogram`. Vana `aggCondition`-ainult kuju annab **`no query support for metric type=undefined`**. `valueExpression` on vajalik, sest alerti config lisab `variables` → macro-töötlus loeb `select[].valueExpression` (puudumisel `TypeError: Cannot read properties of undefined (reading 'length')`). `metricType` määra ClickHouse'i tabeli järgi (`otel_metrics_gauge|sum|histogram|...`): nt `llamacpp:tokens_predicted_total` → `sum`, `node_load1`/`ohmu_service_up` → `gauge`. ### Alert nõuab number/line/stacked bar paneeli Alert **ei toeta `table`-paneeli** (`Unsupported alert source: tile`). Kasuta `number`/`line`/`stacked bar` paneeli. Pärast alerti/paneeli otsest mongo-muudatust **taaskäivita `clickstack`** (vahemälu). ### Chart Explorer: `Syntax error ... toString()` (tühi valueExpression + count) Sama juur kui heatmap'il allpool: min/max päring kasutab tühja `valueExpression`-i. Vaikimisi seeria `{aggFn:"count", valueExpression:""}` → `toFloat64OrDefault(toString())` → `Code: 62 SYNTAX_ERROR ... position 58 (>=)`. **Lahendus:** kas anna **Value**-le arvuline veerg (nt `SeverityNumber`) või vaheta kuva **`table`**-ks (min/max päringut ei tehta). `Value = 1` väldib küll vea, aga annab skaala 1..1 (ei sobi graafikule). Upstream viga. ### Heatmap + y-telg: tühi `valueExpression` → `Syntax error ... toString()` HyperDX 2.37 arvutab heatmap'i/joongraafiku min/max **tooresest** `select[0].valueExpression`-ist (app-chunk `4719-*`, funktsioon `A`): `quantileIf(0.01)(toFloat64OrDefault(toString()), >= 0 ...)`. Kui `expr` on tühi (vaikimisi count-seeria), tekib `toString())` + ` >= 0` → **Code: 62 SYNTAX_ERROR**. Sama kehtib heatmap'i „Low/High" legendile. See on **upstream viga**, mitte andmeviga. **Kontrollitud lahendused (Logs `otel_logs`):** | Value expression | Tulemus | |---|---| | `""` (vaikimisi) | ❌ süntaksiviga | | `1` | ✅ töötab, aga skaala 1..1 (flat — ei sobi) | | `SeverityNumber` / `TraceFlags` | ✅ töötab, sisuline skaala | | `count()` | ❌ agregaadi pesastamine (`quantileIf(count())`) — keelatud | **Soovitus:** heatmap (ja y-telg) vajab **arvulist veergu**, mitte count'i: - **Logs** source: Value = `SeverityNumber` (või `TraceFlags`), „Group by" nt `ServiceName`. - **Metrics** source: Value = `Value` (või `avg(Value)`), filter `MetricName`. - Puhta count-heatmap'i jaoks kasuta mõnda muud kuva (nt `table`) kuni upstream-paranduseni. - Kui metrika valimine ei täida Value välja automaatselt, trüki käsitsi. > **PATCH (meil rakendatud):** bind-mount'itud **kaks** chunki: > `dev/patches/4719-70b065addcfc053d.js` (jagatud, dashboardid) ja > `dev/patches/pages/clickhouse-46029ebbad38b40a.js` (**`/chart` leht**; funktsioonid `N`+`F`). > Patch teeb tühja `valueExpression`'i korral count-põhise min/max'i **ja** bucket-päringu, > seega **count-heatmap/graafik töötab ilma Value't täitmata**. > ⚠️ Chunkid on `Cache-Control: immutable` → **vaja hard-refresh** (Ctrl+Shift+R). > ⚠️ Patch on seotud **chunk-hashiga** (HyperDX 2.37.0, image `...all-in-one:latest`). > Image'i uuendusel: võrdle uut chunk-nime ja kohanda patch + mount. > Kontroll: `curl .../chunks/pages/clickhouse-*.js | grep 'r||!a'`. ### Service Map: trace-instrumentatsioon (TEHTUD 2026-09-27) Service Map on **trace-põhine**. Kui URL-is on `?source=`, näitab UI hoiatust „... is a log source, which this page can't show" — vali päises **Traces** või ava `http://100.87.1.24:18080/service-map?source=6a95600db1eca667454bec7e`. **Instrumenteeritud** (OTel auto-instrumentation, `opentelemetry-distro` + OTLP gRPC): - `nutikodu-web` (FastAPI/uvicorn) — Server + Client spanid (ClickHouse HTTP) - `weather-collector`, `zigbee-collector` — Client spanid (Open-Meteo, ClickHouse, MQTT) Andmevoog: app `OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4317` → `otel-collector` (`otlp` receiver → `traces` pipeline) → `clickstack:4317` → `default.otel_traces`. Dockerfile'ides: `pip install opentelemetry-distro opentelemetry-exporter-otlp-proto-grpc && opentelemetry-bootstrap -a install`, `CMD ["opentelemetry-instrument", ...]`. Kontroll: `SELECT ServiceName, SpanKind, count() FROM default.otel_traces GROUP BY 1,2`. ### Metrics source peab sisaldama `metricTables.exponentialHistogram` Vaikimisi bootstrap võib selle puudumisel metrikate valija katki teha. Kontroll: ```bash docker exec clickstack mongo --quiet hyperdx --eval \ 'printjson(db.sources.findOne({kind:"metric"}).metricTables)' # vajadusel: db.sources.updateOne({kind:"metric"},{$set:{"metricTables.exponentialHistogram":"otel_metrics_exponential_histogram"}}) ``` ## Näidis-dashboardid Loodud 2026-09-25 skriptiga **`~/scripts/create_hyperdx_dashboards.py`**: - **„Ohmu — süsteem (host)"** — RAM vaba, swap vaba, load1, konteinerite mälu (host metrikad) - **„Ohmu — mudelid (llama.cpp)"** — genereeritud/prompt-tokenid, aktiivsed päringud, KV-cache - **„Ohmu — teenused ja konteinerid"** — `ohmu_service_up`, `ohmu_container_restarts` Kordamine: `python3 ~/scripts/create_hyperdx_dashboards.py` (`--delete-tests` koristab testid). **API ligipääs:** HyperDX väline API v2 on **`http://:8000/api/v2`** (hostilt konteineri IP kaudu; `clickstack-proxy` ribab `/api` prefiksi, seega `:18080` kaudu ei tööta). Access key: `docker exec clickstack mongo --quiet hyperdx --eval 'print(db.users.findOne({email:"admin@odamus.com"}).accessKey)'`. **Tile-vorming (API):** ```json {"name":"paneel","x":0,"y":0,"w":6,"h":6,"config":{ "displayType":"line","sourceId":"", "select":[{"valueExpression":"Value","aggFn":"avg","where":"MetricName:\"node_load1\""}], "groupBy":"name"}} ``` ## Alertid (Telegram) Rakendatud: **HyperDX webhook → `alert-relay` → Telegram**. - **`alert-relay`** konteiner (`dev/config/alert-relay/alert_relay.py`, `python:3.12-slim`, port 9099): võtab vastu HyperDX „generic" webhook POST-id ja saadab Telegrami. Token `dev/.env`-st (`TELEGRAM_BOT_TOKEN`), chat `5036685258`. ⚠️ HyperDX **generic** webhook saadab `{"text": "..."}` (Slack-laadne) — relay saadab selle **otse**, ilma `parse_mode`-ita (Markdown `[ ] * _` põhjustaks Telegrami parse-vea). Varem ootas välju `name/state/message` → kuvas **„Seis: ?"** + toor-JSON (parandatud 02.10.2026). - **Webhook:** `Telegram (ohmu)` → `http://alert-relay:9099/` (id `6ab760240c9282a5b0fde124`). - **Alert:** „Teenus maas (ohmu_service_up)" → dashboardi paneel „Teenused üleval", `thresholdType=below`, `threshold=1`, `interval=5m`, **`numConsecutiveWindows=2`** (nõuab 2 järjestikust akent → väldib üksiku andmeaugu valehäiret), kanal webhook (id `6ab7603c0c9282a5b0fde12d`). **Oluline API-piirang:** väline API v2 (`POST /api/v2/alerts`) toetab ainult `source: "tile"` või `"saved_search"` — **`inline` alert on ainult sisemise API jaoks**. Paneeli-alert viitab: `{source:"tile", tileId, dashboardId}`; kanal: `{"type":"webhook","webhookId":"..."}`. **Testimine:** ```bash # relay otse (hostilt pole 9099 avaldatud; kasuta konteinerit võrgus): docker run --rm --network main_ai_network curlimages/curl:latest -s \ -X POST http://alert-relay:9099/ -H 'Content-Type: application/json' \ -d '{"text":"🚨 test (ignoreeri)"}' ``` Tüüpilised täiendavad alertid (sama muster: tile + webhook + below/above): - `node_memory_MemAvailable_bytes` < X (RAM otsas) - `ohmu_container_restarts` > 0 (restart-loop) - `llamacpp:kv_cache_usage_ratio` > 0.9 ### ⚠️ Clickstack'i collector ↔ ClickHouse käivitusrace (valehäirete allikas) Clickstack'i kaasasolev OTLP-collector (`opampsupervisor`) teeb käivitusel ClickHouse'i skeemi seed'i; kui **ClickHouse pole sel hetkel kättesaadav** (`dial tcp :9000: connection refused`), collector **sureb** → OTLP `4317/4318` on maas → kõik meetrikad/logid/trace'id seiskusid (ja `ohmu_app_metrics.py` logib `[Errno 104] Connection reset by peer`). Kuna `ohmu_service_up` andmeid ei tule, arvutab alert'i `min()` tühja akna pealt **0** → vale „Teenus maas" häire. **Ravi:** `docker restart clickstack` (seed õnnestub, collector tuleb tagasi, 4318 → 200). **✅ Parandatud (02.10.2026):** `docker-compose.yml`-is lisatud - `clickhouse` **healthcheck** native TCP 9000 vastu (`clickhouse-client --password "$CLICKHOUSE_PASSWORD" --query 'SELECT 1'`, 10s intervall, start_period 30s); - `clickstack` **`depends_on: clickhouse: condition: service_healthy`**. Nüüd ootab clickstack ClickHouse'i tervist enne käivitumist → collector'i seed ei jookse enam ClickHouse'i käivitumise ajal. Kontroll: `docker compose up -d clickhouse clickstack` → `clickhouse Waiting → Healthy → clickstack Starting`. (Käsitsi ikka: `docker restart clickstack`.) Kontroll: `docker exec clickstack sh -c 'tail -3 /var/log/otel-collector.log'` (peab olema `Schema seed completed successfully` + `Connected to the OpAMP server`); OTLP-test: `curl -X POST -H 'authorization: ' http://127.0.0.1:4318/v1/metrics` → 200. (Pikaajaline: collector võiks seed'i ebaõnnestumisel retry'da, mitte surra — upstream.) ## Seotud - Vahejuhtumite audit: `../LOEMIND_seisevad_konteinerid.md` - Serveri mudelid: `~/servers/LOEMIND/llama-server.md`