Topograph 离线 K8s 部署与使用指南
目标环境:完全离线的内网 Kubernetes 集群(无外网、无私有镜像仓库)
决策基线(经 grill 确认):
- Provider =
test(先做 PoC 验证,不依赖真实硬件 / 云 IMDS)- Engine =
k8s(默认引擎,把拓扑写成 Node 标签)- 镜像获取 = 节点预加载 (containerd 逐节点
ctr import,无 registry)- 加固 = 最小化(不启用 NetworkPolicy / serviceMonitor / ingress)
- 用途 = 仅 PoC 验证标签落地
1. 说明
1.1 项目简介
Topograph(NVIDIA DSX AI Factory 开源,Apache-2.0)用于发现 Kubernetes 集群的物理网络拓扑(fabric topology) ,并把拓扑信息写成 Node 标签(前缀 fabric.topograph.run/),供拓扑感知调度器(Kueue / Volcano / Slinky 等)消费。
核心抽象是两个概念:
- Provider :拓扑数据源。离线 PoC 用
test(纯模拟,不需要任何云 API、NetQ、InfiniBand 或真实标签)。 - Engine :拓扑输出形态。
k8s表示把拓扑写成 K8s Node 标签。
1.2 架构与组件
Helm chart(charts/topograph/,version 1.0.0 / appVersion v1.0.0)部署三个工作负载:
┌─────────────────────────────────────────┐
│ topograph namespace │
│ │
kube-apiserver │ ┌────────────────┐ /v1/generate │
◄─────────────┤──►│ Topograph API │ /v1/topology │
│ │ (Deploy x1) │ /healthz /metrics │
│ │ port 49021 │◄──────────┐ │
│ └────────────────┘ │ watch │
│ ▲ │ │
│ │ trigger ▼ │
│ ┌────────────────┐ ┌────────────────┐ │
│ │ node-observer │ │ node-data- │ │
│ │ (Deploy x1) │ │ broker │ │
│ └────────────────┘ │ (DaemonSet) │ │
│ └────────────────┘ │
└─────────────────────────────────────────┘
│
▼
Node 标签 fabric.topograph.run/*
| 组件 | 形态 | 作用 | 默认副本 |
|---|---|---|---|
| Topograph API server | Deployment | 核心引擎,写 Node 标签,暴露 API(49021) | 1 |
| node-observer | Deployment | 监听节点/API 变化,触发拓扑再生 | 1 |
| node-data-broker | DaemonSet | 采集每节点拓扑属性并写成 annotation | 每节点 1 |
安全基线:镜像以非 root(uid 65532)运行、只读根文件系统、drop ALL capabilities,已满足 K8s restricted Pod Security Standard,离线环境无需额外加固。
1.3 离线关键事实(决定本文写法)
- 只需 1 个容器镜像 :
ghcr.io/dsx-ai-factory/topograph:v1.0.0(Alpine 3.24,含rdma-core,自带 busyboxwget)。 - 无 init 容器、无 sidecar、无额外依赖镜像 。
helm test复用主镜像(自带 wget),因此 air-gap 下helm test也能跑。 - 镜像名必须保持原 ref :节点预加载后 kubelet 按
image.repository:tag查找,若改名会导致ImagePullBackOff。 - Chart 可整体离线 :chart 源码在仓库
charts/topograph/,可helm package成.tgz后随镜像一起拷入 air-gap。
2. 准备环境
2.1 拓扑前提(沿用你的离线方法论)
| 机器 | 网络 | 职责 | 需要的工具 |
|---|---|---|---|
| download-machine | 有外网 | 拉取镜像 + chart,导出 tar,中转 | docker 或 ctr、helm |
| operation-machine | 仅内网,能 kubectl/helm 到集群 |
接收 tar,逐节点导入镜像,执行 helm install |
kubectl、helm、ctr(或 docker)、ssh 到各节点 |
物理中转:U 盘 / 内网跳板 / 对象存储均可。本文把镜像 tar(
topograph-v1.0.0.tar)和 chart(topograph-1.0.0.tgz)从 download-machine 拷到 operation-machine。
2.2 集群与工具版本
- Kubernetes ≥ 1.27(你的 1.36.4 满足)
- Helm ≥ 3.10
- 节点容器运行时 containerd (kubelet 命名空间
k8s.io) kubectl具备在目标集群创建 namespace、安装 chart、读写 Node 标签的权限(默认 cluster-admin 即可)- 节点架构:
amd64(镜像提供 linux/amd64;若集群含 arm64 需另行构建)
2.3 离线制品清单
| 制品 | 来源 | 大小量级 |
|---|---|---|
topograph-v1.0.0.tar |
docker save / ctr export of ghcr.io/dsx-ai-factory/topograph:v1.0.0 |
~数十 MB |
topograph-1.0.0.tgz |
helm pull 或 helm package charts/topograph |
< 100 KB |
| 离线 values 配置 | 内联于本文第 3 节(部署时另存为 values-offline.yaml) |
--- |
3. 配置文件(离线 values)
以下为完整离线 values(已内联本文,不再单独附带文件):
yaml
# values-offline.yaml
# Topograph 离线最小化部署覆盖文件
# 决策(经 grill 确认):
# provider = test # 先做 PoC 验证,不依赖真实硬件/云 IMDS
# engine = k8s # 默认引擎,把拓扑写成 Node 标签
# 镜像获取 = 节点预加载 # 无私有仓库,containerd 逐节点 import
# 加固 = 最小化 # 不启用 NetworkPolicy / serviceMonitor / ingress
#
# 用法:
# helm install topograph ./topograph-1.0.0.tgz \
# --namespace topograph --create-namespace \
# -f values-offline.yaml
provider:
name: test
engine:
name: k8s
image:
# 必须与原镜像 ref 完全一致,否则节点预加载后 kubelet 仍会尝试拉取
repository: ghcr.io/dsx-ai-factory/topograph
tag: v1.0.0
pullPolicy: IfNotPresent
# 无私有仓库,留空
imagePullSecrets: []
service:
type: ClusterIP
port: 49021
replicaCount: 1
verbosity: 3
config:
requestAggregationDelay: 15s
# 最小化:全部关闭
networkPolicy:
enabled: false
serviceMonitor:
enabled: false
ingress:
enabled: false
gatewayAPI:
enabled: false
nodeObserver:
replicaCount: 1
nodeDataBroker:
enabled: true
tests:
enabled: true
# 如需让 node-data-broker DaemonSet 也调度到带 NoSchedule 污点的控制平面节点,
# 取消下面注释并填入对应 tolerations:
# nodeDataBroker:
# tolerations:
# - operator: Exists
# effect: NoSchedule
# nodeObserver:
# tolerations:
# - operator: Exists
# effect: NoSchedule
部署时在 operation-machine 上将上述内容保存为
values-offline.yaml(本文不单独附带该文件),后续命令中的-f values-offline.yaml即引用它。若目标集群控制平面节点带NoSchedule污点、且希望 broker/observer 也跑在 master 上,取消本 values 末尾tolerations注释即可。
4. 制品获取(download-machine,有外网)
4.1 获取 Helm chart(二选一)
方式 A --- 从 chart 仓库拉取(推荐,带版本与校验)
bash
helm repo add topograph https://dsx-ai-factory.github.io/topograph
helm repo update
helm pull topograph/topograph --version 1.0.0
# 产出:topograph-1.0.0.tgz
# 校验(可选):到 GitHub Releases 比对 SHA-256
方式 B --- 从源码仓库打包(无需 chart 仓库可达)
bash
git clone https://github.com/dsx-ai-factory/topograph.git
cd topograph
helm package charts/topograph # 产出 topograph-1.0.0.tgz
若 download-machine 也无法直连 GitHub,可改用
ghCLI 或网页下载 Release 里的topograph-1.0.0.tgz后通过 U 盘中转。
4.2 获取容器镜像
bash
# 用 docker(最常见)
docker pull ghcr.io/dsx-ai-factory/topograph:v1.0.0
docker save ghcr.io/dsx-ai-factory/topograph:v1.0.0 -o topograph-v1.0.0.tar
纯 containerd 环境可改为:
bash
ctr images pull ghcr.io/dsx-ai-factory/topograph:v1.0.0
ctr images export topograph-v1.0.0.tar ghcr.io/dsx-ai-factory/topograph:v1.0.0
4.3 确认镜像 ref(关键)
bash
docker inspect --format '{{index .RepoTags 0}}' topograph-v1.0.0.tar 2>/dev/null || \
tar -xOf topograph-v1.0.0.tar manifest.json | grep -o '"RepoTags":\[[^]]*' | head -1
# 期望看到:ghcr.io/dsx-ai-factory/topograph:v1.0.0
此 ref 必须与第 3 节 values 里的 image.repository+image.tag 字节级一致。
4.4 中转
把 topograph-1.0.0.tgz、topograph-v1.0.0.tar 拷到 operation-machine(例如 /opt/topograph/);离线 values 已内联于本文第 3 节,部署时在该机另存为 values-offline.yaml。
5. 部署(operation-machine,airgap)
5.1 逐节点导入镜像(containerd)
镜像必须存在于每一个会调度 Topograph Pod 的节点上(API/observer 是 Deployment,broker 是 DaemonSet 跑在所有节点):
bash
cd /opt/topograph
for node in node1 node2 node3 master1; do
echo "==> import on $node"
# 把 tar 传过去并导入(命名空间 k8s.io 是 kubelet 默认运行时)
scp topograph-v1.0.0.tar $node:/tmp/
ssh $node 'ctr -n k8s.io images import /tmp/topograph-v1.0.0.tar && rm -f /tmp/topograph-v1.0.0.tar'
done
验证某节点已就绪:
bash
ssh node1 'ctr -n k8s.io images list | grep dsx-ai-factory/topograph'
# 应看到 ghcr.io/dsx-ai-factory/topograph:v1.0.0
docker 运行时 的集群改用:各节点
docker load -i topograph-v1.0.0.tar。
5.2 安装 chart
bash
cd /opt/topograph
helm install topograph ./topograph-1.0.0.tgz \
--namespace topograph --create-namespace \
-f values-offline.yaml
或纯命令行(不依赖 values 文件):
bash
helm install topograph ./topograph-1.0.0.tgz \
--namespace topograph --create-namespace \
--set provider.name=test \
--set engine.name=k8s \
--set image.tag=v1.0.0 \
--set image.pullPolicy=IfNotPresent
5.3 确认工作负载起来
bash
kubectl -n topograph get pods -o wide
# 期望:topograph-xxxxx (API) Running
# topograph-node-observer-xx Running
# topograph-node-data-broker-xxxxx (每个节点一个) Running
6. 访问验证(PoC)
6.1 跑内置 chart 测试(air-gap 友好)
bash
helm test topograph -n topograph --timeout 120s
# 探针 pod 复用主镜像内的 wget,探测 Service 的 /healthz 与 /metrics
6.2 确认拓扑标签已写到 Node
Topograph(test provider + k8s engine)会在安装后数秒内自动把模拟拓扑写成 Node 标签:
bash
kubectl get nodes --show-labels | grep fabric.topograph.run
列出某节点的全部 topograph 标签:
bash
kubectl get node <node-name> --show-labels | tr ',' '\n' | grep topograph.run
前缀说明:
fabric.topograph.run/*为网络 fabric 分层标签;加速器域(若启用)在accelerator.topograph.run/domain。test 模式下这些是模拟值,仅用于验证流水线。
6.3 查看组件日志
bash
kubectl logs -n topograph -l app.kubernetes.io/name=topograph --tail=50
若标签迟迟不出现,先看 API server 日志确认 RBAC 与引擎是否报错。
6.4 API 冒烟测试(可选)
bash
kubectl -n topograph port-forward svc/topograph 49021:49021 &
# 健康检查
curl -i http://localhost:49021/healthz
# 指标
curl -s http://localhost:49021/metrics | grep topograph_
7. 使用
7.1 消费拓扑标签做拓扑感知调度
调度器(Kueue / Volcano / kube-scheduler 插件)读取 fabric.topograph.run/* 标签即可感知同 leaf/spine 归属。示例:一个 Pod 请求与某节点同 fabric 域:
yaml
apiVersion: v1
kind: Pod
metadata:
name: gpu-job
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: fabric.topograph.run/fabric-id # 实际 key 以集群写入为准
operator: In
values: ["<目标节点 fabric-id>"]
topologyKey: kubernetes.io/hostname
containers:
- name: train
image: registry.example.com/your-train-image:latest
真实 key 名用第 6.2 节的命令从集群实际写入结果中读取,再回填到
topologyKey/values。
7.2 按需触发拓扑生成(/v1/generate 工作流)
默认由 node-observer 自动触发;也可手动触发一次:
bash
cat > /tmp/payload.json <<'EOF'
{
"provider": { "name": "test",
"params": { "modelFileName": "small-tree.yaml",
"generateResponseCode": 202, "topologyResponseCode": 200 } },
"engine": { "name": "k8s" }
}
EOF
uid=$(curl -sS -X POST -H "Content-Type: application/json" \
-d @/tmp/payload.json http://localhost:49021/v1/generate)
echo "request uid = $uid"
curl -i "http://localhost:49021/v1/topology?uid=${uid}"
# 202 = 仍在处理;200 = 完成并返回拓扑;404 = uid 未知
testprovider 用内嵌模型small-tree.yaml模拟生成;topologyResponseCode: 200表示成功返回拓扑。
7.3 指标
GET /metrics(同端口 49021)导出 topograph_version、topograph_http_request_duration_seconds、topograph_request_duration_seconds、topograph_missing_topology、topograph_validation_error_total。如需接入 Prometheus,将第 3 节 values 的 serviceMonitor.enabled 改为 true 并在集群已有 Prometheus 时创建 ServiceMonitor。
8. 排错
| 现象 | 根因 | 处理 |
|---|---|---|
Pod ImagePullBackOff |
该节点未导入镜像,或 image.tag 与导入 ref 不一致 |
在该节点 ctr -n k8s.io images import 同 ref 的 tar;核对第 3 节 values 的 image.repository/tag 与 docker save 导出的 ref 完全一致 |
node-data-broker 未调度到 master |
master 有 NoSchedule 污点 |
在第 3 节 values 给 nodeDataBroker.tolerations 加 effect: NoSchedule(文件末尾已给模板) |
| 节点上看不到 fabric.topograph.run/* | 引擎未写标签 / RBAC 受限 | kubectl logs -n topograph -l app.kubernetes.io/name=topograph 看报错;确认 chart 安装时 RBAC 已创建(rbac.create 默认 true) |
| helm test 卡住/失败 | 探针 pod 也需主镜像 | 确保主镜像已导入到探针 pod 调度所在节点 |
| 切真实 provider 后拉不到凭证 | 未配 config.credentialsSecret | 用 kubectl create secret generic 挂载 credentials.yaml,并在 values 设 config.credentialsSecret |
9. 后续:从 test 切到真实 Provider
PoC 跑通后,若要发现真实 fabric 拓扑,只需改 provider.name 与相关参数(镜像/部署方式不变):
| 场景 | provider | 额外前提 |
|---|---|---|
| NVIDIA Spectrum-X / Cumulus NetQ | netq |
NetQ API 地址 + token(config.credentialsSecret) |
| InfiniBand fabric(K8s) | infiniband-k8s |
broker 需 privileged: true(覆盖 podSecurityContext),跑 ibnetdiscover |
NVIDIA GPU clique(已有 nvidia.com/gpu.clique) |
dra |
GPU Operator 已部署 |
| 云集群 | aws/gcp/oci/nebius/nscale/lambdai/crusoe |
对应云 IMDS / 凭证(不适合纯离线) |
切换示例(以 netq 为例):
bash
helm upgrade topograph ./topograph-1.0.0.tgz -n topograph \
-f values-offline.yaml \
--set provider.name=netq \
--set 'provider.params.netq.apiUrl=https://netq.example.com' \
--set config.credentialsSecret=netq-creds
验证方式不变:第 6 节的标签检查与 helm test。