Add capacity limit recommendations
This commit is contained in:
parent
eabc5b049b
commit
1954a2e975
|
|
@ -273,6 +273,7 @@ Run a read-only health snapshot from the Debian server with:
|
||||||
./jeannie status
|
./jeannie status
|
||||||
./jeannie capacity
|
./jeannie capacity
|
||||||
./jeannie capacity-advisor
|
./jeannie capacity-advisor
|
||||||
|
./jeannie capacity-limits
|
||||||
./jeannie recover-plan
|
./jeannie recover-plan
|
||||||
./jeannie recover-power --dry-run
|
./jeannie recover-power --dry-run
|
||||||
./jeannie impact --since HEAD~1
|
./jeannie impact --since HEAD~1
|
||||||
|
|
@ -328,6 +329,11 @@ Kubernetes, Pimox, RPi, and placement signals without dumping full details. Use
|
||||||
placement signals to recommend whether to add Pimox VMs, taint Debian, or fix
|
placement signals to recommend whether to add Pimox VMs, taint Debian, or fix
|
||||||
resource definitions first.
|
resource definitions first.
|
||||||
|
|
||||||
|
`capacity-limits` prints suggested Kubernetes `resources.requests` and
|
||||||
|
`resources.limits` YAML snippets for containers missing them. It uses
|
||||||
|
`infra/resource-recommendations.yml` as a conservative starting profile and does
|
||||||
|
not edit manifests.
|
||||||
|
|
||||||
`recover-plan` prints the disaster recovery order and lightweight prerequisite
|
`recover-plan` prints the disaster recovery order and lightweight prerequisite
|
||||||
checks, from Debian and Gitea through DNS, Pimox, Kubernetes, GitOps apps, and
|
checks, from Debian and Gitea through DNS, Pimox, Kubernetes, GitOps apps, and
|
||||||
edge verification.
|
edge verification.
|
||||||
|
|
|
||||||
|
|
@ -273,6 +273,7 @@ Run a read-only health snapshot from the Debian server with:
|
||||||
./{{ main_script }} status
|
./{{ main_script }} status
|
||||||
./{{ main_script }} capacity
|
./{{ main_script }} capacity
|
||||||
./{{ main_script }} capacity-advisor
|
./{{ main_script }} capacity-advisor
|
||||||
|
./{{ main_script }} capacity-limits
|
||||||
./{{ main_script }} recover-plan
|
./{{ main_script }} recover-plan
|
||||||
./{{ main_script }} recover-power --dry-run
|
./{{ main_script }} recover-power --dry-run
|
||||||
./{{ main_script }} impact --since HEAD~1
|
./{{ main_script }} impact --since HEAD~1
|
||||||
|
|
@ -328,6 +329,11 @@ Kubernetes, Pimox, RPi, and placement signals without dumping full details. Use
|
||||||
placement signals to recommend whether to add Pimox VMs, taint Debian, or fix
|
placement signals to recommend whether to add Pimox VMs, taint Debian, or fix
|
||||||
resource definitions first.
|
resource definitions first.
|
||||||
|
|
||||||
|
`capacity-limits` prints suggested Kubernetes `resources.requests` and
|
||||||
|
`resources.limits` YAML snippets for containers missing them. It uses
|
||||||
|
`infra/resource-recommendations.yml` as a conservative starting profile and does
|
||||||
|
not edit manifests.
|
||||||
|
|
||||||
`recover-plan` prints the disaster recovery order and lightweight prerequisite
|
`recover-plan` prints the disaster recovery order and lightweight prerequisite
|
||||||
checks, from Debian and Gitea through DNS, Pimox, Kubernetes, GitOps apps, and
|
checks, from Debian and Gitea through DNS, Pimox, Kubernetes, GitOps apps, and
|
||||||
edge verification.
|
edge verification.
|
||||||
|
|
|
||||||
|
|
@ -71,6 +71,11 @@ problems`, or `--verbose` for the older full dump.
|
||||||
recommend whether to add Pimox VMs, taint Debian, or fix resource definitions
|
recommend whether to add Pimox VMs, taint Debian, or fix resource definitions
|
||||||
first.
|
first.
|
||||||
|
|
||||||
|
`capacity-limits [namespace]`
|
||||||
|
: Recommend Kubernetes `resources.requests` and `resources.limits` YAML snippets
|
||||||
|
for containers missing them. Uses `infra/resource-recommendations.yml` and does
|
||||||
|
not edit manifests.
|
||||||
|
|
||||||
`recover-plan`
|
`recover-plan`
|
||||||
: Print the ordered disaster recovery checklist and lightweight prerequisite
|
: Print the ordered disaster recovery checklist and lightweight prerequisite
|
||||||
checks for Debian, Gitea, RPi DNS, Pimox, Kubernetes, GitOps apps, and edge
|
checks for Debian, Gitea, RPi DNS, Pimox, Kubernetes, GitOps apps, and edge
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
defaults:
|
||||||
|
request_cpu_m: 50
|
||||||
|
request_memory_mib: 128
|
||||||
|
limit_cpu_multiplier: 4
|
||||||
|
limit_memory_multiplier: 2
|
||||||
|
|
||||||
|
namespaces:
|
||||||
|
website-production:
|
||||||
|
request_cpu_m: 75
|
||||||
|
request_memory_mib: 128
|
||||||
|
limit_cpu_multiplier: 4
|
||||||
|
limit_memory_multiplier: 2
|
||||||
|
demos-static:
|
||||||
|
request_cpu_m: 25
|
||||||
|
request_memory_mib: 64
|
||||||
|
limit_cpu_multiplier: 4
|
||||||
|
limit_memory_multiplier: 2
|
||||||
|
heimdall:
|
||||||
|
request_cpu_m: 50
|
||||||
|
request_memory_mib: 128
|
||||||
|
limit_cpu_multiplier: 4
|
||||||
|
limit_memory_multiplier: 2
|
||||||
|
arr-stack:
|
||||||
|
request_cpu_m: 150
|
||||||
|
request_memory_mib: 512
|
||||||
|
limit_cpu_multiplier: 3
|
||||||
|
limit_memory_multiplier: 2
|
||||||
|
argocd:
|
||||||
|
request_cpu_m: 100
|
||||||
|
request_memory_mib: 256
|
||||||
|
limit_cpu_multiplier: 3
|
||||||
|
limit_memory_multiplier: 2
|
||||||
9
jeannie
9
jeannie
|
|
@ -6016,6 +6016,11 @@ capacity_advisor() {
|
||||||
"${REPO_ROOT}/scripts/capacity-advisor" "${@:2}"
|
"${REPO_ROOT}/scripts/capacity-advisor" "${@:2}"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
capacity_limits() {
|
||||||
|
require_debian_server "capacity-limits"
|
||||||
|
"${REPO_ROOT}/scripts/capacity-limits" "${@:2}"
|
||||||
|
}
|
||||||
|
|
||||||
control_plane() {
|
control_plane() {
|
||||||
require_debian_server "control-plane"
|
require_debian_server "control-plane"
|
||||||
"${REPO_ROOT}/scripts/control-plane" "${@:2}"
|
"${REPO_ROOT}/scripts/control-plane" "${@:2}"
|
||||||
|
|
@ -6133,6 +6138,7 @@ Operator View And Reports
|
||||||
scorecard Compact pass/warn/fail operator scorecard.
|
scorecard Compact pass/warn/fail operator scorecard.
|
||||||
capacity Compact capacity and placement report.
|
capacity Compact capacity and placement report.
|
||||||
capacity-advisor Decide whether to add VMs or fix placement/resources.
|
capacity-advisor Decide whether to add VMs or fix placement/resources.
|
||||||
|
capacity-limits [namespace] Recommend requests/limits YAML snippets.
|
||||||
recover-plan Print disaster recovery order and prerequisites.
|
recover-plan Print disaster recovery order and prerequisites.
|
||||||
recover-power [--dry-run] Run or preview post-outage recovery.
|
recover-power [--dry-run] Run or preview post-outage recovery.
|
||||||
impact [--since REF] [PATH...] Explain affected lab areas before changes.
|
impact [--since REF] [PATH...] Explain affected lab areas before changes.
|
||||||
|
|
@ -6236,6 +6242,9 @@ case "${1:-}" in
|
||||||
capacity-advisor)
|
capacity-advisor)
|
||||||
capacity_advisor "$@"
|
capacity_advisor "$@"
|
||||||
;;
|
;;
|
||||||
|
capacity-limits)
|
||||||
|
capacity_limits "$@"
|
||||||
|
;;
|
||||||
recover-plan)
|
recover-plan)
|
||||||
recover_plan
|
recover_plan
|
||||||
;;
|
;;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,129 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
KUBECONFIG_PATH="${KUBECONFIG:-${LAB_KUBECONFIG_PATH:-/home/jv/.kube/config}}"
|
||||||
|
PROFILE_FILE="${HOMELAB_RESOURCE_RECOMMENDATIONS:-${REPO_ROOT}/infra/resource-recommendations.yml}"
|
||||||
|
NAMESPACE="${1:-}"
|
||||||
|
PODS_JSON=""
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
if [ -n "$PODS_JSON" ] && [ -f "$PODS_JSON" ]; then
|
||||||
|
rm -f "$PODS_JSON"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<'EOF'
|
||||||
|
Usage: ./jeannie capacity-limits [namespace]
|
||||||
|
|
||||||
|
Recommend Kubernetes requests/limits for containers missing them. This command
|
||||||
|
prints YAML snippets only; it does not edit manifests.
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
case "${1:-}" in
|
||||||
|
-h|--help|help)
|
||||||
|
usage
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if ! command -v kubectl >/dev/null 2>&1; then
|
||||||
|
echo "kubectl is required." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [ ! -s "$KUBECONFIG_PATH" ]; then
|
||||||
|
echo "kubeconfig is missing: $KUBECONFIG_PATH" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
PODS_JSON="$(mktemp "${TMPDIR:-/tmp}/jeannie-capacity-limits.XXXXXX")"
|
||||||
|
if [ -n "$NAMESPACE" ]; then
|
||||||
|
kubectl --kubeconfig "$KUBECONFIG_PATH" -n "$NAMESPACE" get pods -o json >"$PODS_JSON"
|
||||||
|
else
|
||||||
|
kubectl --kubeconfig "$KUBECONFIG_PATH" get pods -A -o json >"$PODS_JSON"
|
||||||
|
fi
|
||||||
|
|
||||||
|
python3 - "$PROFILE_FILE" "$PODS_JSON" <<'PY'
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
|
||||||
|
profile_file, pods_json = sys.argv[1:3]
|
||||||
|
try:
|
||||||
|
import yaml
|
||||||
|
except ImportError:
|
||||||
|
yaml = None
|
||||||
|
|
||||||
|
profiles = {
|
||||||
|
"defaults": {
|
||||||
|
"request_cpu_m": 50,
|
||||||
|
"request_memory_mib": 128,
|
||||||
|
"limit_cpu_multiplier": 4,
|
||||||
|
"limit_memory_multiplier": 2,
|
||||||
|
},
|
||||||
|
"namespaces": {},
|
||||||
|
}
|
||||||
|
if yaml:
|
||||||
|
with open(profile_file, encoding="utf-8") as handle:
|
||||||
|
loaded = yaml.safe_load(handle) or {}
|
||||||
|
profiles["defaults"].update(loaded.get("defaults", {}))
|
||||||
|
profiles["namespaces"].update(loaded.get("namespaces", {}))
|
||||||
|
|
||||||
|
with open(pods_json, encoding="utf-8") as handle:
|
||||||
|
pods = json.load(handle).get("items", [])
|
||||||
|
seen = set()
|
||||||
|
|
||||||
|
def profile_for(namespace):
|
||||||
|
profile = dict(profiles["defaults"])
|
||||||
|
profile.update(profiles["namespaces"].get(namespace, {}))
|
||||||
|
return profile
|
||||||
|
|
||||||
|
print("Jeannie Capacity Limits")
|
||||||
|
print("=======================")
|
||||||
|
print("mode: recommendations only")
|
||||||
|
print(f"profile_file: {profile_file}")
|
||||||
|
|
||||||
|
count = 0
|
||||||
|
for pod in pods:
|
||||||
|
namespace = pod["metadata"].get("namespace", "default")
|
||||||
|
owner = namespace
|
||||||
|
for ref in pod["metadata"].get("ownerReferences", []):
|
||||||
|
if ref.get("kind") in {"ReplicaSet", "StatefulSet", "DaemonSet", "Job"}:
|
||||||
|
owner = ref.get("name", owner)
|
||||||
|
break
|
||||||
|
for container in pod.get("spec", {}).get("containers", []):
|
||||||
|
resources = container.get("resources", {})
|
||||||
|
requests = resources.get("requests", {})
|
||||||
|
limits = resources.get("limits", {})
|
||||||
|
if requests.get("cpu") and requests.get("memory") and limits.get("cpu") and limits.get("memory"):
|
||||||
|
continue
|
||||||
|
key = (namespace, owner, container["name"])
|
||||||
|
if key in seen:
|
||||||
|
continue
|
||||||
|
seen.add(key)
|
||||||
|
profile = profile_for(namespace)
|
||||||
|
request_cpu = int(profile["request_cpu_m"])
|
||||||
|
request_memory = int(profile["request_memory_mib"])
|
||||||
|
limit_cpu = request_cpu * int(profile["limit_cpu_multiplier"])
|
||||||
|
limit_memory = request_memory * int(profile["limit_memory_multiplier"])
|
||||||
|
count += 1
|
||||||
|
print(f"\n{count}. {namespace}/{owner}:{container['name']}")
|
||||||
|
print(" Add or tune this resources block in the owning manifest/chart:")
|
||||||
|
print(" resources:")
|
||||||
|
print(" requests:")
|
||||||
|
print(f" cpu: {request_cpu}m")
|
||||||
|
print(f" memory: {request_memory}Mi")
|
||||||
|
print(" limits:")
|
||||||
|
print(f" cpu: {limit_cpu}m")
|
||||||
|
print(f" memory: {limit_memory}Mi")
|
||||||
|
|
||||||
|
if count == 0:
|
||||||
|
print("\nNo containers missing requests/limits were found.")
|
||||||
|
else:
|
||||||
|
print("\nNotes:")
|
||||||
|
print("- Treat these as starting values, not truth.")
|
||||||
|
print("- Prefer app namespaces first; be careful with kube-system/control-plane pods.")
|
||||||
|
print("- After changes, run ./jeannie resource-budget and ./jeannie capacity.")
|
||||||
|
PY
|
||||||
Loading…
Reference in New Issue