Skip to content
Merged
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
7 changes: 7 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
*
!pyproject.toml
!README.md
!LICENSE
!src/
**/__pycache__
**/*.egg-info
13 changes: 13 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,19 @@ jobs:
python-version: '3.11'
- run: python tools/check_sensitive.py .

# Node daemon chart: default values and the simulated-cluster values must render
chart:
name: Helm chart lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: azure/setup-helm@v4
with:
version: v3.16.4
- run: helm lint deploy/helm/rm-abstract-node
- run: helm lint deploy/helm/rm-abstract-node --set provider=sim --set hostBin= --set hostDev=false
- run: helm template rm-abstract-node deploy/helm/rm-abstract-node > /dev/null

# Unit tests (multiple Python versions)
test:
name: Test (Python ${{ matrix.python-version }})
Expand Down
72 changes: 72 additions & 0 deletions .github/workflows/image.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
name: Image

# Node daemon image ghcr.io/ai-computing/rm-accel (Dockerfile).
# Pull requests build and smoke-test it; master pushes :master, tags vX.Y.Z push :X.Y.Z and :X.Y.
on:
push:
branches: [master]
tags: ['v*']
pull_request:
branches: [master]
paths: [Dockerfile, .dockerignore, pyproject.toml, 'src/**', .github/workflows/image.yml]
workflow_dispatch:

permissions:
contents: read
packages: write

env:
IMAGE: ghcr.io/ai-computing/rm-accel

jobs:
image:
name: Build and publish node daemon image
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: docker/setup-buildx-action@v3

- name: Tags
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.IMAGE }}
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=ref,event=branch
type=sha,format=short

- name: Build
uses: docker/build-push-action@v6
with:
context: .
load: true
tags: ${{ env.IMAGE }}:test
cache-from: type=gha
cache-to: type=gha,mode=max

- name: Smoke test (sim provider, no cluster)
run: |
docker run --rm ${{ env.IMAGE }}:test --provider sim --once --dry-run --node ci-node > objects.json
python3 -c "import json; o = json.load(open('objects.json')); kinds = [i['kind'] for i in o['items']]; print(kinds); assert kinds.count('AcceleratorProfile') == 2"
docker run --rm --entrypoint sh ${{ env.IMAGE }}:test -c 'echo "$PATH" | grep -q /host/usr/bin'

- name: Log in to ghcr.io
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Push
if: github.event_name != 'pull_request'
uses: docker/build-push-action@v6
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
# Changelog

## 0.4.2 / Chart 0.5.0 — 2026-10-19

### Added
- Node daemon image `ghcr.io/ai-computing/rm-accel` ([`Dockerfile`](Dockerfile)): python:3.11-slim with
rm-abstract (no torch), rm-core v0.1.1, the Kubernetes client and numpy. Vendor tools are not baked in.
- Workflow `Image`: builds and smoke-tests the image on pull requests, publishes `:master` from master and
`:X.Y.Z` / `:X.Y` from `vX.Y.Z` tags (GITHUB_TOKEN, `packages: write`).
- Chart value `hostBin` (default `/usr/bin`): the host's vendor tools mounted read-only at `/host/usr/bin`,
which is on the image's `PATH`.

### Changed
- Chart default image `ghcr.io/ai-computing/rm-accel` (was the never-published `rm-abstract-node`), tag =
`appVersion` 0.4.2. Sites no longer need the init-container workaround; the extension points stay.

## Chart 0.4.2 — 2026-10-19

Found while running the daemon on the live cluster (ai-rm end-to-end run).
Expand Down
28 changes: 28 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# ai-rm L1 node daemon image (ghcr.io/ai-computing/rm-accel): python -m rm_abstract.airm.daemon
#
# Only what the daemon needs: rm-abstract (without its torch dependency; discovery runs the vendor
# tools), rm-core, the Kubernetes client and numpy. Vendor CLI tools (nvidia-smi, furiosa-smi,
# rbln-stat) are not baked in: they match the host's driver, so the Helm chart mounts the host's
# /usr/bin read-only at /host/usr/bin (value hostBin), which is on PATH here.
#
# docker build -t ghcr.io/ai-computing/rm-accel:dev .
# docker run --rm ghcr.io/ai-computing/rm-accel:dev --provider sim --once --dry-run
FROM python:3.11-slim

ARG RM_CORE_REF=v0.1.1

ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
PIP_DISABLE_PIP_VERSION_CHECK=1 \
PATH=/usr/local/bin:/usr/bin:/bin:/host/usr/bin

COPY pyproject.toml README.md LICENSE /src/
COPY src /src/src
RUN pip install --no-cache-dir --no-deps /src \
"https://github.com/ai-computing/rm-core/archive/refs/tags/${RM_CORE_REF}.tar.gz" \
&& pip install --no-cache-dir "kubernetes>=24.2.0" "numpy>=1.20,<3" \
&& rm -rf /src \
&& python -m rm_abstract.airm.daemon --provider sim --once --dry-run >/dev/null

# Root: the vendor tools read the device nodes (the chart runs the container privileged).
ENTRYPOINT ["python", "-m", "rm_abstract.airm.daemon"]
22 changes: 14 additions & 8 deletions README.ko.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<a href="https://github.com/ai-computing/ai-rm"><img src="https://img.shields.io/badge/ai--rm-L1%20accelerator%20abstraction-7c5cff" alt="ai-rm L1 계층"></a>
<a href="https://github.com/ai-computing/rm-core/blob/v0.1.1/PROTOCOL.md"><img src="https://img.shields.io/badge/protocol-v0.1-7c5cff" alt="프로토콜 v0.1"></a>
<a href="https://github.com/ai-computing/rm-accel/releases/tag/v0.4.1"><img src="https://img.shields.io/badge/release-v0.4.1-22d3ee" alt="릴리스 v0.4.1"></a>
<a href="deploy/helm/rm-abstract-node"><img src="https://img.shields.io/badge/chart-0.4.2-0f1689?logo=helm&amp;logoColor=white" alt="Helm chart 0.4.2"></a>
<a href="deploy/helm/rm-abstract-node"><img src="https://img.shields.io/badge/chart-0.5.0-0f1689?logo=helm&amp;logoColor=white" alt="Helm chart 0.5.0"></a>
<a href="https://www.python.org/downloads/"><img src="https://img.shields.io/badge/python-3.9%2B-3776ab?logo=python&amp;logoColor=white" alt="Python 3.9+"></a>
<a href="LICENSE"><img src="https://img.shields.io/badge/license-BSD--3--Clause-64748b" alt="라이선스: BSD-3-Clause"></a>
</p>
Expand Down Expand Up @@ -127,28 +127,34 @@ for acc in provider.discover():
[`deploy/helm/rm-abstract-node`](deploy/helm/rm-abstract-node)는 데몬을 DaemonSet으로 설치하고 ServiceAccount와
ClusterRole(`acceleratorprofiles` CRUD, `nodes` get/patch)을 만듭니다. `AcceleratorProfile` CRD는
[rm-core](https://github.com/ai-computing/rm-core/tree/v0.1.1/k8s)에 있고 llm-d-setup / ai-rm umbrella가 설치합니다.
이미지는 사이트별로 `image.repository` / `image.tag`로 지정합니다.
이미지는 `ghcr.io/ai-computing/rm-accel`(태그 = chart `appVersion`)이며, [`Dockerfile`](Dockerfile)로 빌드되어
[Image 워크플로](.github/workflows/image.yml)가 `master`와 `v*` 태그에서 배포합니다.

```bash
helm template rm-abstract-node deploy/helm/rm-abstract-node --set image.repository=<registry>/rm-abstract-node
helm template rm-abstract-node deploy/helm/rm-abstract-node # 배포 이미지, 호스트 도구
helm template rm-abstract-node deploy/helm/rm-abstract-node --set provider=sim --set hostBin= --set hostDev=false
docker build -t rm-accel:dev . && docker run --rm rm-accel:dev --provider sim --once --dry-run
```

<details>
<summary><b>Chart 0.4.2: 컨테이너 실행 환경과 사이트 확장 지점</b></summary>
<summary><b>Chart 0.5.0: 이미지, 컨테이너 실행 환경과 사이트 확장 지점</b></summary>
<br>

| 값 | 기본값 | 의미 |
|---|---|---|
| `privileged` | `true` | 벤더 도구가 장치 노드를 읽기 위해 필요 |
| `hostDev` | `true` | 호스트 `/dev`를 실시간으로 마운트해, 드라이버가 다시 만든 장치(예: pod가 놓아준 ATOM)도 계속 탐지되게 함 |
| `hostBin` | `/usr/bin` | 벤더 도구가 있는 호스트 디렉터리, `/host/usr/bin`에 읽기 전용으로 마운트(이미지 `PATH`에 포함); `""`이면 끔 |
| `command` | `[]` | 데몬 명령 재정의 (기본 `python -m rm_abstract.airm.daemon`) |
| `extraEnv` | `[]` | 데몬 env에 덧붙일 `{name, value}` 항목 |
| `initContainers` | `[]` | pod init container, 그대로 반영 |
| `extraVolumes` / `extraVolumeMounts` | `[]` | pod volume / 데몬 volumeMount, 그대로 반영 |

루트 파일시스템은 읽기 전용이고 `/tmp`만 쓰기 가능합니다(`rbln-stat`이 `TMPDIR` 아래에 스스로 압축을 풉니다).
`rm-abstract-node` 이미지가 배포되기 전까지는 확장 지점을 써서 일반 Python 이미지로 데몬을 실행할 수 있습니다.
[`values.yaml`](deploy/helm/rm-abstract-node/values.yaml)과 [변경 이력](CHANGELOG.md)을 참고하세요.
이미지에는 rm-abstract(torch 제외), rm-core, Kubernetes 클라이언트, numpy가 들어 있고 벤더 도구는 없습니다. 도구는
호스트 드라이버와 맞아야 하므로 `hostBin`으로 호스트에서 가져옵니다. 루트 파일시스템은 읽기 전용이고 `/tmp`만 쓰기
가능합니다(`rbln-stat`이 `TMPDIR` 아래에 스스로 압축을 풉니다). 배포 이미지에는 확장 지점이 더 이상 필요 없고, 자체
이미지나 패키지를 쓰는 사이트를 위해 남겨 둡니다. [`values.yaml`](deploy/helm/rm-abstract-node/values.yaml)과
[변경 이력](CHANGELOG.md)을 참고하세요.
</details>

## 🔁 vLLM 호환 API
Expand Down Expand Up @@ -246,7 +252,7 @@ bash scripts/run_examples.sh # 예제 전체 실행 (모델·
## 🗺 로드맵

- [x] rm-core v0.1 `AcceleratorProvider`, 노드 데몬, Helm chart
- [ ] `rm-abstract-node` 컨테이너 이미지 배포
- [x] 노드 데몬 컨테이너 이미지 `ghcr.io/ai-computing/rm-accel`
- [ ] `LLM` API를 통한 RNGD / ATOM 추론 검증
- [ ] Optimus Prime용 엔진 백엔드 어댑터 (ai-rm [엔진 통합 설계](https://github.com/ai-computing/ai-rm/blob/master/docs/ko/engine-integration.md))
- [ ] 장치 모델 확장: 파티션, 인터커넥트 토폴로지, 실시간 사용률
Expand Down
22 changes: 14 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<a href="https://github.com/ai-computing/ai-rm"><img src="https://img.shields.io/badge/ai--rm-L1%20accelerator%20abstraction-7c5cff" alt="ai-rm layer L1"></a>
<a href="https://github.com/ai-computing/rm-core/blob/v0.1.1/PROTOCOL.md"><img src="https://img.shields.io/badge/protocol-v0.1-7c5cff" alt="Protocol v0.1"></a>
<a href="https://github.com/ai-computing/rm-accel/releases/tag/v0.4.1"><img src="https://img.shields.io/badge/release-v0.4.1-22d3ee" alt="Release v0.4.1"></a>
<a href="deploy/helm/rm-abstract-node"><img src="https://img.shields.io/badge/chart-0.4.2-0f1689?logo=helm&amp;logoColor=white" alt="Helm chart 0.4.2"></a>
<a href="deploy/helm/rm-abstract-node"><img src="https://img.shields.io/badge/chart-0.5.0-0f1689?logo=helm&amp;logoColor=white" alt="Helm chart 0.5.0"></a>
<a href="https://www.python.org/downloads/"><img src="https://img.shields.io/badge/python-3.9%2B-3776ab?logo=python&amp;logoColor=white" alt="Python 3.9+"></a>
<a href="LICENSE"><img src="https://img.shields.io/badge/license-BSD--3--Clause-64748b" alt="License: BSD-3-Clause"></a>
</p>
Expand Down Expand Up @@ -127,28 +127,34 @@ Applying to a cluster needs the Kubernetes client: `pip install -e ".[k8s]"`.
[`deploy/helm/rm-abstract-node`](deploy/helm/rm-abstract-node) installs the daemon as a DaemonSet with a
ServiceAccount and ClusterRole (`acceleratorprofiles` CRUD, `nodes` get/patch). The `AcceleratorProfile` CRD
ships with [rm-core](https://github.com/ai-computing/rm-core/tree/v0.1.1/k8s) and is installed by llm-d-setup / the
ai-rm umbrella. Set the image for your site with `image.repository` / `image.tag`.
ai-rm umbrella chart. The image is `ghcr.io/ai-computing/rm-accel` (tag = chart `appVersion`), built from the
[`Dockerfile`](Dockerfile) and published by the [Image workflow](.github/workflows/image.yml) on `master` and on `v*` tags.

```bash
helm template rm-abstract-node deploy/helm/rm-abstract-node --set image.repository=<registry>/rm-abstract-node
helm template rm-abstract-node deploy/helm/rm-abstract-node # published image, host tools
helm template rm-abstract-node deploy/helm/rm-abstract-node --set provider=sim --set hostBin= --set hostDev=false
docker build -t rm-accel:dev . && docker run --rm rm-accel:dev --provider sim --once --dry-run
```

<details>
<summary><b>Chart 0.4.2: container runtime and site extension points</b></summary>
<summary><b>Chart 0.5.0: image, container runtime and site extension points</b></summary>
<br>

| Value | Default | Meaning |
|---|---|---|
| `privileged` | `true` | Vendor tools read the device nodes |
| `hostDev` | `true` | Mount the host's `/dev` live, so a device the driver re-creates (e.g. an ATOM released by a pod) stays in discovery |
| `hostBin` | `/usr/bin` | Host directory with the vendor tools, mounted read-only at `/host/usr/bin` (on the image's `PATH`); `""` disables |
| `command` | `[]` | Override the daemon command (default `python -m rm_abstract.airm.daemon`) |
| `extraEnv` | `[]` | `{name, value}` entries appended to the daemon's env |
| `initContainers` | `[]` | Pod init containers, verbatim |
| `extraVolumes` / `extraVolumeMounts` | `[]` | Pod volumes / daemon volume mounts, verbatim |

The root filesystem is read-only with a writable `/tmp` (`rbln-stat` unpacks itself under `TMPDIR`). Until the
`rm-abstract-node` image is published, the extension points let a site run the daemon from a stock Python image.
See [`values.yaml`](deploy/helm/rm-abstract-node/values.yaml) and the [changelog](CHANGELOG.md).
The image carries rm-abstract (without torch), rm-core, the Kubernetes client and numpy, but no vendor tools: those
must match the host's driver, so they come from the host through `hostBin`. The root filesystem is read-only with a
writable `/tmp` (`rbln-stat` unpacks itself under `TMPDIR`). The extension points are no longer needed for the
published image; they remain for sites with their own image or packages. See
[`values.yaml`](deploy/helm/rm-abstract-node/values.yaml) and the [changelog](CHANGELOG.md).
</details>

## 🔁 vLLM-compatible API
Expand Down Expand Up @@ -248,7 +254,7 @@ Guides: [contributing](docs/CONTRIBUTING.md) · [adding a device](docs/ADDING_RE
## 🗺 Roadmap

- [x] rm-core v0.1 `AcceleratorProvider`, node daemon, Helm chart
- [ ] Published `rm-abstract-node` container image
- [x] Node daemon container image `ghcr.io/ai-computing/rm-accel`
- [ ] Validated RNGD / ATOM inference through the `LLM` API
- [ ] Engine-facing backend adapters for Optimus Prime (see the ai-rm [engine plan](https://github.com/ai-computing/ai-rm/blob/master/docs/engine-integration.md))
- [ ] Wider device model: partitions, interconnect topology, live utilization
Expand Down
2 changes: 1 addition & 1 deletion ai-rm.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ deploy:
target: k8s
manifests: null
chart: deploy/helm/rm-abstract-node
image: null
image: ghcr.io/ai-computing/rm-accel
config:
env_example: null
interfaces:
Expand Down
4 changes: 2 additions & 2 deletions deploy/helm/rm-abstract-node/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ apiVersion: v2
name: rm-abstract-node
description: ai-rm L1 node daemon - publishes each node's accelerators as AcceleratorProfiles and node labels
type: application
version: 0.4.2
appVersion: "0.4.1"
version: 0.5.0
appVersion: "0.4.2"
14 changes: 13 additions & 1 deletion deploy/helm/rm-abstract-node/templates/daemonset.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ spec:
command:
{{- toYaml .Values.command | nindent 12 }}
{{- else }}
command: ["python", "-m", "rm_abstract.airm.daemon"]
command: ["python", "-m", "rm_abstract.airm.daemon"] # the image's entrypoint
{{- end }}
env:
- name: AIRM_NODE_NAME
Expand Down Expand Up @@ -79,6 +79,12 @@ spec:
- name: dev
mountPath: /dev
{{- end }}
{{- if .Values.hostBin }}
# host vendor tools, on the image's PATH
- name: host-bin
mountPath: /host/usr/bin
readOnly: true
{{- end }}
{{- with .Values.extraVolumeMounts }}
{{- toYaml . | nindent 12 }}
{{- end }}
Expand All @@ -90,6 +96,12 @@ spec:
hostPath:
path: /dev
{{- end }}
{{- if .Values.hostBin }}
- name: host-bin
hostPath:
path: {{ .Values.hostBin }}
type: Directory
{{- end }}
{{- with .Values.extraVolumes }}
{{- toYaml . | nindent 8 }}
{{- end }}
Expand Down
13 changes: 8 additions & 5 deletions deploy/helm/rm-abstract-node/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
enabled: true

image:
repository: ghcr.io/ai-computing/rm-abstract-node # rm-abstract[k8s] + vendor tools (nvidia-smi, furiosa-smi, rbln-stat); override per site
tag: "" # default: chart appVersion
repository: ghcr.io/ai-computing/rm-accel # Dockerfile: rm-abstract + rm-core + kubernetes, no vendor tools
tag: "" # default: chart appVersion (image tags follow the rm-accel release, e.g. 0.4.2)
pullPolicy: IfNotPresent

airm:
Expand All @@ -22,14 +22,17 @@ privileged: true
# Mount the host's /dev live. A privileged container otherwise sees /dev as it was at start, and a
# device that a driver re-creates (e.g. after a pod releases an ATOM) drops out of discovery.
hostDev: true
# The vendor tools (nvidia-smi, furiosa-smi, rbln-stat) come from the host, matching its driver: this
# host directory is mounted read-only at /host/usr/bin, which is on the image's PATH. "" disables
# (e.g. the sim provider, or an image that carries its own tools).
hostBin: /usr/bin

serviceAccount:
create: true
name: ""

# Site extension points (all empty by default). A site without the published image can run a
# stock Python image: an init container installs rm-abstract[k8s] into a volume on PYTHONPATH,
# and the host's vendor tools are mounted read-only and appended to PATH.
# Site extension points (all empty by default): the published image needs none of them. A site
# with its own image or packages can still add an init container, env, volumes and mounts.
command: [] # default: python -m rm_abstract.airm.daemon
extraEnv: [] # [{name, value}] appended to the daemon's env
initContainers: [] # pod initContainers, verbatim
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "rm-abstract"
version = "0.4.1"
version = "0.4.2"
description = "Accelerator abstraction for heterogeneous AI infrastructure: GPU/NPU/CPU discovery, AcceleratorProfile publishing and a vLLM-compatible API (ai-rm L1)"
readme = "README.md"
license = {text = "BSD-3-Clause"}
Expand Down
2 changes: 1 addition & 1 deletion src/rm_abstract/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
# Modular components
from .registry import PluginRegistry, PluginType, discover_all, get_registry

__version__ = "0.4.1"
__version__ = "0.4.2"
__all__ = [
# Main LLM interface (recommended)
"LLM",
Expand Down
Loading