You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The agent exposes Prometheus-format metrics at `:8080/metrics` on the same HTTP server used for health checks. Metrics are served using the `prometheus_client` Python library.
1185
-
1186
-
**Counters** (monotonically increasing, reset on pod restart):
Each metric increment is co-located with the corresponding `_emit_k8s_event()` call, so the event catalog and metric catalog map 1:1.
1187
+
The agent exposes Prometheus-format metrics at `:8080/metrics` on the same HTTP server used for health checks. Each metric increment is co-located with the corresponding `_emit_k8s_event()` call. See [instanceha_prometheus.md](instanceha_prometheus.md) for the full metric catalog, alert rules, and Grafana dashboard queries.
**Purpose**: Detect when the InstanceHA pod's worker node is network-isolated from the Kubernetes API, preventing it from fencing healthy hosts it can no longer observe.
1280
+
1281
+
**Architecture**:
1282
+
- Background thread started by `start_k8s_health_check()`
1283
+
- Polls `GET /api/v1/namespaces/{namespace}` using the in-cluster service account token
1284
+
- Interval controlled by `K8S_API_CHECK_INTERVAL` (default 15s, range 5–120)
1285
+
1286
+
**Configuration**: `K8S_API_CHECK_INTERVAL: 15`
1287
+
1288
+
**Behavior**:
1289
+
1. The health check thread pings the K8s API every `K8S_API_CHECK_INTERVAL` seconds
1290
+
2. Any non-5xx response (status code < 500) is treated as reachable — the API is responding, even if RBAC denies the specific request
1291
+
3. After 3 consecutive failures, `k8s_api_reachable` is set to `False` and `ready` is set to `False`
1292
+
4. The main poll loop checks `k8s_api_reachable` before fencing — if `False`, the entire fencing cycle is skipped
1293
+
5. When connectivity restores, `k8s_api_reachable` is set back to `True`; readiness is restored after the next successful Nova poll
**Concurrency**: WORKERS controls the number of hosts processed concurrently and, for smart/orchestrated mode, the per-host server concurrency (capped at `MAX_TOTAL_EVACUATION_THREADS=32`).
1867
-
1868
-
---
1869
-
1870
-
### 3. Memory Management
1871
-
1872
-
**Cleanup Strategies**:
1873
-
- Kdump timestamp cleanup (>2000 entries)
1874
-
- Host processing expiration
1875
-
- Generic cleanup helper
1859
+
For heartbeat filter latency, UDP listener throughput, lock contention, memory overhead, and scalability projections at 1000-node scale, see [instanceha_heartbeat_performance.md](instanceha_heartbeat_performance.md).
Copy file name to clipboardExpand all lines: docs/instanceha_guide.md
+32-31Lines changed: 32 additions & 31 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -51,7 +51,7 @@ InstanceHA runs as a Kubernetes-managed workload alongside the OpenStack control
51
51
52
52
### How It Works
53
53
54
-
1.**Detection**: The agent polls the Nova API every `POLL` seconds (default: 45). It queries all `nova-compute` services and checks each one's `updated_at` timestamp and `state` field. A service is considered failed if it is reported as `down` or if its heartbeat is staler than `DELTA` seconds (default: 30). When heartbeat verification is enabled (`CHECK_HEARTBEAT`), both the Nova service-list poll and the UDP heartbeat channel must agree that a host is unreachable before it is marked as failed.
54
+
1.**Detection**: The agent polls the Nova API every `POLL` seconds (default: 45). It queries all `nova-compute` services and checks each one's `updated_at` timestamp. A service is considered failed if it is reported as `down` or if its `updated_at` timestamp is older than `DELTA` seconds (default: 30). When heartbeat verification is enabled (`CHECK_HEARTBEAT`), both the Nova service-list poll and the UDP heartbeat channel must agree that a host is unreachable before it is marked as failed.
55
55
56
56
2.**Safety checks**: Before acting, the agent verifies that the percentage of failed hosts does not exceed the `THRESHOLD` (default: 50%) — calculated against only active services, excluding already-disabled and already-forced-down hosts — and that at least one `nova-scheduler` is running. These checks prevent cascading evacuations during infrastructure-wide outages. A `ThresholdExceeded` K8s event is emitted when the limit is hit.
57
57
@@ -121,6 +121,7 @@ InstanceHA emits Kubernetes events on the InstanceHa CR to provide observability
121
121
|`ThresholdExceeded`| Warning | Too many hosts down simultaneously |
summary: "Instance evacuations taking longer than 5 minutes (p95)"
190
-
```
165
+
See [instanceha_prometheus.md](instanceha_prometheus.md) for alert rules, Grafana dashboard queries, and PromQL examples.
191
166
192
167
#### Scraping Configuration
193
168
@@ -215,6 +190,27 @@ spec:
215
190
216
191
When the pod receives SIGTERM (during scaling, rolling updates, or node drain), InstanceHA finishes any in-flight fencing or evacuation work before exiting. The Deployment is configured with a 30-second termination grace period. The agent's poll loop and evacuation workers check a shutdown flag between cycles, allowing timely response to shutdown signals.
217
192
193
+
### Network Partition Detection
194
+
195
+
Two complementary mechanisms prevent the agent from fencing healthy hosts when the pod's worker node is network-isolated:
196
+
197
+
**K8s API connectivity check:**
198
+
- A background thread pings the K8s API every `K8S_API_CHECK_INTERVAL` seconds (default: 15) using the in-cluster service account.
199
+
- After 3 consecutive failures, the agent marks itself not-ready and blocks all fencing until connectivity restores and a Nova poll succeeds.
200
+
- The `instanceha_k8s_api_reachable` Prometheus gauge tracks the current state (1=ok, 0=isolated).
201
+
202
+
**Heartbeat cliff detection:**
203
+
- The agent tracks the count of active heartbeat hosts between poll cycles.
204
+
- If the count drops by more than `HEARTBEAT_CLIFF_THRESHOLD`% (default: 50%) in a single cycle (minimum 3 hosts previously active), fencing is skipped for that cycle. A sudden mass-silence is far more likely to indicate a listener-side network issue than simultaneous host failures.
205
+
- A `HeartbeatCliff` K8s event (Warning) is emitted and the `instanceha_heartbeat_cliff_total` counter is incremented.
206
+
- The detection re-evaluates on the next cycle — the baseline is preserved during a cliff, so fencing stays blocked until heartbeats recover.
207
+
208
+
```yaml
209
+
config:
210
+
K8S_API_CHECK_INTERVAL: "15"
211
+
HEARTBEAT_CLIFF_THRESHOLD: "50"
212
+
```
213
+
218
214
---
219
215
220
216
## Deployment
@@ -334,6 +330,7 @@ metadata:
334
330
metallb.universe.tf/loadBalancerIPs: 172.17.0.80
335
331
spec:
336
332
type: LoadBalancer
333
+
externalTrafficPolicy: Local
337
334
selector:
338
335
service: instanceha # Replace with your CR name if different
339
336
ports:
@@ -347,6 +344,8 @@ spec:
347
344
protocol: UDP
348
345
```
349
346
347
+
> **Important:** `externalTrafficPolicy: Local` is required when kdump detection is enabled (`CHECK_KDUMP: "true"`). The kdump listener identifies the crashing host via reverse DNS lookup on the packet's source IP. Without `Local` policy, kube-proxy masquerades (SNATs) the source IP when forwarding traffic to the pod, replacing the compute node's real IP with an internal node address — causing the reverse DNS lookup to fail. `Local` policy preserves the original source IP by delivering traffic directly to the pod on the receiving node without cross-node forwarding or SNAT. (Heartbeat packets are unaffected — the sender's hostname is embedded in the HMAC-authenticated packet payload, so source IP rewriting does not break heartbeat identification.)
348
+
350
349
Replace `172.17.0.80` with an IP from your `internalapi` MetalLB address pool. The `allow-shared-ip` annotation lets this Service share the same IP with other OpenStack services (e.g., keystone, nova, cinder) that already use the `internalapi` pool — since the InstanceHA ports (7410/UDP, 7411/UDP) don't conflict with any existing service ports, sharing is safe and avoids consuming an additional IP. If you prefer a dedicated IP, pick an unused address and remove the `allow-shared-ip` annotation.
351
350
352
351
> **Note:** The `address-pool` value must match the actual MetalLB IPAddressPool name. In a standard openstack-k8s-operators deployment this is `internalapi`. You can verify the pool name with `oc get ipaddresspools -n metallb-system`.
@@ -880,6 +879,8 @@ All values are strings in the ConfigMap. The agent converts and validates them a
| `HEARTBEAT_CLIFF_THRESHOLD` | int | 50 (range: 10–100) | Percentage drop in active heartbeat hosts that triggers cliff detection (skips fencing for that cycle) |
883
+
| `K8S_API_CHECK_INTERVAL` | int | 15 (range: 5–120) | Seconds between Kubernetes API reachability checks |
0 commit comments