LogoTensorFusion 文档
LogoTensorFusion 文档
首页

快速开始

TensorFusion 概览在 Kubernetes 安装在虚拟机/服务器安装(K3S)Helm 快速安装在宿主机/虚拟机安装TensorFusion 架构

应用操作

创建 AI 应用注解使用场景配置自动扩缩容迁移现有应用最佳实践

自定义AI基础设施

生产级部署指南QoS 级别与计价云厂商集成(BYOC)管理许可证

维护与优化

组件更新配置告警GPU 热迁移预加载模型优化 GPU 效率

故障排除

问题处理手册链路追踪/性能分析查询指标和日志

参考

对比

与 NVIDIA vGPU 对比与 MIG/MPS 对比与趋动科技对比与 NVIDIA Run:ai 对比与 HAMi 的对比

云厂商集成(BYOC)

让 GPU 池在你自己的云账号里自动增减 GPU 节点,可以通过 Karpenter,也可以直接调用云 API

GPU 池可以在你自己的云账号里按需扩缩。本文说明池的三种节点供给模式、每种模式目前支持哪些云,以及各自怎么配置。

先说结论:在 AWS EKS 上使用 Karpenter 时,保持默认的 AutoSelect 模式,让 Karpenter NodePool 管理 GPU 节点。这是 Helm chart 的默认路径,也是端到端测试覆盖的路径。另外两种模式按你在池上设置的容量目标工作,目前属于预览能力,请先在测试集群验证。

节点供给模式

模式在每个池的 nodeManagerConfig.provisioningMode 中设置。

AutoSelect(默认)KarpenterProvisioned
谁创建节点TensorFusion 之外的任何方式:手工、节点组或 Karpenter NodePoolTensorFusion 创建 Karpenter NodeClaimTensorFusion 调用云 API
节点如何进入池池的 nodeSelector 匹配节点标签为该池创建为该池创建
扩容触发条件TensorFusion Pod 因 GPU 容量不足而 Pending(要求节点归 Karpenter 管理)池容量低于 minResources,或空闲容量低于 warmResources;以及 Pod Pending同 Karpenter
缩容TensorFusion 给空节点打标签,由 Karpenter 删除TensorFusion 删除节点TensorFusion 删除节点
TensorFusion 是否需要云凭据不需要不需要需要
目前支持任意集群;自动扩容需要 Karpenter任意 Karpenter provider,已在 AWS 上测试阿里云 ECS;AWS EC2 尚不完整,见下文

一个池只能用一种模式,同一集群里的不同池可以用不同模式。

前置条件

  • 已安装 TensorFusion。参见 在 Kubernetes 安装。
  • controller 带 -enable-auto-expander 参数启动,默认命令里已包含。
  • 两种 Karpenter 方案都需要:Karpenter 1.6 或更高版本,并开启 nodeOverlay feature gate;一个能把 GPU 节点拉起并加入集群的 EC2NodeClass(或你所用 provider 的 NodeClass)。
  • GPU 节点带有 initialGpuNodeLabelSelector 选中的标签,默认是 nvidia.com/gpu.present=true。

方案一:AutoSelect + Karpenter NodePool(推荐)

GPU 节点归 Karpenter 管理。TensorFusion 把这些节点选进池,GPU 不够时向 Karpenter 要一台新节点,并标记可以回收的空节点。

第 1 步:先装 Karpenter,再装 TensorFusion

在 Karpenter 的 Helm values 中开启 nodeOverlay feature gate:

settings:
  featureGates:
    nodeOverlay: true

TensorFusion Pod 会申请名为 tensor-fusion.ai/index_* 的扩展资源。Karpenter 只有在认为某种机型能提供 Pod 申请的全部资源时,才会为它拉起节点。所以 TensorFusion chart 会创建一个名为 tensor-fusion-overlay 的 NodeOverlay 来声明这些资源,但前提是安装时集群里已经有 nodeoverlays.karpenter.sh 这个 CRD。

如果你先装了 TensorFusion,装完 Karpenter 后对 TensorFusion 的 release 再执行一次 helm upgrade,然后检查:

kubectl get nodeoverlay tensor-fusion-overlay

第 2 步:为 GPU 节点创建 NodePool

给节点加上默认池所选择的标签,并把机型限定为 GPU 机型:

apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: tf-gpu
spec:
  template:
    metadata:
      labels:
        nvidia.com/gpu.present: "true"
    spec:
      nodeClassRef:
        group: karpenter.k8s.aws
        kind: EC2NodeClass
        name: <your-ec2-node-class>
      expireAfter: Never
      requirements:
        - key: node.kubernetes.io/instance-type
          operator: In
          values: ["g5.xlarge", "g5.12xlarge"]
        - key: karpenter.sh/capacity-type
          operator: In
          values: ["on-demand", "spot"]
        - key: kubernetes.io/arch
          operator: In
          values: ["amd64"]
        - key: kubernetes.io/os
          operator: In
          values: ["linux"]
  # 这个 NodePool 能拉起的资源上限
  limits:
    nvidia.com/gpu: 8
  disruption:
    consolidationPolicy: WhenEmpty
    consolidateAfter: 5m

Helm chart 创建的默认池本来就是 AutoSelect 模式,并选择带 nvidia.com/gpu.present=true 的节点,所以不用修改 TensorFusionCluster。

第 3 步:验证

NodePool 拉起 GPU 节点后,TensorFusion 中会出现对应资源:

kubectl get nodeclaim,gpunode,gpu

把现有 GPU 占满,再创建一个 TensorFusion Pod,观察新节点加入:

kubectl get events -A --field-selector reason=NodeExpansionCompleted
kubectl get nodeclaim -w

节点加入集群、GPU 注册完成后,Pod 从 Pending 变为 Running。

工作方式

  • 扩容。 调度器拒绝一个 TensorFusion Pod 后,controller 先判断是不是只缺 GPU 容量。如果是,它挑一个归 Karpenter NodeClaim 所有的现有 GPU 节点作模板,在同一个 NodePool 里创建新的 NodeClaim。新节点遵守 NodePool 的 requirements,并按 Pod 的 nodeSelector 和节点亲和(仅机型、可用区、地域)进一步收窄。
  • 并发。 同时供给的节点数不超过 dynamicConfig.maxInFlightNodes,默认 15。
  • 缩容。 池的压缩循环(nodeManagerConfig.nodeCompaction.period)给空节点打上标签 tensor-fusion.ai/should-delete=true。TensorFusion 自己不删节点,由 Karpenter 的 consolidation 删除。创建不足 5 分钟的节点不会被标记;删掉后空闲容量会低于 warmResources 的节点也不会被标记。
  • 容量上限。 这种模式下 capacityConfig.minResources 和 maxResources 不驱动节点供给,规模上限请用 NodePool 的 limits 控制。

已知限制:

  • 扩容需要模板节点:池里至少要有一台由 Karpenter 创建的 GPU 节点。托管节点组里的 GPU 节点不能当模板,此时 Pod 保持 Pending,并出现 NodeExpansionFailed 事件。
  • GPU 节点数为 0 时,controller 把决定权交给 Karpenter。Karpenter 会拉起什么机型取决于你的 NodePool 配置,所以建议常驻一台 GPU 节点,或者在你的环境里先验证冷启动。
  • Karpenter 的 consolidation 不知道 TensorFusion 在节点上已经分配了多少 GPU 容量。除非你用自己的负载验证过 WhenEmptyOrUnderutilized,否则使用 consolidationPolicy: WhenEmpty。

方案二:Karpenter 模式

TensorFusion 根据池上的容量目标决定何时增减节点,Karpenter 只负责拉起和终止实例,不需要 NodePool。TensorFusion 先创建 GPUNodeClaim,再创建同名的 Karpenter NodeClaim。

预览能力,正式使用前请在测试集群验证。minResources 不为零时会立即拉起节点,建议先从很小的值开始。

在集群上设置 computingVendor,在池上设置 provisioner:

apiVersion: tensor-fusion.ai/v1
kind: TensorFusionCluster
metadata:
  name: tensor-fusion
spec:
  computingVendor:
    name: karpenter
    type: karpenter
    authType: serviceAccountRole
    enable: true
    params:
      defaultRegion: us-east-1
  gpuPools:
    - name: elastic
      isDefault: false
      specTemplate:
        # componentConfig 和 schedulingConfigTemplate 从现有的池复制
        capacityConfig:
          minResources:
            tflops: "65"
            vram: "16Gi"
          warmResources:
            tflops: "0"
            vram: "0"
          maxResources:
            tflops: "500"
            vram: "128Gi"
        nodeManagerConfig:
          provisioningMode: Karpenter  // [!code highlight]
          nodeProvisioner:
            karpenterNodeClassRef:
              group: karpenter.k8s.aws
              kind: EC2NodeClass
              version: v1
              name: <your-ec2-node-class>
            gpuRequirements:
              - key: node.kubernetes.io/instance-type
                operator: In
                values: ["g4dn.xlarge", "g5.xlarge"]
              - key: karpenter.sh/capacity-type
                operator: In
                values: ["OnDemand"]
            gpuNodeLabels:
              nvidia.com/gpu.present: "true"
            # gpuTaints:
            #   - key: group
            #     value: gpu
            #     effect: NoSchedule
          nodeCompaction:
            period: 5m

容易出错的地方:

  • type: karpenter 不需要云凭据,controller 只访问 Kubernetes API。它会检查自己能否列出 NodeClaim,结果写在集群的 condition CloudVendorConnectionReady 中。
  • karpenterNodeClassRef 必须指向已存在的 NodeClass,四个字段都要填。
  • gpuNodeLabels 必须包含 initialGpuNodeLabelSelector 选中的标签,否则 controller 不会监听新节点。
  • capacity type 的取值是 OnDemand、Spot、Reserved,TensorFusion 会转换成 Karpenter 的 on-demand、spot、reserved。列表里有 Spot 时优先使用 Spot。
  • 每个 NodeClaim 都会申请 nvidia.com/gpu。Karpenter 要等节点上报这个资源,所以节点上必须运行 NVIDIA device plugin。
  • 这样创建的 NodeClaim 带有 karpenter.sh/do-not-disrupt: "true",Karpenter 不会对它们做 consolidation。节点变空、且 warmResources 不再需要它时,由 TensorFusion 删除。

computingVendor.params.extraParams 中的可选项:

键作用
karpenter.gpuResourceNodeClaim 上申请的资源名(默认:nvidia.com/gpu)
karpenter.nodeClaim.terminationGracePeriodNodeClaim 的 terminationGracePeriod,例如 5m

观察进度:

kubectl get tensorfusioncluster tensor-fusion -o jsonpath='{.status.conditions}'
kubectl get gpunodeclaim,nodeclaim,gpunode -w

GPUNodeClaim 在对应的 NodeClaim 创建后从 Pending 变为 Creating,新节点上的 hypervisor 运行起来后变为 Bound。

TensorFusion 如何选择机型

在 Karpenter 和 Provisioned 模式下,TensorFusion 先算出 TFLOPS 和 VRAM 的容量缺口,再从内置的机型目录里选一种机型:补齐缺口所需的每小时总价最低的那种。只有下面这些 gpuRequirements 键会影响选择:

键用途
node.kubernetes.io/instance-type允许的机型
kubernetes.io/archamd64 或 arm64
tensor-fusion.ai/gpu-vendor机型的 GPU 厂商
karpenter.sh/capacity-typeOnDemand、Spot 或 Reserved
topology.kubernetes.io/region地域,取第一个值。默认使用 computingVendor.params.defaultRegion
topology.kubernetes.io/zone可用区,每次扩容随机选一个

机型目录是静态的:里面是 AWS 的 GPU 机型和按需价格,价格不区分地域,Spot 也按按需价格计算。建议直接列出你想用的机型,不要依赖比价结果。

方案三:Provisioned 模式

TensorFusion 自己调用云的计算 API。扩缩容逻辑与 Karpenter 模式相同,区别在于由谁拉起实例,以及实例的规格要用 GPUNodeClass 描述。

预览能力,各云的实现程度不一,见下表。如果你的云有 Karpenter provider,优先使用方案一。

支持的云

CRD 中 computingVendor.type 可以填很多值,controller 实际实现的是这些:

type状态认证方式传给云 API 的内容
alibaba已实现仅 authType: accessKey启动模板 ID、镜像 ID、安全组 ID、交换机 ID、数据盘、标签、user data,以及下文的额外参数。内置机型目录只有 ecs.gn6i-c8g1.2xlarge 和 ecs.gn6i-c16g1.4xlarge
aws不完整AWS 默认凭据链,例如绑定在 controller ServiceAccount 上的 IAM 角色只有 AMI ID、机型和标签。GPUNodeClass 里的子网、安全组、instance profile、块设备、user data 都不会生效,实例无法自行加入集群。在 AWS 上请用方案一或方案二
karpenter已实现无用于 Karpenter 模式,见方案二
gcp、azure 等其他取值未实现controller 报 unsupported cloud provider

配置阿里云

  1. 准备一组可以创建、查询、删除 ECS 实例的 AccessKey。chart 把它保存在 Secret tf-cloud-vendor-credentials(键为 ak 和 sk)中,并挂载到 controller 的 /tmp/secret。

    首次安装时,传入 --set cloudVendorCredentials.accessKey=<access-key-id> --set cloudVendorCredentials.secretKey=<access-key-secret>。chart 只在这个 Secret 不存在时写入,所以已安装的环境要直接更新 Secret:

    kubectl -n tensor-fusion-sys patch secret tf-cloud-vendor-credentials --type merge \
      -p '{"stringData":{"ak":"<access-key-id>","sk":"<access-key-secret>"}}'
  2. 创建描述实例规格的 GPUNodeClass:

    apiVersion: tensor-fusion.ai/v1
    kind: GPUNodeClass
    metadata:
      name: tf-node-class
    spec:
      osImageSelectorTerms:
        - id: <image-id>
      securityGroupSelectorTerms:
        - id: <security-group-id>
      subnetSelectorTerms:
        - id: <vswitch-id>
      blockDeviceMappings:
        - deviceName: data
          ebs:
            volumeSize: "200"
            volumeType: cloud_essd
      tags:
        team: ai-platform
      userData: |
        #!/bin/bash
        # 用你平时的引导命令把节点加入集群,并在注册时带上下面两个节点标签。
        #   tensor-fusion.ai/node-provisioner=__GPU_NODE_RESOURCE_NAME__
        #   nvidia.com/gpu.present=true

    各 selector 目前只按 id 匹配。拉起实例前,controller 会把 userData 中的 __GPU_NODE_RESOURCE_NAME__ 替换成 GPUNodeClaim 的名称。节点注册时必须带有标签 tensor-fusion.ai/node-provisioner,值为这个名称。TensorFusion 靠这个标签把节点和它的 claim 对应起来。

  3. 让集群和池引用它们:

    apiVersion: tensor-fusion.ai/v1
    kind: TensorFusionCluster
    metadata:
      name: tensor-fusion
    spec:
      computingVendor:
        name: alibaba
        type: alibaba
        authType: accessKey
        enable: true
        params:
          defaultRegion: cn-hangzhou
          accessKeyPath: /tmp/secret/ak
          secretKeyPath: /tmp/secret/sk
          extraParams:
            systemDiskSize: "100"
      gpuPools:
        - name: elastic
          isDefault: false
          specTemplate:
            # componentConfig 和 schedulingConfigTemplate 从现有的池复制
            capacityConfig:
              minResources:
                tflops: "65"
                vram: "15Gi"
              maxResources:
                tflops: "260"
                vram: "60Gi"
            nodeManagerConfig:
              provisioningMode: Provisioned  // [!code highlight]
              nodeProvisioner:
                nodeClass: tf-node-class
                gpuRequirements:
                  - key: node.kubernetes.io/instance-type
                    operator: In
                    values: ["ecs.gn6i-c8g1.2xlarge"]
                  - key: topology.kubernetes.io/zone
                    operator: In
                    values: ["cn-hangzhou-h"]
              nodeCompaction:
                period: 5m

阿里云在 computingVendor.params.extraParams 中支持的额外参数:

键作用默认值
systemDiskCategory系统盘类型cloud_essd
systemDiskSize系统盘大小,单位 GiB40
dataDiskPerformanceLevel数据盘性能等级PL0
keyPairName实例的 SSH 密钥对无
spotPriceLimit抢占式实例的每小时最高出价。不设置时按市场价无
spotDuration抢占式实例的保护时长,单位小时0
spotInterruptionBehavior抢占式实例被回收时的处理方式云的默认值

创建出来的实例带公网 IP,按流量计费,出方向带宽上限 100 Mbps。

容量设置

Karpenter 和 Provisioned 模式根据池的 capacityConfig 工作:

字段作用
minResources池的 TFLOPS 和 VRAM 总量低于这些值时持续扩容
warmResources池进入 Running 后,空闲 TFLOPS 或 VRAM 低于这些值就扩容。空节点只有在删除后剩余空闲容量仍高于这些值时才会被回收
maxResourcesTFLOPS 或 VRAM 总量达到这些值后不再加节点,池上会出现 MaxResourceConstraintReached 事件

其他需要了解的行为:

  • 新节点在 5 分钟内不会被压缩回收。
  • 带有标签 tensor-fusion.ai/do-not-disrupt=true 的节点不会被压缩回收。
  • CRD 中有 nodeProvisioner.budget,但当前 controller 并不执行预算限制。请用 maxResources 控制花费上限。

故障排查

现象检查
CloudVendorConnectionReady 为 FalseKarpenter 模式:确认已安装 Karpenter 的 CRD。Provisioned 模式:确认凭据和 defaultRegion 正确
GPUNodeClaim 一直是 Pending查看 controller 日志。常见原因:karpenterNodeClassRef 指向的 NodeClass 不存在,或 nodeClass 指向的 GPUNodeClass 不存在
节点已加入集群,但没有出现 GPUNode节点缺少 initialGpuNodeLabelSelector 选中的标签
AutoSelect 模式下 Pod 一直 Pending,也没有新节点查看 Pod 的事件。NodeExpansionFailed 表示找不到归 Karpenter 管理的 GPU 节点作模板
Karpenter 始终不为 TensorFusion Pod 拉起节点kubectl get nodeoverlay tensor-fusion-overlay 没有结果,或者没有开启 nodeOverlay feature gate
kubectl logs -n tensor-fusion-sys deploy/tensor-fusion-sys-controller -c controller --tail=200

下一步

  • 生产级部署指南
  • GPUPool 字段说明和 GPUNodeClass 字段说明
  • TensorFusionCluster 字段说明,包含 computingVendor
  • Helm 安装 Values 配置

QoS 级别与计价

为工作负载指定 QoS 级别,并设置 TensorFusion 计算工作负载成本和 GPU 节点成本时使用的单价

管理许可证

TensorFusion 哪些组件需要许可证,以及如何在集群中安装、验证和轮换许可证密钥

目录

节点供给模式
前置条件
方案一:AutoSelect + Karpenter NodePool(推荐)
第 1 步:先装 Karpenter,再装 TensorFusion
第 2 步:为 GPU 节点创建 NodePool
第 3 步:验证
工作方式
方案二:Karpenter 模式
TensorFusion 如何选择机型
方案三:Provisioned 模式
支持的云
配置阿里云
容量设置
故障排查
下一步