如何在 OCI 上使用 OKE 大规模部署 Riva#

这是一个在 Oracle Cloud Infrastructure (OCI) Oracle Kubernetes Engine (OKE) 上部署 Riva 语音技能并使用基于 Traefik 的负载均衡的示例。它包括以下步骤

  1. 创建 OKE 集群

  2. 部署 Riva API 服务

  3. 部署 Traefik 边缘路由器

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

  5. 部署示例客户端

  6. 扩展集群

前提条件#

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

创建 OKE 集群#

  1. 导航并登录到 OCI 网页。转到左上角的汉堡菜单。在 “开发者服务” 选项卡下,导航到 Kubernetes 集群 (OKE)。

  2. 单击“创建集群”以开始集群创建。

    创建 Kubernetes 集群将有两个选项:快速创建自定义创建。为了使本教程尽可能简单,我们将使用 快速创建 选项。

  3. 选择 快速创建 选项后,您可以自定义集群形状、节点数量等。将 Kubernetes 工作节点设置为“公共工作节点”以便轻松 ssh 进入。

    私有工作节点具有私有 IP 地址,只能由 VCN 内部的其他资源访问。

  4. 为了获得最简单的部署,创建一个 GPU 节点。本示例中测试的工作节点形状为 VM.GPU3.1 的 GPU 节点,它具有一个 V100 GPU。您将在稍后学习如何扩展到多个节点。

注意 由于我们使用的是 快速创建 选项,因此最初我们只能为一个节点池选择形状。节点池是集群内一组具有相同配置的节点。创建集群后,您将能够 添加更多节点池

注意 本示例的理想集群配置包括

  • 一个配备 GPU 的节点,用于运行主要的 Riva 服务。

  • 一个用于 Traefik 负载均衡器的通用计算节点。

  • 另一个用于客户端应用程序访问 Riva 服务的通用节点。

  1. 增加每个节点的启动卷,否则 Riva Pod 将无法启动。在本示例中,我们将其增加到 500 GB。您可以在集群创建期间指定此值,方法是单击“显示高级选项”,选中“指定自定义启动卷大小”,然后输入您所需的启动卷大小。

  2. 查看资源后,单击“创建集群”。集群创建完成大约需要 15 分钟。

注意 单击“创建集群”按钮后,可能会显示集群处于活动状态,但实际上只有在节点池状态为活动状态时才准备就绪。

访问 OKE 集群#

要在本地访问集群,请导航到 OKE 页面并单击集群名称。从那里,按“访问集群”,然后按“本地访问”。从那里,您可以按照提示获得对集群的本地访问权限。

您可以通过导航到 OCI 控制台上的 “开发者服务” 下的 “Kubernetes 集群 (OKE)” 来验证集群创建。您将在网页上看到您的集群名称。您可以转到“实例”部分,以查看您的工作节点实例。

验证节点现在是否出现在 Kubernetes 中。如果是,则集群已成功创建,您可以在本地访问该集群。

  cat .kube/config
  kubectl get pods -A
  kubectl get nodes

ssh 进入工作节点,您需要创建集群时下载的私钥。您可以使用 IP 地址和主机名 ssh 进入每个工作节点。您可以通过转到控制台并单击要访问的每个实例来找到 IP 地址和主机名。

  chmod 600 private_key.key
  ssh -i private_key.key username@ip_address

如果创建的计算实例的启动卷大于或等于 50 GB,则该实例不会自动使用整个卷。使用 oci-growfs 实用程序扩展根分区,以充分利用分配的启动卷大小。您需要 ssh 进入每个工作节点并运行以下命令

  sudo /usr/libexec/oci-growfs -y
  sudo systemctl restart kubelet

注意 如果您看到“无法扩展 /dev/sda3”,请转到 OCI 控制台并单击左上角的汉堡菜单。在 “存储” 选项卡下,导航到 “块存储”,然后单击左侧列中的 “启动卷”。从这里,您可以单击与工作节点关联的每个启动卷,然后单击“编辑”。在刚刚打开的窗口中更改卷大小,然后单击“保存更改”。将弹出一个包含重新扫描命令的消息。您需要 ssh 进入每个工作节点并输入给定的重新扫描命令。从那里,您可以运行上面的 oci-growfs 命令。

首次访问集群时,您创建的任何 GPU 节点默认情况下都会被污点化,以确保 Pod 不会调度到不适当的节点上(非 GPU 负载不应调度到 GPU 节点上)。使用节点污点,除非您删除污点或添加匹配的容忍度,否则任何 Pod 都无法调度到该节点上。如果您运行 kubectl get pods -A 并看到 CoreDNS Pod 未运行,这通常是由于节点上的污点造成的。

要删除污点

kubectl taint nodes node1 nvidia.com/gpu=value1:NoSchedule-

要添加匹配的容忍度

  tolerations:
  - key: "nvidia.com/gpu"
    operator: "Equal"
    value: "value1"
    effect: "NoSchedule"

部署 Riva API#

Riva 语音技能 Helm Chart 旨在自动化部署到 Kubernetes 集群。下载 Helm Chart 后,稍作调整即可使 Chart 适应本教程其余部分中 Riva 的使用方式。根据模型数量,初始模型部署可能需要一个小时或更长时间。

  1. 下载并解压缩 Riva API Helm Chart。将 VERSION_TAG 替换为所需的特定版本。在 NGC 上查看最新版本。

    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 文件夹中,修改以下文件

    • values.yaml

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

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

  3. 启用集群以使用 nvidia 设备插件运行需要 NVIDIA GPU 的容器

    helm repo add nvdp https://nvidia.github.io/k8s-device-plugin
    helm repo update
    helm install \
        --namespace nvidia-device-plugin \
        --create-namespace nvidia-device-plugin \
        --set failOnInitError=false \
        nvdp/nvidia-device-plugin
    
  4. 确保您位于工作目录中,其中 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`
    
  5. Helm Chart 按顺序运行两个容器:一个下载并部署模型的 riva-model-init 容器,然后是一个启动语音服务 API 的 riva-speech-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,这将创建一个 OCI 负载均衡器 以将流量定向到 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 文件。

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

  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 文件。替换下面文件中的镜像路径。在 NGC 上查看最新的镜像标签

    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:
          imagePullSecrets:
          - name: imagepullsecret
          containers:
          - name: riva-client
            image: "nvcr.io/nvidia/riva/riva-speech-client: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=/work/wav/sample.wav \
       --automatic_punctuation=true \
       --riva_uri=traefik.default.svc.cluster.local:80
    

    根据您选择的音频文件,您将看到类似于以下的输出

    filename: /opt/riva/wav/en-US_sample.wav
    Done loading 1 files
    what
    what
    what is
    what is
    what is
    what is now
    what is natural
    what is natural
    what is natural language
    what is natural language
    what is natural language
    what is natural language
    what is natural language Processing
    what is natural language Processing
    what is natural language Processing
    what is natural language Processing
    what is natural language Processing
    what is language Processing
    what is language Processing
    What is Natural Language Processing?
    -----------------------------------------------------------
    File: /opt/riva/wav/en-US_sample.wav
    
    Final transcripts:
    0 : What is Natural Language Processing?
    
    Timestamps:
    Word                                    Start (ms)      End (ms)
    
    What                                    840             880
    is                                      1160            1200
    Natural                                 1800            2080
    Language                                2200            2520
    Processing?                             2720            3200
    
    Audio processed: 4 sec.
    -----------------------------------------------------------
    
    Not printing latency statistics because the client is run without the --simulate_realtime option and/or the number of requests sent is not equal to number of requests received. To get latency statistics, run with --simulate_realtime and set the --chunk_duration_ms to be the same as the server chunk duration
    Run time: 0.108666 sec.
    Total audio processed: 4.152 sec.
    Throughput: 38.2087 RTFX
    

扩展集群#

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

  1. 通过控制台将 GPU 节点组扩展 到所需的计算节点数量(在本例中为 4 个)。

  2. 扩展 riva-api 部署以使用额外的节点。

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

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