Update observability guide and refresh all dashboard screenshots

Guide changes:
- Architecture diagram now shows auto-discovered targets and optional
  oc-server monitoring
- Fix /host/rootfs → /host/root mount path in node_exporter section
- Document ARGS variable name (not NODE_EXPORTER_OPTS) for Alpine init
- Add --oc-server and --nodes flag documentation to Management section
- Update resource budget with oc-server row
- Expand "No node metrics" troubleshooting with ARGS diagnostic steps
- Update screenshot captions with correct RAM values (19.5 GiB)

Screenshots refreshed after fixing node-exporter RAM bug — all 9
dashboards now show correct host metrics (20 GiB RAM, not 128 MiB).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Maarten 2026-02-25 15:14:56 +01:00
parent be3f989312
commit 5ee32fe08c
10 changed files with 79 additions and 22 deletions

View File

@ -6,8 +6,10 @@ and log aggregation (Loki) for Incus nodes, HAProxy load balancers, and
host-level resources. host-level resources.
The stack runs entirely on OVN and is managed by the `deploy-observability` The stack runs entirely on OVN and is managed by the `deploy-observability`
script. All containers use the `monitoring-allow` ACL to permit scrape script. Cluster nodes are **auto-discovered** — the script queries
traffic on the shared network. `incus cluster list` to find nodes, eliminating hardcoded IPs. All
containers use the `monitoring-allow` ACL to permit scrape traffic on the
shared network.
## Architecture ## Architecture
@ -27,10 +29,15 @@ flowchart TD
promtail["Promtail"] promtail["Promtail"]
end end
subgraph targets["Scrape Targets"] subgraph targets["Scrape Targets (auto-discovered)"]
incus["Incus nodes :8443<br/>.140 · .141 · .142"] incus["Incus nodes :8443<br/>auto-discovered from cluster"]
haproxy["HAProxy :8404<br/>.50 · .51"] haproxy["HAProxy :8404<br/>.50 · .51"]
ne["node-exporters :9100<br/>.71 · .72 · .73"] ne["node-exporters :9100<br/>one per cluster node (OVN)"]
end
subgraph ocserver["oc-server (optional, --oc-server)"]
oc_incus["Incus API :8443<br/>192.168.102.120"]
oc_ne["node-exporter :9100<br/>proxy device on host IP"]
end end
lan --> fwd lan --> fwd
@ -38,6 +45,8 @@ flowchart TD
prometheus -->|"client cert"| incus prometheus -->|"client cert"| incus
prometheus --> haproxy prometheus --> haproxy
prometheus --> ne prometheus --> ne
prometheus -->|"client cert"| oc_incus
prometheus --> oc_ne
classDef external fill:#f5f5f5,color:#333,stroke:#999 classDef external fill:#f5f5f5,color:#333,stroke:#999
classDef network fill:#0072B2,color:#fff,stroke:#005a8e classDef network fill:#0072B2,color:#fff,stroke:#005a8e
@ -49,10 +58,12 @@ flowchart TD
class fwd_grafana,fwd_prom network class fwd_grafana,fwd_prom network
class prometheus,grafana,loki,promtail mgmt class prometheus,grafana,loki,promtail mgmt
class incus,haproxy,ne instance class incus,haproxy,ne instance
class oc_incus,oc_ne node
style fwd fill:#e0eef8,stroke:#0072B2 style fwd fill:#e0eef8,stroke:#0072B2
style mon fill:#f5e6f0,stroke:#CC79A7 style mon fill:#f5e6f0,stroke:#CC79A7
style targets fill:#e0f2fe,stroke:#56B4E9 style targets fill:#e0f2fe,stroke:#56B4E9
style ocserver fill:#e0f5ed,stroke:#009E73
``` ```
## Components ## Components
@ -97,13 +108,18 @@ Log shipping agent, co-located with Loki in the monitoring container.
Host-level metrics via privileged Alpine containers. Host-level metrics via privileged Alpine containers.
- One container per cluster node, pinned with `--target` placement - One container per cluster node, auto-discovered and pinned with `--target`
- Privileged containers with host filesystem bind-mounts: - Privileged containers with host filesystem bind-mounts:
- `/proc` (host) -> `/host/proc` (read-only) - `/proc` (host) -> `/host/proc` (read-only)
- `/sys` (host) -> `/host/sys` (read-only) - `/sys` (host) -> `/host/sys` (read-only)
- `/` (host) -> `/host/rootfs` (read-only) - `/` (host) -> `/host/root` (read-only)
- Configured via `/etc/conf.d/node-exporter` with `ARGS="--path.procfs=/host/proc
--path.sysfs=/host/sys --path.rootfs=/host/root"` (Alpine's init script reads
the `ARGS` variable, not `NODE_EXPORTER_OPTS`)
- Exposes standard node_exporter metrics on `:9100` - Exposes standard node_exporter metrics on `:9100`
- Uses `monitoring-allow` ACL for network access - Uses `monitoring-allow` ACL for network access
- Optional: oc-server gets its own node-exporter with a proxy device
exposing `:9100` on the host IP (since oc-server uses incusbr0, not OVN)
## Access ## Access
@ -147,7 +163,8 @@ Overview of all instances with instance/node template variables.
![Cluster Overview](../screenshots/cluster-overview.png) ![Cluster Overview](../screenshots/cluster-overview.png)
*Cluster-wide view showing 8 instances across 3 nodes with 28 total CPUs, *Cluster-wide view showing 8 instances across 3 nodes with 28 total CPUs,
active memory utilization, and the instance status table.* 20.4 GiB total memory, cluster memory utilization gauge, and the instance
status table with per-instance drill-down links.*
### Incus Cluster — Instance Deep Dive ### Incus Cluster — Instance Deep Dive
@ -190,8 +207,9 @@ Physical host monitoring with node template variable.
- Network traffic by device, network errors and drops - Network traffic by device, network errors and drops
![Host Resources](../screenshots/host-resources.png) ![Host Resources](../screenshots/host-resources.png)
*Host-level metrics from node-exporter showing system info, CPU/memory *Host-level metrics from node-exporter showing system info (19.5 GiB RAM per
breakdown, filesystem usage, and network traffic for all 3 IncusOS nodes.* node, kernel version, boot time), CPU usage/mode breakdown, load average, and
memory utilization for all 3 auto-discovered IncusOS nodes.*
### Incus Infrastructure — Storage & Filesystem ### Incus Infrastructure — Storage & Filesystem
@ -280,8 +298,9 @@ Prometheus self-monitoring (no template variables).
- TSDB size trend, samples ingested trend, compactions - TSDB size trend, samples ingested trend, compactions
![Prometheus Health](../screenshots/prometheus-health.png) ![Prometheus Health](../screenshots/prometheus-health.png)
*Prometheus self-monitoring showing 7 targets UP (3 Incus, 3 node-exporter, *Prometheus self-monitoring showing 7 targets UP (3 Incus nodes, 3
1 self), 2 DOWN (HAProxy), scrape performance, and TSDB stats.* node-exporters with friendly names, 1 self), 2 DOWN (HAProxy not deployed),
scrape duration by job, and samples scraped.*
### Incus Operations — Capacity Planning ### Incus Operations — Capacity Planning
@ -293,8 +312,8 @@ Resource planning with 7-day default time range (no template variables).
- Instance count trend, memory allocation vs active memory per instance - Instance count trend, memory allocation vs active memory per instance
![Capacity Planning](../screenshots/capacity-planning.png) ![Capacity Planning](../screenshots/capacity-planning.png)
*Capacity planning with CPU/memory headroom bars, 7-day usage trends, 30-day *Capacity planning with CPU/memory headroom bars per node, 7-day CPU and
disk space forecast using predict_linear, and instance count over time.* memory usage trends, and 30-day disk space forecast using predict_linear.*
## ACL Configuration ## ACL Configuration
@ -340,6 +359,8 @@ manually or restart the container.
## Resource Budget ## Resource Budget
Node-exporter containers are auto-discovered from the cluster — one per node.
| Container | Image | RAM | Disk | Placement | | Container | Image | RAM | Disk | Placement |
|-----------|-------|-----|------|-----------| |-----------|-------|-----|------|-----------|
| monitoring | Debian/12 | 2 GiB | 20 GiB | oc-node-02 | | monitoring | Debian/12 | 2 GiB | 20 GiB | oc-node-02 |
@ -347,6 +368,12 @@ manually or restart the container.
| node-exp-02 | Alpine | 128 MiB | — | oc-node-02 | | node-exp-02 | Alpine | 128 MiB | — | oc-node-02 |
| node-exp-03 | Alpine | 128 MiB | — | oc-node-03 | | node-exp-03 | Alpine | 128 MiB | — | oc-node-03 |
**OC server monitoring** (optional, deployed via `--oc-server`):
| Container | Image | RAM | Placement | Notes |
|-----------|-------|-----|-----------|-------|
| node-exp-oc-server | Alpine | 128 MiB | oc-server | Proxy device exposes :9100 on host IP |
**Dummy workloads** (optional, deployed via `--workloads`): **Dummy workloads** (optional, deployed via `--workloads`):
| Container | Image | RAM | Placement | Purpose | | Container | Image | RAM | Placement | Purpose |
@ -355,14 +382,15 @@ manually or restart the container.
| workload-api | Alpine | 128 MiB | oc-node-03 | Python HTTP + memory sawtooth | | workload-api | Alpine | 128 MiB | oc-node-03 | Python HTTP + memory sawtooth |
**Totals:** **Totals:**
- RAM: ~2.4 GiB core (2 GiB + 3 x 128 MiB), +256 MiB with workloads - RAM: ~2.4 GiB core (2 GiB + 3 x 128 MiB), +128 MiB with oc-server, +256 MiB with workloads
- Disk: 20 GiB (only the monitoring container needs significant storage) - Disk: 20 GiB (only the monitoring container needs significant storage)
- OVN forward IPs: 1 (192.168.103.201) - OVN forward IPs: 1 (192.168.103.201)
- OVN network IPs: 4 core (10.10.10.70-73), +2 with workloads (.80, .81) - OVN network IPs: 4 core (10.10.10.70-73), +2 with workloads (.80, .81)
## Management ## Management
The `deploy-observability` script handles the full lifecycle. The `deploy-observability` script handles the full lifecycle. Cluster nodes
are **auto-discovered** from `incus cluster list` — no hardcoded IPs needed.
### Deploy the stack ### Deploy the stack
@ -370,10 +398,27 @@ The `deploy-observability` script handles the full lifecycle.
incusos/deploy-observability --deploy incusos/deploy-observability --deploy
``` ```
Creates the monitoring container and all node-exporter containers, Auto-discovers cluster nodes, creates the monitoring container and one
installs and configures Prometheus, Grafana, Loki, and Promtail, node-exporter per node, installs and configures Prometheus, Grafana,
provisions dashboards, sets up the OVN network forward, and configures Loki, and Promtail, provisions dashboards, sets up the OVN network
ACLs. forward, and configures ACLs.
To also monitor the standalone OC management server:
```bash
incusos/deploy-observability --deploy --oc-server 192.168.102.120
```
This deploys an additional node-exporter on oc-server with a proxy device
to expose `:9100` on the host IP (since oc-server uses incusbr0, not OVN).
Requires an `oc-server` Incus remote — the script provides `incus remote add`
instructions if it's missing.
To override auto-discovery with a manual node list:
```bash
incusos/deploy-observability --deploy --nodes oc-node-01,oc-node-02,oc-node-03
```
### Deploy dummy workloads ### Deploy dummy workloads
@ -447,7 +492,7 @@ If Prometheus is not running, check its service status:
incus exec monitoring -- systemctl status prometheus incus exec monitoring -- systemctl status prometheus
``` ```
### No node metrics ### No node metrics (or RAM shows 128 MiB)
1. Verify the node-exporter containers are running and privileged: 1. Verify the node-exporter containers are running and privileged:
```bash ```bash
@ -457,7 +502,19 @@ incus exec monitoring -- systemctl status prometheus
```bash ```bash
incus exec node-exp-01 -- ls /host/proc/stat incus exec node-exp-01 -- ls /host/proc/stat
``` ```
3. Verify node_exporter is listening: 3. Verify node_exporter is running with host path flags:
```bash
incus exec node-exp-01 -- ps aux | grep node_exporter
# Should show: --path.procfs=/host/proc --path.sysfs=/host/sys --path.rootfs=/host/root
```
4. If flags are missing, check `/etc/conf.d/node-exporter` uses `ARGS=`
(not `NODE_EXPORTER_OPTS=`). Alpine's init script reads the `ARGS`
variable:
```bash
incus exec node-exp-01 -- cat /etc/conf.d/node-exporter
# Should show: ARGS="--path.procfs=/host/proc ..."
```
5. Verify node_exporter is listening:
```bash ```bash
incus exec node-exp-01 -- wget -qO- http://localhost:9100/metrics | head incus exec node-exp-01 -- wget -qO- http://localhost:9100/metrics | head
``` ```

Binary file not shown.

Before

Width:  |  Height:  |  Size: 216 KiB

After

Width:  |  Height:  |  Size: 205 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 214 KiB

After

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 229 KiB

After

Width:  |  Height:  |  Size: 159 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 167 KiB

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 349 KiB

After

Width:  |  Height:  |  Size: 352 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 185 KiB

After

Width:  |  Height:  |  Size: 143 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 141 KiB

After

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 170 KiB

After

Width:  |  Height:  |  Size: 58 KiB