From fcdb5e8ba48bff3f0243f90c6d7a0ae5d4fc39d1 Mon Sep 17 00:00:00 2001 From: rudrakshkarpe Date: Fri, 24 Jul 2026 03:52:03 +0530 Subject: [PATCH 1/3] =?UTF-8?q?docs(tutorials):=20add=20Lab=2011=20?= =?UTF-8?q?=E2=80=94=20Run=20SGLang=20on=20HAMi=20GPU=20Shares?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a new intermediate lab that installs HAMi on an existing NVIDIA GPU cluster and schedules an SGLang inference service on a HAMi GPU share. The lab is the SGLang counterpart to Lab 6 (vLLM): it walks through the HAMi install, deploys Qwen3-1.7B with nvidia.com/gpu, nvidia.com/gpumem and nvidia.com/gpucores caps, tests the OpenAI-compatible API, and verifies that the gpumem cap is enforced inside the Pod. All commands and outputs are captured from a verification run on a kind cluster backed by a single NVIDIA H100 80GB. Addresses Project-HAMi/website#528 Co-authored-by: Shivay Lamba <19529592+shivaylamba@users.noreply.github.com> Signed-off-by: rudrakshkarpe --- tutorials/labs/hami-sglang.md | 547 ++++++++++++++++++++++++++++++++++ 1 file changed, 547 insertions(+) create mode 100644 tutorials/labs/hami-sglang.md diff --git a/tutorials/labs/hami-sglang.md b/tutorials/labs/hami-sglang.md new file mode 100644 index 00000000..1d12c346 --- /dev/null +++ b/tutorials/labs/hami-sglang.md @@ -0,0 +1,547 @@ +--- +title: "Lab 11: Run SGLang on HAMi GPU Shares" +description: "Install HAMi on a GPU cluster and schedule SGLang inference services with GPU partitioning." +sidebar_label: "Lab 11: SGLang Inference" +lab: + level: Intermediate + duration: about 45 minutes + environment: Kubernetes cluster with NVIDIA GPUs + authors: + - rudrakshkarpe + - shivaylamba + verified: "2026-07-23" +tags: + - sglang + - inference + - hami +toc_max_heading_level: 2 +--- + +This lab demonstrates how to install HAMi on a Kubernetes cluster that already has NVIDIA GPUs, and use HAMi to schedule an [SGLang](https://github.com/sgl-project/sglang) inference service. Upon completion, you will have an OpenAI-compatible model service that can be verified through `/v1/models` and `/v1/chat/completions`, with HAMi enforcing GPU memory and compute caps inside the Pod. + +This guide is modeled after [Lab 6: Run vLLM on HAMi GPU Shares](./hami-vllm). The steps are not tied to a specific cloud vendor. As long as your Kubernetes cluster has available NVIDIA GPUs, NVIDIA drivers, and container runtime support, you can reproduce the same setup. + +## Learning Objectives + +- Verify that an existing GPU Kubernetes cluster meets the prerequisites for HAMi and SGLang +- Install HAMi scheduler and device plugin +- Add labels required by the HAMi DaemonSet to GPU nodes +- Run SGLang using HAMi's `nvidia.com/gpu`, `nvidia.com/gpumem`, and `nvidia.com/gpucores` resources +- Test the SGLang OpenAI-compatible API via port forwarding +- Confirm that `nvidia.com/gpumem` is enforced inside the SGLang Pod + +## Lab Overview + +```mermaid +%% title: HAMi + SGLang Lab Flowchart +flowchart LR + Step1["Step 1
Check GPU Cluster"] --> Step2["Step 2
Install HAMi"] + Step2 --> Step3["Step 3
Verify HAMi Resources"] + Step3 --> Step4["Step 4
Deploy SGLang"] + Step4 --> Step5["Step 5
Expose Service"] + Step5 --> Step6["Step 6
Test Inference"] + Step6 --> Step7["Step 7
Check GPU Allocation"] + Step7 --> Step8["Step 8
Cleanup"] +``` + +## Deployment Architecture + +```mermaid +%% title: HAMi + SGLang Deployment Architecture +flowchart TB + Client["Client
curl / OpenAI SDK"] --> PF["port-forward / LoadBalancer"] + PF --> SVC["SGLang Service
port 8001"] + SVC --> POD["SGLang Pod
Qwen3-1.7B
1 GPU share / 25000 MiB / 30% cores"] + + subgraph K8S["Kubernetes GPU Cluster"] + HAMI_S["hami-scheduler"] + HAMI_D["hami-device-plugin
DaemonSet"] + POD + N1["GPU Node
NVIDIA H100 / A10 / ..."] + end + + HAMI_S --> POD + HAMI_D --> N1 + POD --> N1 +``` + +## Prerequisites + +You need to prepare in advance: + +- A working Kubernetes cluster +- At least 1 NVIDIA GPU node with enough free memory for the model (this guide uses a single NVIDIA H100 80GB; an A10/L40S-class GPU also works if you keep the small model and `gpumem` values) +- `kubectl` connected to the cluster +- `helm` 3.x +- GPU nodes with NVIDIA drivers and NVIDIA Container Toolkit / runtime support installed +- The cluster can pull the SGLang image (`lmsysorg/sglang`) and download model weights (Hugging Face Hub or a mirror) + +> **Kind note:** If you use [kind](https://kind.sigs.k8s.io/) with GPU passthrough, install and configure `nvidia-container-toolkit` **inside** the kind node and set `default_runtime_name = "nvidia"` for containerd before installing HAMi. On managed GPU clusters this is usually already handled by the vendor GPU operator / device plugin stack. + +## Example Cluster State + +Below is the verification cluster used for this guide: one kind control-plane node backed by a host NVIDIA H100 80GB HBM3. + +```bash +kubectl get nodes -o wide +``` + +```plaintext +NAME STATUS ROLES AGE VERSION INTERNAL-IP OS-IMAGE CONTAINER-RUNTIME +hami-demo-control-plane Ready control-plane 2m v1.36.1 172.19.0.2 Debian GNU/Linux 13 (trixie) containerd://2.3.1 +``` + +Host GPU: + +```bash +nvidia-smi --query-gpu=index,name,memory.total --format=csv +``` + +```plaintext +index, name, memory.total [MiB] +0, NVIDIA H100 80GB HBM3, 81559 MiB +``` + +After HAMi is installed (see steps below), each physical GPU is registered as 10 schedulable shares: + +```bash +kubectl get nodes -o 'custom-columns=NAME:.metadata.name,GPU:.status.allocatable.nvidia\.com/gpu' +``` + +```plaintext +NAME GPU +hami-demo-control-plane 10 +``` + +HAMi component status: + +```bash +kubectl get pods -n kube-system -l app.kubernetes.io/instance=hami -o wide +``` + +```plaintext +NAME READY STATUS NODE +hami-device-plugin-... 2/2 Running hami-demo-control-plane +hami-scheduler-... 2/2 Running hami-demo-control-plane +``` + +## Step 1: Check the GPU Cluster + +Confirm that Kubernetes can see the GPU nodes: + +```bash +kubectl get nodes -o wide +kubectl describe node | grep -A8 -E "Capacity:|Allocatable:" | grep -E "nvidia.com/gpu|cpu:|memory:" +``` + +If the cluster already has a vendor NVIDIA device plugin installed, you may already see `nvidia.com/gpu`. After installing HAMi, `nvidia.com/gpu` becomes the number of vGPUs exposed by HAMi. + +HAMi's device plugin matches `gpu=on` when managed node selectors are enabled. Label your GPU nodes: + +```bash +kubectl label node gpu=on --overwrite +``` + +On Alibaba Cloud ACK, you can also select by vendor labels, for example: + +```bash +kubectl label nodes \ + -l aliyun.accelerator/xpu_type=nvidia \ + gpu=on \ + --overwrite +``` + +## Step 2: Install HAMi + +Add the HAMi Helm repo: + +```bash +helm repo add hami-charts https://project-hami.github.io/HAMi +helm repo update hami-charts +``` + +Create a values file (save as `hami-values.yaml`): + +```yaml +device: + nvidia: + driver: + enabled: false + +global: + managedNodeSelectorEnable: true + managedNodeSelector: + gpu: "on" + +devicePlugin: + deviceSplitCount: 10 + +scheduler: + # Match your cluster Kubernetes minor version (example: kind v1.36.1). + kubeScheduler: + imageTag: "v1.36.1" + leaderElect: false +``` + +Key configuration details: + +| Configuration | Description | +| --- | --- | +| `device.nvidia.driver.enabled: false` | Nodes already have NVIDIA drivers. | +| `global.managedNodeSelector.gpu: "on"` | Only schedule the HAMi device plugin to GPU nodes labeled `gpu=on`. | +| `devicePlugin.deviceSplitCount: 10` | Register each physical GPU as 10 vGPUs. | +| `scheduler.kubeScheduler.imageTag` | Must match the cluster Kubernetes version. | +| `scheduler.leaderElect: false` | Single-replica lab scheduler; avoids extender leader-election waits. | + +Install HAMi: + +```bash +helm upgrade --install hami hami-charts/hami \ + -n kube-system \ + -f hami-values.yaml \ + --version 2.9.0 +``` + +> On some ACK Kubernetes 1.36 clusters, the built-in kube-scheduler in HAMi also needs DRA-related RBAC. If the scheduler logs show `resource.k8s.io` permission errors, apply the same DRA RBAC helper used in [Lab 6](./hami-vllm). + +Wait for components to be running: + +```bash +kubectl rollout status deployment/hami-scheduler -n kube-system +kubectl rollout status daemonset/hami-device-plugin -n kube-system +``` + +Expected result: + +```plaintext +deployment "hami-scheduler" successfully rolled out +daemon set "hami-device-plugin" successfully rolled out +``` + +## Step 3: Verify HAMi Resources + +```bash +kubectl get pods -n kube-system -l app.kubernetes.io/instance=hami -o wide +kubectl get nodes -o 'custom-columns=NAME:.metadata.name,GPU:.status.allocatable.nvidia\.com/gpu' +``` + +You should see `hami-scheduler` and `hami-device-plugin` Running, and each GPU node advertising `nvidia.com/gpu` equal to `deviceSplitCount` (10 in this lab). + +If the device plugin is not Ready, check labels and DaemonSet node selector: + +```bash +kubectl get nodes -L gpu +kubectl get ds hami-device-plugin -n kube-system -o wide +kubectl logs -n kube-system -l app.kubernetes.io/instance=hami -c device-plugin --tail=50 +``` + +## Step 4: Deploy SGLang with HAMi Resources + +This lab deploys **Qwen3-1.7B** with SGLang. The Pod requests one HAMi GPU share, **25000 MiB** GPU memory, and **30%** GPU cores so multiple inference workloads can still share a large GPU. + +Apply the manifests (self-contained): + +```bash +kubectl apply -f - <<'EOF' +apiVersion: v1 +kind: Namespace +metadata: + name: sglang +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: sglang-qwen3-17b + namespace: sglang + labels: + app.kubernetes.io/name: sglang-qwen3-17b +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: sglang-qwen3-17b + template: + metadata: + labels: + app.kubernetes.io/name: sglang-qwen3-17b + annotations: + hami.io/node-scheduler-policy: binpack + hami.io/gpu-scheduler-policy: binpack + spec: + schedulerName: hami-scheduler + containers: + - name: sglang + image: lmsysorg/sglang:latest + imagePullPolicy: IfNotPresent + command: + - python3 + - -m + - sglang.launch_server + - --model-path=Qwen/Qwen3-1.7B + - --host=0.0.0.0 + - --port=30000 + - --mem-fraction-static=0.7 + - --context-length=8192 + - --attention-backend=triton + ports: + - name: http + containerPort: 30000 + resources: + requests: + cpu: "2" + memory: 8Gi + nvidia.com/gpu: "1" + nvidia.com/gpumem: "25000" + nvidia.com/gpucores: "30" + limits: + cpu: "8" + memory: 32Gi + nvidia.com/gpu: "1" + nvidia.com/gpumem: "25000" + nvidia.com/gpucores: "30" + readinessProbe: + httpGet: + path: /health + port: 30000 + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 90 + volumeMounts: + - name: dshm + mountPath: /dev/shm + volumes: + - name: dshm + emptyDir: + medium: Memory + sizeLimit: 8Gi +--- +apiVersion: v1 +kind: Service +metadata: + name: sglang-qwen3-17b + namespace: sglang +spec: + type: ClusterIP + selector: + app.kubernetes.io/name: sglang-qwen3-17b + ports: + - name: http + port: 8001 + targetPort: http +EOF +``` + +Key points: + +| Configuration | Description | +| --- | --- | +| `schedulerName: hami-scheduler` | Explicitly delegate scheduling to HAMi. | +| `nvidia.com/gpu: "1"` | Request 1 HAMi GPU device share. | +| `nvidia.com/gpumem: "25000"` | Hard GPU memory cap in MiB visible inside the container. | +| `nvidia.com/gpucores: "30"` | Cap SM/compute usage to 30%. | +| `hami.io/*-scheduler-policy: binpack` | Prefer packing workloads onto the same physical GPU. | +| `/dev/shm` Memory emptyDir | SGLang benefits from a larger shared-memory mount. | + +Wait for SGLang to become Ready (first start downloads the model and captures CUDA graphs): + +```bash +kubectl rollout status deployment/sglang-qwen3-17b -n sglang --timeout=30m +kubectl get pods -n sglang -o wide +``` + +Example output: + +```plaintext +NAME READY STATUS NODE +sglang-qwen3-17b-6d894b9655-ppxrw 1/1 Running hami-demo-control-plane +``` + +Check HAMi scheduling events: + +```bash +kubectl describe pod -n sglang -l app.kubernetes.io/name=sglang-qwen3-17b \ + | grep -E "hami-scheduler|Filtering|Binding" -A2 +``` + +You should see events such as: + +```plaintext +Successfully assigned sglang/... to hami-demo-control-plane +FilteringSucceed ... find fit node(hami-demo-control-plane)... +BindingSucceed ... Successfully binding node [hami-demo-control-plane] ... +``` + +## Step 5: Expose the SGLang Service + +For local verification, use port forwarding: + +```bash +kubectl -n sglang port-forward svc/sglang-qwen3-17b 8001:8001 +``` + +In another terminal: + +```bash +curl http://127.0.0.1:8001/v1/models +``` + +On cloud clusters you can also create a `LoadBalancer` Service or Ingress in front of `sglang-qwen3-17b`. Verify with port-forward first, then wire public exposure. + +## Step 6: Test Inference + +List models: + +```bash +curl -s http://127.0.0.1:8001/v1/models | python3 -m json.tool +``` + +Example output from the verification cluster: + +```json +{ + "object": "list", + "data": [ + { + "id": "Qwen/Qwen3-1.7B", + "object": "model", + "owned_by": "sglang", + "max_model_len": 8192 + } + ] +} +``` + +Send a chat completion request: + +```bash +curl -s http://127.0.0.1:8001/v1/chat/completions \ + -H "Content-Type: application/json" \ + -d '{ + "model": "Qwen/Qwen3-1.7B", + "messages": [ + {"role": "user", "content": "Explain in one sentence how HAMi and SGLang work together."} + ], + "max_tokens": 128, + "temperature": 0.2, + "chat_template_kwargs": {"enable_thinking": false} + }' | python3 -m json.tool +``` + +If the response contains `choices[0].message.content`, the SGLang inference service is working. + +> Qwen3 models may emit a thinking/reasoning channel depending on the chat template. Passing `"chat_template_kwargs": {"enable_thinking": false}` keeps the demo answer concise. + +## Step 7: Check GPU Allocation + +Confirm the Pod used HAMi scheduling and the expected resource limits: + +```bash +POD=$(kubectl get pod -n sglang -l app.kubernetes.io/name=sglang-qwen3-17b -o jsonpath='{.items[0].metadata.name}') +kubectl get pod -n sglang ${POD} \ + -o jsonpath='{.spec.schedulerName}{"\n"}{.spec.containers[0].resources.limits}{"\n"}' +``` + +Expected: + +```plaintext +hami-scheduler +... nvidia.com/gpu:1 nvidia.com/gpumem:25000 nvidia.com/gpucores:30 ... +``` + +Check environment variables injected by HAMi: + +```bash +kubectl exec -n sglang ${POD} -- env | grep -E 'CUDA_DEVICE|NVIDIA_VISIBLE' +``` + +Example from the verification cluster: + +```plaintext +NVIDIA_VISIBLE_DEVICES=GPU-04b76a6c-da10-342f-e9f5-5f5684eacb86 +CUDA_DEVICE_MEMORY_LIMIT_0=25000m +CUDA_DEVICE_SM_LIMIT=30 +``` + +Finally, check `nvidia-smi` **inside** the container: + +```bash +kubectl exec -n sglang ${POD} -- nvidia-smi +``` + +When `gpumem` is in effect, the total GPU memory visible inside the container is close to **25000 MiB**, not the full physical card (81559 MiB on the H100 verification host): + +```plaintext +| GPU Name ... | Memory-Usage | +| NVIDIA H100 80GB HBM3 ... | 18213MiB / 25000MiB | +``` + +On the host, the same card still reports the full capacity: + +```bash +nvidia-smi --query-gpu=memory.total,memory.used --format=csv +``` + +```plaintext +memory.total [MiB], memory.used [MiB] +81559 MiB, 18560 MiB +``` + +That contrast — full card on the host, capped slice in the Pod — is the key evidence that HAMi memory virtualization is working for SGLang. + +## Troubleshooting + +| Symptom | What to Check | +| --- | --- | +| `hami-device-plugin` CrashLoop / FailedPostStartHook | NVIDIA Container Toolkit must inject driver libs into Pods. On kind, configure the nvidia runtime inside the node. | +| `hami-device-plugin` not Ready | Node missing `gpu=on`, or node selector mismatch. | +| SGLang Pod Pending | `kubectl describe pod` for HAMi filter/bind events; confirm `gpumem` does not exceed physical GPU memory. | +| Image pull fails / disk full | `lmsysorg/sglang` images are large. Prune unused images or `kind load docker-image` from a pre-pulled host image. | +| SGLang starts slowly | First-time Hugging Face download + CUDA graph capture can take several minutes. Watch Pod logs. | +| `/v1/models` empty / connection refused | Wait until readiness probe passes; use `kubectl logs` and `kubectl port-forward` to the Pod port `30000` if the Service is wrong. | +| In-pod `nvidia-smi` still shows full GPU memory | Pod is not on the HAMi memory-limit path. Recheck `schedulerName`, resource limits, and HAMi webhook/scheduler events. | + +Common troubleshooting commands: + +```bash +kubectl get pods -A -o wide +kubectl describe pod -n sglang -l app.kubernetes.io/name=sglang-qwen3-17b +kubectl logs -n sglang -l app.kubernetes.io/name=sglang-qwen3-17b --tail=100 +kubectl logs -n kube-system deploy/hami-scheduler --tail=100 +kubectl get ds hami-device-plugin -n kube-system -o wide +``` + +## Cleanup + +```bash +kubectl delete namespace sglang --ignore-not-found +``` + +If this cluster is only used for this lab, you can also uninstall HAMi: + +```bash +helm uninstall hami -n kube-system +``` + +Optional label cleanup: + +```bash +kubectl label node gpu- +``` + +## Verification Results + +| Claim | Evidence | +| --- | --- | +| HAMi has taken over GPU scheduling | SGLang Pod uses `schedulerName: hami-scheduler` and requests `nvidia.com/gpu`, `nvidia.com/gpumem`, `nvidia.com/gpucores`. | +| GPU node runs HAMi device plugin | `hami-device-plugin` is Ready and advertises `nvidia.com/gpu=10`. | +| SGLang runs on HAMi resources | Pod Ready; HAMi injects `CUDA_DEVICE_MEMORY_LIMIT_0=25000m` and `CUDA_DEVICE_SM_LIMIT=30`. | +| Memory cap is visible in-container | In-pod `nvidia-smi` shows `... / 25000MiB` while host still shows `81559 MiB`. | +| Inference service is accessible | `/v1/models` returns `Qwen/Qwen3-1.7B`; chat endpoint returns content. | + +## Next Steps + +- Increase `replicas` and observe how HAMi packs multiple SGLang Pods with `binpack`. +- Lower `nvidia.com/gpumem` / `nvidia.com/gpucores` further and co-locate another small workload on the same GPU. +- Continue with [Lab 12: Serve Models from a KitOps ModelKit on HAMi](./hami-kitops) to replace the runtime Hugging Face download with an OCI ModelKit. +- For memory isolation and small-slice sharing patterns, see [Lab 3: GPU Partitioning](./gpu-partitioning). From 0c898f7d9179036cfdad04a77704998a23cf8b13 Mon Sep 17 00:00:00 2001 From: rudrakshkarpe Date: Fri, 24 Jul 2026 03:52:03 +0530 Subject: [PATCH 2/3] docs(tutorials): register Lab 11 (SGLang) in the tutorials sidebar Add the labs/hami-sglang entry to sidebars-tutorials.js so the new lab appears in the Labs category navigation. Co-authored-by: Shivay Lamba <19529592+shivaylamba@users.noreply.github.com> Signed-off-by: rudrakshkarpe --- sidebars-tutorials.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sidebars-tutorials.js b/sidebars-tutorials.js index e3881dc7..a6778ab0 100644 --- a/sidebars-tutorials.js +++ b/sidebars-tutorials.js @@ -61,6 +61,11 @@ module.exports = { id: "labs/topology-aware-scheduling", customProps: { level: "Intermediate", duration: "about 45 minutes" }, }, + { + type: "doc", + id: "labs/hami-sglang", + customProps: { level: "Intermediate", duration: "about 45 minutes" }, + }, ], }, ], From 74654fa9ca48f9cd7ab8fdf2e8d176663f969011 Mon Sep 17 00:00:00 2001 From: rudrakshkarpe Date: Fri, 24 Jul 2026 04:15:27 +0530 Subject: [PATCH 3/3] docs(tutorials): fix broken internal link in Lab 11 (SGLang) The "Next Steps" section linked to ./hami-kitops (Lab 12), which does not exist on master yet, so `docusaurus build` failed its broken-link check. Point at the tracking issue (website#561) instead, keeping this lab self-contained and independently buildable. Co-authored-by: Shivay Lamba <19529592+shivaylamba@users.noreply.github.com> Signed-off-by: rudrakshkarpe --- tutorials/labs/hami-sglang.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tutorials/labs/hami-sglang.md b/tutorials/labs/hami-sglang.md index 1d12c346..5e79a2ec 100644 --- a/tutorials/labs/hami-sglang.md +++ b/tutorials/labs/hami-sglang.md @@ -543,5 +543,5 @@ kubectl label node gpu- - Increase `replicas` and observe how HAMi packs multiple SGLang Pods with `binpack`. - Lower `nvidia.com/gpumem` / `nvidia.com/gpucores` further and co-locate another small workload on the same GPU. -- Continue with [Lab 12: Serve Models from a KitOps ModelKit on HAMi](./hami-kitops) to replace the runtime Hugging Face download with an OCI ModelKit. +- Deliver the model from an OCI registry instead of Hugging Face: a companion **KitOps ModelKit** lab replaces the runtime download with a `kitops-init` initContainer ([website#561](https://github.com/Project-HAMi/website/issues/561)). - For memory isolation and small-slice sharing patterns, see [Lab 3: GPU Partitioning](./gpu-partitioning).