跳到内容

使用 Kubernetes 进行开发

直接在 Kubernetes 上进行开发使我们更有信心最终用户的部署将按预期工作。

本页介绍如何通用地使用 Kubernetes 以及如何在本地 Kubernetes 集群上部署 nv-ingest。

注意: 除非另有说明,否则以下所有命令都应从本仓库的根目录运行。

Kubernetes 集群

首先,你需要一个 Kubernetes 集群。我们建议你使用 kind,它会创建一个包含 Kubernetes 集群的单个 Docker 容器。

由于 kind 集群需要访问你系统上的 GPU,你需要安装 nvkind。有关详细信息,请参阅 使用 nvkind 运行带有 GPU 的 kind 集群nvkind 提供以下好处

  • 同一系统上的多个开发者可以拥有隔离的 Kubernetes 集群
  • 易于创建和删除集群

从仓库的根目录,运行以下代码为你的集群创建一个配置文件。

mkdir -p ./.tmp

cat <<EOF > ./.tmp/kind-config.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
name: nv-ingest-${USER}
nodes:
  - role: control-plane
    image: kindest/node:v1.29.2
  {{- range $gpu := until numGPUs }}
  - role: worker
    extraMounts:
      # We inject all NVIDIA GPUs using the nvidia-container-runtime.
      # This requires 'accept-nvidia-visible-devices-as-volume-mounts = true' be set
      # in '/etc/nvidia-container-runtime/config.toml'
      - hostPath: /dev/null
        containerPath: /var/run/nvidia-container-devices/{{ $gpu }}
  {{- end }}
EOF

然后,使用 nvkind CLI 创建你的集群。

nvkind cluster create \
    --config-template ./.tmp/kind-config.yaml

你应该看到如下输出

Creating cluster "jdyer" ...
  Ensuring node image (kindest/node:v1.27.11) 🖼
  Preparing nodes 📦
  Writing configuration 📜
  Starting control-plane 🕹️
  Installing CNI 🔌
  Installing StorageClass 💾
Set kubectl context to "kind-jdyer"
You can now use your cluster with:

kubectl cluster-info --context kind-jdyer

Have a nice day! 👋

你可以使用 kind get clusters 列出系统上的集群。

kind get clusters
# jdyer

你也可以直接使用 docker ps 查看 kind 容器。

docker ps | grep kind
# aaf5216a3cc8   kindest/node:v1.27.11  "/usr/local/bin/entr…"   44 seconds ago   Up 42 seconds 127.0.0.1:45099->6443/tcp jdyer-control-plane

kind create cluster 执行以下操作

  • 为集群添加上下文到 ${HOME}/.kube/config,这是 kubectl 等工具使用的默认配置文件
  • 将默认上下文更改为 ${HOME}/.kube/config

你应该能够立即使用 kubectl,并且它应该指向你刚刚创建的集群。

例如,尝试列出节点以验证集群是否已成功设置。

kubectl get nodes

如果成功,你应该看到如下单个节点

NAME                  STATUS   ROLES           AGE   VERSION
jdyer-control-plane   Ready    control-plane   63s   v1.27.11

注意:并非所有在你的 Kubernetes 集群内部创建的容器在你运行 docker ps 时都会出现,因为其中一些容器嵌套在单独的命名空间中。

如需解决出现的问题,请参阅 故障排除

Skaffold

现在你已经有了一个 Kubernetes 集群,你可以使用 Skaffold 构建和部署你的开发环境。

在单个命令中,Skaffold 执行以下操作

  • 从当前目录构建容器 (通过 docker build)
  • 安装 retriever-ingest helm charts (通过 helm install)
  • 应用额外的 Kubernetes manifests (通过 kustomize)
  • 热重载 - Skaffold 监视你的本地目录中的更改并将它们同步到 Kubernetes 容器中
  • 将 ingest 服务端口转发到主机

目录结构

  • skaffold/sensitive/ 包含你想要部署到集群但不想检入 git 的任何 secrets 或 manifests,因为你的本地集群不太可能安装 ESO。如果安装了,请随意使用 kind: ExternalSecret 代替。
  • skaffold/components 包含你想要在任何 skaffold 文件中部署的任何 k8s manifests。路径是相对的,可以用于 kustomizerawYaml 格式
manifests:
  rawYaml:
    - sensitive/*.yaml
  kustomize:
    paths:
      - components/elasticsearch
  • 如果添加新服务,请首先尝试获取 helm 对象。如果不存在,你可能需要将其与你的 k8s manifests 封装在 skaffold/components 中。我们是一家 k8s 公司,因此可能需要不时编写 manifest。

先决条件

添加 Helm 仓库

retriever-ingest 服务的部署需要从第三方来源(例如 Elasticsearch、OpenTelemetry 和 Postgres)拉取其他服务的配置。

首次将此项目部署到本地 Kubernetes 时,你可能需要通过运行类似于以下代码的代码,告诉你的本地 Helm 版本(Kubernetes 配置的包管理器)在哪里可以找到第三方服务。

helm repo add \
  nvdp \
  https://nvda.org.cn/k8s-device-plugin

helm repo add \
  zipkin \
  https://zipkin.io/zipkin-helm

helm repo add \
  opentelemetry \
  https://open-telemetry.github.io/opentelemetry-helm-charts

helm repo add \
  nvidia \
  https://helm.ngc.nvidia.com/nvidia

helm repo add \
  bitnami \
  https://charts.bitnami.com/bitnami

有关仓库的完整列表,请参阅 Chart.yaml 文件中的 dependencies 部分。

NVIDIA GPU 支持

为了让 Kubernetes pods 访问 NVIDIA GPU 资源,你必须安装 NVIDIA device plugin for Kubernetes。此插件有很多配置,但要开始开发,只需运行以下代码。

kubectl create -f https://raw.githubusercontent.com/NVIDIA/k8s-device-plugin/v0.15.0/deployments/static/nvidia-device-plugin.yml

创建镜像拉取 Secret

你还需要提供一个 Kubernetes Secret,其中包含用于拉取 NVIDIA 私有 Docker 镜像的凭据。

对于生命周期较短的开发集群,只需使用你自己的个人凭据。

DOCKER_CONFIG_JSON=$(
    cat "${HOME}/.docker/config.json" \
    | base64 -w 0
)

cat <<EOF > ./skaffold/sensitive/imagepull.yaml
apiVersion: v1
kind: Secret
metadata:
  name: nvcrimagepullsecret
type: kubernetes.io/dockerconfigjson
data:
  .dockerconfigjson: ${DOCKER_CONFIG_JSON}
EOF

你需要一个 NGC 个人 API 密钥才能访问托管在 NGC 上的模型和镜像。首先,生成 API 密钥。接下来,通过运行以下代码将密钥存储在环境变量中。

export NGC_API_KEY="<YOUR_KEY_HERE>"

然后,使用以下命令创建 secret manifest

kubectl create secret generic ngcapisecrets \
  --from-literal=ngc_api_key="${NGC_API_KEY}" \
  --dry-run=client -o yaml \
  > skaffold/sensitive/ngcapi.yaml

部署服务

运行以下命令将 retriever-ingest 部署到你的集群。

skaffold dev \
    -v info \
    -f ./skaffold/nv-ingest.skaffold.yaml \
    --kube-context "kind-nv-ingest-${USER}"
这些标志的解释(点击我)- -v info = 打印来自 skaffold 及其调用的工具(如 helmkustomize)的 INFO 级别及以上日志 - -f ./skaffold/nv-ingest.skaffold.yaml = 使用特定于 retriever-ingest 的配置 - --tail=false = 不要用已部署容器的所有日志淹没你的控制台 - --kube-context "kind-${USER}" = 目标是你上面使用 kind 创建的特定 Kubernetes 集群

skaffold dev 监视你的本地文件,并在你更改这些文件时自动重新部署应用程序。它还会保持在你运行它的终端中的控制,并在你 Ctrl + C 退出时处理关闭 Kubernetes 中的 pods。

你应该看到类似于这样的输出

Generating tags...
 - ...
Checking cache...
 - ...
Tags used in deployment:
 - ...
Starting deploy...
Loading images into kind cluster nodes...
 - ...
Waiting for deployments to stabilize...
Deployments stabilized in 23.08 seconds
Watching for changes...

当你运行此命令时,skaffold dev 会在系统上找到一个随机的开放端口,并将 retriever-ingest 服务暴露在该端口上。有关更多信息,请参阅 端口转发

你可以在 skaffold 的日志中找到该端口,方法是运行以下代码。

Port forwarding Service/nv-ingest in namespace , remote port http -> http://0.0.0.0:4503

或者,你可以像这样获取它

NV_INGEST_MS_PORT=$(
    ps aux \
    | grep -E "kind\-${USER} port-forward .*Service/nv-ingest" \
    | grep -o -E '[0-9]+:http' \
    | cut -d ':' -f1
)

要确认服务已部署并正常工作,请针对你上面设置端口转发的端口发出请求。

API_HOST="https://:${NV_INGEST_MS_PORT}"

curl \
  -i \
  -X GET \
  "${API_HOST}/health"

当你在新终端中运行 skaffold verify 时,Skaffold 会针对该服务运行验证测试。这些是非常轻量级的健康检查,不应与集成测试混淆。有关更多信息,请参阅 验证

清理

要销毁整个 Kubernetes 集群,请运行以下命令。

kind delete cluster \
    --name "${USER}"

故障排除

调试工具

kubectl 是 Kubernetes 的官方 CLI,并支持许多有用的功能。

例如,要在你的部署中获取 nv-ingest-ms-runtime 容器内的 shell,请运行以下命令

NV_INGEST_POD=$(
    kubectl get pods \
        --context "kind-${USER}" \
        --namespace default \
        -l 'app.kubernetes.io/instance=nv-ingest-ms-runtime' \
        --no-headers \
    | awk '{print $1}'
)
kubectl exec \
    --context "kind-${USER}" \
    --namespace default \
    pod/${NV_INGEST_POD} \
    -i \
    -t \
    -- sh

为了获得交互式的、实时更新的体验,请尝试 k9s。要启动它,请运行 k9s

k9s

安装 Helm 仓库

你可能会遇到如下错误。这表明你的本地 Helm 安装(Kubernetes 配置的包管理器)不知道如何访问包含 Kubernetes 配置的远程仓库。

Error: no repository definition for https://helm.dask.org. Please add the missing repos via 'helm repo add'

要解决此问题,请使用 URL 和信息性名称运行 help repo add

helm repo add \
    bitnami \
    https://charts.bitnami.com/bitnami

从 Skaffold 获取更多日志

你可能会遇到如下错误

Generating tags...
 - retrieval-ms -> retrieval-ms:f181a78-dirty
Checking cache...
 - retrieval-ms: Found Locally
Cleaning up...
 - No resources found
building helm dependencies: exit status 1

如果你只看到 building helm dependencies,你可能在安静模式下运行了 skaffold devskaffold run。使用 -v info-v debug 重新运行命令以获取有关失败原因的更多信息。

参考