相关技术概述
本章涵盖 熟悉使用 TensorFlow 构建模型 了解 Kubernetes 的关键术语 使用 Kubeflow 运行分布式机器学习工作负载 使用 Argo Workflows 部署云原生工作流
在上一章中,我们了解了项目背景和系统组件,以及每个组件的具体实现策略。 我们还讨论了每个组件所面临的挑战,以及解决这些挑战所采用的模式。 如前面所述,我们将在本书的最后一章(第九章)中深入探讨项目的实施细节。 然而,由于我们将在项目中使用不同的技术,而这些技术并不能涵盖所有的基础知识,因此在本章中,将介绍 4 种技术的基本概念(Tensor-Flow、Kubernetes、Kubeflow 和 Argo)及其具体实践。 这 4 种技术都有不同的用途,但都将用于实现第九章中的最终项目。 TensorFlow 用于数据处理、模型构建和评估。 Kubernetes 用于构建核心分布式基础设施。 在此基础上,Kubeflow 用于向 Kubernetes 集群提交分布式模型训练作业,Argo Workflows 用于构建和提交端到端的机器学习工作流。
8.1 TensorFlow:机器学习框架 TensorFlow 是一个端到端的机器学习平台。 它在学术界和工业界被广泛应用于不同的应用和使用案例,例如:图像分类、推荐系统、自然语言处理等。 TensorFlow 具有高度可移植性,可部署在不同的硬件上,并且支持多语言。 TensorFlow 拥有庞大的生态系统。 以下是该生态系统中的一些重点项目: TensorFlow.js 是一个用于 JavaScript 机器学习的库。用户可以直接在浏览器或 Node.js 中使用。 TensorFlow Lite 是一个用于在移动设备、微处理器和其他边缘设备上部署模型的库。 TFX 是一个用于生产环境中部署机器学习流水线的端到端平台。 TensorFlow Serving 是一种灵活、高性能的模型服务系统,专门用于生产环境。 TensorFlow Hub 是一个经过训练的机器学习模型的仓库,可随时进行微调并在任何地方部署。 只需几行代码即可使用 BERT 和 Faster R-CNN 等经过训练的模型。 更多信息可以在 TensorFlow 的 GitHub 仓库 (https://github.com/tensorflow) 中找到。 我们将在模型服务组件中使用 TensorFlow Serving。 在下一节中,我们将介绍 TensorFlow 中的一些基本示例,使用 MNIST 数据集在本地训练一个机器学习模型。
8.1.1 基础知识 我们首先在 Python 3 环境中安装 Anaconda 作为基本示例。 Anaconda(https://www.anaconda.com)是一个用于科学计算的 Python 和 R 编程语言的发行版,旨在简化包管理和部署。 该发行版包括适用于 Windows、Linux 和 macOS 等平台的科学计算包。 Anaconda 安装完成后,我们在控制台中使用以下命令来安装带有 Python 3.9 的 Conda 环境。
代码 8-1 创建 Conda 环境
接下来,我们可以使用以下代码激活该环境。
代码 8-2 激活 Conda 环境
然后,我们就可以在这个 Python 环境中安装 TensorFlow 了。
代码 8-3 安装 TensorFlow
如果安装遇到问题,请参考安装指南(https://www.tensorflow.org/install)。 在某些情况下,您可能需要卸载现有的 NumPy 并重新安装。
代码 8-4 安装 NumPy
如果您使用的是 Mac,可以使用 Metal 插件进行加速 (https://developer.apple.com/metal/tensorflow-plugin/)。 成功安装 TensorFlow 后,我们就可以开始运行一个基本的图像分类示例。 首先加载并预处理简单 MNIST 数据集。 回想一下,MNIST 数据集包含了从 0 到 9 的手写数字的图像,每一行代表一个特定的手写数字图像,如图 8-1 所示。
图 8-1 从 0 到 9 的手写数字的示例图像,其中每行代表一个特定的手写数字图像
每一行代表一个特定的手写数字图像。例如,第一行代表数字 0 的图像。
Keras API (tf.keras) 是 TensorFlow 中用于模型训练的高级 API,我们将用它来加载内置数据集以及进行模型训练和评估。
代码 8-5 加载 MNIST 数据集
如果我们不指定路径,函数 load_data() 将使用默认路径来加载 MNIST 数据集。 该函数将返回 NumPy 数组,用于训练和测试图像和标签。 我们将数据集分为训练集和测试集,以便我们可以在示例中进行模型训练和评估。
NumPy 数组是 Python 科学计算生态系统中的常见数据类型。 它描述了多维数组,具有 3 个属性:data、shape 和 dtype。以图像训练为例。
代码 8-6 检查数据集
x_train 是一个 60,000 × 28 × 28 的三维数组。数据类型为 uint8,范围从 0 到 255。 换句话说,该对象包含了 60,000 张分辨率为 28 × 28 的灰度图像。
接下来,我们可以对原始图像进行一些特征预处理。 由于许多算法和模型对特征的规模敏感,因此我们经常将特征居中并缩放到一个范围内,例如 [0, 1] 或 [-1, 1]。 在我们的例子中,可以通过将图像除以 255 来实现。
代码 8-7 预处理函数
对训练和测试集中的图像进行预处理后,我们可以实例化一个简单的多层神经网络模型。我们使用 tf.keras 来定义模型架构。 首先,我们使用 Flatten 将二维图像展开为一维数组,将输入 shape 指定为 28 × 28。 第二层是全连接层的,使用 ReLU 激活函数引入非线性拟合能力。 第三层是 Dropout 层,用于减少过度拟合并使模型具有更强的泛化性。 由于手写数字由 0 到 9 的 10 个不同数字组成,因此在最后一层中通过 softmax 激活函数对图像做 10 分类。
代码 8-8 顺序模型定义
模型架构定义完成后,我们需要指定 3 个不同的参数:评估函数、损失函数和优化器。
代码 8-9 使用指定的评估函数、损失函数和优化器进行模型编译
然后,我们可以开始进行 5 轮迭代的模型训练和评估,具体代码如下所示。
代码 8-10 使用训练数据进行模型训练
我们能够在日志中看到训练进度:
模型评估的日志如下所示:
我们观察到,随着训练过程中损失的减少,训练数据的准确率上升到 97.8%。 最终训练的模型在测试数据集上的准确率为 97.6%。 由于建模过程的随机性,您得到的结果可能会略有不同。
在训练完模型并对其性能感到满意之后,我们可以使用以下代码保存它,下次就不需要从头开始训练了。
代码 8-11 保存训练好的模型
这段代码将模型保存为当前工作目录中的 my_model.h5 文件。 当我们启动一个新的 Python 会话时,我们可以导入 TensorFlow 并从 my_model.h5 文件加载模型对象。
代码 8-12 加载保存的模型
我们学习了如何使用 TensorFlow 的 Keras API 为一组超参数训练模型。 这些超参数在训练过程中保持不变,并直接影响机器学习程序的性能。 让我们学习如何使用 Keras Tuner (https://keras.io/keras_tuner/) 调整 TensorFlow 程序的超参数。 首先,需要安装 Keras Tuner 库。
代码 8-13 安装 Keras Tuner 包
安装完成后,就能够导入所有需要的库。
代码 8-14 导入需要的包
我们将在示例中使用相同的 MNIST 数据集和预处理函数进行超参数调整。 然后将模型定义封装到 Python 函数中。
代码 8-15 使用 TensorFlow 和 Keras Tuner 的模型构建函数
该代码本质上与我们之前使用一组超参数训练模型的代码基本相同,只是我们还定义了在全链接层和优化器中使用的 hp_units 和 hp_learning_rate 对象。
hp_units 对象实例化一个整数,该整数在 32 到 512 之间进行调整,并用作第一个全连接层中的单元数。 hp_learning_rate 对象将调整 adam 优化器的学习率,该学习率将从以下值中选择:0.01、0.001 或 0.0001。
一旦定义了模型构造器,我们就可以实例化调节器(tuner)。 我们可以使用多种调节算法(例如:随机搜索、贝叶斯优化、Hyperband)。 这里我们使用 hyperband 调节算法。 它使用自适应资源分配和提前终止来实现在高性能模型中更快地收敛。
代码 8-16 Hyperband 模型调节器
我们以验证准确度为目标,模型调优时最大 epoch 数为 10。
为了减少过度拟合,我们可以创建一个 EarlyStopping 回调函数,以便在模型达到验证集的损失阈值时立即停止训练。 如果您启动了新的 Python 会话,请确保将数据集重新加载到内存中。
代码 8-17 EarlyStopping 回调函数
现在我们通过 tuner.search() 开始超参数搜索。
代码 8-18 带有提前终止参数的超参数搜索
搜索完成后,我们可以确定最优的超参数并在数据集上进行 30 次迭代训练。
代码 8-19 获取最优超参数并训练模型
当我们根据测试数据评估模型时,我们会发现它比没有经过超参数调整的基线模型性能更优。
代码 8-20 根据测试数据评估模型
您已经学习了如何在单台机器上本地运行 TensorFlow。 为了充分利用 TensorFlow,模型训练过程应该在分布式集群中运行,这就是 Kubernetes 发挥作用的地方。 在下一节中,我将介绍 Kubernetes 的基础知识和实践示例。
8.1.2 练习 1 能否直接使用之前保存的模型进行模型评估? 2 能否尝试使用随机搜索算法来代替 Hyperband 调节算法?
8.2 Kubernetes:分布式容器编排系统 Kubernetes(也称为 K8s)是一个用于自动化容器应用部署、扩展和管理的开源系统。 它抽象了复杂的容器管理,并提供声明式配置来编排不同环境中的容器。
为了便于管理和发现,容器被分组到特定应用程序的逻辑单元中。 Kubernetes 基于 Google 在生产工作负载方面超过 16 年的经验所建立,并结合了社区的最佳实践和思路。 其主要设计目标是使复杂分布式系统的部署和管理变得容易,同时受益于容器所带来的资源利用率提升。 它的代码是开源的,使得社区可以自由地在私有云、混合云或公有云上构建基础设施,用户可以轻松地对工作负载进行迁移。
Kubernetes 的设计初衷是在不扩大运维团队规模的情况下对应用进行扩展。 图 8-2 是 Kubernetes 及其组件的架构图。 但我们不会详细讨论这些组件,因为它们不是本书的重点。 我们将使用 Kubernetes 的命令行工具 kubectl(图的左侧)与 Kubernetes 集群交互并获取相关信息。
图 8-2 Kubernetes 的架构图
我们将通过一些基本概念和示例来积累相关知识,并为接下来介绍 Kubeflow 和 Argo 工作流的部分做准备。
8.2.1 基础知识 首先,我们创建一个本地 Kubernetes 集群。我们将使用 k3d (https://k3d.io) 来初始化本地集群。 k3d 是一个轻量级的封装程序,用于在 Docker 中运行 k3s(由 Rancher Lab 提供的一个最小 Kubernetes 发行版)。 k3d 可以非常轻松地在 Docker 中创建具有单节点或多节点的 k3s 集群,方便我们进行 Kubernetes 集群的本地开发。 让我们通过 k3s 创建一个名为 distml 的 Kubernetes 集群。
代码 8-21 创建一个本地 Kubernetes 集群
我们可以通过以下命令获取我们创建的集群的节点列表。
代码 8-22 获取集群中的节点列表
在这个示例中,节点是 1 分钟前创建的,我们运行的是 v1.25.3+k3s1 版本的 k3s 发行版。 状态已准备就绪,我们可以继续执行后续步骤。
我们还可以通过 kubectl describe node k3d-distmlserver-0 来查看节点的详细信息。 例如,labels 和 system info 字段包含了操作系统版本、架构、该节点是否为主节点等信息:
节点 IP 地址信息:
节点的容量也能够显示,该字段表示该节点计算资源的大小:
然后我们将在这个集群中创建一个名为 basics 的命名空间。 Kubernetes 中的命名空间提供了一种在单个集群中实现资源隔离的机制(参考 http://mng.bz/BmN1)。 资源名称在同一个命名空间内必须是唯一的,但跨命名空间的资源名不需要保证唯一。 以下示例将在同一个命名空间中演示。
代码 8-23 创建一个新的命名空间
集群和命名空间创建好后,我们将使用一个名为 kubectx 的命令行工具来帮助我们在命名空间和集群之间快速切换 (https://github.com/ahmetb/kubectx)。 需要注意的是,日常使用 Kubernetes 时此工具不是必须的,但它能够让用户更方便地使用 Kubernetes。 例如,我们可以方便地获得可用的集群和命名空间的列表。
代码 8-24 切换上下文和命名空间
例如,我们可以通过 k3d-distml 上下文和我们刚刚使用以下命令创建的 basics 命名空间切换到 distml 集群。
代码 8-25 激活使用上下文
在使用多个集群和命名空间时,通常需要切换上下文和命名空间。 我们正在使用 basics 命名空间来运行本章的示例,我们将在下一章中切换到项目专用的另一个命名空间。
接下来,我们创建一个 Kubernetes Pod。 Pod 是可以在 Kubernetes 中创建和管理的最小可部署计算单元。 一个 Pod 可能由一个或多个具有共享存储和网络资源的容器组成,并规定了如何运行这些容器。 Pod 中的一组容器作为一个整体被统一调度,并在一个共享的上下文中运行。 Pod 的概念模拟了一种特定应用程序的"逻辑主机",这意味着它包含一个或多个相对紧密耦合的应用程序容器。 在非云环境中,在同一物理机或虚拟机上执行的应用程序类似于在同一逻辑主机上执行的云应用程序。 换句话说,Pod 类似于一组具有共享命名空间和共享文件系统卷的容器。
以下代码提供了一个 Pod 示例,该 Pod 包含了一个运行 whalesay 镜像的容器,用于打印 hello world 消息。 我们将该 Pod 配置保存在一个名为 hello-world.yaml 的文件中。
代码 8-26 一个 Pod 示例
运行以下命令创建 Pod。
代码 8-27 在集群中创建示例 Pod
然后我们可以通过获取 Pod 列表来确定 Pod 是否已创建。 注意,命令中的 pods 是复数形式的,这样我们就可以获得已创建 Pod 的完整列表。 然后我们将获取某个特定 Pod 的详细信息。
代码 8-28 获取集群中的 Pod 列表
Pod 状态为"已完成"(Completed),因此我们可以查看 whalesay 容器打印的内容。
代码 8-29 检查 Pod 日志
我们还可以通过 kubectl 获取 Pod 的原始 YAML。请注意,我们在这里使用 -o yaml 参数来获取 YAML 格式的数据,其他格式也是同样支持的,例如 JSON。 如前面所提到的,我们获取的是单个 Pod 的详细信息,而不是所有 Pod 的完整列表,。
代码 8-30 获取 Pod 的 YAML 原始数据
你可能会对创建的 Pod 的 YAML 中出现的附加内容(例如:status 和 conditions 字段)感到奇怪。 这些附加内容是 Kubernetes 服务端组件自动生成和添加的,以便客户端应用程序能够获取到 Pod 的当前状态。 尽管我们没有明确指定命名空间,但 Pod 是在 basics 命名空间中创建的,因为我们使用 kubens 命令设置了当前的命名空间。
至此,我们已经介绍了 Kubernetes 的基础知识。 在下一节中,我们将学习如何使用 Kubeflow 在我们刚刚创建的本地 Kubernetes 集群中运行分布式模型训练任务。
8.2.2 练习 1 如何获取 JSON 格式的 Pod 信息? 2 Pod 可以包含多个容器吗?
8.3 Kubeflow:在 Kubernetes 上运行机器学习工作负载 Kubeflow 项目致力于使 Kubernetes 上的机器学习工作流部署变得简单、可移植和可扩展。 Kubeflow 的目标不是重建服务,而是提供一种简单的方法,将机器学习系统部署在不同的基础设施上。只要运行了 Kubernetes,就能够运行 Kubeflow。 我们将使用 Kubeflow 将分布式机器学习模型训练任务提交到 Kubernetes 集群中。
我们先来看看 Kubeflow 提供了哪些组件,如图 8-3 所示。
图 8-3 Kubeflow 的主要组件
Kubeflow Pipelines(KFP;https://github.com/kubeflow/pipelines)提供了 Python SDK 来简化机器学习流水线的构建。 它是一个使用 Docker 容器构建和部署可移植、可扩展的机器学习工作流的平台。 KFP 的主要目标是实现以下功能: 端到端编排机器学习工作流 通过可重用组件和管道实现流水线的组合 轻松管理、跟踪和可视化流水线的定义、运行、实验和制品 通过缓存减少冗余操作,有效利用计算资源 通过平台中立的 IR YAML 流水线定义实现跨平台流水线的移植 KFP 使用 Argo Workflows 作为后端工作流引擎,将在下一节中介绍,我们将直接使用 Argo Workflows,而不使用像 KFP 这样更高级的封装工具。 ML Metadata 项目已合并到 KFP 中,并作为记录用 KFP 编写的机器学习工作流中生成的元数据的后端日志服务。
接下来是 Katib (https://github.com/kubeflow/katib)。 Katib 是一个用于自动化机器学习的 Kubernetes 原生项目。 Katib 支持超参数调调优、提前终止和神经网络架构搜索。 Katib 不感知具体的机器学习框架。 它可以调整任何语言编写的应用程序的超参数,并原生支持许多机器学习框架,例如:TensorFlow、Apache MXNet、PyTorch 和 XGBoost 等。 Katib 可以使用任何 Kubernetes 自定义资源执行训练任务,并为 Kubeflow Training Operator、Argo Workflows、Tekton Pipelines 等组件提供开箱即用的支持。图 8-4 是执行实验跟踪的 Katib UI 的截图。
图 8-4 执行实验跟踪的 Katib UI 的屏幕截图
Here we can visualize different training and validation accuracies for different set of hyperparameters. 在这里,我们能够可视化不同超参数集的训练和验证精度。 This provides a summary of the trials and highlights the best parameters. 这展示了试验的摘要并突出显示了最佳参数。
KServe (https://github.com/kserve/kserve) 作为 Kubeflow 项目的一部分,之前被称为 KFServing。 KServe 提供了 Kubernetes 自定义资源定义 (CRD),用于在任意机器学习框架上提供模型服务。 它旨在通过为常见的机器学习框架提供高性能接口,满足生产环境中模型服务的使用需求。 它封装了自动弹性伸缩、网络、健康检查和服务器配置等功能,为机器学习的部署带来了 GPU 自动弹性伸缩和金丝雀部署等前沿的服务特性。 图 8-5 是一个说明 KServe 在生态系统中定位的图表。
图 8-5 KServe 在生态系统中的定位
Kubeflow 提供了 Web UI 页面。图 8-6 展示了该 UI 的截图。 用户可以在左侧的每个选项卡中访问模型、流水线、实验、制品等功能,以方便进行端到端模型机生命周期的迭代。 Web UI 集成了 Jupyter Notebooks。还支持不同语言的 SDK ,可以帮助用户在 Kubeflow 中与集成任何内部系统。 此外,用户可以通过 kubectl 与所有 Kubeflow 组件进行交互,因为它们都是原生的 Kubernetes 自定义资源和控制器。 Training Operator(https://github.com/kubeflow/training-operator)提供了 Kubernetes 自定义资源,用户可以轻松地在 Kubernetes 上运行分布式和非分布式的 TensorFlow、PyTorch、Apache MXNet、XGBoost 或 MPI 作业。 Kubeflow 项目已累积拥有超过 500 名贡献者和 20,000 个 GitHub star。 它在多家公司中得到了广泛的采用,包括 10 多家云厂商,如: Amazon AWS、Azure、Google Cloud、IBM 等。 目前有 7 个工作组独立维护 Kubeflow 生态系统中不同的子项目。 我们将使用 Training Operator 提交分布式模型训练作业,并使用 KServe 来构建我们的模型服务组件。 完成下一章后,建议您在需要时自行尝试 Kubeflow 生态系统中的其他子项目。 例如,如果您想调整模型的性能,可以使用 Katib 的自动化机器学习和超参数调优功能。
图 8-6 Kubeflow UI 的截图
Users can access the models, pipelines, experiments, artifacts, etc., to facilitate the iterative process of the end-to-end model machine life cycle. 用户可以访问模型、流水线、实验、制品等,方便进行端到端模型生命周期的迭代。
8.3.1 基础知识 接下来,我们将更深入地研究 Kubeflow 的分布式 Training Operator,并往上一节中创建的 Kubernetes 本地集群中提交一个本地运行的分布式模型训练作业。 首先,在我们之前创建的集群中新建一个专用的 kubeflow 命名空间。
代码 8-31 创建并切换到新的命名空间
然后,我们返回到项目文件夹来安装我们需要的所有工具。
代码 8-32 安装所有工具
我们已经在此清单文件夹中打包好了所有必要的工具: Kubeflow Training Operator,我们将在本章中使用它来进行分布式模型训练。 Argo Workflows (https://github.com/argoproj/argo-workflows),我们将在第九章中讨论工作流编排,并将所有组件链接到机器学习流水线中。现在我们可以暂时忽略 Argo Workflows。 如前所述,Kubeflow Training Operator 提供了 Kubernetes 自定义资源,用户可以轻松地在 Kubernetes 上运行分布式和非分布式的 TensorFlow、PyTorch、Apache MXNet、XGBoost 或 MPI 作业。
在深入研究 Kubeflow 之前,我们需要了解什么是自定义资源。 自定义资源是 Kubernetes API 的扩展,不一定在默认的 Kubernetes 安装中就可用。 自定义资源所代表的是对特定 Kubernetes 安装的一种定制。 不过,很多 Kubernetes 核心功能现在都用定制资源来实现,这使得 Kubernetes 更加模块化(http://mng.bz/lWw2)。
定制资源可以通过动态注册的方式在运行中的集群内或出现或消失,集群管理员可以独立于集群更新定制资源。 一旦某定制资源被安装,用户可以使用 kubectl 来创建和访问其中的对象,就像他们为 Pod 这种内置资源所做的一样。 例如,以下代码定义了 TFJob 自定义资源,它允许我们实例化分布式 TensorFlow 训练作业并将其提交到 Kubernetes 集群中。
代码 8-33 TFJob 自定义资源
所有实例化的 TFJob 自定义资源对象 (tfjobs) 将由 Training Operator 处理。 以下代码定义了如何部署 Training Operator,该 Operator 运行了状态控制器,以持续监听和处理提交的 tfjobs 对象。
代码 8-34 部署 Training Operator
通过这种抽象,数据科学团队可以专注于在 TensorFlow 中编写 Python 代码(这些代码将用作 TFJob 规范的一部分),而不需要自己管理基础设施。 现在,我们可以跳过底层细节并使用 TFJob 来实现我们的分布式模型训练。 接下来,让我们在名为 tfjob.yaml 的文件中定义 TFJob。
代码 8-35 TFJob 定义示例
在此规范中,我们向控制器提交了一个带有 2 个工作副本的分布式 TensorFlow 模型训练模型,其中每个工作副本遵循相同的容器定义,运行 MNIST 图像分类示例。
定义完成后,我们可以通过以下命令将其提交到本地的 Kubernetes 集群。
代码 8-36 提交 TFJob
我们可以通过获取 TFJob 列表来查看 TFJob 是否提交成功。
代码 8-37 获取 TFJob 列表
当获取 Pod 列表时,我们可以看到已创建并开始运行的两个 Pod:distributed-tfjob-qc8fhworker-1 和 distribution-tfjob-qc8fh-worker-0。 其他 Pod 可以忽略,因为它们是运行 Kubeflow 和 Argo Workflow Operator 的 Pod。
代码 8-38 获取 Pod 列表
一个机器学习系统由许多不同的组件组成。 我们仅使用 Kubeflow 提交分布式模型训练作业,但还没有与其他组件连接。 在下一节中,我们将探索 Argo Workflows 的基本功能,在单个工作流中连接不同的步骤,使它们可以按特定顺序执行。
8.3.2 练习 1 如果模型训练需要参数服务器,你能用 TFJob 来描述吗?
8.4 Argo Workflows:容器原生工作流引擎 Argo 项目是一套用于在 Kubernetes 上部署和运行应用程序和工作负载的开源工具套件。 它扩展了 Kubernetes API,并在应用程序部署、容器编排、事件自动化、灰度交付等方面解锁了强大功能。 它由 4 个核心项目组成:Argo CD、Argo Rollouts、Argo Events 和 Argo Workflows。 除了这些核心项目之外,许多其他生态系统项目都基于 Argo、扩展 Argo 或与 Argo 协作。 与 Argo 相关的完整资源列表可以在 https://github.com/terrytangyuan/awesome-argo 中找到。
Argo CD 是 Kubernetes 的声明式 GitOps 应用程序交付工具。 它在 Git 中声明式地管理应用程序定义、配置和环境。 Argo CD 使 Kubernetes 应用程序部署和生命周期管理自动化、可审计且易于理解。 它配备了一个 UI 界面,因此工程师可以清楚看到集群中发生了什么,并监控应用程序的部署情况。 图 8-7 是 Argo CD UI 界面中资源树的截图。
Argo Rollouts 是一个 Kubernetes 控制器和一组自定义资源,提供了灰度部署能力。 它向 Kubernetes 集群引入了蓝绿和金丝雀部署、金丝雀分析、实验和灰度交付功能。
图 8-7 Argo CD UI 界面中资源树的截图
接下来是 Argo Events,一个基于事件的 Kubernetes 依赖管理器。 它可以定义来自各种事件源(例如:webhooks、Amazon S3、计划和流)的多个依赖项,并在成功解决事件依赖项后触发 Kubernetes 对象。 可用事件源的完整列表如图 8-8 所示。
最后,Argo Workflows 是一个容器原生工作流引擎,用于编排并行任务,以 Kubernetes 自定义资源的形式实现。 用户可以定义工作流,其中每个步骤都是一个单独的容器,将多步骤工作流建模为任务序列或使用图来描述任务之间的依赖关系,并运行用于机器学习或数据处理的计算密集型任务。 用户经常将 Argo Workflows 与 Argo Events 一起配合使用来触发基于事件的工作流。 Argo Workflows 主要用于是机器学习流水线、数据处理、ETL(提取、转换、加载)、基础设施自动化、持续交付和集成等场景。
Argo Workflows 还提供了命令行工具 (CLI)、服务器、UI 界面和不同语言的 SDK 接口。 CLI 命令行工具对于管理工作流以及通过命令行执行提交、暂停和删除工作流等操作非常有用。 服务器用于与其他服务集成。 有 REST 和 gRPC 两种服务接口。 UI 界面对于管理和可视化工作流以及工作流创建的任何制品、日志以及查看其他信息(例如:资源使用情况分析)非常有用。 我们将介绍一些 Argo Workflows 示例来为我们的项目做准备。
图 8-8 Argo Events 中可用的事件源
在查看一些示例之前,我们先确保已经有 Argo Workflows UI 界面。 它是可选的,虽然用户仍然可以在命令行中通过 kubectl 直接与 Kubernetes 交互,但在 UI 界面中能将有向无环图(DAG)可视化以及直观地使用其他功能可以提升用户体验。 默认情况下,Argo Workflows UI 界面服务不会暴露外部 IP 提供公开访问。 要访问 UI 界面,请使用以下代码中的方法。
代码 8-39 端口转发 Argo 服务器
接下来,访问以下 URL 来访问 UI 界面:https://localhost:2746。 或者,您可以暴露一个负载均衡器的外部 IP 来访问本地集群中的 Argo Workflows UI 界面。 更多详细信息,请查看官方文档:https://argoproj.github.io/argo-workflows/argo-server/。 图 8-9 是 Map-Reduce 风格工作流的 Argo Workflows UI 界面的截图。
图 8-9 描述 Map-Reduce 风格工作流的 Argo Workflows UI 界面的截图
以下代码是 Argo 工作流的基本 hello world 示例。 我们可以为这个工作流指定容器镜像和启动命令,并打印出 hello world 消息。
代码 8-40 Hello world 示例
让我们继续将工作流提交到我们的集群。
代码 8-41 提交工作流
然后我们可以检查工作流是否提交成功并开始运行。
代码 8-42 获取工作流列表
一旦工作流状态变为成功(Succeeded),我们就可以检查工作流创建的 Pod 状态。首先,让我们找到与工作流关联的所有 Pod。 我们可以使用标签选择器来获取 Pod 列表。
代码 8-43 获取属于该工作流的 Pod 列表
一旦我们知道了 Pod 名称,我们就可以获取该 Pod 的日志。
代码 8-44 检查 Pod 日志
正如预期的那样,我们得到的日志与前几节中 Kubernetes Pod 的日志相同,因为这个工作流仅运行一个了 hello world 步骤。
下一个示例使用资源模板,您可以在其中指定将由工作流提交到 Kubernetes 集群的 Kubernetes 自定义资源。
在这里,我们使用简单的键值对创建一个名为 cm-example 的 Kubernetes ConfigMap 配置。 ConfigMap 是 Kubernetes 原生对象,用于存储键值对。
代码 8-45 资源模板
这个示例适用于 Python 用户,用户可以将 Python 脚本作为模板定义的一部分。 我们可以使用内置的 random Python module 生成一些随机数。 或者,您可以在容器模板内指定脚本的执行逻辑,而无需编写内联 Python 代码,如 hello world 示例中所示。
代码 8-46 脚本模板
让我们提交它。
代码 8-47 提交脚本模板工作流
现在,检查它的日志,看看是否生成了随机数。
代码 8-48 检查 Pod 日志
到目前为止,我们只看到了单步工作流的示例。 Argo Workflow 还允许用户通过指定每个任务的依赖关系,将工作流定义为有向无环图。 使用有向无环图可以更简单地维护复杂的工作流,并在运行任务时允许最大程度的并行。
让我们看一下 Argo Workflows 创建的菱形有向无环图的示例。 该图由 4 个步骤(A、B、C 和 D)组成,每个之间步骤都有依赖关系。 例如,步骤 C 依赖于步骤 A,步骤 D 依赖于步骤 B 和 C。
代码 8-49 使用菱形有向无环图的示例
让我们提交它。
工作流完成后,我们将看到每个步骤都有 4 个 Pod,其中每个步骤都会打印出其步骤名称:A、B、C 和 D。
代码 8-51 获取属于该工作流的 Pod 列表
在 Argo Workflows UI 界面中可以看到有向无环图的示意图。 我们能更直观地看到工作流是如何在 UI 界面中以菱形流程执行的,如图 8-10 所示。
图 8-10 UI 界面中菱形工作流的截图
接下来,我们将通过一个简单的抛硬币示例来展示 Argo Workflows 提供的条件语法。 我们可以指定一个条件来决定是否要运行下一步。 例如,我们首先运行翻转硬币步骤,这是我们之前看到的 Python 脚本,如果结果返回正面,我们运行名为 heads 的模板,它会打印结果为正面的日志。否则,我们打印出结果为反面的日志。 因此,我们可以在不同步骤的 when 语句中指定这些条件。
代码 8-52 抛硬币示例
让我们提交它。
代码 8-53 抛硬币工作流的提交示例
图 8-11 是这个翻转硬币工作流在 UI 界面中的截图。
图 8-11 UI 界面中翻转硬币工作流的截图
当我们获取工作流列表时,我们只看到 2 个 Pod。
代码 8-54 获取属于该工作流的 Pod 列表
我们可以检查翻转硬币步骤的日志,看看它是否打印出结果为反面,因为下一个待执行的步骤是 tails 步骤:
我们刚刚学习了 Argo Workflows 的基本语法,它是我们学习下一章的前提条件。 在下一章中,我们将使用 Argo Workflows 来实现端到端机器学习工作流,该工作流由第七章中介绍的系统组件组成。
8.4.2 练习 1 除了使用 {{steps.flip-coin.outputs.result}} 访问每个步骤的输出,还有哪些其他可用的变量? 2 能否通过 Git 提交或通过其他事件来自动触发工作流?
8.5 习题答案 第 8.1 节 1 可以,通过以下代码实现: model = tf.keras.models.load_model(‘my_model.h5’); modele.evaluate(x_test, y_test) 2 可以,将调节器更改为 kt.RandomSearch(model_builder) 即可。
第 8.2 节 1 kubectl get pod -o json 2 可以,除了现有的单个容器外,您还可以在 pod.spec.containers 中定义多个容器。
第 8.3 节 1 与工作副本类似,在 TFJob 规范中定义参数服务器副本以指定参数服务器的数量。
第 8.4 节 1 完整的列表可在此处获取:http://mng.bz/d1Do。 2 可以,您可以使用 Argo Events 来监控 Git 事件并触发工作流。
总结 我们使用 TensorFlow 在单台机器上通过 MNIST 数据集训练了一个机器学习模型。 我们学习了 Kubernetes 的基本概念,并在本地搭建和运行 Kubernetes 集群。 我们通过 Kubeflow 向 Kubernetes 提交分布式模型训练任务。 我们了解了不同类型的模板,以及如何使用 Argo Workflows 定义有向无环图或顺序步骤。