
在之前几篇关于 MLOps 工具的文章中,我展示了有多少流行的 MLOps 工具跟踪与模型训练实验相关的指标。我还展示了他们如何使用 MinIO 来存储作为模型训练管道一部分的非结构化数据。但是,一个好的 MLOps 工具应该做的不仅仅是管理您的实验、数据集和模型。它应该能够在组织的各种环境中部署您的模型,将它们移动到开发、测试和生产环境。此外,在 MinIO,我们注意到人们对我们的 MLOps 内容的兴趣高于平均水平。我们的许多合作伙伴都看到了同样的情况。也许 2025 年是企业在机器学习项目中占据主导地位并将其拉入由 MLOps 工具管理的正式 CI/CD 管道的一年。在本文中,我将重点介绍 MLflow,并展示如何使用 MLserve(Mlflow 提供的用于测试模型的 RESTful 接口的简单工具)在本地托管经过训练的模型。最后,我将展示如何使用 KServe 将模型部署到 Kubernetes 集群。KServe 是专为 Kubernetes 设计的开源模型服务框架,专门用于大规模部署和服务机器学习 (ML) 模型。它提供了一个标准化的无服务器推理平台,支持各种 ML 框架,包括 TensorFlow、PyTorch、XGBoost 和 Scikit-Learn。
MLFlow 设置
查看使用 MLFlow 和 MinIO 设置开发计算机,在开发计算机上设置 MLflow。本文提供了 MLflow 的一些背景知识,介绍了它在后台使用的产品(PostgreSQL 和 MinIO),最后展示了如何创建和加载 docker-compose 文件以进行本地仿真。下图显示了 MLflow 跟踪服务器、PostreSQLm 和 MinIO 之间的关系。

KServe 设置
要设置 KServe,您需要安装 kind 和 Helm。您还需要克隆 KServe 存储库并在其中运行安装脚本。我将在下面提供一个安装所有内容的配方,这样您就不必在互联网上搜索各种安装说明。如果您不熟悉这些工具或需要更多信息,请查看我提供的链接。
1 . 安装种类
Kind 的下载方式会有所不同,具体取决于您的芯片架构。因此,您需要做的第一件事是使用以下命令确定您的芯片架构。
uname -m
您应该会看到类似 arm64、amd64 或 x86_64 的内容。对于 amd64 或 x86_64请运行以下命令下载 AMD 安装。这将创建一个名为 kind 的新子目录,该子目录将包含运行 kind 所需的一切。
curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.26.0/kind-linux-amd64
对于 arm64,请使用以下命令。这还将创建一个新的子目录。
curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.26.0/kind-linux-arm64
最后,更改此目录的权限,以便其中包含的文件可以执行代码。然后将其移动到 usr/local/bin 目录。
chmod +x ./kind
sudo mv ./kind /usr/local/bin/kind
2 . 设置 Kubernetes 集群
我们现在可以使用 kind 来创建 Kubernetes 集群。运行下面的三个命令来创建名为 'kind' 的默认集群,使用默认的 'kind-kind' 上下文,并为我们的部署创建一个命名空间。
kind create cluster
kubectl config use-context kind-kind
kubectl create namespace mlflow-kserve-test
下面是一些其他用于管理集群的有用 kind 和 kubectl 命令。
kind create cluster --name <cluster_name>
kubectl config get-contexts
kind get clusters
kind delete cluster
3 . 安装 Helm
要安装 Helm,请运行以下三个命令,这些命令将下载 Helm shell 安装脚本,更改其权限以便它可以运行,然后运行它。
curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3
chmod 700 get_helm.sh
./get_helm.sh
4 . 安装 KServe
KServe 的本地 Kubernetes 集群上的 KServe 入门在线指南中,指向其快速安装脚本的链接已断开。为了在链接修复之前解决此问题,我们将克隆 KServe GitHub 存储库并直接导航到安装脚本。
git clone https://github.com/kserve/kserve.git
cd hack
bash quick_install.sh
此命令需要一段时间才能完成。它安装 KServe 和所有 KServe 依赖项:Istio、Knative 和 Cert-manager。这些依赖项如下所述。
Istio 是一个开源服务网格,可帮助管理云原生应用程序中的微服务。它允许应用程序安全地通信和共享数据。
Knative 是一个开源项目,它扩展了 Kubernetes,以帮助用户构建、运行和管理无服务器函数。Knative 是 AWS Lambda 和 Azure Functions 等专有无服务器解决方案的替代方案。
Cert-manager 是一种开源工具,可自动管理 Kubernetes 和 OpenShift 工作负载的 TLS 证书。
记录和注册模型
本文的其余部分将使用一个使用如下所示的 sklearn 代码创建的简单模型。此训练函数创建一个 sklearn 模型,该模型采用一瓶葡萄酒的 13 个特征并预测它是红葡萄酒还是白葡萄酒。
import mlflow
import numpy as np
from sklearn import datasets, metrics
from sklearn.linear_model import ElasticNet
from sklearn.model_selection import train_test_split
def eval_metrics(pred, actual):
rmse = np.sqrt(metrics.mean_squared_error(actual, pred))
mae = metrics.mean_absolute_error(actual, pred)
r2 = metrics.r2_score(actual, pred)
return rmse, mae, r2
def train_model():
# Set th experiment name
mlflow.set_experiment("wine-quality")
mlflow.set_tracking_uri('http://localhost:5001')
# Enable auto-logging to MLflow
#mlflow.sklearn.autolog()
# Load wine quality dataset
X, y = datasets.load_wine(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25)
# Start a run and train a model
with mlflow.start_run(run_name="default-params"):
model = ElasticNet()
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
rmse, mae, r2 = eval_metrics(y_pred, y_test)
mlflow.log_metrics({"rmse": rmse, "r2": r2, "mae": mae})
# Log and register the model.
model_signature = mlflow.models.infer_signature(X_test, y_pred)
mlflow.sklearn.log_model(model, "model",
registered_model_name="wine-quality-model",
signature=model_signature)
return metrics
此代码在 MLflow(突出显示的代码)模型注册表中记录和注册模型。指定 registered_model_name 参数后,log_model 函数将记录并注册模型。这是我们在将模型部署到 KServe 时将拉取模型的位置。下面的屏幕截图显示了 MLflow UI 中记录的模型。路径显示此模型在 MinIO 中的位置,model_uri显示部署模型时需要使用的 URI。此代码在 MLflow(突出显示的代码)模型注册表中记录和注册模型。指定 registered_model_name 参数后,log_model 函数将记录并注册模型。这是我们在将模型部署到 KServe 时将拉取模型的位置。下面的屏幕截图显示了 MLflow UI 中记录的模型。路径显示此模型在 MinIO 中的位置,model_uri显示部署模型时需要使用的 URI。

使用 MLServe 测试模型部署
MLflow 附带一个方便的命令行工具,让您只需一个命令即可运行本地推理服务器。请记住使用 enable-mlserver 标志,该标志指示 MLflow 使用 MLServer 作为推理服务器。这可确保模型以与在 Kubernetes 中相同的方式运行。下面的命令会将记录的模型部署到 MLServer。模型 URI(突出显示)必须与上面屏幕截图中显示的模型 URI 匹配。
export MLFLOW_TRACKING_URI=http://localhost:5000
mlflow models serve -m runs:/dc00cbfeb5cd41ae831009edee45b767/model -p 1234 --enable-mlserver
如果要部署已注册的模型,请使用以下命令。此处,模型引用的格式为 "models/{model name}/{version}"。模型名称是在注册模型时分配的。
mlflow models serve -m models:/wine-quality-model/1 -p 1234 --enable-mlserver
下面的代码片段将向服务发送示例输入并返回预测。模型更喜欢批量输入;因此,下面的 input 是一个列表(或 Matrix)的列表。如果指定简单列表 (或向量) ,则服务将引发错误。
import requests
import json
url = f"http://localhost:1234/invocations"
payload = json.dumps(
{
"inputs": [[14.23, 1.71, 2.43, 15.6, 127.0, 2.8, 3.06, 0.28, 2.29, 5.64, 1.04, 3.92, 1065.0]],
}
)
response = requests.post(
url=url,
data=payload,
headers={"Content-Type": "application/json"},
)
print(response.json())
输出应类似于下面的文本,它表示输入要素表示一瓶红酒的概率。输出应类似于下面的文本,它表示输入要素表示一瓶红酒的概率。
{'predictions': [0.4097722993507402]}
构建 Docker 镜像
在本教程中,我将创建一个 docker 镜像,其中包含我们在上面训练的模型。此映像最终将部署到 Kubernetes 并通过 KServe 运行。MLflow 有一个很好的命令行实用程序,它将引用我们记录的(或注册的)模型并使用它创建一个 docker 镜像。此命令如下所示。在本教程中,我将创建一个 docker 镜像,其中包含我们在上面训练的模型。此映像最终将部署到 Kubernetes 并通过 KServe 运行。MLflow 有一个很好的命令行实用程序,它将引用我们记录的(或注册的)模型并使用它创建一个 docker 镜像。此命令如下所示。
mlflow models build-docker -m runs:/dc00cbfeb5cd41ae831009edee45b767/model -n keithpij/mlflow-wine-classifier --enable-mlserver
请注意 model 参数 (-m),该参数指定要放入图像中的 MLflow 中的模型。此字符串必须与我们在记录训练的模型后在 MLflow UI 中看到的模型名称匹配。image name 参数 (-n) 用于指定映像的名称。确保此名称的第一部分是您的 docker 用户名,因为我们需要将其推送到 Docker 的镜像注册表。我们接下来会这样做。下面的命令会将我们刚刚创建的镜像推送到 Docker Hub。
docker push keithpij/mlflow-wine-classifier
创建映像并将其推送到 Docker Hub 后,您可以登录 Docker Hub 查看映像。
将推理服务部署到 Kubernetes
要使用 KServe 将我们的镜像部署到 Kubernetes,我们需要创建一个 kubectl 文件。如下所示。
apiVersion: "serving.kserve.io/v1beta1"
kind: "InferenceService"
metadata:
name: "mlflow-wine-classifier"
namespace: "mlflow-kserve-test"
spec:
predictor:
containers:
- name: "wine-classifier"
image: "keithpij/mlflow-wine-classifier"
ports:
- containerPort: 8080
protocol: TCP
env:
- name: PROTOCOL
value: "v2"
此 kubectl 文件将创建一个 KServe 推理服务。请注意上面突出显示的 namespace 和 image 字段。命名空间必须是之前创建的命名空间。该镜像必须是推送到 Docker Hub 的镜像。假设上面的文件名为 sklearn-wine.yaml,我们可以运行下面的命令来部署镜像。
kubectl apply -f sklearn-wine.yaml
该服务需要一段时间才能部署。部署后,您可以运行以下命令来查看新推理服务的详细信息。
kubectl get inferenceservices -n mlflow-kserve-test
此命令输出的缩写版本如下所示。
NAME URL READY
mlflow-wine-classifier http://mlflow-wine-classifier.mlflow-kserve-test.example.com True
以下是一些有用的 Kubernetes 命令,可帮助解决此服务的问题,并在需要重新开始时删除该服务。如果您的服务未启动且上一个命令未报告 ready 状态,则查看 Pod 日志特别有用。
kubectl get namespaces
kubectl get pods -n <namespace>
kubectl -n <namespace> logs <pod-name>
kubectl delete -f sklearn-wine.yaml -n mlflow-kserve-test
确定 Ingress Host 和 Service Host
在向新的 Inference 服务发送请求之前,我们必须确定入口和服务主机。回想一下,当我们安装 KServe 时,它附带了 istio,它将充当我们推理服务的代理。因此,我们需要确定 istio 正在侦听的地址。我们还需要确定推理服务的地址,以便我们可以在标头或请求中包含此地址,以便 istio 可以适当地定向请求。首先,让我们弄清楚 istio 的地址。
kubectl get svc istio-ingressgateway -n istio-system
如果设置了 EXTERNAL-IP 值,则表示您正在具有可用于入口网关的外部负载均衡器的环境中运行。使用以下命令获取入口主机地址和入口端口。
export INGRESS_HOST=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
export INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="http2")].port}')
要在像 kind 这样的本地集群上运行,请通过运行以下命令来使用端口转发。
INGRESS_GATEWAY_SERVICE=$(kubectl get svc --namespace istio-system --selector="app=istio-ingressgateway" --output jsonpath='{.items[0].metadata.name}')
echo $INGRESS_GATEWAY_SERVICE
kubectl port-forward --namespace istio-system svc/${INGRESS_GATEWAY_SERVICE} 8080:80
最后,我们需要指向集群中包含模型 Pod 的服务主机名
SERVICE_HOSTNAME=$(kubectl get inferenceservice mlflow-wine-classifier -n mlflow-kserve-test -o jsonpath='{.status.url}' | cut -d "/" -f 3)
echo $SERVICE_HOSTNAME
测试 Inference 服务
现在,我们已准备好测试在 KServe 中运行的推理服务。下面的代码段与我们之前使用的代码段类似。但是,有效负载略有不同。这是 KServe 的 V2 协议。请小心用于此请求地址的 URL。MLflow 文档指出,此 URL 必须包含模型的名称。当您像我们在这里所做的那样构建自己的映像时,这将不起作用。出于某种原因,模型名称被硬编码为"mlflow-model"。(我花了很长时间才弄清楚。KServe 将使用 host 标头查找您的推理服务。
url = f"http://localhost:8080/v2/models/mlflow-model/infer"
payload = json.dumps(
{
"inputs": [
{
"name": "input",
"shape": [1,13],
"datatype": "FP64",
"data": [[14.23, 1.71, 2.43, 15.6, 127.0, 2.8, 3.06, 0.28, 2.29, 5.64, 1.04, 3.92, 1065.0]]
}
]
}
)
response = requests.post(
url=url,
data=payload,
headers={"Host": "mlflow-wine-classifier.mlflow-kserve-test.example.com",
"Content-Type": "application/json"},
)
print(response.json())
总结
如果您已经走到了这一步,那么您已经端到端地使用了 MLflow。在本文中,我们创建了一个模型,在训练后跟踪其指标,记录模型,并使用我们从头开始安装的 KServe 将其部署到本地 Kubernetes 集群。如果您遵循 MLflow 和 KServe 的在线文档,则会出现一些问题,因此请使用本指南作为起点。