跳至主要内容

面向 MLOps 的 Jenkins:完整 CI/CD 教程

学习如何在 Windows 上安装 Jenkins,设置并运行 Jenkins 代理与流水线,并通过 Jenkins 流水线完成从模型训练到模型服务的 MLOps 项目构建。
· 11分钟 阅读

使用 AI 探索

Jenkins Tutorial for MLOps feature image

在本教程中,我们将学习如何安装和使用 Jenkins,以及如何创建代理和流水线并执行它们。

具体来说,我们将:

  1. 在 Windows 机器上顺利搭建 Jenkins。我们也提供其他操作系统的安装链接。
  2. 在 Jenkins 中创建并运行代理,用于执行我们的所有流水线。
  3. 构建并运行一个简单的流水线,演示如何自动化各个项目步骤。
  4. 使用 Jenkins 流水线开发一个 MLOps 项目,完成数据摄取、处理、模型训练、评估以及上线服务。

如果您需要快速复习,可学习简明的 MLOps Concepts 课程,了解如何将机器学习模型从 Jupyter 笔记本带到真正能在生产中创造业务价值的运行模型。

什么是 Jenkins?

Jenkins 是一款开源自动化服务器,通过促进持续集成(CI)与持续部署(CD),在机器学习开发流程中发挥重要作用。

Jenkins 由 Java 编写,可帮助自动化数据处理、训练、评估以及机器学习项目的部署,是实施 MLOps 的关键工具。

Jenkins 特性:

  • 易于安装: Jenkins 可轻松安装在 Windows、Linux、macOS、Docker 和 Kubernetes 上。
  • 便捷的设置与配置: 通过其 Web 界面即可轻松完成设置与配置,包含即时错误检查与内置帮助。
  • 丰富的插件生态:拥有数百个插件,可与 CI/CD 工具链中的几乎所有第三方工具集成。在功能与特性方面,它与 GitHub Actions 类似。
  • 分布式负载:Jenkins 可通过本地与云端代理在多台机器上分配工作,加速在多平台上的测试、训练与部署。

使用 Jenkins 进行任务自动化只是 MLOps 生态的一部分。您可以阅读2024 年值得了解的 25 款顶级 MLOps 工具 博文,了解用于实验跟踪、模型元数据管理、工作流编排、数据与流水线版本管理、模型部署与服务,以及生产环境中的模型监控等方面的工具。

在 Windows 11 上安装 Jenkins

我们可以轻松在 Linux 和 macOS 上安装 Jenkins。但在 Windows 上安装则需要几个步骤,包括安装 Java 开发工具包、设置本地安全策略、使用域用户安装 Jenkins,以及启动 Jenkins 服务器。

安装 OpenJDK

先从安装 Java 开始。前往 Adoptium 网站,下载适用于 Windows 11 的最新 LTS 版本。

为什么需要安装 OpenJDK?Jenkins 是基于 Java 的应用,运行时需要 Java 运行时环境(JRE)或 Java 开发工具包(JDK)。

Downloading the OpenJDK for Windows

图片来源: Adoptium

使用默认选项安装 OpenJDK,但请勾选“Set or override JAVA_HOME variable(设置或覆盖 JAVA_HOME 变量)”。

Installing the OpenJDK on Windows

安装完成后,可在终端中输入 java -version 检查是否安装成功:

Testing OpenJDK on a Windows terminal

为“作为服务登录”设置用户帐户

要安装 Jenkins,您需要修改“本地安全策略”,为安装程序启用用户登录权限。按下键盘上的 Win + R,输入“secpol.msc”并回车。然后依次进入“本地策略” > “用户权限分配” > “作为服务登录”。

Setting up the local security policy for Jenkins

将会打开一个新窗口,输入您的 Windows 用户名并点击“检查名称”按钮。之后点击“确定”并关闭“本地安全策略”窗口。

Checking for the user in Log on as a service

安装 Jenkins

前往 jenkins.io 网站,下载适用于 Windows 的 Jenkins 安装包。

Downloading Jenkins for Windows

图片来源:jenkins.io

当出现“以本地或域用户身份运行服务”的窗口时,输入您的 Windows 用户名和密码,然后点击“测试凭据”。如果通过校验,点击“下一步”。

Logging into Jenkins

其余保持默认并完成安装。设置可能需要几分钟。

Finalizing the Jenkins installation

启动 Jenkins 服务器

启动 Jenkins 服务器很简单。按下 Windows 键并搜索“服务”。在“服务”窗口中找到 Jenkins,点击顶部的播放按钮。

Starting the Jenkins server

默认情况下,Jenkins 运行在 https://localhost:8080/。将该 URL 粘贴到浏览器即可访问控制台。要进入 Jenkins 控制台,您需要输入管理员密码。

Entering the administrator password to unlock Jenkins

要获取默认管理员密码,请进入 Jenkins 目录,找到并打开 Jenkins.err.log 文件。

Jenkins file directory

向下滚动该错误日志文件,找到生成的密码。复制并粘贴到管理员密码输入框中。

Jenkins error log file

随后,服务器将花几分钟安装必需的工具与扩展。

Getting started with the Jenkins server

完成服务器设置后,系统会提示我们创建新用户。填写必要信息并点击“保存并继续”。

Creating the first admin user in Jenkins

接着将进入控制台,在这里我们可以创建、查看并运行各种 Jenkins 流水线。

Jenkins dashboard welcome window

在 Jenkins 中创建代理

代理(亦称节点)是为执行 Jenkins 主服务器分发的作业而配置的机器。这些代理提供运行流水线所需的环境与算力。Windows 11 代理默认可用,但我们也可以创建自定义选项的代理。

配置代理

在主控制台点击“管理 Jenkins”,然后点击如下所示的“设置代理”按钮。或者,您也可以点击“节点”按钮来创建和管理代理。

Setting up Jenkins agents

输入代理名称并选择“永久代理”类型。

Setting up Jenkins dashboard nodes

请确保为代理提供一个目录,用于保存所有文件和日志。添加一个标签,其余设置保持默认。创建流水线时,我们将使用该代理标签来运行任务。

Entering details to set up a Jenkins agent

在本机运行代理

点击“保存”后,会弹出提示,指导我们根据操作系统将相应命令复制并粘贴到终端中执行。

curl.exe -sO http://localhost:8080/jnlpJars/agent.jar & java -jar agent.jar -url http://localhost:8080/ -secret 1a30c62de92630dbcc1e2f19aaf482057e6170ced6835355447bc4ba4eefb76a -name "DC Agent 1" -webSocket -workDir "/home/Jenkins"

将命令粘贴到终端并运行后,我们会看到成功信息,指示代理(本文中为 DC Agent 1)已在后台运行。

Starting Jenkins agents server on the terminal

要检查代理是否正在运行并已就绪以执行作业,请返回 Jenkins 控制台,点击“管理 Jenkins”,再点击“节点”查看代理状态。

Verifing Jenkins agents server has started

创建一个简单的 Jenkins 流水线

Jenkins 流水线是一系列自动化步骤,帮助完成模型的训练、评估和部署。它使用简单的脚本定义这些流程,使项目工作流更易管理与自动化。

本节我们将创建一个示例 Jenkins 流水线,并使用新建的代理作为流水线执行器。

配置 Jenkins 流水线

在控制台点击“新建任务”,输入名称,选择“流水线”,然后点击“确定”。

Creating a Jenkins pipeline

接着会提示我们配置流水线。下拉到“流水线”区域,编写 Jenkins 流水线脚本。

Jenkins pipeline script

在 Jenkins 流水线脚本中,我们首先设置环境与代理。在本例中,我们通过提供之前定义的代理标签来设置 agent。

随后编写 stages 区段,将流水线的各步骤(Build、Test、Deploy)加入其中。本例中,我们仅使用 echo 打印,并通过 bat 运行终端命令。

  • bat 用于 Windows 11
  • sh 用于 Linux

就是这样,十分简单。以下是脚本代码:

pipeline {
    agent {
        label 'MLAgent' // Ensure this label matches your Windows 11 agent
    }

    stages {
        stage('Build') {
            steps {
                echo 'Building...'
                // Add your Windows build commands here
                bat 'echo Running build commands'
            }
        }
        stage('Test') {
            steps {
                echo 'Testing...'
                // Add your Windows test commands here
                bat 'echo Running test commands'
            }
        }
        stage('Deploy') {
            steps {
                echo 'Deploying...'
                // Add your Windows deploy commands here
                bat 'echo Running deploy commands'
            }
        }
    }
}

添加脚本后,点击“应用”和“保存”。

运行 Jenkins 流水线

点击“立即构建”以测试并运行流水线。要查看状态,点击“状态”按钮。

Building the Jenkins pipeline

运行完成后,我们可以在“状态”菜单中点击具体的运行来查看日志。

点击“上次构建(#2)”链接,再点击“控制台输出”。这会打开控制台输出窗口,我们可以在此查看流水线的日志与结果。

Jenkins pipeline output console

我们还可以点击“流水线控制台”,详细查看流水线中的每一步,包括输出、起止时间、流水线命令与日志。

Jenkins pipeline console

完成 Jenkins 的安装与设置后,创建并运行流水线变得既简单又迅速。我用不到一小时就理解了脚本并能自己编写。相比 GitHub Actions,我会使用 Jenkins,因为它为我提供了更大的灵活性。

Author's opinion

为 MLOps 项目创建 Jenkins 流水线

在完成对 Jenkins 的初步了解后,现在我们认真动手开展 MLOps 项目。

我们将创建两个流水线:第一个是 CI,它会加载并处理数据、训练模型、评估模型,然后测试模型服务。随后,它将触发 CD 流水线,启动模型推理服务。

若想深入了解流程,请学习 CI/CD for Machine Learning 课程,掌握如何简化机器学习开发流程。

项目文件

与其他项目一样,我们需要创建项目文件,包括数据加载、模型训练、模型评估和模型服务的 Python 文件。还需要一个 requirements .txt 文件来安装所需的 Python 包。

data_loading.py:

在该脚本中,我们将加载 scikit-learn 的葡萄酒数据集并转换为 pandas DataFrame。随后将数据集划分为训练集与测试集,最后将处理后的数据保存为 pickle 文件。

from sklearn.datasets import load_wine
from sklearn.model_selection import train_test_split
import pandas as pd
import joblib

def load_data():
    # Load the wine dataset
    wine = load_wine(as_frame=True)
    data = pd.DataFrame(data=wine.data, columns=wine.feature_names)
    data["target"] = wine.target
    print(data.head())
    return data

def split_data(data, target_column="target"):
    X = data.drop(columns=[target_column])
    y = data[target_column]
    X_train, X_test, y_train, y_test = train_test_split(
        X, y, test_size=0.2, random_state=42
    )
    return X_train, X_test, y_train, y_test

def save_preprocessed_data(X_train, X_test, y_train, y_test, file_path):
    joblib.dump((X_train, X_test, y_train, y_test), file_path)


if __name__ == "__main__":
    data = load_data()
    X_train, X_test, y_train, y_test = split_data(data)
    save_preprocessed_data(X_train, X_test, y_train, y_test, "preprocessed_data.pkl")

model_training.py:

在该文件中,我们将加载处理后的数据,训练随机森林分类器,并将模型保存为 pickle 文件。

from sklearn.ensemble import RandomForestClassifier
import joblib

def load_preprocessed_data(file_path):
    return joblib.load(file_path)

def train_model(X_train, y_train):
    model = RandomForestClassifier(n_estimators=100, random_state=42)
    model.fit(X_train, y_train)
    return model

def save_model(model, file_path):
    joblib.dump(model, file_path)


if __name__ == "__main__":
    X_train, X_test, y_train, y_test = load_preprocessed_data("preprocessed_data.pkl")

    model = train_model(X_train, y_train)
    save_model(model, "model.pkl")

model_evaluation.py:

为评估模型,我们将同时加载模型与预处理数据集,生成分类报告并打印准确率。

import joblib
from sklearn.metrics import accuracy_score, classification_report

def load_model(file_path):
    return joblib.load(file_path)

def load_preprocessed_data(file_path):
    return joblib.load(file_path)

def evaluate_model(model, X_test, y_test):
    predictions = model.predict(X_test)
    accuracy = accuracy_score(y_test, predictions)
    report = classification_report(y_test, predictions)
    return accuracy, report

if __name__ == "__main__":
    X_train, X_test, y_train, y_test = load_preprocessed_data("preprocessed_data.pkl")

    model = load_model("model.pkl")
    accuracy, report = evaluate_model(model, X_test, y_test)
    print(f"Model Accuracy: {accuracy}")
    print(f"Classification Report:\n{report}")

model_serving.py:

在模型服务部分,我们将使用 FastAPI 创建一个 REST API,用户可以输入特征并生成预测。通常标签较为通用。为更有趣,我们将葡萄酒类别改为 Verdante、Rubresco 和 Floralis。

运行该文件时,我们会启动 FastAPI 服务器,可通过 curl 命令或在 Python 中使用 requests 库进行访问。

from typing import List

import joblib
import uvicorn
from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

# Define the labels corresponding to the target classes
LABELS = [
    "Verdante",  # A vibrant and fresh wine, inspired by its balanced acidity and crisp flavors.
    "Rubresco",  # A rich and robust wine, named for its deep, ruby color and bold taste profile.
    "Floralis",  # A fragrant and elegant wine, known for its floral notes and smooth finish.
]

class Features(BaseModel):
    features: List[float]

def load_model(file_path):
    return joblib.load(file_path)

model = load_model("model.pkl")

@app.post("/predict")
def predict(features: Features):
    # Get the numerical prediction
    prediction_index = model.predict([features.features])[0]
    # Map the numerical prediction to the label
    prediction_label = LABELS[prediction_index]
    return {"prediction": prediction_label}

if __name__ == "__main__":
    uvicorn.run(app, host="127.0.0.1", port=9000)

requirements.txt:

该文件将帮助我们下载并安装运行上述 Python 文件所需的所有包。

scikit-learn
pandas
fastapi
uvicorn

主 MLOps(CI)流水线

现在我们来创建持续集成 Jenkins 流水线。与创建简单流水线的方法相同,我们将创建 “MLOps-pipe”,并编写从数据处理到模型评估的完整脚本。

Jenkins pipeline script

MLOps 流水线脚本包括:

  1. 使用 label “MLAgent” 初始化 agent。
  2. 设置工作区目录,即所有 Python 文件所在位置。
  3. 为机器学习流水线的每一步创建阶段:
    1. 安装所有必要的 Python 包(Initialize)。
    2. 处理已加载的数据集(Load and Preprocess Data)。
    3. 在处理后的数据集上训练模型(Train Model)。
    4. 评估模型并生成报告(Evaluate Model)。
    5. 使用 curl 命令测试模型服务(Test Serve Model)。
  4. 通过服务测试后,将触发另一条流水线用于部署服务器。下一节将介绍。
  5. 在 post 节点中,我们会处理数据并以 pickle 文件扩展名的形式将其保存为制品。

注意: start /B 命令会在后台启动一个新的终端窗口并运行模型服务脚本。此外,该 Jenkins 流水线脚本仅适用于 Windows,若在 Linux 或其他系统上运行,需要调整命令。

pipeline {
    agent { 
        node { 
            label 'MLAgent' 
            customWorkspace 'C:\\Repository\\GitHub\\MLOps-Jenkins' 
        } 
    }

    stages {
        stage('Initialize') {
            steps {
                script {
                    // Install necessary Python packages
                    bat "pip install -r requirements.txt"
                }
            }
        }

        stage('Load and Preprocess Data') {
            steps {
                script {
                    // Run data loading script
                    bat "python data_loading.py"
                }
            }
        }

        stage('Train Model') {
            steps {
                script {
                    // Run model training script
                    bat "python model_training.py"
                }
            }
        }

        stage('Evaluate Model') {
            steps {
                script {
                    // Run model evaluation script
                    bat "python model_evaluation.py"
                }
            }
        }

        stage('Serve Model') {
            steps {
                script {
                    // Start FastAPI server in the background
                    bat 'start /B python model_serving.py'
                    // Wait for the server to start
                    sleep time: 10, unit: 'SECONDS'
                }
            }
        }

        stage('Test Serve Model') {
            steps {
                script {
                    // Test the server with sample values
                    bat '''
                        curl -X POST "http://127.0.0.1:9000/predict" ^
                        -H "Content-Type: application/json" ^
                        -d "{\\"features\\": [13.2, 2.77, 2.51, 18.5, 103.0, 1.15, 2.61, 0.26, 1.46, 3.0, 1.05, 3.33, 820.0]}"
                    '''
                }
            }
        }

        stage('Deploy Model') {
            steps {
                script {
                    // Trigger another Jenkins job for model serving
                    build job: 'ModelServingPipeline', wait: false
                }
            }
        }
    }

    post {
        always {
            archiveArtifacts artifacts: '**.pkl', fingerprint: true
            echo 'Pipeline execution complete.'
        }
    }
}

模型服务(CD)流水线

现在我们将创建持续部署流水线,在本地部署并运行服务。

该流水线脚本很简单。我们先定义代理并切换到项目工作目录,然后无限期运行服务器。

注意: 在 Jenkins 中无限期运行服务器并不推荐,因为流水线应有明确的开始与结束。此示例中我们假定已完成部署,但在实践中更好是集成 Docker 并在 Docker 服务器上运行应用。

如果您希望学习如何以机器学习工程师的思维方式开展工作,建议学习 Developing Machine Learning Models for Production with an MLOps Mindset 课程,这将帮助您将机器学习模型训练、文档化、维护与扩展到最佳状态。

pipeline {
    agent { 
        node { 
            label 'MLAgent' 
            customWorkspace 'C:/Repository/GitHub/MLOps-Jenkins/' 
        } 
    }

    stages {
        stage('Start FastAPI Server') {
            steps {
                script {
                    // Start the FastAPI server
                    bat 'python model_serving.py'
                }
            }
        }
    }

}

运行 CI/CD 流水线

进入 “MLOps-pipe” 流水线并点击“立即构建”来启动 CI/CD 流水线。

流水线成功执行后,会生成两个制品:一个为模型,另一个为处理后的数据集。

Jenkins pipeline status

点击 Jenkins 控制台上的“阶段”按钮可可视化流水线并查看所有步骤。

Jenkins pipeline stages

要查看每一步的详细日志,请打开“流水线控制台”菜单。

Jenkins pipeline console

我们也可以检查 “ModelServingPipeline”,确认服务器正在本地 URL http://127.0.0.1:9000 上运行。

Jenkins pipeline output console

在浏览器中试用 FastAPI 应用

FastAPI 自带 Swagger UI,可通过在本地 URL 后添加 /docs 访问:http://127.0.0.1:9000/docs。

Swagger UI 允许我们在浏览器中测试 FastAPI 应用。界面简洁,我们可以看到模型服务运行正常。对样例数值的预测表明葡萄酒类型为 Rubresco。

Swager UI for ML application

完成实验后,请务必关闭模型服务器。您可以在 Jenkins 控制台的“状态”菜单中,点击如下所示的叉号按钮完成关闭:

Jenkins pipeline run status

本教程中的所有代码、数据集、模型和元文件,均可在 GitHub 代码库中获取以便您试用: kingabzpro/MLOps-with-Jenkins。

结语

Jenkins 是各类 MLOps 任务的优秀自动化服务器。与 GitHub Actions 相比,它提供了更多特性、更强的可控性以及更佳的隐私保护,因而是一个出色的替代方案。

Jenkins 最吸引人的地方之一在于其创建与执行流水线的简洁性,对新手与有经验的用户都十分友好。

如果您有兴趣用 GitHub Actions 探索类似能力,别错过 A Beginner's Guide to CI/CD for Machine Learning 教程。若希望深入理解 MLOps,Fully Automated MLOps 课程是不容错过的优质资源,提供关于 MLOps 架构、CI/CD/CM/CT 技术以及自动化范式的全面洞见,助您部署能够持续创造价值的机器学习系统。

Abid Ali Awan

作者

Abid Ali Awan

作为一名持证的数据科学家,我热衷于利用前沿技术打造创新的机器学习应用。凭借在语音识别、数据分析与报告、MLOps、对话式人工智能以及自然语言处理方面的扎实背景,我不断打磨构建智能系统的能力,力求带来切实影响。除技术专长外,我也擅长沟通,能够将复杂概念提炼为清晰、简明的表述。因此,我成为数据科学领域备受关注的博主,与不断壮大的数据专业人士社区分享洞见与实践经验。目前,我专注于内容创作与编辑,借助大语言模型打造有力且吸引人的内容,帮助企业与个人更好地发挥数据价值。

主题