问题处理手册
按现象排查 TensorFusion 的常见问题,从 GPU 没被发现、Pod 一直 Pending 到限额不生效
本手册按现象组织。找到与你看到的情况对应的小节,先执行检查命令,再按匹配的原因处理。文中的命令都假设 Helm release 和命名空间均为 tensor-fusion-sys,不一样的话请自行替换。
先收集基本信息
大多数问题都能从下面五条命令的输出里看出来,先把它们跑一遍:
kubectl get tensorfusioncluster,gpupool,gpunode,gpu
kubectl get pods -n tensor-fusion-sys -o wide
kubectl logs -n tensor-fusion-sys deploy/tensor-fusion-sys-controller -c controller --tail=200
kubectl logs -n tensor-fusion-sys hypervisor-<node> --all-containers --tail=200
kubectl describe pod <pod> -n <namespace>正常的安装应该是这样的:集群和资源池都是 Running;每个 GPU 节点有一个 Running 的 GPUNode 和一个 hypervisor-<node> Pod;每张卡有一个 GPU 资源,带总量和可用量。
controller 反复重启
检查
kubectl get pods -n tensor-fusion-sys -l tensor-fusion.ai/component=operator
kubectl logs -n tensor-fusion-sys deploy/tensor-fusion-sys-controller -c controller --previous
kubectl describe pod -n tensor-fusion-sys -l tensor-fusion.ai/component=operator| 现象 | 原因 | 处理 |
|---|---|---|
日志最后是 unable to load global config file | Helm values 里的 dynamicConfig 不合法 | 修正 values 后执行 helm upgrade |
日志最后是 invalid isolation mode policy | controller.isolationModePolicy 的值不受支持 | 改成 dynamic 或 static |
Pod 停在 ContainerCreating,事件里提到 Secret tensor-fusion-webhook-secret | webhook 证书没有生成。证书由 Helm hook job 创建,而用 helm template 加 kubectl apply 安装时 hook 不会执行 | 用 helm upgrade --install 安装或升级 |
| 升级之后马上出现“未知的 kind 或字段”一类报错 | CRD 比 controller 旧。Helm 不会升级 CRD | 手动 apply chart 里的 CRD,见组件更新 |
上一次退出状态是 OOMKilled | 内存上限对当前集群规模来说太小 | 调大 controller.resources.limits.memory |
没有发现 GPU
现象: kubectl get gpu 为空或缺卡,或者资源池的总量为零。
设备发现是一条链:打了标签的 Kubernetes Node 对应一个 GPUNode,GPUNode 对应一个 hypervisor Pod,hypervisor 再把每张卡上报成一个 GPU。从前往后找到第一个断掉的环节。
检查
kubectl get nodes -l nvidia.com/gpu.present=true
kubectl get gpunode
kubectl get pods -n tensor-fusion-sys -l tensor-fusion.ai/component=hypervisor -o wide
kubectl get gpu -o custom-columns='NAME:.metadata.name,MODEL:.status.gpuModel,PHASE:.status.phase,USED_BY:.status.usedBy,CAPACITY:.status.capacity'| 现象 | 原因 | 处理 |
|---|---|---|
| 第一条命令里没有这个节点 | 节点缺少 TensorFusion 用来筛选的标签。controller 只监听匹配 initialGpuNodeLabelSelector(默认 nvidia.com/gpu.present=true)的节点,资源池再通过 nodeManagerConfig.nodeSelector 选节点 | 给节点打标签:kubectl label node <node> nvidia.com/gpu.present=true。或者把 initialGpuNodeLabelSelector 和 cluster.pool.nodeSelectorKey 改成 GPU 节点上已有的标签 |
节点有标签,但没有 GPUNode | 资源池的节点选择器没有选中它 | 对比 kubectl get gpupool <pool> -o jsonpath='{.spec.nodeManagerConfig.nodeSelector}' 和节点的标签 |
有 GPUNode,但没有 hypervisor Pod,或者 Pod 不是 Running | hypervisor 起不来 | 见 hypervisor Pod 未就绪 |
hypervisor 是 Running,但没有 GPU | hypervisor 没有找到设备:厂商驱动没装,或者容器里看不到驱动 | 在节点上执行 nvidia-smi。查看 hypervisor 的日志和设备列表,方法见查看节点上的 hypervisor |
有 GPU,但 USED_BY 不是 tensor-fusion | 这张卡被以原生方式申请 GPU 的 Pod 占着,例如申请了 nvidia.com/gpu。TensorFusion 不会分配这样的卡 | 把那个工作负载迁到 TensorFusion,见迁移现有应用;或者把这张卡留给它 |
CAPACITY 里的 TFLOPS 缺失或明显不对 | 这个 GPU 型号不在厂商的 ProviderConfig 里,算力是运行时估算的 | 在 Helm values 的 providerConfigs.nvidia.hardwareMetadata 里补上这个型号,fullModelName 必须与 MODEL 列完全一致 |
hypervisor Pod 未就绪
现象: hypervisor-<node> 处于 Init、CrashLoopBackOff 或 Pending;GPUNode 一直是 Pending;或者 Pod 调度失败,提示 node tensor-fusion.ai/index_0 allocatable is <= 0, hypervisor likely unhealthy。
检查
kubectl describe pod -n tensor-fusion-sys hypervisor-<node>
kubectl logs -n tensor-fusion-sys hypervisor-<node> --all-containers --tail=200
kubectl logs -n tensor-fusion-sys hypervisor-<node> --all-containers --previous
kubectl describe gpunode <node>| 现象 | 原因 | 处理 |
|---|---|---|
卡在 init 容器 init-runtime 或 init-shm,状态为 ImagePullBackOff | GPU 节点拉不到厂商 provider 镜像(providerConfigs.nvidia.images.middleware)或 hypervisor 镜像 | 见镜像拉取失败 |
卡在 init 容器 toolkit-validation,日志是 waiting for nvidia container stack to be setup | controller.compatibleWithNvidiaContainerToolkit 为 true,hypervisor 在等 /run/nvidia/validations/toolkit-ready 这个文件,它由 NVIDIA GPU Operator 写入 | 修复节点上的 GPU Operator。没有使用 GPU Operator 的话,把这个值设为 false |
日志里有 failed to load accelerator library | TF_ACCELERATOR_LIB_PATH 指向的厂商库不存在,或者容器里看不到 GPU 驱动库 | 确认节点上 nvidia-smi 正常,并且已经为容器运行时安装、配置好 NVIDIA Container Toolkit |
Pod 被反复删除重建,GPUNode 上有 HypervisorCrashLoopRecreate 事件 | 容器重启达到 5 次(dynamicConfig.hypervisorMaxCrashCount)后,controller 会重建 hypervisor Pod | 赶在 Pod 被替换之前用 --previous 看日志,按日志里的崩溃原因处理 |
| controller 日志显示创建 Pod 被 Pod Security 拒绝 | hypervisor 需要特权模式、host PID 和 host 网络,而命名空间强制了更严格的 Pod Security 级别 | 给 TensorFusion 所在命名空间放开到 privileged 级别 |
| 日志显示 HTTP 端口已被占用 | hypervisor 使用主机网络,节点上有别的进程占了 8001 端口 | 释放该端口;或者在资源池上设置 componentConfig.hypervisor.portNumber,并重建该资源池的工作负载 |
处理之后,Pod 变为 Running,GPUNode 也随之变为 Running。
webhook 没有注入 Pod
现象: Pod 在运行,但看到的是整张 GPU,或者看不到 GPU,也没有 TensorFusion 的 init 容器。
检查
kubectl get pod <pod> -n <namespace> --show-labels
kubectl get pod <pod> -n <namespace> -o jsonpath='{.spec.schedulerName}{"\n"}{.spec.initContainers[*].name}{"\n"}'
kubectl logs -n tensor-fusion-sys deploy/tensor-fusion-sys-controller -c controller | grep "Mutating pod"被注入的 Pod 带有 tensor-fusion.ai/gpupool=<pool> 标签。使用本地 GPU 的 Pod,调度器是 tensor-fusion-scheduler;soft 模式下有 init-soft-limiter 和 tf-soft-preload-0 两个 init 容器;使用远程 GPU 或 hard 隔离时有 inject-lib;shared 和 partitioned 的 Pod 没有 init 容器。
| 原因 | 处理 |
|---|---|
没有 tensor-fusion.ai/enabled: "true" 标签。webhook 只会收到带这个标签的 Pod | 加到 spec.template.metadata.labels 下 |
| 标签或注解写在了 Deployment 自身的 metadata 上,而不是 Pod 模板上 | 挪到 spec.template.metadata 下 |
| Pod 是在加标签之前创建的。webhook 只在 Pod 创建时起作用 | 重建 Pod:kubectl rollout restart deployment/<name> |
tensor-fusion.ai/inject-container 写了一个不存在的容器名。这种情况下 webhook 直接跳过,不报错 | 改成正确的容器名 |
设置了 tensor-fusion.ai/enabled-replicas,而这个工作负载已经有这么多 Pod 用上了 TensorFusion | 灰度期间的正常行为。想覆盖更多副本就调大这个数 |
| webhook 没有注册 | kubectl get mutatingwebhookconfiguration tensor-fusion-sys-mutating-webhook 应该能查到。查不到就重新执行 helm upgrade |
创建 Pod 时被 webhook 拒绝
现象: Deployment 的 Pod 数少于副本数。报错在 ReplicaSet 上,不在 Pod 上。
检查
kubectl describe replicaset -n <namespace> -l <your-pod-label>| 报错内容包含 | 原因 | 处理 |
|---|---|---|
gpu pool(...) does not exist | tensor-fusion.ai/gpupool 指定的资源池不存在;或者没写这条注解,而集群里也没有默认资源池 | 从 kubectl get gpupool 里选一个名字;或者在 TensorFusionCluster 里给某个资源池设置 isDefault: true |
tflops request is not set | Pod 没有声明资源请求 | 加上 tensor-fusion.ai/tflops-request 或 tensor-fusion.ai/compute-percent-request,以及 tensor-fusion.ai/vram-request |
parse tf resources | 某条注解的值不合法 | 对照注解参考 |
failed calling webhook "mpod.tensor-fusion.ai" | 没有就绪的 controller 副本,或者证书无效。webhook 的失败策略是 Fail,这期间带标签的 Pod 都创建不了 | 见 controller 反复重启 |
Pod 一直 Pending
现象: 工作负载的 Pod,或者远程 GPU 工作负载的 worker Pod,一直没有被调度。
先找出真正需要 GPU 的那个 Pod。使用本地 GPU 时就是应用 Pod 本身;使用远程 GPU 时,应用 Pod 等的是 worker Pod,它由 controller 在同一个命名空间里创建:
kubectl get tensorfusionworkload,tensorfusionconnection -n <namespace>
kubectl get pods -n <namespace> -l tensor-fusion.ai/component=worker检查
kubectl describe pod <pending-pod> -n <namespace>
kubectl get gpu -o custom-columns='NAME:.metadata.name,MODEL:.status.gpuModel,PHASE:.status.phase,USED_BY:.status.usedBy,MODE:.status.isolationMode,ACTIVE_MODE:.status.activeIsolationMode,AVAILABLE:.status.available'
kubectl get gpuresourcequota -n <namespace>| 事件或提示 | 原因 | 处理 |
|---|---|---|
GPUQuotaOrCapacityNotEnough | 没有哪张 GPU 的可用 TFLOPS 和显存够这次请求用,或者命名空间的 GPUResourceQuota 已经用完 | 把请求值和 AVAILABLE 列对比。调低请求、腾出容量、加 GPU 节点,或者调大配额 |
每个节点都提示 GPU not fit | 有容量足够的 GPU,但都没通过其他筛选条件:gpu-model、gpu-indices、厂商或隔离模式 | 把这些注解和 MODEL、MODE、ACTIVE_MODE 列对照。一张 GPU 同一时间只服务一种隔离模式 |
Pod 申请的是 tensor-fusion.ai/isolation: shared | shared 占用整张 GPU,需要一张上面没有任何分配的卡 | 腾出一张卡,或者改用 soft |
hypervisor likely unhealthy | 该节点的 hypervisor 挂了 | 见 hypervisor Pod 未就绪 |
gang scheduling timeout,或者有 GangWaitingForMembers、GangSchedulingTimeout 事件 | Pod 属于一个 gang,成员没法同时全部放下 | 为整组腾出容量,或者调低 tensor-fusion.ai/gang-min-members |
| 只有 Kubernetes 的常规原因,例如污点、节点亲和性、CPU 或内存不足 | GPU 放得下,节点放不下 | 和普通 Pod 一样处理:调整容忍、选择器或 CPU、内存请求 |
| 完全没有调度事件 | Pod 用的是 tensor-fusion-scheduler,它运行在 controller 进程里,而当前没有就绪的 controller 副本 | 见 controller 反复重启 |
资源池的分配失败次数记录在 tf_system_metrics 表里,见查询指标和日志。
Pod 卡在 Init 阶段
现象: Pod 已经调度到节点,但一直是 Init:... 或 Init:ImagePullBackOff。
检查
kubectl describe pod <pod> -n <namespace>
kubectl logs <pod> -n <namespace> -c <init-container>| init 容器 | 原因 | 处理 |
|---|---|---|
init-soft-limiter | 拉不到厂商 provider 镜像 | 见镜像拉取失败 |
inject-lib | 拉不到 client 镜像 | 同上 |
tf-soft-preload-0 | 这个容器用的是你的应用镜像,需要镜像里有 /bin/sh、cat 和 chmod。distroless 和 scratch 镜像没有这些 | 换一个带 shell 的应用镜像,或者改用别的隔离模式 |
限额没有生效
现象: soft 隔离的工作负载用掉的显存或算力超过了限制值,或者容器里的 nvidia-smi 显示的是整张卡的显存。
检查
kubectl exec <pod> -n <namespace> -c <container> -- sh -c '
cat /etc/ld.so.preload
env | grep -E "^TF_(ISOLATION_MODE|SHM_PATH)="
ls -l "$TF_SHM_PATH"
nvidia-smi'限额正常生效时:/etc/ld.so.preload 里有 /tensor-fusion-limiter/libcuda_limiter.so;TF_ISOLATION_MODE 是 soft;TF_SHM_PATH 指向的文件存在;nvidia-smi 显示的显存总量就是显存限制值。
| 现象 | 原因 | 处理 |
|---|---|---|
没有 /etc/ld.so.preload,或者里面没有 limiter | Pod 没有被注入,或者隔离模式不是 soft | 见 webhook 没有注入 Pod |
隔离模式是 shared | 设计如此。shared 分配整张卡,不做任何限制 | 需要限制时用 soft 或 hard |
TF_SHM_PATH 指向的文件不存在 | 节点上的 hypervisor 还没有为这个 Pod 写入限额 | 先让 hypervisor 恢复正常,再重建 Pod |
| 上面几项都正常,但进程没有被限制 | 进程没有加载 /etc/ld.so.preload。静态链接的二进制和基于 musl 的镜像(如 Alpine)会这样 | 换成基于 glibc 的镜像。可以在应用进程的 /proc/<pid>/maps 里查 limiter 是否被加载来确认 |
| 算力用量短时间超过限制值 | soft 模式用限流器限制算力,短时突发会放行,平均值收敛到限制值 | 需要严格封顶时用 hard 隔离 |
清空 LD_PRELOAD 并不能关掉 limiter,因为它是通过 /etc/ld.so.preload 加载的。
hard 隔离的限额由 worker 执行。确认 worker 容器上有 TF_CUDA_SM_PERCENT_LIMIT 和 TF_GPU_MEMORY_LIMIT:
kubectl get pod <worker-pod> -n <namespace> -o yaml | grep -A1 -E 'TF_CUDA_SM_PERCENT_LIMIT|TF_GPU_MEMORY_LIMIT'CUDA 显存不足
现象: 应用报 CUDA out of memory 或 CUDA_ERROR_OUT_OF_MEMORY。
检查
kubectl exec <pod> -n <namespace> -c <container> -- nvidia-smi
kubectl get pod <pod> -n <namespace> -o jsonpath='{.metadata.annotations}'
kubectl describe pod <pod> -n <namespace>| 现象 | 原因 | 处理 |
|---|---|---|
容器里 nvidia-smi 显示用量已到显存限制值,而物理卡上还有空闲显存 | 工作负载触到了自己的 tensor-fusion.ai/vram-limit。soft 模式下 limiter 会让这次分配以显存不足失败 | 调大 vram-request 和 vram-limit,减小 batch size 或上下文长度,或者开启自动扩缩容 |
| 刚启动、还没真正干活就报错 | 框架一上来就预留固定比例的显存 | 在框架里限制预留量。TensorFlow:设置 TF_FORCE_GPU_ALLOW_GROWTH=true。vLLM:调低 --gpu-memory-utilization |
| 工作负载没到自己的限制值,但物理卡已经满了 | 这张卡上的工作负载显存限制值高于请求值,加起来超过了卡的实际显存 | 需要保证显存的工作负载,把 vram-request 设成和 vram-limit 一样 |
Pod 是 OOMKilled,或者被驱逐并提示 The node was low on resource: memory | 这是主机内存不足,不是显存 | 按模型加载时的实际需要,给容器设置内存请求和限制 |
CUDA 或 NVML 库报错
现象: NVML Function Not Found;libnvidia-ml.so.1: cannot open shared object file;worker 断言失败,例如 cuInit_fn != nullptr;或者 PyTorch 报 device >= 0 && device < num_gpus INTERNAL ASSERT FAILED。
这些报错说明进程找不到驱动的 libcuda.so.1 或 libnvidia-ml.so.1,或者找到的路径和 TensorFusion 预期的不一样。
检查:先在 GPU 节点上执行,再到出错的容器里执行:
nvidia-smi
ldconfig -p | grep -E 'libcuda.so.1|libnvidia-ml.so.1'
cat /etc/ld.so.preload| 原因 | 处理 |
|---|---|
| 节点上没有安装驱动,或驱动没有加载 | lsmod | grep nvidia 没有输出。安装 NVIDIA 驱动 |
| 节点上有驱动库,容器里没有 | 为容器运行时安装并配置 NVIDIA Container Toolkit |
库所在的目录和 worker 预期的不一样。chart 的 worker 模板指向 /usr/lib/x86_64-linux-gnu,而 RHEL 系发行版用的是 /usr/lib64 | 在出错的容器上设置 TF_CUDA_LIB_PATH 和 TF_NVML_LIB_PATH,指向容器里真实存在的 .so.1 路径。不要用带驱动版本号的路径,驱动一升级它就变了 |
| PyTorch 一个设备都看不到 | 应用进程没有加载 TensorFusion 的库,见 webhook 没有注入 Pod |
远程 GPU 工作负载
远程 GPU 工作负载的应用 Pod 通过 TensorFusionConnection 连到 worker Pod。先看两端的状态:
kubectl get tensorfusionconnection -n <namespace>
kubectl logs <worker-pod> -n <namespace> --tail=200| 现象 | 原因 | 处理 |
|---|---|---|
| connection 里没有 worker 或连接地址 | worker Pod 还没跑起来 | 见 Pod 一直 Pending |
| worker Pod 反复重启 | 先看它的日志。想看更多细节就调高日志级别,见链路追踪/性能分析 | 按日志里的原因处理。寻求帮助时把日志一起带上 |
应用报 CUDA driver error: named symbol not found | 应用调用了远程模式不支持的 CUDA API。worker 日志会写出是哪个,例如 call api=cuMemAddressReserve failed。PyTorch 设置了 PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True 时就会触发这一个 | 去掉触发该调用的设置,或者让工作负载使用本地 GPU |
应用报 Invalid Memory Address | 与 worker 的显存管理器之间的一种少见冲突 | 给这个工作负载加注解 tensor-fusion.ai/disable-features: mem-manager,关掉显存管理器。依赖它的功能对该工作负载不再可用 |
镜像拉取失败
TensorFusion 的镜像在两类节点上拉取:controller、Alertmanager 和 GreptimeDB 的镜像在这些 Pod 所在的节点上拉;hypervisor、厂商 provider、client 和 worker 的镜像在 GPU 节点和业务工作负载所在的节点上拉。
检查
kubectl get pods -A | grep -E 'ImagePullBackOff|ErrImagePull'
kubectl describe pod <pod> -n <namespace>| 原因 | 处理 |
|---|---|
| 集群访问不了 Docker Hub 等公共镜像仓库 | 在每个节点的容器运行时里配置镜像加速。或者把镜像同步到自己的仓库,再用 Helm values 覆盖:controller.image.repository、cluster.hypervisorImage、providerConfigs.nvidia.images.*、greptime.image.repository、alert.image.repository |
| 集群在中国大陆 | 加上 values-cn.yaml,见在 Kubernetes 安装 |
| 私有仓库需要凭据 | 在 Helm values 里设置 imagePullSecrets |
申请 nvidia.com/gpu 的 Pod
TensorFusion 不会注册 nvidia.com/gpu 资源。申请了它但没有 TensorFusion 标签的 Pod,webhook 不做任何处理;只有装了 NVIDIA device plugin,这样的 Pod 才能被调度:
helm upgrade --install --create-namespace --namespace nvidia-device-plugin \
--repo https://nvidia.github.io/k8s-device-plugin/ nvdp nvidia-device-plugin想把这类工作负载改由 TensorFusion 管理,见迁移现有应用。
可以忽略的报错
Error in cpuinfo: prctl(PR_SVE_GET_VL) failed:ARM 容器镜像跑在不支持 SVE 指令的 CPU 上时会出现,例如在 macOS 上。大多数情况下可以忽略。
获取帮助
以上都对不上的话,可以到 github.com/NexusGPU/tensor-fusion 提 issue,或者发邮件到 support@tensor-fusion.com,并附上先收集基本信息里那几条命令的输出。分享日志之前,请去掉其中的 token、密钥和内网地址。
TensorFusion 文档