Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/developers/scheduler-event-log.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ title: Scheduler Event Log
sidebar_label: Scheduler Event Log
---

This page records the design of the scheduler's event and log format. If you are diagnosing a Pod that is stuck in `Pending` right now, read [Scheduling Failures](../troubleshooting/scheduling-failures.md) instead: it lists every reason code the scheduler emits and what to do about each one.

## Current Status

### Ambiguous Event Descriptions Make Problem Diagnosis Difficult
Expand Down
176 changes: 176 additions & 0 deletions docs/troubleshooting/scheduling-failures.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
---
title: Scheduling Failures
sidebar_label: Scheduling Failures
---

When a Pod that requests HAMi resources stays `Pending`, the HAMi scheduler extender has already made a decision and told you why. It records that decision as `FilteringFailed` events on the Pod, using a fixed set of reason codes such as `CardInsufficientMemory` or `CardTimeSlicingExhausted`.

This page explains how to read those messages and what each reason code means.

:::info

Reason codes are defined in `pkg/device/common/common.go` in the [HAMi repository](https://github.com/Project-HAMi/HAMi). The list below reflects HAMi v2.9.0. Older versions emit a subset of these codes; a Pod scheduled by an older scheduler may show free-form messages instead.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- tracked documentation files ---'
git ls-files 'docs/troubleshooting/scheduling-failures.md' 'i18n/zh/docusaurus-plugin-content-docs/current/troubleshooting/scheduling-failures.md'
printf '%s\n' '--- English context ---'
sed -n '1,110p' docs/troubleshooting/scheduling-failures.md
printf '%s\n' '--- Chinese context ---'
sed -n '1,110p' i18n/zh/docusaurus-plugin-content-docs/current/troubleshooting/scheduling-failures.md
printf '%s\n' '--- HAMi v2.9.0 reason constants ---'
curl -fsSL https://raw.githubusercontent.com/Project-HAMi/HAMi/v2.9.0/pkg/device/common/common.go |
  grep -n -E 'Reason|Card|Mig|const|iota' | head -120
printf '%s\n' '--- HAMi v2.9.0 source status ---'
curl -fsSL -o /tmp/hami-v2.9.0-common.go \
  https://raw.githubusercontent.com/Project-HAMi/HAMi/v2.9.0/pkg/device/common/common.go
sha256sum /tmp/hami-v2.9.0-common.go

Repository: Project-HAMi/website

Length of output: 15122


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- complete HAMi v2.9.0 declaration block ---'
sed -n '20,42p' /tmp/hami-v2.9.0-common.go
printf '%s\n' '--- parsed string-valued constants in the block ---'
python3 - <<'PY'
from pathlib import Path
import re
text = Path('/tmp/hami-v2.9.0-common.go').read_text()
block = re.search(r'const\s*\((.*?)\n\)', text, re.S).group(1)
items = re.findall(r'^\s*(\w+)\s*=\s*"([^"]+)"', block, re.M)
print(f'count={len(items)}')
for name, value in items:
    print(f'{name}={value}')
print('CardMigTopologyInfeasible present:', 'CardMigTopologyInfeasible' in text)
PY
printf '%s\n' '--- referenced documentation targets ---'
for f in \
  docs/userguide/device-supported.md \
  docs/userguide/nvidia-device/dynamic-mig-support.md \
  i18n/zh/docusaurus-plugin-content-docs/current/userguide/device-supported.md \
  i18n/zh/docusaurus-plugin-content-docs/current/userguide/nvidia-device/dynamic-mig-support.md
do
  if git ls-files --error-unmatch "$f" >/dev/null 2>&1; then
    printf 'present %s\n' "$f"
  else
    printf 'missing %s\n' "$f"
  fi
done
printf '%s\n' '--- matching reason-code uses in v2.9.0 source ---'
grep -R -n --exclude-dir=.git 'CardMigTopologyInfeasible' /tmp/hami-v2.9.0-common.go docs i18n/zh/docusaurus-plugin-content-docs/current 2>/dev/null || true

Repository: Project-HAMi/website

Length of output: 3039


Align the reason-code documentation with HAMi v2.9.0.

HAMi v2.9.0 defines 16 reason constants and does not define CardMigTopologyInfeasible. Update the English and Chinese introductions to link to the v2.9.0 source and state 16 codes. Remove the unsupported row from both tables or move it to a matching HAMi version.

🧰 Tools
🪛 LanguageTool

[grammar] ~12-~12: Ensure spelling is correct
Context: ...ommon/common.go` in the HAMi repository. The list below reflects HAMi v2.9.0. Ol...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

📍 Affects 2 files
  • docs/troubleshooting/scheduling-failures.md#L12-L12 (this comment)
  • docs/troubleshooting/scheduling-failures.md#L95-L96
  • i18n/zh/docusaurus-plugin-content-docs/current/troubleshooting/scheduling-failures.md#L13-L13
  • i18n/zh/docusaurus-plugin-content-docs/current/troubleshooting/scheduling-failures.md#L96-L97
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/troubleshooting/scheduling-failures.md` at line 12, Align the HAMi
reason-code documentation with v2.9.0: in
docs/troubleshooting/scheduling-failures.md at lines 12 and 95-96, update the
introduction to link to the v2.9.0 source and state 16 codes, then remove
CardMigTopologyInfeasible from the table. Apply the same introduction and table
changes in
i18n/zh/docusaurus-plugin-content-docs/current/troubleshooting/scheduling-failures.md
at lines 13 and 96-97.

Source: MCP tools


:::

## Step 1: Read the Pod events

```bash
kubectl describe pod <pod-name> -n <namespace>
```

The `Events` section holds two different kinds of message. Both matter:

```plaintext
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling 15s default-scheduler 0/3 nodes are available: 3 NodeUnfitPod.
Warning FilteringFailed 16s hami-scheduler 2 nodes CardInsufficientMemory(node-a,node-b)
Warning FilteringFailed 16s hami-scheduler 1 nodes CardTypeMismatch(node-c)
```

- `FailedScheduling` comes from the Kubernetes scheduler. It only tells you how many nodes were rejected.
- `FilteringFailed` comes from `hami-scheduler` and carries the actual reason. **This is the line to act on.**

If you see no `FilteringFailed` event at all, the Pod never reached HAMi. Check that the Pod uses HAMi's scheduler and that the admission webhook is running. See [Verify HAMi](../get-started/verify-hami.md).

## Step 2: Decode the message

### The event format

```plaintext
<node-count> nodes <ReasonCode>(<node-a>,<node-b>,...)
```

Three properties of this format are easy to misread:

- **One event per reason code.** A cluster where two nodes ran out of memory and one node had the wrong card type produces _two_ `FilteringFailed` events, not one. Read all of them before concluding anything.
- **These events appear only when no node fits.** If at least one node is viable, the Pod is scheduled and you get a single `FilteringSucceed` event instead, even though other nodes were rejected.
- **The node list is the set of nodes rejected for that specific reason.** A node can appear under more than one reason code, because different GPUs on the same node can fail for different reasons.

### The scheduler log format

The events aggregate per node. To see which _device_ failed and why, read the scheduler extender log:

```bash
kubectl logs -n kube-system deploy/hami-scheduler -c vgpu-scheduler-extender --tail=200
```

At the default verbosity (`-v=4`) each rejected node produces one `NodeUnfitPod` line:

```plaintext
NodeUnfitPod pod="default/gpu-pod" node="node-a" reason="3/8 CardInsufficientMemory, 5/8 CardInsufficientCore"
```

Read the fraction as `<devices rejected for this reason>/<total devices of that type on the node>`. In the line above, node-a has 8 GPUs: 3 were short on memory and 5 were short on compute. Each rejected device is counted once, under the **first** check it failed, so a card that is short on both memory and compute appears only under `CardInsufficientMemory`. Fixing the reason with the largest count is not always the fastest route to a scheduled Pod.

:::warning The one exception to the fraction rule

`AllocatedCardsInsufficientRequest` inverts the numerator. There it counts the cards that **did** fit, not the ones that were rejected. `2/8 AllocatedCardsInsufficientRequest` means the node offered 2 usable cards for a request that needed more.

:::

Successful nodes log a matching `NodeFitPod` line with the score that decided the placement.

## Step 3: Look up the reason code

### Node-level rejections

| Reason code | What the scheduler found | What to do |
| --- | --- | --- |
| `NodeInsufficientDevice` | The node has fewer devices of the requested type than the Pod asks for. Evaluated before any per-device check. | Lower the card count, or add nodes with more cards. A 4-GPU request never fits a 2-GPU node, regardless of how idle it is. |
| `NodeUnfitPod` | Summary line: this node was rejected. It is always accompanied by the per-device reasons. | Read the per-device reasons in the same log line. |
| `NodeFitPod` | Not a failure. The node passed filtering and was scored. | Nothing. |

### The card was excluded before capacity was considered

| Reason code | What the scheduler found | What to do |
| --- | --- | --- |
| `CardNotHealth` | The device plugin reported the device as unhealthy, so it is skipped entirely. | Check the device plugin logs and `nvidia-smi` on that node. An unhealthy card is a node problem, not a request problem. |
| `CardTypeMismatch` | The card model does not satisfy the Pod's type constraints. | Review the `nvidia.com/use-gputype` / `nvidia.com/nouse-gputype` annotations. Also triggered by `nvidia.com/vgpu-mode` when the card does not run the requested mode. See [Specify device type to use](../userguide/nvidia-device/specify-device-type-to-use.md). |
| `CardUuidMismatch` | The card's UUID is excluded by the Pod's UUID constraints. | Review `nvidia.com/use-gpuuuid` / `nvidia.com/nouse-gpuuuid`. A stale UUID pinned in a Deployment template survives node replacement and silently blocks every rescheduling attempt. See [Specify device UUID to use](../userguide/nvidia-device/specify-device-uuid-to-use.md). |
| `NumaNotFit` | The Pod requires all its cards on one NUMA node, and the candidate cards span a NUMA boundary. | Only applies when the Pod sets `nvidia.com/numa-bind: "true"`. Drop the annotation if NUMA locality is not required, or request a card count that a single NUMA node can serve. |
| `ModeNotFit` | The node cannot run the requested virtualization mode for that vendor. | Vendor-specific. On Ascend, it means HAMi-core sharing was requested on a node that does not support it; on Enflame, that no GCU profile matches the request. |
| `CardNotFoundCustomFilterRule` | A vendor-specific filter rule rejected the card. | Consult the guide for that vendor under [User Guide](../userguide/device-supported.md). NVIDIA cards outside MIG mode never produce this code. |
| `CardMigTopologyInfeasible` | The card is in MIG mode, but no allowed MIG profile with a free placement matches the requested memory. | The card may have free memory in total while still having no contiguous slot of the right shape. Align the request with a real MIG profile size, or drain the card. See [Dynamic MIG support](../userguide/nvidia-device/dynamic-mig-support.md). |

### The card matched but had no room

| Reason code | What the scheduler found | What to do |
| --- | --- | --- |
| `CardInsufficientMemory` | Free device memory is below the request: `total - used < requested`. | The most common code. Lower `nvidia.com/gpumem`, wait for a workload to finish, or add capacity. Remember that HAMi counts _allocated_ memory, not memory currently in use, so an idle-looking card can still be full. |
| `CardInsufficientCore` | Free compute percentage is below `nvidia.com/gpucores`. | Lower `gpucores`, or place the Pod on a card with fewer streaming workloads. |
| `CardTimeSlicingExhausted` | The card already hosts the maximum number of tasks. | Each card accepts `deviceSplitCount` tasks (default 10) regardless of remaining memory. A card with plenty of free memory still rejects task 11. Raise the split count if the workloads are small enough to justify it. See [Global configuration](../userguide/configure.md). |
| `CardComputeUnitsExhausted` | The Pod requests no cores at all, and the card's compute is fully committed. | A request that omits `gpucores` is not free to place: it still cannot land on a card at 100% committed compute. Give the Pod an explicit `gpucores` value, or free compute on the card. |
| `ExclusiveDeviceAllocateConflict` | Exclusive use was requested for a card that is already shared, or a shared request hit a card held exclusively. | Raised either when `nvidia.com/gpucores: 100` is requested on a card with existing tasks, or when the `mutex` GPU scheduler policy is in effect. See [Scheduling policy](../userguide/nvidia-device/scheduling-policy.md). |
| `ResourceQuotaNotFit` | The allocation would exceed the namespace's HAMi `ResourceQuota`. | A cluster-capacity problem in disguise: the cards are free, the namespace budget is not. See [Using ResourceQuota](../userguide/nvidia-device/using-resourcequota.md). |
Comment on lines +106 to +107

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- candidate files ---'
git ls-files | rg '(^|/)(scheduling-failures\.md|device\.go)$|nvidia/.*/device\.go$' | head -80
printf '%s\n' '--- documentation context ---'
for f in docs/troubleshooting/scheduling-failures.md \
  i18n/zh/docusaurus-plugin-content-docs/current/troubleshooting/scheduling-failures.md; do
  if [ -f "$f" ]; then
    printf '\n--- %s ---\n' "$f"
    sed -n '1,145p' "$f"
  fi
done
printf '%s\n' '--- local reason-code references ---'
rg -n -C 3 'ExclusiveDeviceAllocateConflict|ResourceQuotaNotFit|mutex|gpucores' --glob '*.go' --glob '*.md' --glob '*.yaml' --glob '*.yml' . | head -300

Repository: Project-HAMi/website

Length of output: 48176


🏁 Script executed:

#!/bin/bash
set -eu
tmpdir="$(mktemp -d)"
trap 'rm -rf "$tmpdir"' EXIT

for spec in \
  "device.go|https://raw.githubusercontent.com/Project-HAMi/HAMi/refs/tags/v2.9.0/pkg/device/nvidia/device.go" \
  "score.go|https://raw.githubusercontent.com/Project-HAMi/HAMi/refs/tags/v2.9.0/pkg/scheduler/score.go" \
  "common.go|https://raw.githubusercontent.com/Project-HAMi/HAMi/refs/tags/v2.9.0/pkg/device/common/common.go"; do
  name="${spec%%|*}"
  url="${spec#*|}"
  if curl -fsSL "$url" -o "$tmpdir/$name"; then
    printf '\n--- %s ---\n' "$url"
    rg -n -C 8 'ExclusiveDeviceAllocateConflict|mutex|gpucores|used' "$tmpdir/$name" || true
  else
    printf '\n--- unavailable: %s ---\n' "$url"
  fi
done

printf '\n--- all v2.9.0 source paths mentioning the reason ---\n'
api='https://api.github.com/repos/Project-HAMi/HAMi/git/trees/v2.9.0?recursive=1'
curl -fsSL "$api" |
  jq -r '.tree[] | select(.type == "blob") | .path' |
  while read -r path; do
    case "$path" in
      *.go)
        url="https://raw.githubusercontent.com/Project-HAMi/HAMi/refs/tags/v2.9.0/$path"
        body="$(curl -fsSL "$url" 2>/dev/null || true)"
        if printf '%s' "$body" | grep -q 'ExclusiveDeviceAllocateConflict'; then
          printf '%s\n' "$path"
          printf '%s' "$body" | grep -n -C 8 'ExclusiveDeviceAllocateConflict'
        fi
        ;;
    esac
  done

Repository: Project-HAMi/website

Length of output: 4657


🌐 Web query:

site:github.com/Project-HAMi/HAMi v2.9.0 "ExclusiveDeviceAllocateConflict" mutex

💡 Result:

In the context of the Project-HAMi (HAMi) project, ExclusiveDeviceAllocateConflict is a scheduling failure reason indicating that a container’s request for exclusive device access cannot be satisfied because the targeted device is already in use [1][2][3]. The mention of mutex in relation to this conflict refers to a specific GPU scheduling policy introduced to enforce exclusive access [4]. When a pod sets the annotation hami.io/gpu-scheduler-policy to mutex, the HAMi scheduler is instructed to allocate only those GPUs that have no existing users (where the usage count is 0) [4]. Key points regarding this functionality include: 1. Policy Enforcement: The mutex policy ensures that if a device is already occupied (Used > 0), the scheduler's Fit function will reject that device, triggering the ExclusiveDeviceAllocateConflict reason [4][3]. 2. Diagnostic Clarity: In earlier versions, this conflict was sometimes misreported as other errors (such as NumaNotFit) [4]. Updates were implemented to ensure that the ExclusiveDeviceAllocateConflict reason is explicitly reported when the mutex policy restricts device allocation, improving diagnostic clarity for users [4][5]. 3. Reporting: This reason appears in scheduler logs and event messages when the system determines that a node or device cannot fulfill the pod's requirement for exclusive access due to current device occupancy [1][6]. While v2.9.0 is a specific release version of HAMi [7], the ExclusiveDeviceAllocateConflict reason is a persistent part of the HAMi scheduling diagnostic framework, used across multiple versions to signify that a requested exclusive device is unavailable [1][8][2].

Citations:


🏁 Script executed:

#!/bin/bash
set -eu
base='https://api.github.com/repos/Project-HAMi/HAMi'
commit='06d9b907f19e78551d616e106209d2a98ec20f01'

printf '%s\n' '--- cited commit metadata ---'
curl -fsSL "$base/commits/$commit" |
  jq -r '[.sha, .commit.message, (.files | length | tostring)] | `@tsv`'

printf '%s\n' '--- cited commit file patches ---'
curl -fsSL "$base/commits/$commit" |
  jq -r '.files[] | select((.patch // "") | test("ExclusiveDeviceAllocateConflict|gpu-scheduler-policy|mutex")) |
    "\nFILE: \(.filename)\n\(.patch)"'

printf '%s\n' '--- v2.9.0 tag resolution ---'
curl -fsSL "$base/git/ref/tags/v2.9.0" |
  jq -r '"type=\(.object.type) sha=\(.object.sha)"'

Repository: Project-HAMi/website

Length of output: 399


🏁 Script executed:

#!/bin/bash
set -eu
tmp="$(mktemp)"
trap 'rm -f "$tmp"' EXIT

for url in \
  'https://github.com/Project-HAMi/HAMi/commit/06d9b907f19e78551d616e106209d2a98ec20f01.patch' \
  'https://github.com/Project-HAMi/HAMi/commit/06d9b907f19e78551d616e106209d2a98ec20f01.diff'; do
  printf '\n--- %s ---\n' "$url"
  if curl -fsSL "$url" -o "$tmp"; then
    rg -n -C 12 'ExclusiveDeviceAllocateConflict|gpu-scheduler-policy|mutex|Fit\(' "$tmp" || cat "$tmp"
  else
    printf '%s\n' 'unavailable'
  fi
done

printf '\n--- v2.9.0 NVIDIA policy-related source references ---\n'
for path in \
  pkg/device/nvidia/device.go \
  pkg/device/nvidia/nvidia.go \
  pkg/device/nvidia/nvidia_test.go \
  pkg/device/common/common.go \
  pkg/device/device.go \
  pkg/scheduler/scheduler.go \
  pkg/scheduler/score.go; do
  url="https://raw.githubusercontent.com/Project-HAMi/HAMi/refs/tags/v2.9.0/$path"
  body="$(curl -fsSL "$url" 2>/dev/null || true)"
  if [ -n "$body" ]; then
    hits="$(printf '%s' "$body" | rg -n -C 6 'ExclusiveDeviceAllocateConflict|gpu-scheduler-policy|mutex|NodeLockNvidia' || true)"
    if [ -n "$hits" ]; then
      printf '\nFILE: %s\n%s\n' "$path" "$hits"
    fi
  fi
done

Repository: Project-HAMi/website

Length of output: 50377


🏁 Script executed:

#!/bin/bash
set -eu
python3 - <<'PY'
from urllib.request import urlopen

tag_url = "https://raw.githubusercontent.com/Project-HAMi/HAMi/refs/tags/v2.9.0/pkg/device/nvidia/device.go"
patch_url = "https://github.com/Project-HAMi/HAMi/commit/06d9b907f19e78551d616e106209d2a98ec20f01.patch"

tag = urlopen(tag_url).read().decode()
patch = urlopen(patch_url).read().decode()

tag_conflict = "if dev.Totalcore == 100 && k.Coresreq == 100 && dev.Used > 0"
tag_mutex_guard = "if isMutex && dev.Used > 0"
patch_nvidia_mutex = "+\t\tif isMutex && dev.Used > 0"
patch_reason = "+\t\t\treason[common.ExclusiveDeviceAllocateConflict]++"

print("v2.9.0 contains exclusive-used guard:", tag_conflict in tag)
print("v2.9.0 contains mutex guard:", tag_mutex_guard in tag)
print("later patch adds NVIDIA mutex guard:", patch_nvidia_mutex in patch)
print("later patch reports ExclusiveDeviceAllocateConflict for mutex:", patch_reason in patch)
print("v2.9.0 conflict-branch count:", tag.count("reason[common.ExclusiveDeviceAllocateConflict]++"))
PY

Repository: Project-HAMi/website

Length of output: 2444


Remove the unsupported mutex trigger from both locales.

In HAMi v2.9.0, the NVIDIA implementation emits ExclusiveDeviceAllocateConflict only when k.Coresreq == 100 and the card already has tasks. The mutex rejection path was added later and is absent from v2.9.0. The general shared-request wording is also unsupported.

  • docs/troubleshooting/scheduling-failures.md#L106-L107: describe only the v2.9.0 exclusive request on a used card.
  • i18n/zh/docusaurus-plugin-content-docs/current/troubleshooting/scheduling-failures.md#L107-L108: apply the same correction.
📍 Affects 2 files
  • docs/troubleshooting/scheduling-failures.md#L106-L107 (this comment)
  • i18n/zh/docusaurus-plugin-content-docs/current/troubleshooting/scheduling-failures.md#L107-L108
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/troubleshooting/scheduling-failures.md` around lines 106 - 107, Update
the ExclusiveDeviceAllocateConflict description in
docs/troubleshooting/scheduling-failures.md lines 106-107 and
i18n/zh/docusaurus-plugin-content-docs/current/troubleshooting/scheduling-failures.md
lines 107-108 to describe only the v2.9.0 case where k.Coresreq == 100 is
requested on a card that already has tasks; remove the mutex scheduler trigger
and unsupported general shared-request wording from both locales.

Source: MCP tools

| `AllocatedCardsInsufficientRequest` | Some cards on the node fit, but fewer than the requested count. | The node is partially usable. Reduce the card count, or free enough cards on one node. HAMi does not split one container's cards across nodes. |

## Step 4: Get per-device detail when the node summary is not enough

The `NodeUnfitPod` summary tells you _how many_ devices failed, not _which_ ones. Device identity is logged at `-v=5`:

```bash
helm upgrade hami hami-charts/hami \
--namespace kube-system \
--reuse-values \
--set-json 'scheduler.extender.extraArgs=["--debug","-v=5"]'

Comment on lines +115 to +119

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- target files ---'
for f in \
  docs/troubleshooting/scheduling-failures.md \
  i18n/zh/docusaurus-plugin-content-docs/current/troubleshooting/scheduling-failures.md
do
  printf '\n### %s\n' "$f"
  nl -ba "$f" | sed -n '1,155p'
done

printf '\n--- chart references and extraArgs definitions ---\n'
rg -n --hidden -S \
  'scheduler\.extender\.extraArgs|extraArgs:|hami-charts/hami|version:' \
  --glob '!node_modules' --glob '!dist' --glob '!build' .

Repository: Project-HAMi/website

Length of output: 36028


🏁 Script executed:

#!/bin/bash
set -eu

for f in \
  docs/troubleshooting/scheduling-failures.md \
  i18n/zh/docusaurus-plugin-content-docs/current/troubleshooting/scheduling-failures.md \
  docs/developers/profiling-scheduler.md \
  i18n/zh/docusaurus-plugin-content-docs/current/developers/profiling-scheduler.md \
  docs/installation/upgrade.md
do
  printf '\n### %s\n' "$f"
  sed -n '1,155p' "$f" | awk '{printf "%5d  %s\n", NR, $0}'
done

printf '\n--- all repository definitions/usages of scheduler.extender.extraArgs ---\n'
rg -n -C 8 --hidden -S \
  'scheduler\.extender\.extraArgs|extraArgs:' \
  --glob '!node_modules' --glob '!dist' --glob '!build' .

printf '\n--- available Helm tooling ---\n'
command -v helm || true

Repository: Project-HAMi/website

Length of output: 50378


🌐 Web query:

Helm upgrade --reuse-values --set-json array override precedence official documentation

💡 Result:

When using helm upgrade with --reuse-values, the behavior regarding precedence and merging is defined by how Helm handles existing versus new values [1][2][3]. Precedence and Merging Behavior 1. --reuse-values takes the computed values from the previous release, rather than the default values.yaml from the new chart [3][4]. 2. Any values provided via --set, --set-json, --values, or other override flags are then merged into these existing values [1][2][5]. 3. In this merge, the new values from your command-line flags take precedence over the previously stored values [1][2]. Array Handling with --set-json When using --set-json or --set to override arrays, it is important to understand that Helm treats arrays as single entities during the merge process [6]. Unlike maps (which are merged key-by-key), arrays are typically overwritten entirely by the new value provided [6]. If you provide an array via --set-json, that array will replace the entire array currently stored in the release values [6]. It will not perform a partial merge or append items to an existing array unless you explicitly include the original items in your new JSON input [6]. Summary of Precedence - New overrides (--set, --set-json, --values) always take priority over the values reused from the previous release [1][2]. - When multiple override flags are used in the same command, the right-most (last) flag specified takes precedence [2]. - Because --reuse-values explicitly excludes the new chart's default values.yaml, any changes to default values in a new chart version will be ignored unless you explicitly apply those values via a -f or --set flag [3][4].

Citations:


🏁 Script executed:

#!/bin/bash
set -eu

python3 - <<'PY'
import json
import re
from pathlib import Path

files = [
    Path("docs/troubleshooting/scheduling-failures.md"),
    Path("i18n/zh/docusaurus-plugin-content-docs/current/troubleshooting/scheduling-failures.md"),
]

found = []
for path in files:
    text = path.read_text()
    for match in re.finditer(
        r"helm upgrade hami hami-charts/hami \\\n"
        r"\s+--namespace kube-system \\\n"
        r"\s+--reuse-values \\\n"
        r"\s+--set-json 'scheduler\.extender\.extraArgs=(\[[^']+\])'",
        text,
    ):
        args = json.loads(match.group(1))
        found.append((str(path), args, "--version" in match.group(0)))

assert len(found) == 4, found
assert {tuple(args) for _, args, _ in found} == {
    ("--debug", "-v=5"),
    ("--debug", "-v=4"),
}
assert all(not has_version for _, _, has_version in found)

for path, args, _ in found:
    print(f"{path}: extraArgs={args}; chart_version_pinned=False")
PY

Repository: Project-HAMi/website

Length of output: 654


Pin the chart version and preserve the complete extraArgs array.

Without --version, each command can upgrade to the latest chart. --set-json replaces the existing scheduler.extender.extraArgs array and can remove custom flags. Apply both safeguards to the -v=5 and -v=4 commands in the English and Chinese files.

📍 Affects 2 files
  • docs/troubleshooting/scheduling-failures.md#L115-L119 (this comment)
  • docs/troubleshooting/scheduling-failures.md#L131-L136
  • i18n/zh/docusaurus-plugin-content-docs/current/troubleshooting/scheduling-failures.md#L115-L119
  • i18n/zh/docusaurus-plugin-content-docs/current/troubleshooting/scheduling-failures.md#L132-L137
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/troubleshooting/scheduling-failures.md` around lines 115 - 119, Update
all four documented Helm upgrade commands in
docs/troubleshooting/scheduling-failures.md lines 115-119 and 131-136, and
i18n/zh/docusaurus-plugin-content-docs/current/troubleshooting/scheduling-failures.md
lines 115-119 and 132-137: pin each chart with the documented version and
preserve the complete existing scheduler.extender.extraArgs array while adding
the -v=5 or -v=4 flag, rather than replacing the array.

Source: MCP tools

kubectl rollout status deploy/hami-scheduler -n kube-system
```

Re-create the Pod, then read the log again. Each rejected device now names itself:

```plaintext
CardInsufficientMemory pod="default/gpu-pod" node="node-a" device="GPU-62b7408e-edb2-41d1-bc91-f46165c61130" device total memory=40960 device used memory=39000 request memory=8000
```

`-v=5` is verbose in proportion to cluster size: a 10-node cluster with 8 GPUs per node emits up to 80 lines for a single failed Pod. Return to the default once you have the answer:

```bash
helm upgrade hami hami-charts/hami \
--namespace kube-system \
--reuse-values \
--set-json 'scheduler.extender.extraArgs=["--debug","-v=4"]'
```

## Messages that carry no reason code

Some failures are reported before per-device filtering runs, so they never produce a reason code.

| Message | Meaning |
| --- | --- |
| `no available node, N nodes do not meet` | Every candidate node was rejected. The specific reasons are in the other `FilteringFailed` events on the same Pod, so do not stop reading at this one. |
| `no available node, all node scores do not meet` | Same situation, reported by older HAMi versions without per-reason breakdown. Fall back to the scheduler log. |
| `node unregistered` (log only, `-v=5`) | The node has no HAMi device registration. Either its device plugin is not running, or it genuinely has no supported accelerator. Check `kubectl get node <name> -o jsonpath='{.metadata.annotations}'` for `hami.io/node-*-register`. |
| `Device type not found` | The Pod requests a device type the scheduler was not built or configured to handle, for example an Ascend request on a scheduler started without `--enable-ascend=true`. |

:::note Why some reasons never reach the events

The aggregated events are built by parsing the `<n>/<m> <Code>` fractions out of each node's reason string. Reason codes reported bare, without a fraction (`NodeInsufficientDevice` is the notable one), are recorded against the node but do not become their own `FilteringFailed` event. If the events look thinner than the failure, read the scheduler log.

:::

## Putting it together

```mermaid
%% title: Diagnosing a Pending HAMi Pod
flowchart TD
A["Pod stays Pending"] --> B{"FilteringFailed<br/>event present?"}
B -->|No| C["Pod never reached HAMi:<br/>check schedulerName<br/>and admission webhook"]
B -->|Yes| D["Read every FilteringFailed event<br/>one per reason code"]
D --> E{"Reason code<br/>recognised?"}
E -->|Yes| F["Look up the code<br/>in the tables above"]
E -->|No| G["Read NodeUnfitPod lines<br/>in the scheduler log"]
G --> H{"Need the<br/>failing device?"}
H -->|Yes| I["Raise extender to -v=5,<br/>reproduce, revert to -v=4"]
H -->|No| F
```

## Related Pages

- [Troubleshooting](./troubleshooting.md): installation and runtime problems that are not scheduling decisions
- [Scheduler Event Log](../developers/scheduler-event-log.md): the design behind these events and logs
- [Scheduling Policy](../userguide/nvidia-device/scheduling-policy.md): node and GPU selection policies that change which cards are considered
- [FAQ](../faq/faq.md)
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ translated: true
sidebar_label: 调度器事件日志
---

本页记录的是调度器事件与日志格式的设计。如果你正在排查一个卡在 `Pending` 状态的 Pod,请改看[调度失败排查](../troubleshooting/scheduling-failures.md):那里列出了调度器会输出的每一个原因码及其处理方式。

## 当前状态

### 模糊的事件描述使问题诊断变得困难
Expand Down
Loading