使用 Elyra 在 JupyterLab 中运行 Kubeflow Pipelines

Elyra 允许你在 JupyterLab 中通过在画布上连接 notebook 来构建可视化 pipeline,然后将该 pipeline 提交到 Kubeflow Pipelines (KFP)。本指南将通过一个由两个 notebook 组成的“hello world” pipeline 演示整个过程,并展示如何在 KFP UI 中验证运行结果。

此工作流使用 KFP v2。每个 notebook 节点都会作为容器化的 pipeline 步骤执行,KFP 会将运行产物存储在与 S3 兼容的对象存储中,例如 Ceph Object Storage。

前提条件

开始之前,请确保以下平台前提条件已就绪:

  • 已安装 Alauda AI Workbench,并且你可以创建或打开一个 JupyterLab workbench。有关 workbench 创建步骤,请参见 创建 Workbench
  • 已部署 Kubeflow Base (kfbase) 和 Kubeflow Pipelines (kfp)。有关部署步骤,请参见 安装 Kubeflow Plugins
  • 你的 namespace 在 Kubeflow 中可见。如果在登录 Kubeflow 后 namespace 仍未显示,请按照 安装 Kubeflow Plugins 中的 namespace 绑定说明进行操作。
  • 已配置 KFP 对象存储。有关 namespace 侧 kfp-launcher 配置,请参见 使用 Kubeflow Pipelines
  • 你使用的 JupyterLab workbench 镜像包含 Elyra 和 KFP SDK 2.x,例如 创建 Workbench 中列出的 Standard Data Science Jupyter 镜像。
  • Elyra 运行时元数据已挂载到 workbench 中。运行时配置会告诉 Elyra 如何提交到 KFP,而运行时镜像元数据会告诉 Elyra 每个 pipeline 节点可以使用哪个镜像运行。
NOTE

code-server workbench 镜像不提供 Elyra 可视化 pipeline 编辑器。若要创建 Elyra pipeline,请使用 JupyterLab 镜像。

验证 namespace 的 KFP 运行时配置

在使用 Elyra 之前,请让平台管理员确认 workbench 所在的 namespace 已准备好用于 KFP v2 运行:

本指南不会重复介绍 KFP 安装和对象存储 manifest,而是重点介绍 JupyterLab 中的 Elyra 配置。

配置 Elyra 元数据挂载

本节通常由平台管理员完成。JupyterLab workbench 启动时,Elyra 会读取两个不同的元数据目录:

挂载路径用途推荐来源
/opt/app-root/runtimesKFP 运行时配置。该配置会向 Elyra 提供 KFP API endpoint、namespace、认证类型以及对象存储设置。在当前 Workbench 版本中临时使用 PVC。
/opt/app-root/pipeline-runtimesPipeline 运行时镜像元数据。该配置会告诉 Elyra 每个 pipeline 节点可用的容器镜像。通过 WorkspaceKind 挂载 namespace 级别的 ConfigMap。

需要使用 ..data 路径。Kubernetes 会为 ConfigMap 和 Secret 卷自动创建这种符号链接布局,JupyterLab 镜像也会按设计遵循该布局。当前针对 /opt/app-root/runtimes 的 PVC 方案也需要相同布局,因此在准备 PVC 时需要自行创建 ..data 目录。

在未来的 Workbench 版本中,Workspace 级别的 Secret 挂载将允许直接从 Secret 挂载 /opt/app-root/runtimes。等到该功能可用后,你就可以停止使用 PVC 方案来存放敏感的 Elyra 运行时配置。

挂载到 /opt/app-root/runtimes 的 KFP 运行时配置

当前 Workbench 版本不支持在 Workspace 中直接挂载 Secret。由于 Elyra 的 KFP 运行时配置可能包含对象存储凭据,请使用专用 PVC 作为临时方案,并将该 PVC 视为敏感数据。

PVC 中必须包含如下文件:

/opt/app-root/runtimes/
  ..data/
    mlops-kfp.json
  mlops-kfp.json -> ..data/mlops-kfp.json

请使用以下 mlops-kfp.json 作为模板。将 namespace、endpoint、bucket 和凭据替换为你自己的值:

mlops-kfp.json
{
  "display_name": "MLOps KFP",
  "metadata": {
    "runtime_type": "KUBEFLOW_PIPELINES",
    "description": "Kubeflow Pipelines runtime for the workbench namespace",
    "api_endpoint": "http://ml-pipeline.kubeflow.svc:8888",
    "user_namespace": "<your-namespace>",
    "engine": "Argo",
    "auth_type": "KUBERNETES_SERVICE_ACCOUNT_TOKEN",
    "cos_endpoint": "http://<ceph-rgw-service>.<ceph-namespace>.svc:7480",
    "cos_bucket": "<your-kfp-artifact-bucket>",
    "cos_auth_type": "KUBERNETES_SECRET",
    "cos_secret": "elyra-cos-credentials",
    "cos_username": "<object-storage-access-key>",
    "cos_password": "<object-storage-secret-key>",
    "tags": [
      "kfp",
      "mlops",
      "<your-namespace>"
    ],
    "public_api_endpoint": "https://<kubeflow-domain>/_/pipeline"
  },
  "schema_name": "kfp"
}

如果该 PVC 已经挂载到 workbench 中,你可以在 JupyterLab 终端中创建该文件:

mkdir -p /opt/app-root/runtimes/..data
cat > /opt/app-root/runtimes/..data/mlops-kfp.json <<'EOF'
{
  "display_name": "MLOps KFP",
  "metadata": {
    "runtime_type": "KUBEFLOW_PIPELINES",
    "description": "Kubeflow Pipelines runtime for the workbench namespace",
    "api_endpoint": "http://ml-pipeline.kubeflow.svc:8888",
    "user_namespace": "<your-namespace>",
    "engine": "Argo",
    "auth_type": "KUBERNETES_SERVICE_ACCOUNT_TOKEN",
    "cos_endpoint": "http://<ceph-rgw-service>.<ceph-namespace>.svc:7480",
    "cos_bucket": "<your-kfp-artifact-bucket>",
    "cos_auth_type": "KUBERNETES_SECRET",
    "cos_secret": "elyra-cos-credentials",
    "cos_username": "<object-storage-access-key>",
    "cos_password": "<object-storage-secret-key>",
    "tags": ["kfp", "mlops", "<your-namespace>"],
    "public_api_endpoint": "https://<kubeflow-domain>/_/pipeline"
  },
  "schema_name": "kfp"
}
EOF
ln -sf ..data/mlops-kfp.json /opt/app-root/runtimes/mlops-kfp.json

将 PVC 挂载到现有 Workspace 中。以下片段是一个 patch,而不是完整的 manifest:spec.kindspec.podTemplate.optionsWorkspace 上的必填字段,因此单独应用此 YAML 会被拒绝。请改为 patch 现有对象:

kubectl patch workspace <workspace-name> -n <your-namespace> --type=merge -p '
spec:
  podTemplate:
    volumes:
      data:
        - mountPath: /opt/app-root/runtimes
          pvcName: <elyra-runtime-config-pvc>
          readOnly: false
'

请将 <workspace-name><your-namespace><elyra-runtime-config-pvc> 替换为环境中的实际值。

创建或更新文件后,请重启 workbench,以便 Elyra 加载运行时配置。

挂载到 /opt/app-root/pipeline-runtimes 的 Pipeline 运行时镜像元数据

Pipeline 运行时镜像元数据不是用户特定的。namespace 级别的 ConfigMap 更适合这一用途,并且可以通过 WorkspaceKind 挂载,这样该类型下的所有 JupyterLab workspaces 都会获得相同的运行时镜像列表。

在每个运行 Elyra pipeline 的 namespace 中创建一个名为 pipeline-runtime-images 的 ConfigMap:

pipeline-runtime-images.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: pipeline-runtime-images
  namespace: <your-namespace>
data:
  odh-pipeline-runtime-minimal-cpu-py312-ubi9.json: |
    {
      "schema_name": "runtime-image",
      "display_name": "Runtime | Minimal | CPU | Python 3.12",
      "metadata": {
        "description": "Minimal runtime image for Elyra pipeline nodes.",
        "image_name": "docker.io/alaudadockerhub/odh-pipeline-runtime-minimal-cpu-py312-ubi9:<tag>",
        "pull_policy": "IfNotPresent",
        "tags": ["kfp", "minimal", "cpu", "python-3.12", "ubi9"]
      }
    }
  odh-pipeline-runtime-datascience-cpu-py312-ubi9.json: |
    {
      "schema_name": "runtime-image",
      "display_name": "Runtime | Data Science | CPU | Python 3.12",
      "metadata": {
        "description": "Data science runtime image for Elyra pipeline nodes.",
        "image_name": "docker.io/alaudadockerhub/odh-pipeline-runtime-datascience-cpu-py312-ubi9:<tag>",
        "pull_policy": "IfNotPresent",
        "tags": ["kfp", "datascience", "cpu", "python-3.12", "ubi9"]
      }
    }
  odh-pipeline-runtime-tensorflow-cuda-py312-ubi9.json: |
    {
      "schema_name": "runtime-image",
      "display_name": "Runtime | TensorFlow | CUDA | Python 3.12",
      "metadata": {
        "description": "TensorFlow CUDA runtime image for Elyra pipeline nodes.",
        "image_name": "docker.io/alaudadockerhub/odh-pipeline-runtime-tensorflow-cuda-py312-ubi9:<tag>",
        "pull_policy": "IfNotPresent",
        "tags": ["kfp", "tensorflow", "cuda", "python-3.12", "ubi9"]
      }
    }
  odh-pipeline-runtime-pytorch-cuda-py312-ubi9.json: |
    {
      "schema_name": "runtime-image",
      "display_name": "Runtime | PyTorch | CUDA | Python 3.12",
      "metadata": {
        "description": "PyTorch CUDA runtime image for Elyra pipeline nodes.",
        "image_name": "docker.io/alaudadockerhub/odh-pipeline-runtime-pytorch-cuda-py312-ubi9:<tag>",
        "pull_policy": "IfNotPresent",
        "tags": ["kfp", "pytorch", "cuda", "python-3.12", "ubi9"]
      }
    }
  odh-pipeline-runtime-pytorch-llmcompressor-cuda-py312-ubi9.json: |
    {
      "schema_name": "runtime-image",
      "display_name": "Runtime | PyTorch LLM Compressor | CUDA | Python 3.12",
      "metadata": {
        "description": "PyTorch and LLM Compressor CUDA runtime image for Elyra pipeline nodes.",
        "image_name": "docker.io/alaudadockerhub/odh-pipeline-runtime-pytorch-llmcompressor-cuda-py312-ubi9:<tag>",
        "pull_policy": "IfNotPresent",
        "tags": ["kfp", "pytorch", "llmcompressor", "cuda", "python-3.12", "ubi9"]
      }
    }

如果你的集群从私有镜像仓库镜像源拉取镜像,请将 docker.io/alaudadockerhub/...:<tag> 替换为镜像仓库中的地址。

通过 JupyterLab WorkspaceKind 挂载该 ConfigMap:

apiVersion: kubeflow.org/v1beta1
kind: WorkspaceKind
metadata:
  name: <jupyterlab-workspacekind-name>
spec:
  podTemplate:
    extraVolumeMounts:
      - name: pipeline-runtime-images
        mountPath: /opt/app-root/pipeline-runtimes
    extraVolumes:
      - name: pipeline-runtime-images
        configMap:
          name: pipeline-runtime-images
          optional: true

ConfigMap 挂载会自动创建 Kubernetes 原子卷布局。在 pod 内部,Elyra 会看到如下文件:

/opt/app-root/pipeline-runtimes/
  ..data -> ..2026_...
  odh-pipeline-runtime-minimal-cpu-py312-ubi9.json -> ..data/odh-pipeline-runtime-minimal-cpu-py312-ubi9.json

可用的 Elyra Pipeline 运行时镜像

Elyra pipeline 运行时镜像发布在 Docker Hub 上的 alaudadockerhub 下。它们与 JupyterLab workbench 镜像不同:workbench 镜像运行编写界面,而这些运行时镜像用于运行单个 KFP pipeline 节点。

创建 Workbench 中介绍的其他 workbench 镜像一样,这些 Docker Hub 地址属于公开源镜像。在私有环境或离线环境中,请先将所需的 pipeline 运行时镜像同步到你内部的镜像仓库,然后更新 pipeline-runtime-images ConfigMap 中的 metadata.image_name 字段,使其指向内部镜像仓库地址。

下表介绍了五个常用的运行时镜像。包列表仅作参考,基于镜像构建仓库中 runtimes/ 下对应的源目录。

运行时镜像描述主要包
Minimal CPU
alaudadockerhub/odh-pipeline-runtime-minimal-cpu-py312-ubi9
适用于轻量级 Python notebook 节点和简单的控制流步骤。Python 3.12
Elyra notebook 执行依赖,例如 papermillnbclientnbconvertnbformatipykernel
minio 客户端
requests
Data Science CPU
alaudadockerhub/odh-pipeline-runtime-datascience-cpu-py312-ubi9
适用于通用的基于 CPU 的数据处理和 ML pipeline 节点。Python 3.12
NumPy
pandas 2.3.3
SciPy 1.16.x
scikit-learn 1.8.0
Matplotlib 3.10.x
Plotly 6.5.2
CodeFlare SDK 0.35.x
Feast 0.60.x
TensorFlow CUDA
alaudadockerhub/odh-pipeline-runtime-tensorflow-cuda-py312-ubi9
适用于 NVIDIA GPU 节点上的 TensorFlow pipeline 节点。Python 3.12
CUDA 基础镜像
TensorFlow 2.20.x
TensorBoard 2.20.x
数据科学运行时依赖
PyTorch CUDA
alaudadockerhub/odh-pipeline-runtime-pytorch-cuda-py312-ubi9
适用于 NVIDIA GPU 节点上的 PyTorch pipeline 节点。Python 3.12
CUDA 基础镜像
PyTorch 2.9.1
torchvision 0.24.1
TensorBoard 2.20.x
数据科学运行时依赖
PyTorch LLM Compressor CUDA
alaudadockerhub/odh-pipeline-runtime-pytorch-llmcompressor-cuda-py312-ubi9
适用于 NVIDIA GPU 节点上的 LLM 压缩和评估 pipeline 节点。Python 3.12
CUDA 基础镜像
PyTorch 2.9.1
torchvision 0.24.1
LLM Compressor 0.9.0.2
transformers 4.57.3
datasets 4.4.1
accelerate 1.12.0
compressed-tensors 0.13.0
lm-eval 0.4.x

打开 JupyterLab

  1. 登录 Alauda AI。
  2. 进入 Workbench
  3. 打开一个现有的 JupyterLab workbench,或使用包含 Elyra 的 JupyterLab 镜像创建一个新的 workbench。
  4. 等待 workbench 状态变为 Running
  5. 单击 Connect 打开 JupyterLab。

JupyterLab 打开后,你可以选择在 JupyterLab 终端中验证 KFP SDK 版本:

python -c "import kfp; print(kfp.__version__)"

版本应为 2.x

创建两个演示 notebook

这个 hello world pipeline 使用你直接在 JupyterLab 中创建的三个文件:

文件用途
01-hello.ipynb第一个 notebook 节点。它会打印一条消息并完成执行。
02-world.ipynb第二个 notebook 节点。只有在第一个 notebook 成功后才会运行。
hello-two-nodes.pipeline连接这两个 notebook 的 Elyra pipeline 画布。

在 JupyterLab 文件浏览器中,创建一个名为 hello-two-nodes 的文件夹。

在该文件夹中,创建第一个名为 01-hello.ipynb 的 notebook,并添加以下单元:

print("hello from the first Elyra node")
message = "first notebook completed"
print(message)

创建第二个名为 02-world.ipynb 的 notebook,并添加以下单元:

print("hello from the second Elyra node")
print("this notebook runs after the first notebook succeeds")

在 JupyterLab 中分别运行这两个 notebook 一次,以确认它们可以在本地执行且没有语法错误。

在 Elyra 中创建 pipeline

JupyterLab 打开后,请等待主启动器页面和 Elyra 面板加载完成。在基于浏览器的验证中,JupyterLab 有时需要几秒钟之后,Pipeline EditorRuntime Images 面板才可用。

打开 Pipeline Editor

  1. 在 JupyterLab 文件浏览器中打开 hello-two-nodes 文件夹。
  2. 打开 Launcher 选项卡。如果 launcher 不可见,请单击 File > New Launcher
  3. 单击 Pipeline Editor。JupyterLab 会打开一个新的未命名 Elyra pipeline 画布。
  4. 将空 pipeline 另存为 hello-two-nodes.pipeline,并保存在 hello-two-nodes 文件夹中。

添加 Notebook 节点

  1. 01-hello.ipynb 从 JupyterLab 文件浏览器拖到 pipeline 画布上。
  2. 02-world.ipynb 从文件浏览器拖到同一画布上。
  3. 将节点从左到右排列,便于阅读执行顺序。
  4. 01-hello.ipynb 的输出端口连接到 02-world.ipynb 的输入端口。该边表示依赖关系,使第二个 notebook 只会在第一个 notebook 成功后启动。

配置节点属性

  1. 单击 01-hello.ipynb 节点。

  2. 打开节点属性面板。根据你的 JupyterLab 布局,属性面板可能显示在画布右侧,或者通过节点上下文菜单打开。

  3. 确认节点文件指向 01-hello.ipynb

  4. Runtime Image 设置为适用于你平台的 Elyra pipeline 运行时镜像之一,例如:

    Runtime | Minimal | CPU | Python 3.12
  5. 如果该节点有 CPU、memory、GPU、环境变量、输入文件或输出文件字段,请在此 hello world 示例中保留默认值。

  6. 单击 02-world.ipynb 节点,并重复相同的 Runtime Image 设置。

  7. 再次保存 pipeline 文件。

TIP

对于这个 hello world 示例,连接仅表示执行依赖。如果你的第二个 notebook 必须读取第一个 notebook 创建的文件,请在 Elyra 中配置节点文件依赖和输出,以便文件通过 pipeline 产物存储进行传递。

将 pipeline 提交到 KFP

  1. 在 Elyra pipeline editor 中,单击 Run Pipeline
  2. 在运行对话框中,将 Runtime Platform 设置为 Kubeflow Pipelines
  3. 选择管理员提供的 KFP v2 运行时配置,例如 MLOps KFP
  4. 输入 pipeline 名称,例如 hello-two-nodes
  5. 输入运行名称,或保留自动生成的运行名称。
  6. 选择一个现有 experiment,或创建一个新的 experiment,例如 elyra-demo
  7. 检查两个 notebook 节点的运行时镜像值。
  8. 单击 OKSubmit

提交成功后,Elyra 会显示类似 Job submission to Pipelines succeeded 的对话框。

NOTE

Elyra 是用于 pipeline 编写和提交的 UI。提交之后,它不会在 JupyterLab 画布中提供持久的运行历史视图。请使用 Kubeflow Pipelines 查看执行状态。

在 Kubeflow Pipelines 中验证运行结果

  1. 打开 Kubeflow UI。

  2. 选择你的 workbench 所在的相同 namespace。

  3. 进入 Pipelines > Runs

  4. 打开 hello-two-nodes 的最新运行记录。

  5. Graph 选项卡中,确认两个 notebook 节点按顺序显示。

  6. 单击每个节点并查看日志。你应该会看到:

    hello from the first Elyra node
    hello from the second Elyra node
  7. 等待运行状态变为 Succeeded

根据你的 Kubeflow route,运行详情 URL 可能类似以下其中一种:

https://<kubeflow-domain>/_/pipeline/#/runs/details/<run-id>
https://<kubeflow-domain>/_/pipeline/?ns=<your-namespace>#/runs/details/<run-id>

你也可以检查该 namespace 中的 Kubernetes 资源:

kubectl get pod -n <your-namespace>
kubectl get workflow -n <your-namespace>

对于 KFP v2,运行中出现 driver 和 implementation pods 是正常的。用户 notebook 代码在 implementation 容器中运行。

故障排查

namespace 在 Kubeflow 中不可见

该 namespace 必须关联到 Kubeflow Profile,并且用户必须绑定到该 namespace。请按照 安装 Kubeflow Plugins 中的 namespace 绑定步骤进行操作。

Elyra 未显示 KFP 运行时配置

请使用包含 Elyra 的 JupyterLab workbench 镜像。然后请管理员确认 Elyra 运行时元数据已挂载到 workbench 中,并且该运行时指向你的 KFP endpoint 和对象存储。

Elyra 中没有可用的运行时镜像

请管理员创建或更新 pipeline-runtime-images ConfigMap,并通过 WorkspaceKind 将其挂载到 /opt/app-root/pipeline-runtimes。修改 ConfigMap 后请重启 workbench。

在 notebook 代码开始执行前运行失败

检查 namespace 侧的 KFP 对象:

kubectl get secret -n <your-namespace> mlpipeline-minio-artifact
kubectl get configmap -n <your-namespace> kfp-launcher
kubectl get configmap -n <your-namespace> metadata-grpc-configmap

同时检查该 namespace 中失败 pod 的日志。与存储相关的失败通常指向对象存储 endpoint、bucket、凭据或 kfp-launcher 配置。与元数据上报相关的失败通常指向 metadata-grpc-configmap 或 metadata gRPC 服务。

Elyra 的成功对话框已关闭

直接打开 Kubeflow Pipelines,并检查同一 namespace 下的 Runs。Elyra 在成功对话框关闭后不会保留持久的运行链接。