TT Lab
博客

한국어 English 日本語 中文

开始
博客

博客

安装 Dynamo 运营商前必读:Webhook 范围、两种安装方式、载入清单

状态:草稿 (2026-11-14),实测。 Dynamo v1.6.2(Chart dynamo-platform-1.6.2,运营商镜像 kubernetes-operator:1.6.2)于 2026-11-13 直接安装到 Kubernetes v1.34、Helm v3.18 时所写的文章。资源数量从 helm template 的输出中统计,错误提示为实际输出原文。全局方式仅作绘制查看,并未安装。全局方式的 Pod Webhook 对其他命名空间的 Pod 创建造成的延迟未进行实测。 编写规则:以实测数据为基础,未能测量的数据标记为「未实测」,无法确认的数据标记为「未确认」。

目录

1. 仅使用 vLLM 时没有的东西

vLLM 只要有 Deployment 和 Service 就能启动。它不会创建任何集群范围的资源。

Dynamo 则不同。它通过名为 DynamoGraphDeployment 的单个自定义资源来编写工作线程和前端,运营商读取该资源后会创建 Deployment 和 Service。为此,必须先在集群中放置 CRD、运营商、准入 Webhook 以及相应的权限。如果多个团队共用一个集群,这一部分将成为比引擎性能更优先审查的对象。

以下从 Chart 的默认值开始写起。在 1.6.2 中,etcd、NATS、Grove、KAI 调度器和 snapshot 都是关闭的。因为默认的发现机制使用 Kubernetes API,请求平面使用 TCP,KV 事件使用 ZMQ。虽然仓库中的多篇文档仍然将 NATS 和 etcd 列为必需项,但在没有这四项的情况下进行安装,也能顺利跑完整篇系列的第三部分测试。

2. 全局方式:所有 Pod 创建都会经过 Webhook

默认安装采用单个运营商监控整个集群的全局方式。在安装前,通过 helm template 进行渲染并统计了所创建的资源(关闭了检查点功能的值)。

种类 全局方式 命名空间限制方式
ClusterRole 12 7
ClusterRoleBinding 10 7
Role 1 6
RoleBinding 1 6
ServiceAccount 2 5
Webhook(变异 4 + 校验 4) 8,无选择器 8,目标命名空间选择器
PodMonitor 5 5

全局方式的八个 Webhook 中既没有 namespaceSelector 也没有 objectSelector 。其中七个只监控 Dynamo 的自定义资源,因此对其他团队没有影响。剩下的一个才是问题所在。

mpodcheckpointrestore.kb.io   resources: pods   operations: CREATE
failurePolicy: Ignore         timeoutSeconds: 10

集群中所有 Pod 的创建都会经过这个 Webhook。 即使关闭检查点功能(checkpoint.enabled: false),它也会被注册。由于 failurePolicy 是 Ignore ,所以即使运营商宕机,也不会阻塞 Pod 的创建。不过在这种情况下,API 服务器等待 Webhook 响应的时间可能会附加到每个 Pod 的创建过程中。由于未安装全局方式,该延迟实际上是多少并未测得(未实测)。

我们不想把用于测试的运营商塞进其他团队的 Pod 创建路径中。因此,我们试图将 Webhook 缩减到测试命名空间中。

3. 无法通过参数收窄 Webhook

Chart 中有一个名为 webhook.namespaceSelector 的参数。如果在其中写入选择器进行安装,Helm 会拒绝。

VALIDATION ERROR: webhook.namespaceSelector must be empty. Helm manages admission
scope: cluster-wide webhooks cover every namespace and namespace-restricted
webhooks cover only their target namespace.

这意味着 Webhook 的范围由安装方式决定,用户无法单独选择。剩下的唯一途径就是命名空间限制方式(namespaceRestriction.enabled: true)。用这种方式渲染时,所有八个 Webhook 都会附带以下选择器:

namespaceSelector:
  matchLabels:
    kubernetes.io/metadata.name: <대상 네임스페이스>

限制方式不能与 CRD 自动更新一起使用。如果同时开启这两项,同样会被拒绝。

VALIDATION ERROR: namespaceRestriction.enabled=true is incompatible with
upgradeCRD=true. Set upgradeCRD=false so the namespaced release does not install
or update cluster-wide CRDs.

并且,当运营商以限制方式启动时,日志中会打印一条大横幅:「DEVELOPMENT AND TESTING ONLY: Namespace-restricted mode is not supported for production.」Chart 的校验文本也将限制方式解释为「与全局运营商并排放置以供开发和测试使用」。 生产环境中支持的唯一途径是全局方式,如果选择该方式,就必须接受第 2 节中的 Pod Webhook。

4. 单独使用限制方式时缺少的三样东西

在没有全局运营商的情况下仅安装限制方式时,有三样东西是空的。因为这些原本是全局运营商做的事情。

缺少 CRD。 upgradeCRD: false 由于需要 deploy/operator/config/crd/bases/ ,必须手动添加 CRD。我们从仓库的 kubectl apply 中获取了六个。最大的文件为 3,038,832 字节。客户端侧的 kubectl apply --server-side 会将完整应用内容重新包含在 annotation 中,因此无法超过此大小。我们通过

进行了添加。 转换 Webhook 指向的服务名称不同。 dynamo-system CRD 原本的转换 Webhook 指向 dynamo-operator-webhook-service 命名空间中的 <릴리스 전체 이름>-webhook-service 。Chart 创建的服务名称为 dynamo-platform ,如果将发布名称设为 dynamo-platform-dynamo-operator-webhook-service ,则会变成

。在添加 CRD 之前,必须将此名称和命名空间修改为实际值。 未包含转换 Webhook 的 CA。 caBundle Webhook 证书由运营商自行生成并放入 Secret 中,但将该 CA 放入 CRD 的 ca.crt 中的权限仅归全局运营商所有。安装后,从 Secret 中取出dynamocomponentdeployments, dynamographdeploymentrequests, dynamographdeployments, dynamographdeploymentscalingadapters)中直接进行了输入。

直到填满这三者之后, v1beta1 才能创建资源。

5. 划分命名空间后,Operator 崩溃了 161 次

我们将 Operator 放在 dynamo-system 中,而将工作负载放在另一个命名空间中。这是一个将具有权限的进程与模型服务器分离开来的常见部署。在使用限制模式的部署中,Operator 无法存活太久。

failed to list *v1.Secret: secrets is forbidden: User "system:serviceaccount:
dynamo-system:dynamo-platform-dynamo-operator-controller-manager" cannot list
resource "secrets" in API group "" in the namespace "dynamo-system"
...
problem running manager  {"error": "failed to wait for cert-rotator caches to sync
kind source: *v1.Secret: timed out waiting for cache to be synced ..."}

Operator 内部的证书轮换器会监视其自身命名空间的 Secret。为了进行监视,它需要列表查询(list)和监视(watch)权限。Chart 在发布命名空间中创建的 Role 如下所示:

rules:
- apiGroups: [""]
  resources: [secrets]
  resourceNames: [webhook-server-cert]
  verbs: [get, list, watch, update, patch]

resourceNames 以 命名的规则不允许对未指定名称的列表查询进行放行。 这是 Kubernetes RBAC 的一个已知特性。因此,如果缓存同步未完成且超时,进程将自行终止。

在全局模式下,ClusterRole 以及在发布命名空间与目标命名空间相同的限制模式下,目标命名空间的 Role 允许进行 Secret 列表查询,因此这个问题被掩盖了。只有在两个命名空间不同时才会暴露出来。

实际看到的情况如下。

由于原因是权限不足,解决方法是在发布命名空间中添加另一个 Role,允许对 Secret 进行 get·list·watch 。或者,将 Operator 和工作负载放在同一个命名空间中。由于本文中的测量是通过等待 Operator 存活时应用而结束的,因此并未通过应用这两种方法来进行确认(未实际测量)。

在 Running 和 CrashLoopBackOff 之间切换的 Pod 乍一看可能很正常。安装 Operator 后,必须以几分钟为间隔检查两次重启次数。

6. 引入离线/封闭环境

从外部获取的东西有 4 个。

内容 名称 大小 是否必需
运行时镜像 nvcr.io/nvidia/ai-dynamo/vllm-runtime:1.6.2 10,203,035,478 字节 是。Worker 和前端共用
Operator 镜像 nvcr.io/nvidia/ai-dynamo/kubernetes-operator:1.6.2 94,673,132 字节 是
Chart dynamo-platform-1.6.2.tgz 516,410 字节 是
模型 Qwen/Qwen3-0.6B,固定版本 1,519,209,243 字节 是。每个 Worker 节点都需要
前端专用镜像 dynamo-frontend:1.6.2 约 3.76 GiB 否。用运行时镜像代替
NATS, etcd, Grove, KAI 否。默认关闭

运行时镜像中包含 vLLM 0.31.0、 ai-dynamo 1.6.2、PyTorch 2.13.0+cu130、NIXL 1.4.1。负载工具 AIPerf 不在运行时镜像中,仅在前端专用镜像中。

在引入过程中遇到了三个问题。

10GB 的镜像会阻塞同一节点上的其他部署。 每个节点拉取运行时镜像大约花费了 11 分钟。在容器运行时被配置为一次只能拉取一个镜像的节点上,其他镜像在此期间需要排队。实际上,本应在同一节点上启动的另一个部署在等待了 12 分钟 11 秒后超时失败。对于体积庞大的镜像,应在没有部署任务的时间段提前拉取。

如果通过哈希值拉取版本,则在离线状态下找不到模型。 snapshot_download(..., revision="<커밋 해시>") 如果通过获取,缓存中将不会生成 refs/main 。在这种状态下,如果给定 HF_HUB_OFFLINE=1 作为 --model Qwen/Qwen3-0.6B ,将无法以快照形式解压仓库名称。我们在获取之后,将该哈希值写入了 refs/main 文件中。不建议不固定版本的做法。如果只写名称,下个月可能会指向不同的文件。

模型目录可以以只读方式挂载。 Worker 尝试写入一个缓存索引文件时会留下 Permission denied 警告,但加载可以正常完成。

7. 总结:安装前需确定的事项

问题 全局模式 命名空间限制模式
生产环境是否支持 是 否(日志中记录为仅限开发和测试使用)
是否会影响其他团队的 Pod 创建 是。Pod CREATE 变更 Webhook, Ignore, 10秒 否
谁来添加 CRD Operator 人工手动
转换 Webhook 地址和 CA Operator 进行配置 人工手动
分离 Operator 与工作负载的命名空间 可能 需要额外添加一个 Role
集群范围权限 12 个 ClusterRole 7 个 ClusterRole + 6 个 Role

在尝试添加时,限制方式不会影响其他团队,因此比较安全,但需要较多人工操作。在投入生产环境时属于全局方式,在此之前,必须与集群管理员达成共识,即 Pod Webhook 的范围是整个集群。无论哪种方式,我们都确认过,即使 Operator 宕机,已经运行的服务仍将继续运行。

参考

登录后即可点赞

评论

还没有评论。

登录后即可发表评论