Skip to content

RunAI Upgrade

1. Get Helm Files for Import

  1. Run the following command to browse all available air-gapped packages:
    curl -H "Authorization: Bearer <token>" "https://runai.jfrog.io/artifactory/api/storage/runai-airgapped-prod/?list"
    

Note

via their jfrog: https://runai.jfrog.io/ui/native/runai-airgapped-prod/runai-airgapped-package-2.XX.XX.tar.gz Get the auth from the authority/account manager.

  1. Download the desired package:
    curl -L -H "Authorization: Bearer <token>" -O "https://runai.jfrog.io/artifactory/runai-airgapped-prod/runai-airgapped-package-<VERSION>.tar.gz"
    
  2. SSH Into a node with OC, Helm and Docker/Podman (Bastion in most cases)
  3. Extract the downloaded package:
    tar xvf runai-airgapped-package-<VERSION>.tar.gz
    
  4. Push the Charts to Git (Optional):
    curl --user $LDAP_USERNAME:$LDAP_PASSWORD -X POST --upload-file runai-$RUNAI_VERSION/charts/control-plane.tgz https://gitea.mlopslab.ncs/api/packages/mlopslab/helm/api/charts                   
    
    curl --user $LDAP_USERNAME:$LDAP_PASSWORD -X POST --upload-file runai-$RUNAI_VERSION/charts/runai-cluster.tgz https://gitea.mlopslab.ncs/api/packages/mlopslab/helm/api/charts
    
  5. Push to your image registry:

Note

No https:// and set the environment variables REGISTRY_USERNAME & REGISTRY_PASSWORD or use an REGISTRY_AUTH_FILE This step pushes to the image registry exported as an environment variable.

export REGISTRY_URL=<DOCKER REGISTRY ADDRESS>/runai
## NO "HTTPS://"
## Fallback: add in the REGISTRY_USERNAME and REGISTRY_PASSWORD env variables.
export REGISTRY_AUTH_FILE=$HOME/.docker/config.json
sudo ./setup.sh

2. Upgrade Instructions

Note

Backup current helm values before trying to run the helm upgrade command.

  1. Backup Current Config:
    helm get values runai-backend -n runai-backend > runai_control_plane_values.yaml
    
  2. Refresh Repo Metadata
    helm repo update
    helm search repo runai --versions
    
  3. Push Helm Upgrade to Control Plane
    helm upgrade runai-backend control-plane.tgz -n runai-backend -f runai_control_plane_values.yaml --reset-then-reuse-values
    
  4. Push upgrade to Cluster Get the installation instructions from Clusters > Select cluster > INSTALLATION INSTRUCTIONS > Run the helm upgrade commands.
    helm repo add runai --force-update
    helm repo update
    helm upgrade -i runai-cluster <runai/runai-cluster> <runai-cluster.tgz> -n runai \
    --set controlPlane.url=runai.apps.ocp.local \
    --set controlPlane.clientSecret=<> \
    --set cluster.uid=<> \
    --set cluster.url=runai.apps.ocp.local --version="2.23.31" --create-namespace
    --set global.customCA.enabled=true
    

3. Miscellaneous Patches

  1. Enabling GPU Swap

    oc patch -n runai runaiconfigs.run.ai/runai --type='merge' --patch '{"spec":{"global":{"core":{"swap":{"enabled": true}}}}}'
    
    oc patch -n runai runaiconfigs.run.ai/runai --type='merge' --patch '{"spec":{"global":{"core":{"swap":{"biDirectional": {"enabled": true}}}}}}'
    
    oc patch -n runai runaiconfigs.run.ai/runai --type='merge' --patch '{"spec":{"global":{"core":{"swap":{"mode": "mapped"}}}}}'
    

  2. Node Level Scheduling

    oc patch -n runai runaiconfigs.run.ai/runai --type='merge' --patch '{"spec":{"global":{"core":{"nodeScheduler":{"enabled": true}}}}}'
    

  3. Select Resources → Node pools

  4. Create a new node pool or edit an existing node pool

  5. Under the Resource Utilization Optimization tab, change the number of workloads on each GPU to any value other than Not Enforced (i.e. 2, 3, 4, 5)

The Node Level Scheduler is now ready to be used on that node pool.

Note

In order for a workload to be considered by the Node Level Scheduler for rerouting, it must be submitted with a GPU Request and Limit where the Limit is larger than the Request.

apiVersion: v1
kind: Pod
metadata:
  annotations:
    user: test
    gpu-fraction: "0.5"
    gpu-fraction-num-devices: "2"
  labels:
    runai/queue: test
  name: multi-fractional-pod-job
  namespace: test
spec:
  containers:
  - image: gcr.io/run-ai-demo/quickstart-cuda
    imagePullPolicy: Always
    name: job
    env:
    - name: RUNAI_VERBOSE
      value: "1"
    - name: RUNAI_GPU_MEMORY_LIMIT
      value: "0.95"
    resources:
      limits:
        cpu: 200m
        memory: 200Mi
      requests:
        cpu: 100m
        memory: 100Mi
    securityContext:
      capabilities:
        drop: ["ALL"]
  schedulerName: runai-scheduler
  serviceAccount: default
  serviceAccountName: default
  terminationGracePeriodSeconds: 5
  1. Node Selector for Runai
    global:
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
              - matchExpressions:
                  - key: node-role.kubernetes.io/runai-system
                    operator: Exists
    
kubectl label nodes <node-name> node-role.kubernetes.io/runai-system=true
kubectl label nodes <node-name> node-role.kubernetes.io/runai-system=false

Flow of Helm, Gitea & Harbor

                   ┌──────────────────────────────────────────────┐
                   │              YOU (on bastion)                │
                   │  helm upgrade runai-cluster runai/runai-...   │
                   └──────────────────────────────────────────────┘
                                     │
                                     v
┌───────────────────────────────────────────────────────────────────────────┐
│ 1) HELM CLIENT (local)                                                    │
│    - Fetch chart (from Gitea Helm repo OR local .tgz)                     │
│    - Merge values.yaml + --set                                             │
│    - Render Kubernetes manifests (Deployments/Jobs/Services/CRDs/...)      │
│    NOTE: No images are pulled here                                         │
└───────────────────────────────────────────────────────────────────────────┘
                                     │
                                     v
┌───────────────────────────────────────────────────────────────────────────┐
│ 2) KUBERNETES API SERVER (via your kubeconfig context)                    │
│    - Helm sends rendered YAML to the cluster API                           │
│    - Cluster stores desired state (etcd)                                   │
│    - Controllers see new/changed workloads                                 │
└───────────────────────────────────────────────────────────────────────────┘
                                     │
                                     v
┌───────────────────────────────────────────────────────────────────────────┐
│ 3) CONTROLLERS + SCHEDULER                                                 │
│    - Create ReplicaSets/Pods from Deployments/StatefulSets                 │
│    - Scheduler assigns each Pod to a Node                                  │
└───────────────────────────────────────────────────────────────────────────┘
                                     │
                                     v
┌───────────────────────────────────────────────────────────────────────────┐
│ 4) NODE (kubelet + CRI-O)                                                  │
│    - kubelet sees Pod spec with image: <registry>/<repo>:<tag>             │
│    - CRI-O pulls image from registry referenced in Pod spec                │
│                                                                           │
│      IF image points to Harbor + exists + trusted TLS + pull secret OK     │
│          -> pull succeeds                                                  │
│          -> container starts                                               │
│          -> readiness probes pass                                          │
│      ELSE                                                                  │
│          -> ImagePullBackOff / ErrImagePull                                │
└───────────────────────────────────────────────────────────────────────────┘
                                     │
                                     v
┌───────────────────────────────────────────────────────────────────────────┐
│ 5) HELM (only if you used --wait)                                          │
│    - watches Kubernetes resources                                          │
│    - waits for Pods Ready / Jobs complete / hooks succeed                  │
│    - marks release DEPLOYED or FAILED                                      │
└───────────────────────────────────────────────────────────────────────────┘


Air-gapped “must-haves” before upgrade:
  [Gitea Helm repo]  -> chart reachable
  [Harbor registry]  -> ALL referenced images exist internally
  [values.yaml]      -> renders ONLY internal image references

Break glass reinstall commands

helm upgrade -i runai-backend runai/control-plane --version 2.22.74 -n runai-backend --set global.domain=runai.apps.ocp.mlopslab.ncs --set global.config.kubernetesDistribution=openshift --set global.customCA.enabled=true --set tenantsManager.config.adminUsername=test@run.ai --set tenantsManager.config.adminPassword="Abcd!234" -f custom-env.yaml 
From Installation Instructions, append the following keys: 
--set global.customCA.enabled=true --set global.image.registry=repo.mlopslab.ncs/runai