机器学习 (ML) 计划可能会将计算和存储基础设施推至极限。许多 DataOps 团队依赖于基于 Kubernetes 的混合云架构来满足可扩展性、效率、可靠性、多租户以及对 RESTful API 支持的计算和对象存储需求。DataOps 团队已将工具标准化,这些工具依赖于高性能的与 S3 API 兼容的对象存储来满足其管道、训练和推理需求。
Kubeflow 是 Kubernetes 的标准机器学习工具包,它需要 S3 API 兼容性。Kubeflow 在整个数据科学社区中被广泛使用,但对 S3 API 兼容的对象存储的需求限制了部署选项。当 Azure 或 GCP 缺少对其对象存储产品的 S3 API 支持时,您将如何在这些平台上运行 Kubeflow?
MinIO Kubernetes 原生对象存储与 S3 API 兼容,因此您可以在任何托管的 Kubernetes 服务(Azure Kubernetes Service 、Google Kubernetes Engine 、Amazon Kubernetes Service )以及任何 Kubernetes 发行版(VMware Tanzu 、Red Hat OpenShift ,甚至 Minikube )上运行您首选的数据科学工具。
在这篇文章中,我们将使用 Azure Kubernetes Service (AKS) 和 MinIO 作为整个设置的基础存储来设置一个 Kubeflow 集群,并进行端到端测试,我们将部署一个管道,该管道访问 MinIO 上的数据并将生成模型存储在那里。我们将使用的问题是传统的 MNIST 挑战,它包括一个光学字符识别 (OCR) 问题。
设置 Kubernetes 集群 让我们首先设置名为 **KubeFlowMinIO** 的 AKS 集群,该集群在名为 **MinIOKubeFlow** 的资源组中包含四个节点。
export RESOURCE_GROUP_NAME=MinIOKubeFlow export LOCATION=westus
az group create -n $RESOURCE_GROUP_NAME -l $LOCATION
export NAME=KubeFlowMinIO
export AGENT_SIZE=Standard_D4s_v3
export AGENT_COUNT=4
az aks create -g $RESOURCE_GROUP_NAME -n $NAME -s $AGENT_SIZE -c $AGENT_COUNT -l $LOCATION --generate-ssh-keys
此过程需要几分钟,之后您将拥有一个可运行的 Kubernetes 集群。您只需要使用此集群的访问权限配置本地 **kubectl**。
az aks get-credentials -n $NAME -g $RESOURCE_GROUP_NAME
设置 MinIO 下一步是设置 MinIO Operator 以管理我们在 Azure 上的对象存储。我们已经 简化了 MinIO 在 Kubernetes 上的管理 ,因此有多种方法可以安装 MinIO operator,您可以选择最适合您工作流程的方法。对于本文,我们将使用 MinIO 的 krew 插件来设置 MinIO Operator 和我们的对象存储。
下载 MinIO Krew 插件。
kubectl krew install minio
然后初始化 operator。
现在,让我们进入 MinIO Operator UI 创建我们的第一个租户。输入以下命令以接收本地可访问的端点和登录令牌。
kubectl minio proxy -n minio-operator
预期输出为
正在启动控制台 UI 的端口转发。 要连接,请打开浏览器并访问 http://localhost:9090 当前用于登录的 JWT:eyJhbGciOiJSUzI1NiIsImtpZCI6IkhWclVWMmc2YjNuZlRKcGY1YUxJTTh1Mjd2d3ZKZmh5dzBKaE10cm5QYUUifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJtaW5pby1vcGVyYXRvciIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJjb25zb2xlLXNhLXRva2VuLTh2cDRxIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQubmFtZSI6ImNvbnNvbGUtc2EiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiI4MDJkMmFlZi02ZTQxLTQyMzctYjIyYS04OGVkNjhhNTFkMWMiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6bWluaW8tb3BlcmF0b3I6Y29uc29sZS1zYSJ9.CxaS7Xy6l63Z90FLDL0XV0FB4iYYD93-EZ9lT6dUxHTkaYIwGzuVAOVYKclIAslpJqvANzurnuCQv2DSYuptBokqNyJqBZ_Mdfxk_BD8k9LNvvhH2B75FXJOlLUvO43HZP-vWqiBLHvhWD86KI5YdCqgXq0KB2Yuw03pIeAkGhdo-QN7EnTVt-mu6OniB6q_oSC61wUoToHCZKbq7OLeg2zzwqo9JGCBvghBbiVFzeMTYAQHdad69PsWjBRBlUKbG7u5eNWiVPiV44r0-fUZxdCr-1JEP9e4Ag-8J2GzIU1-yBIc_Yn1ok59HxXwiT-_fmp2tpe2WsArY7Hwzza2qLoVSkITzPX6eMVbGfRdzbcxd396LcQfg8GJn4Rbs1Z4YCRqMK_DpoQqYOFf-pjZ6Oa90GlZpMVSH_6_H4xxBuuobyn3WK7XyuBxJuFcl7KoIKoa4qwi87eUE139RXPOZKsCrMX-YmKxTAixKlGux2U4jRaN2lav6_y-ayUvHt0syEJqu0uhqdPNVxGIWW0sabJJ0sSfQdacmrBY1VazIYsN2NAL1N2QCwmQvvjRlqpEAWPF_uhuVwGtgcDX8-CxRKtfoY-8gn7ujwCKl1GMpyr-nE8p88eMIxEkaXqBia0erRLwUGTHrS2ymGN0Ii85_2wRZmDuCGA9QiQ01r89ZXU 转发自 0.0.0.0:9090 -> 9090
现在让我们进入 http://localhost:9090 并使用建议的令牌登录。
登录后,我们将看到一个空的租户列表,让我们通过点击右上方的 **+ 创建租户** 来创建一个。
为了在设置过程中保持简单,我们将在这个集群的 **默认** 命名空间中创建一个名为 **machine-learning-cluster** 的租户。当然,您可以将其更改为任何适合您需求的命名空间。然后我们将选择一个存储类,并且由于我们旨在获得高性能的数据存储库,因此我们将使用 Azure 的托管高级存储 以获得 Kubeflow 管道的最佳性能。完成这些字段后,选择 **高级**。在这里,您可以配置高级功能,例如自定义 Docker 注册表、身份提供程序、加密和 Pod 部署。目前,我们将点击 **下一步** 直到到达安全步骤并关闭 TLS,以便我们可以在无需设置域名和外部 TLS 证书的情况下完成本指南。
关闭此租户的 TLS。
现在,我们将告诉 MinIO Operator 我们希望租户有多大。我将选择 4 个节点以匹配我们当前的设置,并选择 **1 TB** 的容量,但您可以根据您的需要进行调整。
最后一步是查看将要发生的事情。只需点击 **创建**,MinIO 就会完成其余工作!
记下自动生成的访问对象存储的凭据,我们将使用这些凭据访问底层存储。
就是这样!您已配置了一个高性能的对象存储,并且只花了短短几分钟。再过几分钟,您将看到租户 **已初始化**,并且可以使用了。
租户详细信息是您可以更新和扩展对象存储的位置。我们还可以看到对象存储和管理对象存储的公共 IP。在本指南中我们不会使用它,但您可以使用它从集群外部开始使用对象存储。
我们已准备好对象存储方面的工作 - 我们已经设置了一个高性能集群,现在我们需要在 Kubeflow 管道中利用它。
设置 Kubeflow 要在 AKS 上设置 Kubeflow,我们将使用命令行实用程序 **kfctl**,该实用程序可以从 kfctl 发布页面 下载。有适用于 Mac 和 Linux 的二进制文件,但如果您使用的是 Windows,则必须从源代码编译该二进制文件。只需确保 kfctl 二进制文件位于您的 PATH 中即可。
运行以下命令,这些命令取自 Azure 上的 Kubeflow 安装 文档。
# 将 KF_NAME 设置为您的 Kubeflow 部署名称。您也可以在创建配置目录时使用此 # 值作为目录名称。 # 例如,您的部署名称可以是 ' my-kubeflow ' 或 ' kf-test '。 export KF_NAME=my-kubeflow # 设置您希望存储一个或多个 Kubeflow 部署的基本目录路径。例如,/opt/。 # 然后设置此部署的 Kubeflow 应用程序目录。 export BASE_DIR=kubeflowsetup export KF_DIR=${BASE_DIR}/${KF_NAME} # 设置部署 Kubeflow 时要使用的配置文件。 # 以下配置默认安装 Istio。注释掉配置文件中的 # Istio 组件以跳过 Istio 安装。 # 请参阅 https://github.com/kubeflow/kubeflow/pull/3663 export CONFIG_URI="https://raw.githubusercontent.com/kubeflow/manifests/v1.2-branch/kfdef/kfctl_k8s_istio.v1.2.0.yaml" mkdir -p ${KF_DIR} cd ${KF_DIR} kfctl apply -V -f ${CONFIG_URI}
按照配置,此过程大约需要八分钟,所以请泡一杯咖啡,并使用以下命令监控完成情况。
kubectl get all -n kubeflow
一旦所有 Pod 都处于运行状态,我们就可以继续构建利用 MinIO 的 Kubeflow Pipeline 了。
通过运行以下端口转发命令并访问 http://localhost:8080 打开 Kubeflow 仪表盘。
kubectl port-forward svc/istio-ingressgateway -n istio-system 8080:80
然后通过创建一个名为 machine-learning 的命名空间来完成 Kubeflow 设置。
配置命名空间后,Kubeflow 仪表盘将打开。
让我们设置一个 Jupyter Notebook 服务器,并从那里进行配置。使用 Tensorflow 1.15 镜像,创建一个名为 setup-pipeline 的 Notebook。
服务器准备就绪后,连接到它,然后创建一个名为 Setup Pipeline 的 Python 3 Notebook。
最后一步是配置您的 Docker 账户。Kubeflow 会将您在整个 Pipeline 中构建的每个新模型推送到 Docker,您可能会很快达到每小时 100 个请求的限制。当您使用 Docker 账户时,此限制将提高到每小时 200 个请求。
USER=<DOCKERUSER>; PASSWORD=<DOCKERPASSWORD>; echo -n $USER:$PASSWORD | base64 | xargs echo -n |xargs -0 printf '{ "auths": { "https://index.docker.io/v1/": { "auth": "%s" } } }\n' > /tmp/config.json && kubectl create --namespace ${NAMESPACE} configmap docker-config --from-file=/tmp/config.json && rm /tmp/config.json
运行 Kubeflow Pipeline 现在回到我们的 Notebook。从这里开始,我们将遵循 Kubeflow 团队提供的 适用于普通 Kubernetes 的优秀示例 。我们将学习如何将模型提交到 Kubeflow 进行分布式训练,以及如何部署和服务它们。
为了使此 Notebook 正常运行,您将需要几个文件,主要是**model.py、k8s_util.py、notebook_setup.py、requirements.txt 和 Dockerfile.model**,用于构建您的模型,将其提交到 Kubeflow 然后部署它。让我们从以下代码片段开始,将这些文件下载到我们的 Notebook 中。
import urllib.request import shutil file_list = [ "https://raw.githubusercontent.com/kubeflow/examples/master/mnist/k8s_util.py" , "https://raw.githubusercontent.com/kubeflow/examples/master/mnist/Dockerfile.model" , "https://raw.githubusercontent.com/kubeflow/examples/master/mnist/model.py" , "https://raw.githubusercontent.com/kubeflow/examples/master/mnist/notebook_setup.py" , "https://raw.githubusercontent.com/kubeflow/examples/master/mnist/requirements.txt" ] for url in file_list: file_name = url.split( "/" ).pop() with urllib.request.urlopen(url) as response, open(file_name, 'wb' ) as out_file: shutil.copyfileobj(response, out_file)
现在,让我们准备命名空间并配置 MinIO 凭据。对于我们的端点,我们将使用内部 Kubernetes 服务名称**minio.default.svc.cluster.local**,对于**DOCKER_REGISTRY**,我们将输入我们的 Docker 用户名。
from kubernetes import client as k8s_client from kubernetes.client import rest as k8s_rest from kubeflow import fairing from kubeflow.fairing import utils as fairing_utils from kubeflow.fairing.builders import append from kubeflow.fairing.deployers import job from kubeflow.fairing.preprocessors import base as base_preprocessor DOCKER_REGISTRY = "miniodev" namespace = fairing_utils.get_current_k8s_namespace() from kubernetes import client as k8s_client from kubernetes.client.rest import ApiException api_client = k8s_client.CoreV1Api() minio_service_endpoint = "minio.default.svc.cluster.local" s3_endpoint = minio_service_endpoint minio_endpoint = "http://" +s3_endpoint minio_username = "AXNENHDUBB2LU24Y" minio_key = "GPONOCU0IDQZBMP55TTELR00D4HGFPJK" minio_region = "us-east-1" logging.info( f"Running in namespace {namespace}" ) logging.info( f"Using docker registry {DOCKER_REGISTRY}" ) logging.info( f"Using minio instance with endpoint '{s3_endpoint}'" )
接下来,我们将通过安装依赖项和下载所需数据来准备本地 Notebook。所有这些都可以在一个代码块中完成,但我使用了与示例 Notebook 相同的分离代码块,以便您更容易理解。
import logging import os import uuid from importlib import reload import notebook_setup reload(notebook_setup) notebook_setup.notebook_setup(platform= 'none' )
import k8s_util # 强制重新加载 kubeflow;由于 kubeflow 是一个多命名空间模块 # 在 notebook_setup 中执行此操作可能不够 import kubeflow reload(kubeflow) from kubernetes import client as k8s_client from kubernetes import config as k8s_config from kubeflow.tfjob.api import tf_job_client as tf_job_client_module from IPython.core.display import display, HTML import yaml
# TODO(https://github.com/kubeflow/fairing/issues/426): 一旦默认的 # Kaniko 镜像更新到比 0.7.0 新的镜像后,我们应该去掉这段代码。 from kubeflow.fairing import constants constants.constants.KANIKO_IMAGE = "gcr.io/kaniko-project/executor:v0.14.0"
from kubeflow.fairing.builders import cluster # output_map 是一个用于将额外文件添加到笔记本的映射。 # 它是一个从源位置到上下文内位置的映射。 output_map = { "Dockerfile.model" : "Dockerfile" , "model.py" : "model.py" } preprocessor = base_preprocessor.BasePreProcessor( command=[ "python" ], # 基类将设置此项。 input_files=[], path_prefix= "/app" , # 与我们不预处理任何文件无关 output_map=output_map) preprocessor.preprocess()
# 使用 Tensorflow 镜像作为基础镜像 # 我们使用自定义的 Dockerfile from kubeflow.fairing.cloud.k8s import MinioUploader from kubeflow.fairing.builders.cluster.minio_context import MinioContextSource minio_uploader = MinioUploader(endpoint_url=minio_endpoint, minio_secret=minio_username, minio_secret_key=minio_key, region_name=minio_region) minio_context_source = MinioContextSource(endpoint_url=minio_endpoint, minio_secret=minio_username, minio_secret_key=minio_key, region_name=minio_region)
cluster_builder = cluster.cluster.ClusterBuilder(registry=DOCKER_REGISTRY, base_image= "" , # base_image 在 Dockerfile 中设置 preprocessor=preprocessor, image_name= "mnist" , dockerfile_path= "Dockerfile" , context_source=minio_context_source) cluster_builder.build() logging.info( f"构建镜像 {cluster_builder.image_tag}" )
此时,您可以访问您的个人 Docker 镜像仓库,并确认已为 MNIST 模型创建了一个新的 Docker 镜像。
下一步是创建一个 MinIO Bucket。
mnist_bucket = f"{DOCKER_REGISTRY}-mnist" minio_uploader.create_bucket(mnist_bucket) logging.info( f"Bucket {mnist_bucket} 已创建或已存在" )
接下来,我们只需构建一个 **TFJob** 和 **Deployment** 来训练我们的模型,使用 **TensorBoard** 检查它,最后提供服务,所有中间步骤都存储在您的 MinIO 租户上。
让我们开始逐步浏览这些代码块,请记住,这些代码是从 kubeflow vanilla kubernete 示例 中逐字复制的。
train_name = f"mnist-train-{uuid.uuid4().hex[: 4 ]}" num_ps = 1 num_workers = 2 model_dir = f"s3://{mnist_bucket}/mnist" export_path = f"s3://{mnist_bucket}/mnist/export" train_steps = 200 batch_size = 100 learning_rate = .01 image = cluster_builder.image_tag
train_spec = f"""apiVersion: kubeflow.org/v1 kind: TFJob metadata: name: {train_name} spec: tfReplicaSpecs: Ps: replicas: {num_ps} template: metadata: annotations: sidecar.istio.io/inject: "false" spec: serviceAccount: default-editor containers: - name: tensorflow command: - python - /opt/model.py - --tf-model-dir={model_dir} - --tf-export-dir={export_path} - --tf-train-steps={train_steps} - --tf-batch-size={batch_size} - --tf-learning-rate={learning_rate} env: - name: S3_ENDPOINT value: {s3_endpoint} - name: AWS_ENDPOINT_URL value: {minio_endpoint} - name: AWS_REGION value: {minio_region} - name: BUCKET_NAME value: {mnist_bucket} - name: S3_USE_HTTPS value: "0" - name: S3_VERIFY_SSL value: "0" - name: AWS_ACCESS_KEY_ID value: {minio_username} - name: AWS_SECRET_ACCESS_KEY value: {minio_key} image: {image} workingDir: /opt restartPolicy: OnFailure Chief: replicas: 1 template: metadata: annotations: sidecar.istio.io/inject: "false" spec: serviceAccount: default-editor containers: - name: tensorflow command: - python - /opt/model.py - --tf-model-dir={model_dir} - --tf-export-dir={export_path} - --tf-train-steps={train_steps} - --tf-batch-size={batch_size} - --tf-learning-rate={learning_rate} env: - name: S3_ENDPOINT value: {s3_endpoint} - name: AWS_ENDPOINT_URL value: {minio_endpoint} - name: AWS_REGION value: {minio_region} - name: BUCKET_NAME value: {mnist_bucket} - name: S3_USE_HTTPS value: "0" - name: S3_VERIFY_SSL value: "0" - name: AWS_ACCESS_KEY_ID value: {minio_username} - name: AWS_SECRET_ACCESS_KEY value: {minio_key} image: {image} workingDir: /opt restartPolicy: OnFailure Worker: replicas: 1 template: metadata: annotations: sidecar.istio.io/inject: "false" spec: serviceAccount: default-editor containers: - name: tensorflow command: - python - /opt/model.py - --tf-model-dir={model_dir} - --tf-export-dir={export_path} - --tf-train-steps={train_steps} - --tf-batch-size={batch_size} - --tf-learning-rate={learning_rate} env: - name: S3_ENDPOINT value: {s3_endpoint} - name: AWS_ENDPOINT_URL value: {minio_endpoint} - name: AWS_REGION value: {minio_region} - name: BUCKET_NAME value: {mnist_bucket} - name: S3_USE_HTTPS value: "0" - name: S3_VERIFY_SSL value: "0" - name: AWS_ACCESS_KEY_ID value: {minio_username} - name: AWS_SECRET_ACCESS_KEY value: {minio_key} image: {image} workingDir: /opt restartPolicy: OnFailure """
接下来,我们通过 Kubernetes Python SDK 提交作业。
tf_job_client = tf_job_client_module.TFJobClient()
tf_job_body = yaml.safe_load(train_spec) tf_job = tf_job_client.create(tf_job_body, namespace=namespace) logging.info( f"创建作业 {namespace}.{train_name}" )
from kubeflow.tfjob import TFJobClient tfjob_client = TFJobClient() tfjob_client.wait_for_job(train_name, namespace=namespace, watch= True )
然后,我们获取作业的日志。
tfjob_client.get_logs(train_name, namespace=namespace)
我们准备在 MinIO 上检查模型。我们可以通过我们的笔记本或 MinIO 控制台来完成此操作。首先,我将展示如何通过笔记本完成此操作。
from botocore.exceptions import ClientError try : model_response = minio_uploader.client.list_objects(Bucket=mnist_bucket) # 最小检查以查看是否至少创建了存储桶 if model_response[ "ResponseMetadata" ][ "HTTPStatusCode" ] == 200 : logging.info( f"{model_dir} 位于 {mnist_bucket} 存储桶中" ) except ClientError as err: logging.error(err)
现在我将展示如何使用 Operator 控制台来完成此操作。在 Operator GUI 中进入租户详细信息,然后点击控制台 URL。
在此处,登录到 MinIO 控制台,进入对象浏览器并浏览 **miniodev-mnist** 存储桶,在那里我们可以看到检查点和模型本身。
让我们探索训练过程。使用 **TensorBoard**,我们将创建一个部署。
tb_name = "mnist-tensorboard" tb_deploy = f"""apiVersion: apps/v1 kind: Deployment metadata: labels: app: mnist-tensorboard name: {tb_name} namespace: {namespace} spec: selector: matchLabels: app: mnist-tensorboard template: metadata: labels: app: mnist-tensorboard version: v1 spec: serviceAccount: default-editor containers: - command: - /usr/local/bin/tensorboard - --logdir={model_dir} - --port=80 image: tensorflow/tensorflow:1.15.2-py3 env: - name: S3_ENDPOINT value: {s3_endpoint} - name: AWS_ENDPOINT_URL value: {minio_endpoint} - name: AWS_REGION value: {minio_region} - name: BUCKET_NAME value: {mnist_bucket} - name: S3_USE_HTTPS value: "0" - name: S3_VERIFY_SSL value: "0" - name: AWS_ACCESS_KEY_ID value: {minio_username} - name: AWS_SECRET_ACCESS_KEY value: {minio_key} name: tensorboard ports: - containerPort: 80 """ tb_service = f"""apiVersion: v1 kind: Service metadata: labels: app: mnist-tensorboard name: {tb_name} namespace: {namespace} spec: ports: - name: http-tb port: 80 targetPort: 80 selector: app: mnist-tensorboard type: ClusterIP """ tb_virtual_service = f"""apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: {tb_name} namespace: {namespace} spec: gateways: - kubeflow/kubeflow-gateway hosts: - '*' http: - match: - uri: prefix: /mnist/{namespace}/tensorboard/ rewrite: uri: / route: - destination: host: {tb_name}.{namespace}.svc.cluster.local port: number: 80 timeout: 300s """ tb_specs = [tb_deploy, tb_service, tb_virtual_service]
k8s_util.apply_k8s_specs(tb_specs, k8s_util.K8S_CREATE_OR_REPLACE)
现在让我们通过访问 http://localhost:8080/mnist/machine-learning/tensorboard/ 来探索 **TensorBoard**。
如您所见,训练过程简短且平淡无奇,但您学习了如何直接从 MinIO 读取训练数据。
最后,让我们部署此模型并稍作尝试。
deploy_name = "mnist-model" model_base_path = export_path # The web ui defaults to mnist-service so if you change it you will # need to change it in the UI as well to send predictions to the mode model_service = "mnist-service" deploy_spec = f"""apiVersion: apps/v1 kind: Deployment metadata: labels: app: mnist name: {deploy_name} namespace: {namespace} spec: selector: matchLabels: app: mnist-model template: metadata: # TODO(jlewi): Right now we disable the istio side car because otherwise ISTIO rbac will prevent the # UI from sending RPCs to the server. We should create an appropriate ISTIO rbac authorization # policy to allow traffic from the UI to the model servier. # https://istio.ac.cn/docs/concepts/security/#target-selectors annotations: sidecar.istio.io/inject: "false" labels: app: mnist-model version: v1 spec: serviceAccount: default-editor containers: - args: - --port=9000 - --rest_api_port=8500 - --model_name=mnist - --model_base_path={model_base_path} command: - /usr/bin/tensorflow_model_server env: - name: modelBasePath value: {model_base_path} - name: S3_ENDPOINT value: {s3_endpoint} - name: AWS_ENDPOINT_URL value: {minio_endpoint} - name: AWS_REGION value: {minio_region} - name: BUCKET_NAME value: {mnist_bucket} - name: S3_USE_HTTPS value: "0" - name: S3_VERIFY_SSL value: "0" - name: AWS_ACCESS_KEY_ID value: {minio_username} - name: AWS_SECRET_ACCESS_KEY value: {minio_key} image: tensorflow/serving:1.15.0 imagePullPolicy: IfNotPresent livenessProbe: initialDelaySeconds: 30 periodSeconds: 30 tcpSocket: port: 9000 name: mnist ports: - containerPort: 9000 - containerPort: 8500 resources: limits: cpu: "4" memory: 4Gi requests: cpu: "1" memory: 1Gi volumeMounts: - mountPath: /var/config/ name: model-config volumes: - configMap: name: {deploy_name} name: model-config """ service_spec = f"""apiVersion: v1 kind: Service metadata: annotations: prometheus.io/path: /monitoring/prometheus/metrics prometheus.io/port: "8500" prometheus.io/scrape: "true" labels: app: mnist-model name: {model_service} namespace: {namespace} spec: ports: - name: grpc-tf-serving port: 9000 targetPort: 9000 - name: http-tf-serving port: 8500 targetPort: 8500 selector: app: mnist-model type: ClusterIP """ monitoring_config = f"""kind: ConfigMap apiVersion: v1 metadata: name: {deploy_name} namespace: {namespace} data: monitoring_config.txt: |- prometheus_config: {{ enable: true, path: "/monitoring/prometheus/metrics" }} """ model_specs = [deploy_spec, service_spec, monitoring_config]
k8s_util.apply_k8s_specs(model_specs, k8s_util.K8S_CREATE_OR_REPLACE)
就是这样,但模型尚未提供服务。让我们部署一个示例 UI 以便我们可以对其进行操作。
ui_name = "mnist-ui" ui_deploy = f"""apiVersion: apps/v1 kind: Deployment metadata: name: {ui_name} namespace: {namespace} spec: replicas: 1 selector: matchLabels: app: mnist-web-ui template: metadata: labels: app: mnist-web-ui spec: containers: - image: gcr.io/kubeflow-examples/mnist/web-ui:v20190112-v0.2-142-g3b38225 name: web-ui ports: - containerPort: 5000 serviceAccount: default-editor """ ui_service = f"""apiVersion: v1 kind: Service metadata: annotations: name: {ui_name} namespace: {namespace} spec: ports: - name: http-mnist-ui port: 80 targetPort: 5000 selector: app: mnist-web-ui type: ClusterIP """ ui_virtual_service = f"""apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: {ui_name} namespace: {namespace} spec: gateways: - kubeflow/kubeflow-gateway hosts: - '*' http: - match: - uri: prefix: /mnist/{namespace}/ui/ rewrite: uri: / route: - destination: host: {ui_name}.{namespace}.svc.cluster.local port: number: 80 timeout: 300s """ ui_specs = [ui_deploy, ui_service, ui_virtual_service]
k8s_util.apply_k8s_specs(ui_specs, k8s_util.K8S_CREATE_OR_REPLACE)
现在访问 http://localhost:8080/mnist/machine-learning/ui/?ns=machine-learning ,您将看到一个漂亮的 UI,它与直接从 MinIO 提供服务的模型进行交互。
MinIO 赋能数据科学和数据运营无处不在 好了!我们完成了本大型指南的结尾,该指南解释了如何在 Azure Kubernetes Service 上设置 MinIO,然后部署 Kubeflow 以开箱即用地与 MinIO 协同工作。由于高度自动化,设置 AKS、MinIO 和 Kubeflow 的基础构建块是最简单的部分。这使您可以专注于更重要的任务,例如构建机器学习管道以在 Kubeflow 上平稳运行,利用直接来自 MinIO 的大型数据集,以及直接从对象存储中存储和部署模型。
下载 MinIO 开始使用,如果您有任何疑问,请加入我们的 Slack 频道 或发送邮件至 hello@min.io。我们随时为您提供帮助。