使用 AIStor、MLflow 和 KServe 将模型部署到 Kubernetes

在之前几篇关于 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 的在线文档,则会出现一些问题,因此请使用本指南作为起点。

相关推荐
能不能别报错2 小时前
K8s学习笔记(十六) 探针(Probe)
笔记·学习·kubernetes
能不能别报错3 小时前
K8s学习笔记(十四) DaemonSet
笔记·学习·kubernetes
火星MARK5 小时前
k8s面试题
容器·面试·kubernetes
Serverless社区6 小时前
阿里云函数计算 AgentRun 全新发布,构筑智能体时代的基础设施
阿里云·云原生·serverless·函数计算
赵渝强老师6 小时前
【赵渝强老师】Docker容器的资源管理机制
linux·docker·容器·kubernetes
能不能别报错8 小时前
K8s学习笔记(十五) pause容器与init容器
笔记·学习·kubernetes
稚辉君.MCA_P8_Java8 小时前
kafka解决了什么问题?mmap 和sendfile
java·spring boot·分布式·kafka·kubernetes
乄bluefox8 小时前
保姆级docker部署nacos集群
java·docker·容器
每天进步一点_JL8 小时前
Docker 是什么?
后端·docker·容器
一叶飘零_sweeeet9 小时前
从 0 到 1 掌控云原生部署:Java 项目的 Docker 容器化与 K8s 集群实战指南
docker·云原生·kubernetes·项目部署