使用 Elyra 从 JupyterLab 运行 Kubeflow Pipelines

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

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

前提条件

在开始之前,请确保以下平台前提条件已满足:

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

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

验证 Namespace 的 KFP Runtime 配置

在使用 Elyra 之前,请让平台管理员确认 workbench namespace 已为 KFP v2 运行准备就绪:

本指南不重复介绍 KFP 的安装和 object-storage manifests,而是重点说明 JupyterLab 内的 Elyra 配置。

配置 Elyra Metadata 挂载

此部分通常由平台管理员完成。JupyterLab workbench 启动时,Elyra 会读取两个不同的 metadata 目录:

挂载路径用途推荐来源
/opt/app-root/runtimesKFP runtime 配置。该配置会告诉 Elyra KFP API endpoint、namespace、认证类型以及 object-storage 设置。在当前 Workbench 版本中临时使用 PVC。
/opt/app-root/pipeline-runtimesPipeline runtime image metadata。该配置会告诉 Elyra 每个 pipeline node 可用的容器镜像。使用通过 WorkspaceKind 挂载的 namespace ConfigMap。

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

在未来的 Workbench 版本中,将支持通过 Workspace 级别的 Secret 挂载直接从 Secret 挂载 /opt/app-root/runtimes。届时,你可以停止对敏感 Elyra runtime 配置使用 PVC 临时方案。

挂载到 /opt/app-root/runtimes 的 KFP Runtime 配置

当前 Workbench 版本不支持在 Workspace 中直接挂载 Secret。由于 Elyra 的 KFP runtime 配置可能包含 object-storage 凭据,因此请使用专用 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 terminal 中创建该文件:

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:

apiVersion: kubeflow.org/v1beta1
kind: Workspace
metadata:
  name: <workspace-name>
  namespace: <your-namespace>
spec:
  podTemplate:
    volumes:
      data:
        - mountPath: /opt/app-root/runtimes
          pvcName: <elyra-runtime-config-pvc>
          readOnly: false

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

挂载到 /opt/app-root/pipeline-runtimes 的 Pipeline Runtime Image Metadata

Pipeline runtime image metadata 不具有用户特定性。namespace ConfigMap 更适合这一用途,并且可以通过 WorkspaceKind 进行挂载,从而使该类型下所有 JupyterLab workspaces 都获得相同的 runtime image 列表。

在每个运行 Elyra pipelines 的 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"]
      }
    }

如果你的集群从私有 registry mirror 拉取镜像,请将 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 atomic volume 布局。在 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 Runtime Images

Elyra pipeline runtime images 发布在 Docker Hub 上的 alaudadockerhub 下。它们与 JupyterLab workbench 镜像不同:workbench 镜像负责运行编排 UI,而这些 runtime images 负责运行单个 KFP pipeline node。

创建 Workbench 中描述的额外 workbench 镜像一样,这些 Docker Hub 地址都是公开的 source images。在私有环境或离线环境中,请先将所需的 pipeline runtime images 同步到内部镜像仓库,然后更新 pipeline-runtime-images ConfigMap 中的 metadata.image_name 字段,使其指向内部仓库地址。

下表描述了五种常用的 runtime image。包列表仅为代表性示例,基于镜像构建仓库中 runtimes/ 下对应的源目录。

Runtime image描述主要包
Minimal CPU
alaudadockerhub/odh-pipeline-runtime-minimal-cpu-py312-ubi9
适用于轻量级 Python notebook node 和简单的控制流步骤。Python 3.12
Elyra notebook 执行依赖,例如 papermillnbclientnbconvertnbformatipykernel
minio 客户端
requests
Data Science CPU
alaudadockerhub/odh-pipeline-runtime-datascience-cpu-py312-ubi9
适用于通用的基于 CPU 的数据处理和 ML pipeline node。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 node 上的 TensorFlow pipeline node。Python 3.12
CUDA base image
TensorFlow 2.20.x
TensorBoard 2.20.x
数据科学 runtime 依赖
PyTorch CUDA
alaudadockerhub/odh-pipeline-runtime-pytorch-cuda-py312-ubi9
适用于 NVIDIA GPU node 上的 PyTorch pipeline node。Python 3.12
CUDA base image
PyTorch 2.9.1
torchvision 0.24.1
TensorBoard 2.20.x
数据科学 runtime 依赖
PyTorch LLM Compressor CUDA
alaudadockerhub/odh-pipeline-runtime-pytorch-llmcompressor-cuda-py312-ubi9
适用于 NVIDIA GPU node 上的 LLM 压缩和评估 pipeline node。Python 3.12
CUDA base image
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 terminal 中验证 KFP SDK 版本:

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

版本应为 2.x

创建两个演示 Notebooks

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

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

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

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

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

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

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

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

在 Elyra 中创建 Pipeline

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

打开 Pipeline Editor

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

添加 Notebook Node

  1. 01-hello.ipynb 从 JupyterLab 文件浏览器拖到 pipeline canvas 上。
  2. 02-world.ipynb 从文件浏览器拖到同一个 canvas 上。
  3. 将这些 node 从左到右排列,以便更容易阅读执行顺序。
  4. 01-hello.ipynb 的输出端口连接到 02-world.ipynb 的输入端口。该边表示依赖关系,使第二个 notebook 仅在第一个 notebook 成功后才启动。

配置 Node 属性

  1. 单击 01-hello.ipynb node。

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

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

  4. Runtime Image 设置为平台已准备好的 Elyra pipeline runtime images 之一,例如:

    Runtime | Minimal | CPU | Python 3.12
  5. 如果该 node 具有 CPU、memory、GPU、环境变量、输入文件或输出文件字段,在这个 hello world 示例中保持默认值即可。

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

  7. 再次保存 pipeline 文件。

TIP

对于这个 hello world 示例,连接关系仅表示执行依赖。如果你的第二个 notebook 必须使用第一个 notebook 创建的文件,请在 Elyra 中配置 node 文件依赖和输出,使文件通过 pipeline artifact store 传输。

将 Pipeline 提交到 KFP

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

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

NOTE

Elyra 是一个 pipeline 编排和提交 UI。提交后,它不会在 JupyterLab canvas 内提供持久化的 run history 视图。请使用 Kubeflow Pipelines 查看执行状态。

在 Kubeflow Pipelines 中验证运行结果

  1. 打开 Kubeflow UI。

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

  3. 进入 Pipelines > Runs

  4. 打开 hello-two-nodes 的最新 run。

  5. Graph 选项卡中,确认这两个 notebook node 按顺序出现。

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

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

根据你的 Kubeflow route,run 详情 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,run 中出现 driver pod 和 implementation pod 是正常的。用户 notebook 代码在 implementation 容器中运行。

故障排查

该 namespace 在 Kubeflow 中不可见

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

Elyra 未显示 KFP runtime 配置

请使用包含 Elyra 的 JupyterLab workbench 镜像。然后请管理员确认 Elyra runtime metadata 已挂载到 workbench,并且 runtime 指向你的 KFP endpoint 和 object storage。

Elyra 中不可用 runtime image

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

run 在 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 的日志。与存储相关的失败通常指向 object-storage endpoint、bucket、凭据或 kfp-launcher 配置。metadata 上报失败通常指向 metadata-grpc-configmap 或 metadata gRPC service。

Elyra 的成功对话框已关闭

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