hermes: reclaim healthy worker capacity
This commit is contained in:
parent
8a8df5ee4f
commit
46a44241c1
@ -82,9 +82,8 @@ spec:
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if k get node titan-21 >/dev/null 2>&1; then
|
if k get node titan-21 >/dev/null 2>&1; then
|
||||||
# Keep replicas off the Jetson, but allow Longhorn to attach
|
# The Tegra iSCSI initiator cannot host Longhorn engines.
|
||||||
# stateful fallback workloads there.
|
k label node titan-21 longhorn-host- || true
|
||||||
k label node titan-21 longhorn-host=true --overwrite=true || true
|
|
||||||
else
|
else
|
||||||
echo "skipping missing node titan-21"
|
echo "skipping missing node titan-21"
|
||||||
fi
|
fi
|
||||||
|
|||||||
@ -42,6 +42,8 @@ spec:
|
|||||||
agentImage:
|
agentImage:
|
||||||
repository: hashicorp/vault
|
repository: hashicorp/vault
|
||||||
tag: "1.17.6"
|
tag: "1.17.6"
|
||||||
|
agentDefaults:
|
||||||
|
cpuRequest: 25m
|
||||||
webhook:
|
webhook:
|
||||||
failurePolicy: Ignore
|
failurePolicy: Ignore
|
||||||
nodeSelector:
|
nodeSelector:
|
||||||
|
|||||||
@ -24,7 +24,7 @@ spec:
|
|||||||
ai.bstein.dev/router-wire-contract: ollama-numeric-keepalive
|
ai.bstein.dev/router-wire-contract: ollama-numeric-keepalive
|
||||||
ai.bstein.dev/execution: Hermes Kanban with durable direct Codex and Claude Code CLI workers
|
ai.bstein.dev/execution: Hermes Kanban with durable direct Codex and Claude Code CLI workers
|
||||||
ai.bstein.dev/model-policy: Jetson-assisted AUTO routing, low through xhigh, cross-provider fallback
|
ai.bstein.dev/model-policy: Jetson-assisted AUTO routing, low through xhigh, cross-provider fallback
|
||||||
ai.bstein.dev/placement: rpi5 preferred; rpi4 next; titan-21 is the stateful fallback
|
ai.bstein.dev/placement: rpi5 preferred; Jetson deferred until state storage is available
|
||||||
ai.bstein.dev/config-rev: "20260818-ai-usage-exporter-split-v3"
|
ai.bstein.dev/config-rev: "20260818-ai-usage-exporter-split-v3"
|
||||||
prometheus.io/scrape: "true"
|
prometheus.io/scrape: "true"
|
||||||
prometheus.io/path: /metrics
|
prometheus.io/path: /metrics
|
||||||
@ -107,18 +107,6 @@ spec:
|
|||||||
- key: kubernetes.io/hostname
|
- key: kubernetes.io/hostname
|
||||||
operator: NotIn
|
operator: NotIn
|
||||||
values: [titan-04, titan-08, titan-13, titan-14, titan-17, titan-18, titan-19]
|
values: [titan-04, titan-08, titan-13, titan-14, titan-17, titan-18, titan-19]
|
||||||
- matchExpressions:
|
|
||||||
- key: kubernetes.io/arch
|
|
||||||
operator: In
|
|
||||||
values: [arm64]
|
|
||||||
- key: node-role.kubernetes.io/accelerator
|
|
||||||
operator: Exists
|
|
||||||
- key: kubernetes.io/hostname
|
|
||||||
operator: In
|
|
||||||
values: [titan-21]
|
|
||||||
- key: longhorn-host
|
|
||||||
operator: In
|
|
||||||
values: ["true"]
|
|
||||||
preferredDuringSchedulingIgnoredDuringExecution:
|
preferredDuringSchedulingIgnoredDuringExecution:
|
||||||
- weight: 100
|
- weight: 100
|
||||||
preference:
|
preference:
|
||||||
@ -126,18 +114,6 @@ spec:
|
|||||||
- key: hardware
|
- key: hardware
|
||||||
operator: In
|
operator: In
|
||||||
values: [rpi5]
|
values: [rpi5]
|
||||||
- weight: 50
|
|
||||||
preference:
|
|
||||||
matchExpressions:
|
|
||||||
- key: hardware
|
|
||||||
operator: In
|
|
||||||
values: [rpi4]
|
|
||||||
- weight: 25
|
|
||||||
preference:
|
|
||||||
matchExpressions:
|
|
||||||
- key: kubernetes.io/hostname
|
|
||||||
operator: In
|
|
||||||
values: [titan-21]
|
|
||||||
initContainers:
|
initContainers:
|
||||||
- name: init-config
|
- name: init-config
|
||||||
image: busybox:1.37
|
image: busybox:1.37
|
||||||
@ -711,7 +687,7 @@ spec:
|
|||||||
seccompProfile:
|
seccompProfile:
|
||||||
type: RuntimeDefault
|
type: RuntimeDefault
|
||||||
resources:
|
resources:
|
||||||
requests: {cpu: 200m, memory: 512Mi}
|
requests: {cpu: 150m, memory: 384Mi}
|
||||||
limits: {cpu: "3", memory: 6Gi}
|
limits: {cpu: "3", memory: 6Gi}
|
||||||
- name: oauth2-proxy
|
- name: oauth2-proxy
|
||||||
image: quay.io/oauth2-proxy/oauth2-proxy:v7.15.3@sha256:10a1165743a192e1940b4708fb9647027185ce11a681a1c5519b442ff7f1f561
|
image: quay.io/oauth2-proxy/oauth2-proxy:v7.15.3@sha256:10a1165743a192e1940b4708fb9647027185ce11a681a1c5519b442ff7f1f561
|
||||||
@ -881,7 +857,7 @@ spec:
|
|||||||
seccompProfile:
|
seccompProfile:
|
||||||
type: RuntimeDefault
|
type: RuntimeDefault
|
||||||
resources:
|
resources:
|
||||||
requests: {cpu: 25m, memory: 64Mi}
|
requests: {cpu: 25m, memory: 48Mi}
|
||||||
limits: {cpu: 500m, memory: 512Mi}
|
limits: {cpu: 500m, memory: 512Mi}
|
||||||
- name: cli-lane-runner
|
- name: cli-lane-runner
|
||||||
image: registry.bstein.dev/bstein/hermes-agent@sha256:81970563e542f0720773e72297810b3a844b83e381e278f25c0916c78d930107
|
image: registry.bstein.dev/bstein/hermes-agent@sha256:81970563e542f0720773e72297810b3a844b83e381e278f25c0916c78d930107
|
||||||
@ -939,7 +915,7 @@ spec:
|
|||||||
timeoutSeconds: 5
|
timeoutSeconds: 5
|
||||||
failureThreshold: 3
|
failureThreshold: 3
|
||||||
resources:
|
resources:
|
||||||
requests: {cpu: 50m, memory: 192Mi}
|
requests: {cpu: 25m, memory: 128Mi}
|
||||||
limits: {cpu: "2", memory: 6Gi}
|
limits: {cpu: "2", memory: 6Gi}
|
||||||
- name: model-steward
|
- name: model-steward
|
||||||
image: registry.bstein.dev/bstein/hermes-agent@sha256:81970563e542f0720773e72297810b3a844b83e381e278f25c0916c78d930107
|
image: registry.bstein.dev/bstein/hermes-agent@sha256:81970563e542f0720773e72297810b3a844b83e381e278f25c0916c78d930107
|
||||||
@ -968,7 +944,7 @@ spec:
|
|||||||
- {name: auth-patch, mountPath: /opt/hermes/hermes_cli/auth.py, subPath: auth.py}
|
- {name: auth-patch, mountPath: /opt/hermes/hermes_cli/auth.py, subPath: auth.py}
|
||||||
- {name: routing-catalog, mountPath: /routing-catalog}
|
- {name: routing-catalog, mountPath: /routing-catalog}
|
||||||
resources:
|
resources:
|
||||||
requests: {cpu: 25m, memory: 64Mi}
|
requests: {cpu: 25m, memory: 48Mi}
|
||||||
limits: {cpu: 250m, memory: 512Mi}
|
limits: {cpu: 250m, memory: 512Mi}
|
||||||
- name: kanban-supervisor
|
- name: kanban-supervisor
|
||||||
image: registry.bstein.dev/bstein/hermes-agent@sha256:81970563e542f0720773e72297810b3a844b83e381e278f25c0916c78d930107
|
image: registry.bstein.dev/bstein/hermes-agent@sha256:81970563e542f0720773e72297810b3a844b83e381e278f25c0916c78d930107
|
||||||
@ -999,7 +975,7 @@ spec:
|
|||||||
- {name: coordinator, mountPath: /opt/coordinator, readOnly: true}
|
- {name: coordinator, mountPath: /opt/coordinator, readOnly: true}
|
||||||
- {name: auth-patch, mountPath: /opt/hermes/hermes_cli/auth.py, subPath: auth.py}
|
- {name: auth-patch, mountPath: /opt/hermes/hermes_cli/auth.py, subPath: auth.py}
|
||||||
resources:
|
resources:
|
||||||
requests: {cpu: 10m, memory: 64Mi}
|
requests: {cpu: 10m, memory: 48Mi}
|
||||||
limits: {cpu: 250m, memory: 256Mi}
|
limits: {cpu: 250m, memory: 256Mi}
|
||||||
- name: credential-sync
|
- name: credential-sync
|
||||||
image: registry.bstein.dev/bstein/hermes-agent@sha256:81970563e542f0720773e72297810b3a844b83e381e278f25c0916c78d930107
|
image: registry.bstein.dev/bstein/hermes-agent@sha256:81970563e542f0720773e72297810b3a844b83e381e278f25c0916c78d930107
|
||||||
|
|||||||
@ -59,7 +59,7 @@ spec:
|
|||||||
initialDelaySeconds: 30
|
initialDelaySeconds: 30
|
||||||
periodSeconds: 30
|
periodSeconds: 30
|
||||||
resources:
|
resources:
|
||||||
requests: {cpu: 50m, memory: 128Mi}
|
requests: {cpu: 50m, memory: 96Mi}
|
||||||
limits: {cpu: 500m, memory: 512Mi}
|
limits: {cpu: 500m, memory: 512Mi}
|
||||||
volumes:
|
volumes:
|
||||||
- name: execution-pool-code
|
- name: execution-pool-code
|
||||||
|
|||||||
@ -134,13 +134,13 @@ def test_cli_lane_reserves_cpu_headroom_for_ui_and_auth():
|
|||||||
|
|
||||||
assert environment["HERMES_CLI_LANE_CONCURRENCY"] == "2"
|
assert environment["HERMES_CLI_LANE_CONCURRENCY"] == "2"
|
||||||
assert lane["resources"] == {
|
assert lane["resources"] == {
|
||||||
"requests": {"cpu": "50m", "memory": "192Mi"},
|
"requests": {"cpu": "25m", "memory": "128Mi"},
|
||||||
"limits": {"cpu": "2", "memory": "6Gi"},
|
"limits": {"cpu": "2", "memory": "6Gi"},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def test_agent_avoids_unhealthy_nodes_and_has_a_stateful_fallback():
|
def test_agent_avoids_unhealthy_nodes_and_stays_on_storage_workers():
|
||||||
"""The owner agent keeps a bounded fallback when Pi workers are full."""
|
"""The owner agent stays off unhealthy and attach-incompatible nodes."""
|
||||||
pod = _agent_deployment()["spec"]["template"]["spec"]
|
pod = _agent_deployment()["spec"]["template"]["spec"]
|
||||||
terms = pod["affinity"]["nodeAffinity"][
|
terms = pod["affinity"]["nodeAffinity"][
|
||||||
"requiredDuringSchedulingIgnoredDuringExecution"
|
"requiredDuringSchedulingIgnoredDuringExecution"
|
||||||
@ -154,24 +154,24 @@ def test_agent_avoids_unhealthy_nodes_and_has_a_stateful_fallback():
|
|||||||
assert hostnames["operator"] == "NotIn"
|
assert hostnames["operator"] == "NotIn"
|
||||||
assert set(hostnames["values"]) >= {"titan-04", "titan-19"}
|
assert set(hostnames["values"]) >= {"titan-04", "titan-19"}
|
||||||
|
|
||||||
fallback = {item["key"]: item for item in terms[1]["matchExpressions"]}
|
assert len(terms) == 1
|
||||||
assert fallback["node-role.kubernetes.io/accelerator"]["operator"] == "Exists"
|
|
||||||
assert fallback["kubernetes.io/hostname"]["values"] == ["titan-21"]
|
|
||||||
assert fallback["longhorn-host"]["values"] == ["true"]
|
|
||||||
|
|
||||||
preferences = pod["affinity"]["nodeAffinity"][
|
preferences = pod["affinity"]["nodeAffinity"][
|
||||||
"preferredDuringSchedulingIgnoredDuringExecution"
|
"preferredDuringSchedulingIgnoredDuringExecution"
|
||||||
]
|
]
|
||||||
assert [item["weight"] for item in preferences] == [100, 50, 25]
|
assert [item["weight"] for item in preferences] == [100]
|
||||||
|
|
||||||
hermes = next(item for item in pod["containers"] if item["name"] == "hermes")
|
hermes = next(item for item in pod["containers"] if item["name"] == "hermes")
|
||||||
assert hermes["resources"]["requests"]["cpu"] == "200m"
|
assert hermes["resources"]["requests"] == {
|
||||||
|
"cpu": "150m",
|
||||||
|
"memory": "384Mi",
|
||||||
|
}
|
||||||
|
|
||||||
node_labels = (
|
node_labels = (
|
||||||
Path(__file__).parents[2]
|
Path(__file__).parents[2]
|
||||||
/ "infrastructure/core/node-prefer-noschedule-cronjob.yaml"
|
/ "infrastructure/core/node-prefer-noschedule-cronjob.yaml"
|
||||||
).read_text()
|
).read_text()
|
||||||
assert "k label node titan-21 longhorn-host=true" in node_labels
|
assert "k label node titan-21 longhorn-host-" in node_labels
|
||||||
|
|
||||||
|
|
||||||
def test_agent_root_is_stock_dashboard_and_terminal_is_a_separate_path():
|
def test_agent_root_is_stock_dashboard_and_terminal_is_a_separate_path():
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user