{{ post.title }}
+ +{% include post-summary.html %}
+diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..5aa2070 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,9 @@ +.git +_site +.jekyll-cache +.jekyll-metadata +vendor +.bundle +Gemfile.lock +node_modules +assets/lib diff --git a/.gitignore b/.gitignore index 7dd7cef..f117f5c 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,7 @@ node_modules package-lock.json # IDE configurations +.claude .idea .vscode/* !.vscode/settings.json diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..e2d2697 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,17 @@ +FROM ruby:3.2-slim + +RUN apt-get update -qq && apt-get install -y --no-install-recommends \ + build-essential \ + git \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /srv/jekyll + +COPY Gemfile ./ +RUN bundle install + +COPY . . + +EXPOSE 4000 + +CMD ["bundle", "exec", "jekyll", "serve", "--host", "0.0.0.0", "--port", "4000"] diff --git a/README.md b/README.md index f8f4c5b..1f014a1 100644 --- a/README.md +++ b/README.md @@ -5,3 +5,61 @@ Personal blog covering platform engineering, Kubernetes, and AI/ML infrastructur Built with [Jekyll](https://jekyllrb.com) + [Chirpy](https://github.com/cotes2020/jekyll-theme-chirpy), hosted on GitHub Pages. 🌐 [allexistence.github.io](https://allexistence.github.io) + +## 1. Local development + +Requires [Docker](https://www.docker.com/) — no local Ruby/Jekyll install needed. + +```bash +docker compose up +``` + +Open [http://localhost:4000](http://localhost:4000). Edit any file and refresh the page — Jekyll +auto-rebuilds on save. If you edit `_config.yml`, restart instead of just refreshing: + +```bash +docker compose restart +``` + +Stop the server with `Ctrl+C`, or `docker compose down` if it's running in the background. If you +add a gem to the `Gemfile`, rebuild the image: + +```bash +docker compose up --build +``` + +## 2. How to add a post + +Create a new file in [`_posts/`](_posts/), inside a folder named after the post's date, e.g.: + +``` +_posts/YYYY-MM-DD/YYYY-MM-DD-a-short-slug.md +``` + +The date must appear in both the folder name (for organization) and the filename itself — Jekyll +only recognizes a post by its filename, not its folder, so the date prefix on the file is required +either way. + +Add front matter at the top of the file: + +```yaml +--- +title: "Post Title" +date: 2026-08-15 09:00:00 +0800 +categories: [Category Name] +tags: [tag-one, tag-two, tag-three] +--- +``` + +Then write the post in Markdown below the front matter. A few notes: + +- **Categories** are broad sections (shown on the `/categories/` page) — keep each post to one or + two. +- **Tags** are finer-grained keywords (shown on the `/tags/` page) — reuse existing tags where they + fit so the tag cloud stays useful; check `/tags/` for what already exists. +- The homepage shows the 4 most recent posts with a "See more posts" button — no extra config + needed, new posts just appear at the top automatically, newest first. +- Comments and emoji reactions are enabled by default on every post (via giscus) — nothing to turn + on per post. +- To link out to an externally-hosted post instead of writing one here, see any of the existing + Medium-redirect posts in `_posts/` for the pattern (a ` diff --git a/_layouts/home.html b/_layouts/home.html new file mode 100644 index 0000000..389b49c --- /dev/null +++ b/_layouts/home.html @@ -0,0 +1,144 @@ +--- +layout: default +--- + +{% include lang.html %} + +{% assign all_pinned = site.posts | where: 'pin', 'true' %} +{% assign all_normal = site.posts | where_exp: 'item', 'item.pin != true and item.hidden != true' %} + +{% assign posts = '' | split: '' %} + + + +{% assign visible_start = paginator.page | minus: 1 | times: paginator.per_page %} +{% assign visible_end = visible_start | plus: paginator.per_page %} + +{% if all_pinned.size > visible_start %} + {% if all_pinned.size > visible_end %} + {% assign pinned_size = paginator.per_page %} + {% else %} + {% assign pinned_size = all_pinned.size | minus: visible_start %} + {% endif %} + + {% for i in (visible_start..all_pinned.size) limit: pinned_size %} + {% assign posts = posts | push: all_pinned[i] %} + {% endfor %} +{% else %} + {% assign pinned_size = 0 %} +{% endif %} + + + +{% assign normal_size = paginator.posts | size | minus: pinned_size %} + +{% if normal_size > 0 %} + {% if pinned_size > 0 %} + {% assign normal_start = 0 %} + {% else %} + {% assign normal_start = visible_start | minus: all_pinned.size %} + {% endif %} + + {% assign normal_end = normal_start | plus: normal_size | minus: 1 %} + + {% for i in (normal_start..normal_end) %} + {% assign posts = posts | push: all_normal[i] %} + {% endfor %} +{% endif %} + +
+ + +{% if paginator.next_page %} + +{% endif %} diff --git a/_posts/.placeholder b/_posts/.placeholder deleted file mode 100644 index 8b13789..0000000 --- a/_posts/.placeholder +++ /dev/null @@ -1 +0,0 @@ - diff --git a/_posts/2026-06-06/2026-06-06-vsphere-vm-to-kubevirt-vm-migration-guide.md b/_posts/2026-06-06/2026-06-06-vsphere-vm-to-kubevirt-vm-migration-guide.md new file mode 100644 index 0000000..0ab4563 --- /dev/null +++ b/_posts/2026-06-06/2026-06-06-vsphere-vm-to-kubevirt-vm-migration-guide.md @@ -0,0 +1,10 @@ +--- +title: "vSphere VM to KubeVirt VM: Complete Migration Guide" +date: 2026-06-06 09:00:00 +0800 +categories: [Virtualization] +tags: [kubevirt, vsphere, migration, harvester] +--- + + + +Redirecting to Medium... [continue reading →](https://medium.com/@0.all_existence.0/vsphere-vm-to-kubevirt-vm-complete-migration-guide-959c90ef5950) diff --git a/_posts/2026-06-20/2026-06-20-openclaw-and-virtualisation-infrastructure.md b/_posts/2026-06-20/2026-06-20-openclaw-and-virtualisation-infrastructure.md new file mode 100644 index 0000000..e06e879 --- /dev/null +++ b/_posts/2026-06-20/2026-06-20-openclaw-and-virtualisation-infrastructure.md @@ -0,0 +1,10 @@ +--- +title: "OpenClaw and Virtualisation Infrastructure: Talking to Harvester HCI Like a Human" +date: 2026-06-20 09:00:00 +0800 +categories: [Virtualization] +tags: [harvester, automation, ai] +--- + + + +Redirecting to Medium... [continue reading →](https://medium.com/@0.all_existence.0/openclaw-and-virtualisation-infrastructure-talking-to-harvester-hci-like-a-human-52fb4a502f10) diff --git a/_posts/2026-07-04/2026-07-04-integrating-harvester-with-rancher.md b/_posts/2026-07-04/2026-07-04-integrating-harvester-with-rancher.md new file mode 100644 index 0000000..63edcdf --- /dev/null +++ b/_posts/2026-07-04/2026-07-04-integrating-harvester-with-rancher.md @@ -0,0 +1,10 @@ +--- +title: "Integrating Harvester with Rancher" +date: 2026-07-04 09:00:00 +0800 +categories: [Virtualization] +tags: [harvester, rancher, kubernetes] +--- + + + +Redirecting to Medium... [continue reading →](https://medium.com/@0.all_existence.0/integrating-harvester-with-rancher-1bc417ebb538) diff --git a/_posts/2026-07-18/2026-07-18-accelerate-vm-deployments-with-packer-and-ova-templates.md b/_posts/2026-07-18/2026-07-18-accelerate-vm-deployments-with-packer-and-ova-templates.md new file mode 100644 index 0000000..8148f81 --- /dev/null +++ b/_posts/2026-07-18/2026-07-18-accelerate-vm-deployments-with-packer-and-ova-templates.md @@ -0,0 +1,10 @@ +--- +title: "Accelerate VM Deployments with Packer and OVA Templates in vSphere" +date: 2026-07-18 09:00:00 +0800 +categories: [Automation] +tags: [packer, vsphere, ova, iac] +--- + + + +Redirecting to Medium... [continue reading →](https://medium.com/@0.all_existence.0/accelerate-vm-deployments-with-packer-and-ova-templates-in-vsphere-e55303cb8eae) diff --git a/_posts/2026-08-01/2026-08-01-get-vsphere-vm-notes-using-python-and-pyvmomi.md b/_posts/2026-08-01/2026-08-01-get-vsphere-vm-notes-using-python-and-pyvmomi.md new file mode 100644 index 0000000..600b1e0 --- /dev/null +++ b/_posts/2026-08-01/2026-08-01-get-vsphere-vm-notes-using-python-and-pyvmomi.md @@ -0,0 +1,10 @@ +--- +title: "Get vSphere VM Notes Using Python and PyVmomi" +date: 2026-08-01 09:00:00 +0800 +categories: [Automation] +tags: [python, pyvmomi, vsphere] +--- + + + +Redirecting to Medium... [continue reading →](https://medium.com/@0.all_existence.0/use-pyvmomi-to-read-vm-notes-annotations-from-vsphere-f69ee92ba2e3) diff --git a/_posts/2026-08-10/2026-08-10-gpu-accelerated-ai-inference-with-llama-cpp.md b/_posts/2026-08-10/2026-08-10-gpu-accelerated-ai-inference-with-llama-cpp.md new file mode 100644 index 0000000..b2c9615 --- /dev/null +++ b/_posts/2026-08-10/2026-08-10-gpu-accelerated-ai-inference-with-llama-cpp.md @@ -0,0 +1,10 @@ +--- +title: "GPU Accelerated AI Inference with llama.cpp in a Linux Container on macOS" +date: 2026-08-10 09:00:00 +0800 +categories: [AI-ML] +tags: [llama.cpp, gpu, ai-inference, macos] +--- + + + +Redirecting to Medium... [continue reading →](https://medium.com/@0.all_existence.0/gpu-accelerated-ai-inference-with-llama-cpp-in-a-linux-container-on-macos-37b6557f5d59) diff --git a/_posts/2026-08-12/2026-08-12-installing-nvidia-fabric-manager-air-gapped-harvester-hgx-h200.md b/_posts/2026-08-12/2026-08-12-installing-nvidia-fabric-manager-air-gapped-harvester-hgx-h200.md new file mode 100644 index 0000000..a6fd5d7 --- /dev/null +++ b/_posts/2026-08-12/2026-08-12-installing-nvidia-fabric-manager-air-gapped-harvester-hgx-h200.md @@ -0,0 +1,668 @@ +--- +title: "Installing NVIDIA Fabric Manager on an Air-Gapped Harvester HGX H200 Host" +date: 2026-08-12 09:00:00 +0800 +categories: [GPU Infrastructure] +tags: [nvidia, fabric-manager, hgx, h200, nvswitch, harvester, kubevirt, gpu, air-gapped, vfio] +--- + +Getting CUDA working inside passthrough VMs on an 8× H200 SXM HGX system running Harvester, +where the host OS is immutable and the environment has no internet access and no NVIDIA vGPU licence. + +--- + +## Part 1 — Background: the NVIDIA concepts that matter here + +### NVLink and NVSwitch + +On an HGX baseboard, the GPUs are not only connected by PCIe. They also have **NVLink** ports — +a much faster, GPU-to-GPU interconnect used for peer-to-peer memory access and collective +operations in multi-GPU training. + +On small systems, NVLink is a fixed mesh of direct GPU-to-GPU cables. On an HGX baseboard, every +GPU's NVLink ports instead connect to **NVSwitch** — a crossbar switch ASIC that routes NVLink +traffic between any pair of GPUs at full bandwidth. An 8-GPU HGX board typically carries four +NVSwitch ASICs forming a single fabric domain across all eight GPUs. + +Two things follow from this, and they drive everything else in this document: + +1. **NVSwitch is not a per-GPU resource.** It is shared infrastructure belonging to the whole + baseboard. It has its own routing tables and memory-mapped address space, and appears as its + own PCI device — distinct from the GPUs. +2. **NVSwitch is not a PCIe switch.** It switches NVLink traffic. The PCIe switches on the board + (e.g. Broadcom PEX-series) are a completely separate layer, and they are what determines your + IOMMU groupings. Don't conflate the two — they are different fabrics with different constraints. + +> **Further reading** +> - [NVIDIA NVLink and NVSwitch overview](https://www.nvidia.com/en-us/data-center/nvlink/) — what the interconnect is and why it exists +> - [NVIDIA HGX Software User Guide](https://docs.nvidia.com/datacenter/tesla/hgx-software-guide/index.html) — the full software stack for an HGX baseboard, bottom to top + +### Fabric Manager + +The NVSwitch ASICs do not configure themselves. **Fabric Manager (FM)** is a host-side service +that: + +- discovers the NVSwitches and GPUs on the baseboard, +- loads the correct topology file for the board, +- programs the switch routing tables, +- and reports fabric health. + +Until FM has run successfully, GPUs on an NVSwitch system report: + +``` +Fabric State : In Progress +``` + +and CUDA refuses to initialise, typically with: + +``` +Error 802: system not yet initialized +``` + +FM depends on two other pieces: + +| Component | Role | +|---|---| +| NVIDIA kernel driver | Provides `nvidia-nvswitch` — the kernel-side driver that claims the NVSwitch PCI devices | +| `libnvidia-nscq` | NSCQ = NVSwitch Configuration and Query. The library FM uses to talk to the switches | + +So the dependency chain is strictly: **driver → NSCQ → Fabric Manager**. Installing FM alone +achieves nothing; it will start, fail to query NVSwitch device information, and exit. + +FM's four documented responsibilities are worth internalising, since they explain every symptom in +this document: + +1. Coordinate with the NVSwitch driver to initialise and train switch-to-switch NVLinks +2. Coordinate with the GPU driver to initialise and train switch-to-GPU NVLinks +3. Configure routing among NVSwitch ports +4. Monitor the fabric for NVLink and NVSwitch errors + +> **Further reading** +> - [NVIDIA Fabric Manager User Guide](https://docs.nvidia.com/datacenter/tesla/fabric-manager-user-guide/index.html) — the primary reference for everything in this document +> - [Fabric Manager config options](https://docs.nvidia.com/datacenter/tesla/fabric-manager-user-guide/contents.html) — full table of contents, including config options, startup options and the service file +> - [Fabric Manager Client (GitHub)](https://github.com/NVIDIA/Fabric-Manager-Client) — CLI for the partition-management API used in Shared NVSwitch mode + +### Where each piece runs in a virtualised setup + +This is the single most important design point, and getting it wrong is a very common mistake. + +| Component | Host | Guest VM | +|---|---|---| +| NVIDIA kernel driver (for NVSwitch) | ✅ | — | +| Fabric Manager + NSCQ | ✅ | ❌ **never** | +| GPU driver | — | ✅ | +| CUDA toolkit, PyTorch, workloads | — | ✅ | + +A guest VM that receives individual GPUs via PCI passthrough **cannot see the NVSwitch hardware** +— and should not. Running FM inside such a guest fails immediately, because there is no NVSwitch +device for it to query. + +The handshake between the two sides happens through the GPU hardware itself: host-side FM programs +the fabric, and when the guest's GPU driver initialises, it reads that state from the GPU and +reports `Fabric State: Completed`. + +### The two supported virtualisation models + +| Model | GPUs | NVSwitch | Fabric Manager | Use when | +|---|---|---|---|---| +| **Full baseboard passthrough** | All 8 to one VM | Also passed through | Runs **inside the guest** | One workload needs all 8 GPUs with full NVLink | +| **Shared NVSwitch** | Passed through individually to different VMs | Stays host-owned | Runs **on the host** | GPUs split across multiple VMs | + +Passing through *some* GPUs plus the NVSwitch is not a valid configuration. The switch is either +entirely host-owned, or it goes to a single VM along with every GPU on the board. + +This document covers the **Shared NVSwitch** model. + +> **Further reading** +> - [Full Passthrough Virtualization Model](https://docs.nvidia.com/datacenter/tesla/fabric-manager-user-guide/index.html#full-passthrough-virtualization-model) — supported VM configurations for 1, 2, 4, 8 and 16 GPUs, and their limitations +> - [Shared NVSwitch Virtualization Model](https://docs.nvidia.com/datacenter/tesla/fabric-manager-user-guide/index.html#shared-nvswitch-virtualization-model) — service VM design, guest lifecycle, and the partition APIs +> - [Bare Metal Mode](https://docs.nvidia.com/datacenter/tesla/fabric-manager-user-guide/index.html#bare-metal-mode) — the `FABRIC_MODE=0` behaviour this setup ended up relying on + +--- + +## Part 2 — The problem + +**Symptom (inside the guest VM):** + +``` +Error 802: system not yet initialized +torch.cuda.is_available() -> False +``` + +``` +Fabric + State : In Progress + Status : N/A +GPU Fabric GUID : N/A +``` + +**First wrong turn:** Fabric Manager had been installed *inside the guest VM*, where it failed with: + +``` +request to query NVSwitch device information from NVSwitch driver ... failed +``` + +That is correct behaviour — a passthrough guest has no NVSwitch. FM was in the wrong place. + +**Actual root cause**, found by inspecting the host: + +```bash +lspci -k +``` + +``` +xx:00.0 Bridge: NVIDIA Corporation GH100 [H100 NVSwitch] (rev a1) + Subsystem: NVIDIA Corporation Device 1796 + <-- no "Kernel driver in use" line at all +``` + +All four NVSwitch bridges had **no kernel driver bound**. Meanwhile the GPUs were correctly bound +to `vfio-pci` for passthrough, and NVMe was on `nvme`. Only the switches were orphaned. + +No driver on the switches → FM cannot initialise the fabric → every passthrough GPU waits forever +on a handshake that never comes. + +--- + +## Part 3 — Approaches that did not work + +Documented so they are not retried. + +| Attempt | Outcome | +|---|---| +| Harvester `nvidia-driver-toolkit` add-on | Requires a **licensed vGPU/GRID `.run` driver** from NVIDIA's licensing portal. Also vGPU-specific — the documentation makes no mention of NVSwitch or Fabric Manager. Not applicable to full PCI passthrough. | +| NVIDIA Container Toolkit | Wrong layer entirely. It exposes GPUs to *containers* and explicitly assumes a working host driver already exists. Irrelevant when GPUs go straight into VMs via VFIO. | +| `zypper install` on host | `The target filesystem is mounted as read-only.` | +| `transactional-update pkg install` | `command not found` — removed from this build. | +| `mount -o remount,rw /` | `cannot remount /dev/loopN read-write, is write-protected` — the root is a loop-mounted OS **image**, not merely a read-only mount flag. | +| `.run` driver installer | Designed for a normal writable filesystem; incompatible with an immutable/transactional root. | +| Passing NVSwitch through to a guest | Breaks the shared-fabric model; guest gains control over routing affecting GPUs owned by other VMs. | + +### Why no package can be installed + +The host is SUSE Linux Micro with an elemental-style immutable root. From `/proc/cmdline`: + +``` +root=LABEL=COS_STATE cos-img/filename=/cOS/active.img +rd.cos.mount=LABEL=COS_OEM:/oem +rd.cos.mount=LABEL=COS_PERSISTENT:/usr/local +``` + +The root filesystem is a read-only image file. But note the last line: **`/usr/local` is a +writable, persistent partition.** That is the opening. + +> **Further reading** +> - [Harvester PCI Devices add-on](https://docs.harvesterhci.io/v1.4/advanced/addons/pcidevices/) — how `PCIDevice` / `PCIDeviceClaim` bind host devices to `vfio-pci` +> - [`harvester/pcidevices` controller (GitHub)](https://github.com/harvester/pcidevices) — the controller doing the runtime binding, and the CRD shapes +> - [Harvester NVIDIA driver toolkit add-on](https://docs.harvesterhci.io/v1.8/advanced/addons/nvidiadrivertoolkit/) — the vGPU-oriented add-on, and why it doesn't apply here +> - [Harvester CloudInit CRD](https://docs.harvesterhci.io/v1.8/advanced/cloudinitcrd/) — the supported way to persist host-level config on an immutable OS +> - [NVIDIA Container Toolkit install guide](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html) — note its prerequisite that a host driver already exists; it is a container-layer tool, not a passthrough one + +--- + +## Part 4 — The approach that worked + +> Kernel modules do not have to be installed to be loaded. `insmod` accepts any file path. +> So: extract the RPMs into the writable partition and load the module by hand. + +No package installation, no filesystem remount, no OS modification. + +--- + +## Part 5 — Phase 1: obtaining the packages (air-gapped) + +Performed on a separate staging VM that has internet access and Docker. The staging VM ran Ubuntu, +which has no `zypper`, so a SUSE container was used to resolve dependencies properly rather than +scraping HTML directory listings. + +### 5.1 Start a SUSE container + +```bash +mkdir -p rpms +docker run -it --rm -v $(pwd)/rpms:/rpms registry.opensuse.org/opensuse/leap:16.0 bash +``` + +### 5.2 Add the NVIDIA repositories + +```bash +zypper ar -G https://download.nvidia.com/suse/sle16/ nvidia-gfx +zypper ar -G https://developer.download.nvidia.com/compute/cuda/repos/sles15/x86_64/ nvidia-cuda +zypper refresh +``` + +### 5.3 Find what actually exists + +```bash +zypper se -s nvidia-open-driver-G06-signed-kmp +zypper se -s nvidia-fabricmanager +zypper se -s libnvidia-nscq +``` + +**Key discovery — the packages are spread across three repositories:** + +| Package | Repository | +|---|---| +| `nvidia-open-driver-G06-signed-kmp-default` — *the actual kernel module* | **Leap `repo-oss`** (the distribution repo) | +| `nvidia-open-driver-G06-signed-kmp-meta` — a 36 KB pointer only | `nvidia-gfx` | +| `nvidia-fabricmanager`, `libnvidia-nscq` | `nvidia-cuda` | +| `nvidia-compute-utils-G06`, `nvidia-common-G06`, `nvidia-compute-G06` | `nvidia-gfx` / `nvidia-cuda` | + +NVIDIA's SUSE repo ships **only** userspace packages and meta-packages. SUSE builds and signs the +kernel module itself, so the real binary comes from the distribution repository. Scraping NVIDIA's +repo alone will never find it. + +### 5.4 Choosing a version + +Two constraints must hold simultaneously: + +1. The version must exist in **all** the repos involved — FM must match the driver version exactly, + or it refuses to attach. +2. The kernel module package (KMP) must be **kABI-compatible** with the running host kernel. + +The KMP filename encodes the kernel it was built against, e.g.: + +``` +nvidia-open-driver-G06-signed-kmp-default-