seldon-core 管道卸载实战:深入解析 `seldon pipeline unload` 命令的完整调用链与配置参数
seldon-core 管道卸载实战:深入解析 seldon pipeline unload 命令的完整调用链与配置参数
导读
在 seldon-core(Seldon Core 2)的 MLOps 体系中,Pipeline(管道)用于将多个模型编排成推理图(inference graph),而 seldon pipeline unload 正是将已加载的管道从运行态卸载的控制面命令。本文以仓库中该命令的官方文档 seldon_pipeline_unload.md 为骨架,结合 operator、scheduler 模块的 Go 源码,完整讲解命令用法、全部参数语义、底层 gRPC 调用链与管道状态机迁移过程,帮助你在本地开发或生产集群中安全、精准地卸载管道。
命令概览与基本用法
seldon pipeline unload 的职责是卸载(unload)一个已加载的管道。它的命令骨架由 operator CLI 中的 cobra 命令定义,命令的全形态为:
seldon pipeline unload <pipelineName> [flags]
其中 <pipelineName> 是位置参数,指定要卸载的管道名称。该命令隶属于 seldon pipeline 管理组,其注册关系可在 root.go 中看到:cmdPipeline 下挂载了 load / unload / status / infer / list / inspect 六个子命令,unload 对应的实现位于 pipeline_unload.go。
命令定义中 Use 字段为 "unload <pipelineName> or -f <filename>"(pipeline_unload.go),这揭示了两种卸载方式:既可以直接传管道名称,也可以通过 -f 传入管道 manifest 文件,由 CLI 自行解析出管道名称。
参数详解:每个 flag 的含义与默认值
原文档列出的参数如下表,这里结合源码 flags.go 与 pipeline_unload.go 补充完整语义、默认值与环境变量来源:
| 参数 | 短选项 | 类型/默认值 | 含义 |
|---|---|---|---|
--authority string |
无 | 默认空字符串 | authority(HTTP/2)或虚拟主机(HTTP/1),用于 gRPC 请求的 :authority 头改写,常见于通过 Envoy 代理访问 scheduler 的场景 |
-h, --help |
-h |
布尔 | 显示 unload 命令的帮助信息 |
--scheduler-host string |
无 | "0.0.0.0:9004" |
Seldon scheduler 的地址,对应源码中的 defaultSchedulerHost;可通过环境变量 SELDON_SCHEDULE_HOST 覆盖 |
-r, --show-request |
-r |
布尔,默认 false |
显示请求内容(文档帮助文本列出,命令实现中由 -v 的 verbose 模式承载请求打印逻辑) |
-o, --show-response |
-o |
布尔,默认 true |
显示响应内容(文档帮助文本列出) |
-v, --verbose |
-v |
布尔,默认 false |
详细输出;开启后会在发送请求前打印 UnloadPipelineRequest,并在成功时打印 UnloadPipelineResponse(见 scheduler.go) |
-f, --file-path |
-f |
字符串,默认空 | 管道 manifest 文件(YAML),CLI 会从文件中解析管道名称 |
--force |
无 | 布尔,默认 false |
强制控制面模式;默认由环境变量 SELDON_FORCE_CONTROL_PLANE 决定,默认值为 false |
注意:文档生成的帮助文本(
-r/--show-request、-o/--show-response)源于命令帮助输出;而实现层面更关键的开关是--verbose与--force。这一点在后面“源码级调用链”一节会详细展开。
命令执行的安全闸门:--force
这是 unload(以及 load、experiment 系列命令)共有的安全机制。看 pipeline_unload.go 的实现:
fmt.Println(helpForceControlPlaneWarning)
if !force {
return nil
}
每次执行命令都会先打印警告:
This command is likely to cause inconsistencies in your k8s cluster, enable with care.
只有在 --force 为 true 时,命令才会真正继续向 scheduler 发送卸载请求,否则直接返回。--force 的默认值来自环境变量 SELDON_FORCE_CONTROL_PLANE(默认 false),定义于 flags.go。
这一设计的背景是:Seldon Core 2 中,标准做法是通过 Kubernetes CRD 声明管道并由 operator 的控制器(如 pipeline_controller.go)驱动生命周期。直接用 CLI 卸载属于“控制面直连”操作,可能与 K8s 集群中的声明状态产生不一致,因此必须显式开启 --force。
两种卸载方式与文件解析逻辑
命令支持通过位置参数或 -f 文件两种方式指定管道,解析逻辑由 extractFileOrName 完成(file.go):
func extractFileOrName(flags *pflag.FlagSet, args []string) ([]byte, string, error) {
filename, err := flags.GetString(flagFile)
...
if filename != "" {
fileBytes = loadFile(filename) // 读取文件内容
} else {
if len(args) > 0 {
name = args[0] // 取位置参数作为管道名
}
}
return fileBytes, name, nil
}
即:
- 指定了
-f <file>时,读取文件字节流,位置参数被忽略; - 未指定
-f时,取第一个位置参数作为管道名; - 两者都不提供时,
pipelineName为空字符串,最终会构造一个空名称的卸载请求。
源码级调用链:从 CLI 到 Scheduler 的完整旅程
第一步:SchedulerClient 组装请求
命令的 RunE 回调中(pipeline_unload.go),在 --force 校验通过后执行:
schedulerClient, err := cli.NewSchedulerClient(schedulerHost, schedulerHostIsSet, authority, verbose)
...
res, err := schedulerClient.UnloadPipeline(pipelineName, fileBytes)
NewSchedulerClient 负责根据 host、authority 建立 gRPC 连接。若通过 -f 传入 manifest 且未提供名称,UnloadPipeline 内部会先严格解析 YAML(unMarshallYamlStrict),从 mlopsv1alpha1.Pipeline 对象中取出 pipeline.Name,见 scheduler.go:
if len(pipelineBytes) > 0 && pipelineName == "" {
pipeline := &mlopsv1alpha1.Pipeline{}
err := unMarshallYamlStrict(pipelineBytes, pipeline)
...
pipelineName = pipeline.Name
}
req := &scheduler.UnloadPipelineRequest{
Name: pipelineName,
}
...
grpcClient := scheduler.NewSchedulerClient(conn)
res, err := grpcClient.UnloadPipeline(context.Background(), req)
这里构建的 scheduler.UnloadPipelineRequest 是 gRPC 协议消息,定义于 scheduler.proto:
message UnloadPipelineRequest {
string name = 1;
}
message UnloadPipelineResponse {
}
可见卸载请求本质上只携带一个字段——管道名称;响应体为空。
第二步:Scheduler 服务端处理
请求到达 scheduler 服务端后,由 gRPC 服务方法 UnloadPipeline 处理(server.go):
func (s *SchedulerServer) UnloadPipeline(ctx context.Context, req *pb.UnloadPipelineRequest) (*pb.UnloadPipelineResponse, error) {
err := s.pipelineHandler.RemovePipeline(req.GetName())
if err != nil {
return nil, status.Errorf(codes.FailedPrecondition, "%s", err.Error())
}
return &pb.UnloadPipelineResponse{}, nil
}
RemovePipeline 由管道存储层(PipelineStore)实现,是卸载的核心业务逻辑。
第三步:管道状态机迁移
PipelineStore.RemovePipeline 及内部 removePipelineImpl(store.go)执行了精细的状态迁移:
- 幂等检查:若管道最新版本的状态已经是
PipelineTerminated(scheduler 与 pipeline-gateway 双端均已终止),直接返回PipelineAlreadyTerminatedErr,不做任何事; - 重复终止保护:若已处于
PipelineTerminating,直接返回PipelineTerminatingErr,避免重复触发终止流程; - 正常卸载路径:将
pipeline.Deleted = true并记录DeletedAt时间戳,同时把 scheduler 与 pipeline-gateway 两端的版本状态都置为PipelineTerminate(原因记为"pipeline removed"),确保双方感知到终止; - 持久化:若配置了数据库(
ps.db),先保存管道状态; - 事件广播:通过事件总线(
eventHub)发布PipelineEventMsg(包含管道名、版本号与 UID),通知下游(如 pipeline-gateway、数据面)回收资源。
若管道名称不存在,则返回 PipelineNotFoundErr,服务端会以 codes.FailedPrecondition 状态码返回给 CLI。
与 operator 侧的协调一致,operator 的 SchedulerClient.UnloadPipeline(pipeline.go)也做了类似处理:成功卸载后把 CR 的 PipelineReady 条件置为 false,reason 设置为 PipelineTerminating,并回写状态。
与相邻命令的配合:管道生命周期管理
卸载是管道生命周期的一环。在 seldon_pipeline.md 中可以看到 seldon pipeline 组的完整命令矩阵:
| 子命令 | 职责 |
|---|---|
seldon pipeline load |
加载管道(可传 manifest,-f 必选) |
seldon pipeline unload |
卸载管道(本文主题) |
seldon pipeline status |
查询管道状态(支持 --wait 条件与 --timeout-secs) |
seldon pipeline infer |
对管道发起推理 |
seldon pipeline list |
列出所有管道 |
seldon pipeline inspect |
检查管道内数据流消息 |
典型运维流程是:load 加载 → status 确认就绪 → infer 验证效果 → unload 释放资源。卸载后可用 seldon pipeline list 确认管道已不在列表中,或用 seldon pipeline status <name> 观察其最终变为 PipelineTerminated。
另外,seldon pipeline unload 也可通过 -f 传入 manifest;此时请确保 manifest 的 metadata.name 与目标管道一致,CLI 会按前文所述自动提取名称。
适用前提与注意事项
- 命令形态以当前仓库为准:本文参数、默认值(如
0.0.0.0:9004)均取自 pipeline_unload.go 与 flags.go,若你的版本不同,请以seldon pipeline unload --help输出为准; - 必须显式
--force:默认SELDON_FORCE_CONTROL_PLANE=false时命令只打印警告便退出;请务必确认你确实要绕过 CRD 声明直接卸载管道; - Scheduler 地址:本地运行默认
0.0.0.0:9004;生产环境可通过--scheduler-host或环境变量SELDON_SCHEDULE_HOST指定; - 通过 Envoy 的场景:若 scheduler 位于 Envoy 代理之后,用
--authority指定虚拟主机名; - 幂等语义:对已终止或正在终止的管道重复执行 unload 不会报错破坏状态,而是返回对应提示,可安全重试。
总结
seldon pipeline unload 是 Seldon Core 2 管道生命周期管理的关键控制面命令。它的实现路径清晰:CLI 层(pipeline_unload.go)解析参数并强制 --force 校验 → 客户端层(scheduler.go)构造 UnloadPipelineRequest → gRPC 服务端(server.go)调用 RemovePipeline → 存储层(store.go)执行幂等状态迁移并广播事件。理解这条链路,你就能在运维中准确预判每条命令执行后的集群状态,安全地管理管道资源的加载与回收。