Add what-broke status signals
This commit is contained in:
parent
5f8fdfef3d
commit
329a3d1abd
|
|
@ -274,7 +274,9 @@ Run a read-only health snapshot from the Debian server with:
|
||||||
|
|
||||||
It reports host memory/disk, systemd services, Docker Compose stacks,
|
It reports host memory/disk, systemd services, Docker Compose stacks,
|
||||||
Kubernetes health when the API is reachable, Pimox worker VM status, RPi
|
Kubernetes health when the API is reachable, Pimox worker VM status, RPi
|
||||||
services, Tailscale, and key local/public HTTP endpoints.
|
services, Tailscale, key local/public HTTP endpoints, and "what broke" signals
|
||||||
|
such as deployment readiness, pod restarts, node pressure, disk pressure,
|
||||||
|
Traefik 5xx/404 log evidence, and recent Gitea errors.
|
||||||
|
|
||||||
Focused doctor commands run narrower read-only checks and print the most likely
|
Focused doctor commands run narrower read-only checks and print the most likely
|
||||||
next step:
|
next step:
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,9 @@ cluster state.
|
||||||
`status`
|
`status`
|
||||||
: Print a cascade health report for the configured homelab, from bootstrap
|
: Print a cascade health report for the configured homelab, from bootstrap
|
||||||
readiness through public website health. Set `LAB_STATUS_DETAILS=true` to append
|
readiness through public website health. Set `LAB_STATUS_DETAILS=true` to append
|
||||||
the older detailed tables.
|
the older detailed tables. The cascade includes "what broke" signals for recent
|
||||||
|
deployment readiness, pod restarts, node pressure, disk pressure, Traefik
|
||||||
|
5xx/404 log evidence, and recent Gitea errors.
|
||||||
|
|
||||||
`nuke`
|
`nuke`
|
||||||
: Destroy Kubernetes state and Pimox worker VMs. Requires
|
: Destroy Kubernetes state and Pimox worker VMs. Requires
|
||||||
|
|
@ -214,6 +216,17 @@ model.
|
||||||
`HOMELAB_INVENTORY_FILE`
|
`HOMELAB_INVENTORY_FILE`
|
||||||
: Path to the canonical non-secret inventory. Defaults to `./homelab.yml`.
|
: Path to the canonical non-secret inventory. Defaults to `./homelab.yml`.
|
||||||
|
|
||||||
|
`LAB_STATUS_LOG_SINCE`
|
||||||
|
: Log window for status log checks. Defaults to `30m`.
|
||||||
|
|
||||||
|
`LAB_STATUS_RESTART_THRESHOLD`
|
||||||
|
: Pod restart count that turns the status restart-pressure check into a warning.
|
||||||
|
Defaults to `3`.
|
||||||
|
|
||||||
|
`LAB_STATUS_DISK_USE_THRESHOLD`
|
||||||
|
: Disk usage percentage that turns the status disk check into a warning.
|
||||||
|
Defaults to `85`.
|
||||||
|
|
||||||
`HOMELAB_STATE_DIR`
|
`HOMELAB_STATE_DIR`
|
||||||
: Local runtime state directory. Defaults to
|
: Local runtime state directory. Defaults to
|
||||||
`${XDG_DATA_HOME:-$HOME/.local/share}/homelab`.
|
`${XDG_DATA_HOME:-$HOME/.local/share}/homelab`.
|
||||||
|
|
|
||||||
105
jeannie
105
jeannie
|
|
@ -4767,6 +4767,103 @@ status_no_problem_pods() {
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
status_recent_deployments() {
|
||||||
|
local rows
|
||||||
|
|
||||||
|
rows="$(
|
||||||
|
kubectl --kubeconfig "${KUBECONFIG_PATH}" get deployments -A \
|
||||||
|
-o custom-columns='NAMESPACE:.metadata.namespace,NAME:.metadata.name,READY:.status.readyReplicas,DESIRED:.spec.replicas,UPDATED:.status.updatedReplicas,AVAILABLE:.status.availableReplicas,AGE:.metadata.creationTimestamp' \
|
||||||
|
--no-headers 2>/dev/null |
|
||||||
|
awk '$3 != $4 || $5 != $4 || $6 != $4 { print }'
|
||||||
|
)"
|
||||||
|
if [[ -n "${rows}" ]]; then
|
||||||
|
printf '%s\n' "${rows}" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
status_pod_restart_pressure() {
|
||||||
|
local threshold="${LAB_STATUS_RESTART_THRESHOLD:-3}"
|
||||||
|
local rows
|
||||||
|
|
||||||
|
rows="$(
|
||||||
|
kubectl --kubeconfig "${KUBECONFIG_PATH}" get pods -A --no-headers 2>/dev/null |
|
||||||
|
awk -v threshold="${threshold}" '$5 + 0 >= threshold { print $1 "/" $2 ": restarts=" $5 " age=" $6 }'
|
||||||
|
)"
|
||||||
|
if [[ -n "${rows}" ]]; then
|
||||||
|
printf '%s\n' "${rows}" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
status_node_pressure() {
|
||||||
|
local rows
|
||||||
|
|
||||||
|
rows="$(
|
||||||
|
kubectl --kubeconfig "${KUBECONFIG_PATH}" get nodes -o jsonpath='{range .items[*]}{.metadata.name}{" "}{range .status.conditions[*]}{.type}{"="}{.status}{" "}{end}{"\n"}{end}' 2>/dev/null |
|
||||||
|
awk '/MemoryPressure=True|DiskPressure=True|PIDPressure=True|NetworkUnavailable=True/ { print }'
|
||||||
|
)"
|
||||||
|
if [[ -n "${rows}" ]]; then
|
||||||
|
printf '%s\n' "${rows}" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
status_disk_pressure() {
|
||||||
|
local threshold="${LAB_STATUS_DISK_USE_THRESHOLD:-85}"
|
||||||
|
local rows
|
||||||
|
|
||||||
|
rows="$(df -P / /data 2>/dev/null | awk -v threshold="${threshold}" 'NR > 1 { gsub(/%/, "", $5); if ($5 + 0 >= threshold) print $6 ": " $5 "% used" }')"
|
||||||
|
if [[ -n "${rows}" ]]; then
|
||||||
|
printf '%s\n' "${rows}" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
status_traefik_error_rates() {
|
||||||
|
local since="${LAB_STATUS_LOG_SINCE:-30m}"
|
||||||
|
local sample
|
||||||
|
local count_502
|
||||||
|
local count_404
|
||||||
|
|
||||||
|
sample="$(kubectl --kubeconfig "${KUBECONFIG_PATH}" -n traefik logs -l app.kubernetes.io/name=traefik --since="${since}" --tail=2000 2>/dev/null || true)"
|
||||||
|
if [[ -z "${sample}" ]]; then
|
||||||
|
echo "no recent Traefik logs available" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
count_502="$(grep -Eac '(^|[^0-9])50[234]([^0-9]|$)|level=error' <<<"${sample}" || true)"
|
||||||
|
count_404="$(grep -Eac '(^|[^0-9])404([^0-9]|$)' <<<"${sample}" || true)"
|
||||||
|
if ((count_502 > 0)); then
|
||||||
|
printf '5xx/error lines in last %s: %s\n' "${since}" "${count_502}" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
printf '404 lines in last %s: %s\n' "${since}" "${count_404}" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
status_gitea_recent_errors() {
|
||||||
|
local since="${LAB_STATUS_LOG_SINCE:-30m}"
|
||||||
|
local container="${GITEA_CONTAINER_NAME:-homelab-gitea}"
|
||||||
|
local sample
|
||||||
|
local matches
|
||||||
|
|
||||||
|
if ! command -v docker >/dev/null 2>&1; then
|
||||||
|
echo "docker not installed" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if ! sudo docker inspect "${container}" >/dev/null 2>&1; then
|
||||||
|
echo "container ${container} not found" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
sample="$(sudo docker logs --since "${since}" "${container}" 2>&1 || true)"
|
||||||
|
matches="$(grep -Eai 'error|panic|fatal|authentication failed|denied| 50[0-9] ' <<<"${sample}" | tail -10 || true)"
|
||||||
|
if [[ -n "${matches}" ]]; then
|
||||||
|
printf '%s\n' "${matches}" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
status_registry_ok() {
|
status_registry_ok() {
|
||||||
status_http_ok "http://${LAB_REGISTRY_ENDPOINT:-192.168.100.73:30500}/v2/"
|
status_http_ok "http://${LAB_REGISTRY_ENDPOINT:-192.168.100.73:30500}/v2/"
|
||||||
}
|
}
|
||||||
|
|
@ -4843,6 +4940,14 @@ status_cascade_report() {
|
||||||
status_cascade_check "Kubernetes nodes Ready" status_kubernetes_nodes_ready || failures=$((failures + 1))
|
status_cascade_check "Kubernetes nodes Ready" status_kubernetes_nodes_ready || failures=$((failures + 1))
|
||||||
status_cascade_warn "No problem pods" status_no_problem_pods
|
status_cascade_warn "No problem pods" status_no_problem_pods
|
||||||
|
|
||||||
|
status_section "What Broke Signals"
|
||||||
|
status_cascade_warn "Recent deployments healthy" status_recent_deployments
|
||||||
|
status_cascade_warn "Pod restart pressure" status_pod_restart_pressure
|
||||||
|
status_cascade_warn "Node pressure" status_node_pressure
|
||||||
|
status_cascade_warn "Disk usage" status_disk_pressure
|
||||||
|
status_cascade_warn "Traefik 5xx/404 signals" status_traefik_error_rates
|
||||||
|
status_cascade_warn "Gitea recent errors" status_gitea_recent_errors
|
||||||
|
|
||||||
status_section "Platform And Apps"
|
status_section "Platform And Apps"
|
||||||
status_cascade_check "Local registry" status_registry_ok || failures=$((failures + 1))
|
status_cascade_check "Local registry" status_registry_ok || failures=$((failures + 1))
|
||||||
status_cascade_check "Traefik deployment" status_kubernetes_deployment_ready traefik traefik || failures=$((failures + 1))
|
status_cascade_check "Traefik deployment" status_kubernetes_deployment_ready traefik traefik || failures=$((failures + 1))
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue