Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions e2e/cases/c11_qwen3_06b_mindspore.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,24 @@ set -euo pipefail
HERE="$(cd "$(dirname "$0")" && pwd)"
source "${HERE}/../lib.sh"

require_env NPU_NAMESPACE "namespace for NPU e2e resources"
require_env NPU_RESOURCE_NAME "extended resource name for one NPU, for example huawei.com/Ascend910B4"
NS="${NPU_NAMESPACE}"
JOB_NAME="c11-mindspore-cann-smoke-$(printf '%05x' $$)"
IMAGE="${C11_IMAGE:-docker.io/alaudadockerhub/alauda-workbench-jupyter-mindspore-cann-py312-ubi9:v0.1.7}"
IMAGE_PULL_SECRET="${C11_IMAGE_PULL_SECRET:-${E2E_IMAGE_PULL_SECRET:-}}"
NPU_RESOURCE_VALUE="${NPU_RESOURCE_VALUE:-1}"
NPU_MEMORY_RESOURCE_NAME="${NPU_MEMORY_RESOURCE_NAME:-}"
NPU_MEMORY_RESOURCE_VALUE="${NPU_MEMORY_RESOURCE_VALUE:-8192}"
NPU_RUNTIME_CLASS="${NPU_RUNTIME_CLASS:-}"

cleanup() {
npu_kc -n "${NS}" delete job "${JOB_NAME}" --ignore-not-found --wait=false || true
}
trap cleanup EXIT

log "C11: submitting Job ${JOB_NAME} (image=${IMAGE})"
cat <<YAML | retry_create npu_kc >/dev/null
cat <<YAML | mirror_dockerhub "${NPU_DH_MIRROR}" | retry_create npu_kc >/dev/null
apiVersion: batch/v1
kind: Job
metadata:
Expand All @@ -35,7 +42,8 @@ spec:
labels: { e2e.alauda.io/case: c11 }
spec:
restartPolicy: Never
runtimeClassName: ascend
$(yaml_scalar_field 6 runtimeClassName "${NPU_RUNTIME_CLASS}")
$(yaml_image_pull_secrets 6 "${IMAGE_PULL_SECRET}")
securityContext: { runAsNonRoot: true, runAsUser: 1001, runAsGroup: 0, fsGroup: 1000 }
containers:
- name: probe
Expand All @@ -51,7 +59,8 @@ spec:
limits:
cpu: "2"
memory: 8Gi
huawei.com/Ascend910: "1"
$(yaml_resource_limit 14 "${NPU_RESOURCE_NAME}" "${NPU_RESOURCE_VALUE}")
$(yaml_resource_limit 14 "${NPU_MEMORY_RESOURCE_NAME}" "${NPU_MEMORY_RESOURCE_VALUE}")
command: [bash, -lc]
args:
- |
Expand Down
26 changes: 15 additions & 11 deletions e2e/cases/c12_kueue_preemption.sh
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ set -euo pipefail
HERE="$(cd "$(dirname "$0")" && pwd)"
source "${HERE}/../lib.sh"

require_env GPU_NAMESPACE "namespace for GPU e2e resources"
NS="${GPU_NAMESPACE}"
RUN_ID="$(printf '%05x' $$)-$(date -u +%s)"

Expand All @@ -40,13 +41,16 @@ RUNTIME="c12-checkpoint-runtime-${RUN_ID}"
PVC_NAME="c12-ckpt-${RUN_ID}"
TRAIN_LABEL="e2e.alauda.io/c12-run=${RUN_ID}"

# Same harbor image C5/C6 already use — guaranteed cached on the GPU node.
IMAGE="${LF_IMAGE:-build-harbor.alauda.cn/mlops/llamafactory0.9-cu126-amd64:v0.1.0-build.20260603021903}"
IMAGE="${LF_IMAGE:-docker.io/alaudadockerhub/llamafactory0.9-cu126-amd64:v0.1.0}"
IMAGE_PULL_SECRET="${LF_IMAGE_PULL_SECRET:-${E2E_IMAGE_PULL_SECRET:-}}"
RWX_STORAGE_CLASS="${C12_RWX_STORAGE_CLASS:-${E2E_RWX_STORAGE_CLASS:-}}"
NODE_SELECTOR_KEY="${C12_NODE_SELECTOR_KEY:-${E2E_GPU_NODE_SELECTOR_KEY:-}}"
NODE_SELECTOR_VALUE="${C12_NODE_SELECTOR_VALUE:-${E2E_GPU_NODE_SELECTOR_VALUE:-}}"

# --- Preflight: Kueue must be installed; skip otherwise. -----------------
if ! gpu_kc api-resources --api-group=kueue.x-k8s.io 2>/dev/null | grep -q clusterqueues; then
log "C12: kueue.x-k8s.io API group not present — install Kueue (see preemptible-trainjobs-with-kueue.mdx) and re-run"
exit 77
exit "${E2E_SKIP_RC}"
fi

cleanup() {
Expand All @@ -63,7 +67,7 @@ cleanup() {
trap cleanup EXIT

# --- Step 1: cohort + queues + priorities. -------------------------------
# Quotas are sized for one Tesla P100 + HAMI (gpucores 50%, 4 GiB GPU mem):
# Quotas are sized for one fractional HAMI GPU slice:
# both inference and training Workloads each ask for that slice, so the
# inference reclaim path empties exactly one borrowed training slot.
log "C12: applying cohort (CQ ${CQ_INF}, ${CQ_TRAIN}; flavor ${FLAVOR})"
Expand Down Expand Up @@ -142,7 +146,7 @@ apiVersion: v1
kind: PersistentVolumeClaim
metadata: { name: ${PVC_NAME}, namespace: ${NS} }
spec:
storageClassName: cephfs
$(yaml_storage_class 2 "${RWX_STORAGE_CLASS}")
accessModes: ["ReadWriteMany"]
resources: { requests: { storage: 1Gi } }
YAML
Expand All @@ -153,7 +157,7 @@ YAML
# elsewhere. HuggingFace Trainer auto-resumes from the newest
# checkpoint-N/ in CKPT_DIR if we pass it to .train(resume_from_checkpoint=).
log "C12: applying TrainingRuntime ${RUNTIME}"
cat <<YAML | retry_apply gpu_kc
cat <<YAML | mirror_dockerhub "${GPU_DH_MIRROR}" | retry_apply gpu_kc
apiVersion: trainer.kubeflow.org/v1alpha1
kind: TrainingRuntime
metadata:
Expand All @@ -176,8 +180,8 @@ spec:
template:
spec:
terminationGracePeriodSeconds: 60
nodeSelector: { kubernetes.io/hostname: 192.168.138.15 }
imagePullSecrets: [{ name: harbor-mlops-regcred }]
$(yaml_node_selector 18 "${NODE_SELECTOR_KEY}" "${NODE_SELECTOR_VALUE}")
$(yaml_image_pull_secrets 18 "${IMAGE_PULL_SECRET}")
securityContext: { runAsNonRoot: true, runAsUser: 65534, runAsGroup: 65534, fsGroup: 65534 }
volumes:
- { name: workspace, emptyDir: {} }
Expand Down Expand Up @@ -330,7 +334,7 @@ log "C12: checkpoint visible on PVC"

# --- Step 5: high-priority preemptor. -----------------------------------
log "C12: submitting high-priority preemptor Job"
INF_JOB=$(cat <<YAML | retry_create gpu_kc -o jsonpath='{.metadata.name}'
INF_JOB=$(cat <<YAML | mirror_dockerhub "${GPU_DH_MIRROR}" | retry_create gpu_kc -o jsonpath='{.metadata.name}'
apiVersion: batch/v1
kind: Job
metadata:
Expand All @@ -350,8 +354,8 @@ spec:
e2e.alauda.io/c12-run: "${RUN_ID}"
spec:
restartPolicy: Never
nodeSelector: { kubernetes.io/hostname: 192.168.138.15 }
imagePullSecrets: [{ name: harbor-mlops-regcred }]
$(yaml_node_selector 6 "${NODE_SELECTOR_KEY}" "${NODE_SELECTOR_VALUE}")
$(yaml_image_pull_secrets 6 "${IMAGE_PULL_SECRET}")
securityContext: { runAsNonRoot: true, runAsUser: 65534, runAsGroup: 65534, fsGroup: 65534 }
containers:
- name: serve
Expand Down
13 changes: 8 additions & 5 deletions e2e/cases/c1_smoke_gpu.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,21 @@ set -euo pipefail
HERE="$(cd "$(dirname "$0")" && pwd)"
source "${HERE}/../lib.sh"

require_env GPU_NAMESPACE "namespace for GPU e2e resources"
NS="${GPU_NAMESPACE}"
ASSETS="${E2E_ROOT}/../docs/en/training_guides/assets/training-runtimes"

log "C1: applying torch2.6-cu126-amd64 TrainingRuntime to ns/${NS} (rewriting docker.io → ${GPU_DH_MIRROR})"
sed "s/namespace: kubeflow-admin-cpaas-io/namespace: ${NS}/" \
"${ASSETS}/torch2.6-cu126-amd64-trainingruntime.yaml" \
if [ -n "${GPU_DH_MIRROR}" ]; then
log "C1: applying torch2.6-cu126-amd64 TrainingRuntime to ns/${NS} (Docker Hub mirror=${GPU_DH_MIRROR})"
else
log "C1: applying torch2.6-cu126-amd64 TrainingRuntime to ns/${NS}"
fi
set_metadata_namespace "${NS}" < "${ASSETS}/torch2.6-cu126-amd64-trainingruntime.yaml" \
| mirror_dockerhub "${GPU_DH_MIRROR}" \
| retry_apply gpu_kc

log "C1: submitting TrainJob from trainjob-smoke.yaml"
TJ_NAME=$(sed -e "s/namespace: kubeflow-admin-cpaas-io/namespace: ${NS}/" \
"${ASSETS}/trainjob-smoke.yaml" \
TJ_NAME=$(set_metadata_namespace "${NS}" < "${ASSETS}/trainjob-smoke.yaml" \
| retry_create gpu_kc -o jsonpath='{.metadata.name}')
log "C1: trainjob=${TJ_NAME}"

Expand Down
13 changes: 7 additions & 6 deletions e2e/cases/c2_kubeflow_trainer_mnist.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,17 @@ set -euo pipefail
HERE="$(cd "$(dirname "$0")" && pwd)"
source "${HERE}/../lib.sh"

require_env GPU_NAMESPACE "namespace for GPU e2e resources"
NS="${GPU_NAMESPACE}"

log "C2: applying ClusterTrainingRuntime torch-distributed (from kubeflow-trainer-quick-start.md)"
# Pulled directly out of docs/en/training_guides/kubeflow-trainer-quick-start.md.
# Image override: docker-mirrors.alauda.cn proxies docker.io but the lazy-cache
# returns EOF on the torch-distributed blobs (only torch2.6-* are cached). The
# alauda-local registry mirror has the image pre-pulled. Doc itself stays at
# the public alaudadockerhub/ name; only the test overrides for network reasons.
TORCH_DIST_IMAGE="${TORCH_DIST_IMAGE:-152-231-registry.alauda.cn:60070/mlops/torch-distributed:v2.9.1-aml2}"
cat <<'YAML' | sed "s@alaudadockerhub/torch-distributed:v2.9.1-aml2@${TORCH_DIST_IMAGE}@g" | retry_apply gpu_kc
# Override TORCH_DIST_IMAGE only when the public Docker Hub image is mirrored locally.
TORCH_DIST_IMAGE="${TORCH_DIST_IMAGE:-alaudadockerhub/torch-distributed:v2.9.1-aml2}"
cat <<'YAML' \
| sed "s@alaudadockerhub/torch-distributed:v2.9.1-aml2@${TORCH_DIST_IMAGE}@g" \
| mirror_dockerhub "${GPU_DH_MIRROR}" \
| retry_apply gpu_kc
apiVersion: trainer.kubeflow.org/v1alpha1
kind: ClusterTrainingRuntime
metadata:
Expand Down
14 changes: 7 additions & 7 deletions e2e/cases/c3_traininghub_sft.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,17 @@
# C3 — exercises training_hub.sft against the published
# traininghub0.1-cu126-amd64:v0.1.0 runtime image (used by sft-comprehensive-tutorial.ipynb).
# Tiny synthetic Qwen2-style HF checkpoint + synthetic JSONL are generated inside the
# Pod so the case has no external dependency. nproc_per_node is forced to 1 because
# the dev GPU node exposes only 1 whole GPU.
# Pod so the case has no external dependency. nproc_per_node is forced to 1
# to keep the smoke case small.
set -euo pipefail
HERE="$(cd "$(dirname "$0")" && pwd)"
source "${HERE}/../lib.sh"

require_env GPU_NAMESPACE "namespace for GPU e2e resources"
NS="${GPU_NAMESPACE}"
JOB_NAME="c3-traininghub-sft-$(printf '%05x' $$)"
# Bundled-training_hub image built from kubeflow-plugin/training-runtimes/traininghub0.1-cu126-amd64.
IMAGE="${C3_IMAGE:-build-harbor.alauda.cn/mlops/traininghub0.1-cu126-amd64:v0.1.0-build.20260609030710}"
IMAGE="${C3_IMAGE:-docker.io/alaudadockerhub/traininghub0.1-cu126-amd64:v0.1.0}"
IMAGE_PULL_SECRET="${C3_IMAGE_PULL_SECRET:-${E2E_IMAGE_PULL_SECRET:-}}"

cleanup() {
gpu_kc -n "${NS}" delete job "${JOB_NAME}" --ignore-not-found --wait=false || true
Expand All @@ -24,7 +25,7 @@ log "C3: submitting Job ${JOB_NAME} (image=${IMAGE})"
# single_gpu_dev preset; we do the same here. Liger is disabled because the
# image's CUDA *runtime* lacks nvcc (see training-runtimes.mdx — same caveat
# applies to JIT op compilation).
cat <<YAML | gpu_kc -n "${NS}" create -f -
cat <<YAML | mirror_dockerhub "${GPU_DH_MIRROR}" | gpu_kc -n "${NS}" create -f -
apiVersion: batch/v1
kind: Job
metadata:
Expand All @@ -44,8 +45,7 @@ spec:
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
imagePullSecrets:
- name: harbor-mlops-regcred
$(yaml_image_pull_secrets 6 "${IMAGE_PULL_SECRET}")
volumes:
- name: workspace
emptyDir: {}
Expand Down
11 changes: 6 additions & 5 deletions e2e/cases/c4_traininghub_osft.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ set -euo pipefail
HERE="$(cd "$(dirname "$0")" && pwd)"
source "${HERE}/../lib.sh"

require_env GPU_NAMESPACE "namespace for GPU e2e resources"
NS="${GPU_NAMESPACE}"
JOB_NAME="c4-traininghub-osft-$(printf '%05x' $$)"
IMAGE="${C4_IMAGE:-build-harbor.alauda.cn/mlops/traininghub0.1-cu126-amd64:v0.1.0-build.20260609030710}"
IMAGE="${C4_IMAGE:-docker.io/alaudadockerhub/traininghub0.1-cu126-amd64:v0.1.0}"
IMAGE_PULL_SECRET="${C4_IMAGE_PULL_SECRET:-${E2E_IMAGE_PULL_SECRET:-}}"

cleanup() {
gpu_kc -n "${NS}" delete job "${JOB_NAME}" --ignore-not-found --wait=false || true
Expand All @@ -17,7 +19,7 @@ trap cleanup EXIT

log "C4: submitting Job ${JOB_NAME} (image=${IMAGE})"

cat <<YAML | gpu_kc -n "${NS}" create -f -
cat <<YAML | mirror_dockerhub "${GPU_DH_MIRROR}" | gpu_kc -n "${NS}" create -f -
apiVersion: batch/v1
kind: Job
metadata:
Expand All @@ -37,8 +39,7 @@ spec:
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
imagePullSecrets:
- name: harbor-mlops-regcred
$(yaml_image_pull_secrets 6 "${IMAGE_PULL_SECRET}")
volumes:
- name: workspace
emptyDir: {}
Expand Down Expand Up @@ -80,7 +81,7 @@ spec:
cd /workspace
# training_hub is bundled in the runtime image. mini_trainer (the
# OSFT backend) unconditionally `import flash_attn`, which only
# supports sm_75+; the dev GPU node is Tesla P100 (sm_60).
# supports sm_75+; older GPU architectures need this stub.
# Stub the module so the import succeeds; transformers falls
# through to torch SDPA, which is what training_hub.sft uses too.
mkdir -p /workspace/stubs/flash_attn
Expand Down
36 changes: 15 additions & 21 deletions e2e/cases/c5_trainer_v2_llamafactory.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,34 +12,34 @@ set -euo pipefail
HERE="$(cd "$(dirname "$0")" && pwd)"
source "${HERE}/../lib.sh"

require_env GPU_NAMESPACE "namespace for GPU e2e resources"
NS="${GPU_NAMESPACE}"
# Per-run suffix so re-runs / concurrent runs don't share PVC contents or fight
# over the same TrainingRuntime. The trap below tears down both.
RUN_ID="$(printf '%05x' $$)-$(date -u +%s)"
RUNTIME="c5-llamafactory-finetune-runtime-${RUN_ID}"
PVC_NAME="c5-models-${RUN_ID}"
# The doc notebook says "Use `alaudadockerhub/fine_tune_with_llamafactory:v0.1.11`,
# or build your own". The Dockerhub copy is firewalled (mirror blob EOF on large
# images), the harbor rebuild at the same tag lacks torch, and the catalog runtime
# image `llamafactory0.9-cu126-amd64:v0.1.0` has the same blob-EOF issue. Use the
# build-suffixed copy of the catalog image already cached on the dev cluster.
IMAGE="${LF_IMAGE:-build-harbor.alauda.cn/mlops/llamafactory0.9-cu126-amd64:v0.1.0-build.20260603021903}"
IMAGE="${LF_IMAGE:-docker.io/alaudadockerhub/llamafactory0.9-cu126-amd64:v0.1.0}"
IMAGE_PULL_SECRET="${LF_IMAGE_PULL_SECRET:-${E2E_IMAGE_PULL_SECRET:-}}"
RWX_STORAGE_CLASS="${C5_RWX_STORAGE_CLASS:-${E2E_RWX_STORAGE_CLASS:-}}"
NODE_SELECTOR_KEY="${C5_NODE_SELECTOR_KEY:-${E2E_GPU_NODE_SELECTOR_KEY:-}}"
NODE_SELECTOR_VALUE="${C5_NODE_SELECTOR_VALUE:-${E2E_GPU_NODE_SELECTOR_VALUE:-}}"

log "C5: ensuring shared PVC ${PVC_NAME} exists (RWX on cephfs so any node can mount)"
log "C5: ensuring shared RWX PVC ${PVC_NAME} exists"
cat <<YAML | retry_apply gpu_kc
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: ${PVC_NAME}
namespace: ${NS}
spec:
storageClassName: cephfs
$(yaml_storage_class 2 "${RWX_STORAGE_CLASS}")
accessModes: ["ReadWriteMany"]
resources: { requests: { storage: 4Gi } }
YAML

log "C5: applying TrainingRuntime ${RUNTIME} to ns/${NS} (image=${IMAGE})"
cat <<YAML | retry_apply gpu_kc
cat <<YAML | mirror_dockerhub "${GPU_DH_MIRROR}" | retry_apply gpu_kc
apiVersion: trainer.kubeflow.org/v1alpha1
kind: TrainingRuntime
metadata:
Expand All @@ -64,10 +64,8 @@ spec:
template:
spec:
securityContext: { runAsNonRoot: true, runAsUser: 65534, runAsGroup: 65534, fsGroup: 65534 }
nodeSelector:
kubernetes.io/hostname: 192.168.138.15
imagePullSecrets:
- name: harbor-mlops-regcred
$(yaml_node_selector 18 "${NODE_SELECTOR_KEY}" "${NODE_SELECTOR_VALUE}")
$(yaml_image_pull_secrets 18 "${IMAGE_PULL_SECRET}")
volumes:
- name: models
persistentVolumeClaim: { claimName: ${PVC_NAME} }
Expand Down Expand Up @@ -123,10 +121,8 @@ spec:
template:
spec:
securityContext: { runAsNonRoot: true, runAsUser: 65534, runAsGroup: 65534, fsGroup: 65534 }
nodeSelector:
kubernetes.io/hostname: 192.168.138.15
imagePullSecrets:
- name: harbor-mlops-regcred
$(yaml_node_selector 18 "${NODE_SELECTOR_KEY}" "${NODE_SELECTOR_VALUE}")
$(yaml_image_pull_secrets 18 "${IMAGE_PULL_SECRET}")
volumes:
- name: models
persistentVolumeClaim: { claimName: ${PVC_NAME} }
Expand Down Expand Up @@ -192,10 +188,8 @@ spec:
template:
spec:
securityContext: { runAsNonRoot: true, runAsUser: 65534, runAsGroup: 65534, fsGroup: 65534 }
nodeSelector:
kubernetes.io/hostname: 192.168.138.15
imagePullSecrets:
- name: harbor-mlops-regcred
$(yaml_node_selector 18 "${NODE_SELECTOR_KEY}" "${NODE_SELECTOR_VALUE}")
$(yaml_image_pull_secrets 18 "${IMAGE_PULL_SECRET}")
volumes:
- { name: workspace, emptyDir: {} }
- name: models
Expand Down
Loading