【阅读原文】戳:NVIDIA NIM on ACK:优化生成式AI模型的部署与管理
NVIDIA NIM是一套易于使用的微服务,旨在加速在云、数据中心和工作站等不同环境中部署生成式AI模型。然而,在Kubernetes(K8s)环境下,使用NIM部署推理服务时,我们仍然需要更多的服务化管理能力,以及针对具体场景的最佳实践。例如,弹性伸缩配置、提高弹性效率和冷启动速度、实现流量控制和灰度发布,以及实时监控能力等。本文结合NVIDIA NIM和阿里云容器服务(ACK)等产品,提供了NVIDIA NIM在ACK上的完整服务化管理方案。
方案介绍
在ACK集群上,我们使用Knative + Fluid的架构来部署和管理NVIDIA NIM服务。Knative通过基于请求的自动弹性策略KPA,能够迅速响应突发流量,灵活扩展实例以应对高峰负载,并在流量超出应用承载能力时提供缓冲处理。同时,通过NIM的离线下载能力、Fluid的数据加速能力,将LLM弹性效率从分钟,降低到秒级。Knative强大的流量管理能力能够让我们快速实施灰度发布和版本回退。此外,我们结合阿里云的 Prometheus 和 Grafana 服务,快速搭建监控大盘,实时观测推理服务状态。
环境准备
一、已创建ACK集群并包含多个A10 GPU节点
二、安装ack-knative:[1]部署Knative_容器服务Kubernetes版ACK(ACK)-阿里云帮助中心
三、安装ack-fluid: 已安装云原生AI套件并部署ack-fluid组件。具体操作,请参见[2]安装云原生AI套件。
四、准备NIM相关API key、存储卷等资源。
1. 参考[3]NVIDIA NIM文档,生成NVIDIANGC API key,并申请需要部署的模型镜像访问权限,比如本文中使用的Llama3-8b-instruct。请阅读并承诺遵守Llama模型的[4]自定义可商用开源协议。
2. 创建imagePullSecret,用于从NGC私有仓库拉取NIM镜像。
$ export NGC_API_KEY=<your-ngc-api-key> $ kubectl create secret docker-registry ngc-secret \ --docker-server=nvcr.io\ --docker-username='$oauthtoken'\ --docker-password=${NGC_API_KEY}
3. 创建nvidia-nim-secret,用于在容器内访问NGC私有仓库,参考[5]nim-deploy部署文档。
$ kubectl apply -f-<<EOF apiVersion: v1 kind: Secret metadata: name: nvidia-nim-secrets stringData: NGC_API_KEY: <your-ngc-api-key> EOF
4. 为目标集群配置存储卷PV和存储声明PVC,后续模型文件将会下载到创建的共享存储中。具体操作,请参见[6]使用NAS静态存储卷。
以下为示例PV的配置信息:
配置项 | 说明 |
存储卷类型 | NAS |
名称 | nim-models |
访问模式 | ReadWriteMany |
挂载点域名 | 您可以通过选择挂载点或者自定义的方式定义集群在NAS文件系统中挂载点的挂载地址。关于如何查看挂载点地址,请参见[7]查看挂载点地址。 |
挂载路径 | 指定模型所在的路径,如/nim-models |
以下为示例PVC的配置信息:
配置项 | 说明 |
存储声明类型 | NAS |
名称 | nim-models |
分配模式 | 选择已有存储卷 |
已有存储卷 | 单击选择已有存储卷链接,选择已创建的存储卷PV。 |
弹性伸缩
- 基于请求的自动弹性 -
Knative与NVIDIA NIM的契合之处在于,Knative具备强大的流量管理能力,默认支持基于请求的自动弹性。在大规模语言模型(LLM)推理场景下,由于单次推理时间相对较长,合理的弹性指标主要是针对单个Pod的并发处理能力。我们可以利用NVIDIA NIM提供的丰富监控指标,如num_requests_waiting,来配置推理服务的水平自动扩展(HPA)策略。也可以使用Knative默认的基于并发量的[8]自动扩展(KPA)策略。基于NVIDIA NIM的监控指标的HPA伸缩配置,可以参照[9]使用NVIDIA NIM在阿里云容器服务(ACK)中加速LLM推理。本节主要展示基于Knative默认支持的KPA策略,通过并发量实现弹性伸缩。
Knative通过其框架自身的能力,实现了基于请求的自动弹性,不需要业务Pod暴露请求相关的指标。Knative的具体实现方式是通过Knative Serving为每个Pod注入QUEUE代理容器(queue-proxy),该容器负责收集用户容器的并发数(concurrency)或请求数(rps)指标。Autoscaler通过定时获取这些指标,并依据特定算法调整部署的Pod数量,从而实现基于请求的自动扩缩容。
我们可以基于以下配置配置NIM基于并发量的KPA弹性伸缩策略:
apiVersion: serving.knative.dev/v1 kind: Service metadata: labels: release: nim-llama3-8b-instruct name: nim-llama3-8b-instruct namespace: default spec: template: metadata: annotations: autoscaling.knative.dev/class: kpa.autoscaling.knative.dev autoscaling.knative.dev/metric: "concurrency" # 配置并发数为弹性伸缩指标 autoscaling.knative.dev/target: "5" # 设置当前最大并发请求数为5 autoscaling.knative.dev/min-scale: "1" autoscaling.knative.dev/max-scale: "5" labels: release: nim-llama3-8b-instruct spec: containers: - image: nvcr.io/nim/meta/llama3-8b-instruct:1.0.0 securityContext: runAsUser: 0 imagePullPolicy: IfNotPresent name: nim-container env: - name: "NGC_API_KEY" valueFrom: secretKeyRef: name: nvidia-nim-secrets key: NGC_API_KEY - name: "NIM_CACHE_PATH" value: /opt/nim/.cache ports: - containerPort: 8000 readinessProbe: successThreshold: 1 tcpSocket: port: 8000 resources: limits: cpu: "32" memory: 64Gi nvidia.com/gpu: "1" requests: cpu: "16" memory: 64Gi nvidia.com/gpu: "1" volumeMounts: - mountPath: /opt/nim/.cache name: nim-cache imagePullSecrets: - name: ngc-secret volumes: - name: nim-cache persistentVolumeClaim: claimName: nim-cache
检查服务是否部署成功
$ kubectl get ksvc NAME URL LATESTCREATED LATESTREADY READY REASON nim-llama3-8b-instruct http://nim-llama3-8b-instruct.default.example.com nim-llama3-8b-instruct-00001 nim-llama3-8b-instruct-00001 True
访问服务
# 获取推理服务地址。 $ INGRESS_HOSTNAME=$(kubectl get ingress nim-llama3-8b-instruct -ojsonpath='{.status.loadBalancer.ingress[0].hostname}') $ SERVICE_HOSTNAME=$(kubectl get ingress nim-llama3-8b-instruct -o jsonpath='{.spec.rules[0].host}') # 发送请求访问推理服务。 $ curl -H "Host: $SERVICE_HOSTNAME" -H "Content-Type: application/json" http://$INGRESS_HOSTNAME:80/v1/chat/completions -d '{"model": "meta/llama3-8b-instruct", "messages": [{"role": "user", "content": "Once upon a time"}], "max_tokens": 64, "temperature": 0.7, "top_p": 0.9, "seed": 10}'
预期输出
{"id":"cmpl-f459c888d13e4c758d3f15e36239a74d","object":"chat.completion","created":1732604109,"model":"meta/llama3-8b-instruct","choices":[{"index":0,"message":{"role":"assistant","content":"There once was a GPU so fine,\nWhose computing power was divine.\nIt processed with speed,\nAnd calculations did proceed,\nIn parallel, with wonders that shine."},"logprobs":null,"finish_reason":"stop","stop_reason":128009}],"usage":{"prompt_tokens":22,"total_tokens":56,"completion_tokens":34}}
输出结果表明,已经能够通过ALB Ingress网关地址访问推理服务,并成功返回推理结果。
Knative KPA结合了“稳定模式”和“恐慌模式”这两种机制,以实现精准的弹性伸缩管理。在“稳定模式”中,KPA在默认的稳定窗口期(通常为60秒)内计算Pod的平均并发数。然而,在应对突发的大量请求情境时,基于稳定窗口期的调整可能导致弹性反应滞后。因此,当“恐慌窗口期”(默认设置为6秒)内接收到大量请求,并且导致当前Pod的使用率超出恐慌阈值时,KPA会迅速增加Pod的数量以满足即时负载需求。
此外,KPA还允许配置突发请求容量(target-burst-capacity)。当应用的承载能力较低时,activator组件会暂时缓冲超过应用处理能力的请求,以防止Pod因超出预期的流量负载而崩溃。当Pod的请求处理量减少后,这些被缓冲的请求会重新分配到适当的Pod上。
通过Knative基于请求的KPA弹性伸缩策略,我们能够更精细化的管理NIM服务的弹性伸缩。
- 弹性能力增强 -
在对NIM服务的请求进行弹性伸缩的过程中,弹性效率是一个关键关注点。大模型的Pod通常需要存储大容量的推理镜像和模型文件,这样的存储需求导致数据传输耗时较长,进而使得大模型的冷启动时间通常以分钟计。这种情况下,弹性效率的低下无疑会导致弹性响应的严重滞后,使许多用户在考虑服务水平协议(SLA)时对使用即时的弹性伸缩策略感到犹豫。
为了解决这一问题,我们针对NIM场景提出了一系列增强弹性能力的策略,将Pod的冷启动时间从分钟级缩短到秒级。在基于Llama3-8b-instruct进行的测试中,我们成功地将冷启动时间从约2分钟减少到30秒左右。
离线下载Profile
当前,通过NIM部署推理服务的标准启动流程是,NIM会自动检测当前节点的GPU类型,并选择合适的优化配置文件进行下载。在弹性伸缩过程中进行这种下载操作无疑会极大地降低效率。因此,一个合理的措施是预先将相应的配置文件下载至存储卷中,例如NAS或OSS存储卷。之后,Pod只需挂载该存储卷即可。
NIM已经在镜像中内置了关于Model Profile的工具,因此,在部署应用之前,我们可以通过如下方式提前下载对应的Profile。
apiVersion: batch/v1 kind: Job metadata: name: predownload-llama3-8b-instruct labels: app: predownload-llama3-8b-instruct spec: template: metadata: labels: app: predownload-llama3-8b-instruct name: predownload-llama3-8b-instruct spec: containers: - name: predownload-llama3-8b-instruct image: nvcr.io/nim/meta/llama3-8b-instruct:1.0.0 args: #为对应GPU选择可用的Profile,这里以A10为例 - --profile - 8835c31752fbc67ef658b20a9f78e056914fdef0660206d82f252d62fd96064d - --model-store - /opt/nim/model-repository command: - create-model-store securityContext: runAsUser: 0 env: - name: "NGC_API_KEY" valueFrom: secretKeyRef: name: nvidia-nim-secrets key: NGC_API_KEY resources: requests: cpu: "8" memory: 16Gi volumeMounts: - mountPath: /opt/nim/model-repository name: nim-models imagePullSecrets: - name: ngc-secret restartPolicy: OnFailure volumes: - name: nim-models persistentVolumeClaim: claimName: nim-models
预期输出
$ kubectl get job NAME STATUS COMPLETIONS DURATION AGE predownload-llama3-8b-instruct Complete 1/1 11m 12m
注意:如果弹性过程中涉及到多种GPU卡型,务必保证每种卡型的Profile都被下载过,确定GPU卡型和Profile的对应关系可以参照[10]Serving models from local assets。
启动加速
通过以上操作,我们虽然通过离线下载的方式,将Model Profile提前下载到存储卷中。但在AI模型推理服务启动场景下:发布或更新某AI模型推理服务时,大量推理服务实例被并发拉起,各推理服务实例并发读取存储系统中相同的模型文件,并加载到推理服务实例的GPU内存中。这时候就会遇到Kubernetes集群中访问存储系统数据时容易出现的高延迟及带宽受限问题。
为保证应用在访问数据时的性能,Fluid通过数据预加载提前将远程存储系统中的数据拉取到靠近计算节点的分布式缓存引擎中,使得消费该数据集的应用能够在首次运行时即可享受到缓存带来的加速效果。在对Llama3-8b-instruct的测试中,Pod冷启动时间可以从2min左右降低到30s左右。
apiVersion: data.fluid.io/v1alpha1 kind: Dataset metadata: name: nim-models-dataset spec: mounts: - mountPoint: pvc://nim-models name: data path: / accessModes: - ReadWriteMany # 配置缓存系统Worker Pod调度到指定ECS机型的node上 nodeAffinity: required: nodeSelectorTerms: - matchExpressions: - key: node.kubernetes.io/instance-type operator: In values: - "ecs.g8i.24xlarge" --- apiVersion: data.fluid.io/v1alpha1 kind: JindoRuntime metadata: name: nim-models-dataset spec: replicas: 2 tieredstore: levels: # 配置存储介质为内存 - mediumtype: MEM volumeType: emptyDir path: /dev/shm quota: 20Gi high: "0.9" low: "0.8"
预期输出
$ kubectl get dataset NAME UFS TOTAL SIZE CACHED CACHE CAPACITY CACHED PERCENTAGE PHASE AGE nim-models-dataset 14.97GiB 0.00B 20.00GiB 0.0% Bound 3m41s
缩容到0与保留实例
Knative的KPA弹性伸缩,允许用户配置在没有推理请求时将实例缩容至零,以此来降低成本。当有推理请求到来的时候,通过Knative中的activator组件来缓存对应的请求,待推理服务Ready后再将请求调度给推理服务,确保请求不丢失。缩容到0降低了常驻的NIM推理实例的成本,但也会在应用重新启动时带来较长的冷启动时间。对于部分离线推理场景,对冷启动时延有比较高容忍性的场景可以使用此策略。
如果您的推理场景对冷启动延时较为敏感,比如在线推理场景,推荐您使用ACK Knative提供的保留实例功能。该功能在没有推理请求时,会自动降低实例规格,保留一个低规格、低成本的实例。例如,在流量高峰期使用ecs.gn7i-c32g1.8xlarge实例(21.253元/小时)处理请求,而在流量低谷期则降级为 ecs.gn6i-c4g1.xlarge实例(8.896元/小时),从而在低谷期间节省约60%的成本。闲置的高规格GPU实例还可以用于模型训练等任务,进一步提高资源利用率。
在第一个请求到达时,保留实例将立即提供服务,并同时触发默认规格实例的自动扩容。当默认规格实例的扩容完成后,所有后续新请求将被转发至这些默认规格实例。保留实例将在处理完接收到的请求后,自动下线。这种无缝切换的机制能够有效帮助您在成本、效率和启动延迟之间实现更佳的平衡。
apiVersion: serving.knative.dev/v1 kind: Service metadata: labels: release: nim-llama3-8b-instruct name: nim-llama3-8b-instruct namespace: default spec: template: metadata: annotations: autoscaling.knative.dev/class: kpa.autoscaling.knative.dev autoscaling.knative.dev/metric: "concurrency" # 配置并发数为弹性伸缩指标 autoscaling.knative.dev/target: "5" # 设置当前最大并发请求数为5 autoscaling.knative.dev/min-scale: "0" autoscaling.knative.dev/max-scale: "5" knative.aliyun.com/reserve-instance: enable # 开启保留实例功能 knative.aliyun.com/reserve-instance-type: ecs # 配置保留实例类型 knative.aliyun.com/reserve-instance-ecs-use-specs: ecs.gn7i-c8g1.2xlarge # 配置保留实例规格,支持配置多个规格 labels: release: nim-llama3-8b-instruct spec: containers: - image: nvcr.io/nim/meta/llama3-8b-instruct:1.0.0 securityContext: runAsUser: 0 imagePullPolicy: IfNotPresent name: nim-container env: - name: "NIM_MODEL_NAME" value: /model-repo ports: - containerPort: 8000 readinessProbe: successThreshold: 1 tcpSocket: port: 8000 resources: limits: nvidia.com/gpu: "1" requests: nvidia.com/gpu: "1" volumeMounts: - mountPath: /model-repo name: nim-models imagePullSecrets: - name: ngc-secret volumes: - name: nim-models persistentVolumeClaim: claimName: nim-models-dataset
预期输出
# Pod name中带有“reserve”字样的为资源降配的实例 $ kubectl get po NAME READY STATUS RESTARTS AGE nim-llama3-8b-instruct-00001-deployment-reserve-6766cc457blcm9t 2/2 Running 0 4m32s
NIM灰度发布/回滚
- NIM服务灰度发布 -
Knative提供了基于流量的灰度发布能力,您可以根据流量百分比灰度发布服务。在初次部署NIM ksvc(Knative service)时,所有的流量(traffic)都路由到latestRevision, 即nim-llama3-8b-instruct-00001。
apiVersion: serving.knative.dev/v1 kind: Service metadata: name: nim-llama3-8b-instruct namespace: default spec: ... traffic: - latestRevision: true percent: 100 status: ... traffic: - latestRevision: true percent: 100 revisionName: nim-llama3-8b-instruct-00001
当我们需要发布新版本的NIM推理服务时,我们可以配置新版本的NIM traffic字段为如下配置。以下配置将创建出新版本的NIM推理服务,即nim-llama3-8b-instruct-00002,且灰度20%的流量路由到新版本。
apiVersion: serving.knative.dev/v1 kind: Service metadata: name: nim-llama3-8b-instruct namespace: default spec: ... traffic: - tag: current revisionName: nim-llama3-8b-instruct-00001 percent: 80 # 配置80%的流量路由到nim-llama3-8b-instruct-00001 svc - tag: latest latestRevision: true percent: 20 # 配置20%的流量路由到最新的revision svc
在持续有推理请求导入的情况下,可以看到不同版本的NIM服务的实例数基本和请求量成正比。
$ kubectl get revisions.serving.knative.dev NAME CONFIG NAME K8S SERVICE NAME GENERATION READY REASON ACTUAL REPLICAS DESIRED REPLICAS nim-llama3-8b-instruct-00001 nim-llama3-8b-instruct 1 True 4 4 nim-llama3-8b-instruct-00002 nim-llama3-8b-instruct 2 True 1 1
- NIM服务版本回退 -
在NIM推理服务版本发布过程中,如发现效果不符合预期且需要回滚,可将latestRevision的流量百分比设置为0。此配置将使后续流量导向先前版本的NIM推理服务。
$ kubectl edit ksvc qwen ... traffic: - tag: current revisionName: nim-llama3-8b-instruct-00001 percent: 100 # 配置100%的流量路由到nim-llama3-8b-instruct-00001 svc - tag: latest latestRevision: true percent: 0 # 不把新的流量路由到最新的revision svc ...
在没有新流量导入的情况下,latestRevision(nim-llama3-8b-instruct-00002)的实例数在KPA的影响下,会逐渐缩容至最小副本数0。
$ kubectl get revisions.serving.knative.dev NAME CONFIG NAME K8S SERVICE NAME GENERATION READY REASON ACTUAL REPLICAS DESIRED REPLICAS nim-llama3-8b-instruct-00001 nim-llama3-8b-instruct 1 True 5 5 nim-llama3-8b-instruct-00002 nim-llama3-8b-instruct 2 True 0 0
监控
NVIDIA NIM提供了丰富的Prometheus监控指标,比如首token时延、当前正在运行的请求数、请求token数、生成token数等指标。结合阿里云Prometheus和Grafana服务,可以快速在Grafana中搭建监控大盘,实时观测推理服务状态。
1. 已开启阿里云Prometheus监控组件。具体操作,请参见[11]开启阿里云Prometheus监控。
2. 创建grafana工作区,登录Grafana的Dashboards页面。
3. 导入NVIDIA NIM提供的[12]dashboard样例。
4. 成功导入后,dashboard示例如下
总结
本文基于阿里云容器服务ACK的平台能力,采用Knative与Fluid相结合的架构,对NIM服务化部署和管理提供了较为全面的解决方案。利用NIM的离线下载能力以及Fluid的数据加速特性,将模型推理的弹性效率从分钟级大幅提升至秒级。同时,Knative的强大流量管理功能、结合阿里云的实时监控服务,支持高效的模型版本发布和回滚机制,保证了对动态负载的快速响应。这些措施除了提升弹性能力和降低成本外,还提升了应用的稳定性与易维护性,帮助用户在实际应用中快速部署和高效管理NVIDIA NIM推理服务。
参考链接:
[1] 部署Knative_容器服务Kubernetes版ACK(ACK)-阿里云帮助中心
[2] 安装云原生AI套件
[3] NVIDIA NIM文档
https://docs.nvidia.com/nim/large-language-models/latest/getting-started.html#generate-an-api-key
[4] 自定义可商用开源协议
https://ai.meta.com/resources/models-and-libraries/llama-downloads/
[5] nim-deploy部署文档
https://github.com/NVIDIA/nim-deploy/blob/main/kserve/README.md
[6] 使用NAS静态存储卷
[7] 查看挂载点地址
https://help.aliyun.com/zh/nas/user-guide/manage-mount-targets#section-sjv-ozt-711
[8] 自动扩展(KPA)
[9] 使用NVIDIA NIM在阿里云容器服务(ACK)中加速LLM推理
https://developer.nvidia.com/zh-cn/blog/nim-aliyun-accelerates-llm-inference/
[10] Serving models from local assets
[11] 开启阿里云Prometheus监控
[12] dashboard样例
我们是阿里巴巴云计算和大数据技术幕后的核心技术输出者。
获取关于我们的更多信息~