如何在 Azure 云上使用 AKS 大规模部署 Riva?#

这是一个在 Azure 云的 Azure Kubernetes 服务 (AKS) 上部署和扩展 Riva Speech Skills 并使用基于 Traefik 的负载均衡的示例。它包括以下步骤

  1. 创建 AKS 集群

  2. 部署 Riva API 服务

  3. 部署 Traefik 边缘路由器

  4. 创建 IngressRoute 以处理传入请求

  5. 部署示例客户端

  6. 扩展集群

先决条件#

在继续之前,请确保您拥有

创建 AKS 集群#

该集群包含三个独立的节点池

  • rivaserver:配备 GPU 的节点,主 Riva 服务在此节点上运行。Standard_NC8as_T4_v3 实例,每个实例使用一个 Tesla T4 GPU,这为许多应用程序提供了良好的价值和足够的容量。

  • loadbalancer:用于 Traefik 负载均衡器的通用计算节点,使用 Standard_D4s_v3 实例。

  • rivaclient:具有 Standard_D8s_v3 实例的通用节点,用于访问 Riva 服务的客户端应用程序。

  1. 创建一个 Azure 资源组,因为为 AKS 集群创建的所有资源都将是此资源组的一部分

    AKS_RESOURCE_GROUP=riva-resource
    
    # Set a unique name for your cluster.
    AKS_CLUSTER_NAME=riva-aks
    
    az group create --name ${AKS_RESOURCE_GROUP} --location eastus
    
  2. 创建 AKS 集群。这将需要一些时间,因为它将在后端旋转节点并设置 Kubernetes 控制平面。

    az aks create  --resource-group  ${AKS_RESOURCE_GROUP}  --name ${AKS_CLUSTER_NAME}  --node-count 1 --generate-ssh-keys
    
  3. 集群创建完成后,将集群配置拉取到本地计算机,以便 kubectl 可以连接到集群

    az aks get-credentials --resource-group ${AKS_RESOURCE_GROUP}  --name ${AKS_CLUSTER_NAME} --admin
    
  4. 验证您是否可以使用 kubectl 连接到集群。您应该看到节点和 Pod 正在运行。

    kubectl get nodes
    kubectl get po -A
    
  5. 为 GPU 工作节点、负载均衡器和客户端创建三个节点池

    • GPU LINUX 工作节点

      az aks nodepool add --name rivaserver --resource-group ${AKS_RESOURCE_GROUP} --cluster-name ${AKS_CLUSTER_NAME} --node-vm-size Standard_NC8as_T4_v3 --node-count 1 --labels role=workers
      
    • CPU LINUX 负载均衡器

      az aks nodepool add --name loadbalancer --resource-group ${AKS_RESOURCE_GROUP} --cluster-name ${AKS_CLUSTER_NAME} --node-vm-size Standard_D4s_v3 --node-count 1 --labels role=loadbalancers
      
    • CPU LINUX 客户端

      az aks nodepool add --name rivaclient --resource-group ${AKS_RESOURCE_GROUP} --cluster-name ${AKS_CLUSTER_NAME} --node-vm-size Standard_D8s_v3 --node-count 1 --labels role=clients
      
  6. 验证新添加的节点现在是否出现在 Kubernetes 集群中。

    kubectl get nodes --show-labels
    kubectl get nodes --selector role=workers
    kubectl get nodes --selector role=clients
    kubectl get nodes --selector role=loadbalancers
    

部署 Riva API#

Riva Speech Skills Helm chart 旨在自动化部署到 Kubernetes 集群。下载 Helm chart 后,进行少量调整将使该 chart 适应 Riva 在本教程其余部分中的使用方式。

  1. 下载并解压缩 Riva API Helm chart。将 VERSION_TAG 替换为所需的特定版本。

    export NGC_CLI_API_KEY=<your NGC API key>
    export VERSION_TAG="2.18.0"
    helm fetch https://helm.ngc.nvidia.com/nvidia/riva/charts/riva-api-${VERSION_TAG}.tgz --username='$oauthtoken' --password=$NGC_CLI_API_KEY
    tar -xvzf riva-api-${VERSION_TAG}.tgz
    
  2. riva-api 文件夹中,修改以下文件

    1. values.yaml

      • modelRepoGenerator.ngcModelConfigs 中,根据需要注释或取消注释特定模型或语言。

      • service.typeLoadBalancer 更改为 ClusterIP。这会将服务直接仅暴露给集群内的其他服务,例如下面要安装的代理服务。

      • persistentVolumeClaim.usePVC 设置为 true,将 persistentVolumeClaim.storageClassName 设置为 azurefile,将 persistentVolumeClaim.storageAccessMode 设置为 ReadWriteOnce。这会将 Riva 模型存储在已创建的持久卷中。

    2. templates/deployment.yaml

      • 添加节点选择器约束,以确保 Riva 仅部署在正确的 GPU 资源上。在 spec.template.spec 中,添加

        nodeSelector:
          kubernetes.azure.com/agentpool: rivaserver
        
  3. 安装 NVIDIA GPU 设备插件。Azure 默认情况下不会安装它。使用以下命令验证安装

    helm repo add nvdp https://nvidia.github.io/k8s-device-plugin
    helm repo update
    helm install \
        --generate-name \
        --set failOnInitError=false \
        nvdp/nvidia-device-plugin \
        --namespace nvidia-device-plugin \
        --create-namespace
    
  4. 使用以下任一命令验证 GPU 插件安装

    kubectl get pod -A | grep nvidia
                       
                       or
    
    kubectl get nodes "-o=custom-columns=NAME:.metadata.name,GPU:.status.allocatable.nvidia\.com/gpu"
    
  5. 确保您在工作目录中,其中 riva-api 作为子目录,然后安装 Riva Helm chart。您可以显式覆盖 values.yaml 文件中的变量,例如 modelRepoGenerator.modelDeployKey 设置。

    helm install riva-api riva-api/ \
        --set ngcCredentials.password=`echo -n $NGC_CLI_API_KEY | base64 -w0` \
        --set modelRepoGenerator.modelDeployKey=`echo -n tlt_encode | base64 -w0`
    
  6. Helm chart 依次运行两个容器:一个 riva-model-init 容器,用于下载和部署模型,然后是一个 riva-speech-api 容器,用于启动语音服务 API。根据模型的数量,初始模型部署可能需要一个小时或更长时间。要监视部署,请使用 kubectl 描述 riva-api Pod 并观看容器日志。

    export pod=`kubectl get pods | cut -d " " -f 1 | grep riva-api`
    kubectl describe pod $pod
    
    kubectl logs -f $pod -c riva-model-init
    kubectl logs -f $pod -c riva-speech-api
    

部署 Traefik 边缘路由器#

现在 Riva 服务正在运行,集群需要一种机制来将请求路由到 Riva。

riva-api Helm chart 的默认 values.yaml 中,service.type 设置为 LoadBalancer,这将自动创建一个 Azure 经典负载均衡器,以将流量定向到 Riva 服务。相反,开源 Traefik 边缘路由器将用于此目的。

  1. 下载并解压缩 Traefik Helm chart。

    helm repo add traefik https://helm.traefik.io/traefik
    helm repo update
    helm fetch traefik/traefik
    tar -zxvf traefik-*.tgz
    
  2. 修改 traefik/values.yaml 文件。

    1. service.typeLoadBalancer 更改为 ClusterIP。这会将服务暴露在集群内部 IP 上。

    2. nodeSelector 设置为 {  kubernetes.azure.com/agentpool: loadbalancer}。与您对 Riva API 服务所做的操作类似,这告诉 Traefik 服务在 loadbalancer 节点池上运行。

  3. 部署修改后的 traefik Helm chart。

    helm install traefik traefik/
    

创建 IngressRoute#

IngressRoute 使 Traefik 负载均衡器能够识别传入请求并将它们分配到多个 riva-api 服务。

当您在上面部署 traefik Helm chart 时,Kubernetes 会自动为该服务创建一个本地 DNS 条目:traefik.default.svc.cluster.local。下面的 IngressRoute 定义与这些 DNS 条目匹配,并将请求定向到 riva-api 服务。您可以根据您的要求修改条目以支持不同的 DNS 安排。

  1. 创建以下 riva-ingress.yaml 文件

    apiVersion: traefik.containo.us/v1alpha1
    kind: IngressRoute
    metadata:
      name: riva-ingressroute
    spec:
      entryPoints:
        - web
      routes:
        - match: "Host(`traefik.default.svc.cluster.local`)"
          kind: Rule
          services:
            - name: riva-api
              port: 50051
              scheme: h2c
    
  2. 部署 IngressRoute。

    kubectl apply -f riva-ingress.yaml
    

Riva 服务现在能够通过地址 traefik.default.svc.cluster.local 在集群内提供 gRPC 请求。如果您计划在集群中部署自己的客户端应用程序以与 Riva 通信,则可以将请求发送到该地址。在下一节中,您将部署一个 Riva 示例客户端并使用它来测试部署。

部署示例客户端#

Riva 提供了一个包含一组预构建示例客户端的容器,用于测试 Riva 服务。客户端 也可在 GitHub 上获得,供那些有兴趣调整它们的人使用。

  1. 创建 client-deployment.yaml 文件,该文件定义了部署并包含以下内容

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: riva-client
      labels:
        app: "rivaasrclient"
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: "rivaasrclient"
      template:
        metadata:
          labels:
            app: "rivaasrclient"
        spec:
          nodeSelector:
              kubernetes.azure.com/agentpool: rivaclient
          imagePullSecrets:
          - name: imagepullsecret
          containers:
          - name: riva-client
            image: "nvcr.io/{NgcOrg}/{NgcTeam}/riva-speech:2.18.0"
            command: ["/bin/bash"]
            args: ["-c", "while true; do sleep 5; done"]
    
  2. 部署客户端服务。

    kubectl apply -f client-deployment.yaml
    
  3. 连接到客户端 Pod。

    export cpod=`kubectl get pods | cut -d " " -f 1 | grep riva-client`
    kubectl exec --stdin --tty $cpod /bin/bash
    
  4. 从客户端 Pod 的 shell 内部,在示例 .wav 文件上运行示例 ASR 客户端。将 traefik.default.svc.cluster.local 端点指定为服务地址,端口为 80。

    riva_streaming_asr_client \
       --audio_file=wav/en-US_sample.wav \
       --automatic_punctuation=true \
       --riva_uri=traefik.default.svc.cluster.local:80
    

扩展集群#

如上所述部署,AKS 集群仅配置单个 GPU 节点,尽管我们可以扩展节点。虽然单个 GPU 可以处理 大量请求,但集群可以轻松地使用更多节点进行扩展。

  1. 将 GPU 节点池扩展到所需数量的计算节点(在本例中为 2 个)。

    az aks nodepool scale --name rivaserver --resource-group ${AKS_RESOURCE_GROUP} --cluster-name ${AKS_CLUSTER_NAME} --node-count 2
    
  2. 扩展 riva-api 部署以使用其他节点。

    kubectl scale deployments/riva-api --replicas=2
    

与原始 riva-api 部署一样,每个副本 Pod 在启动 Riva 服务之前都会下载并初始化必要的模型。