在之前幾篇關于 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 mlflowimport numpy as np
from sklearn import datasets, metrics
from sklearn.linear_model import ElasticNet
from sklearn.model_selection import train_test_splitdef 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, r2def train_model():# Set th experiment namemlflow.set_experiment("wine-quality")mlflow.set_tracking_uri('http://localhost:5001')# Enable auto-logging to MLflow#mlflow.sklearn.autolog()# Load wine quality datasetX, 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 modelwith 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 jsonurl = 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: 8080protocol: TCPenv:- name: PROTOCOLvalue: "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_SERVICEkubectl 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 的在線文檔,則會出現一些問題,因此請使用本指南作為起點。