TODO
- The ray dashboard, worker and head must only be available from inside your kubeflow user namespace
- Reenable the istio sidecar for the ray head and worker in the user namespace and provide the corresponding Istio Authorizationpolicies. We can keep the istio sidecar for the deployment kuberay-operator in the namespace kubeflow, since it does NOT use a webhook, but something else to reconcile rayclusters. This means we also do not need a networkpolicy for the ray operator.
Credit: This manifest refers a lot to the engineering blog "Building a Machine Learning Platform with Kubeflow and Ray on Google Kubernetes Engine" from Google Cloud.
Ray
Ray is a unified framework for scaling AI and Python applications. Ray consists of a core distributed runtime and a toolkit of libraries (Ray AIR) for simplifying ML compute.
KubeRay
KubeRay is an open-source Kubernetes operator for Ray. It provides several CRDs to simplify managing Ray clusters on Kubernetes. We will integrate Kubeflow and KubeRay in this document.
Requirements
-
Dependencies
kustomize: v5.2.1+ (Kubeflow manifest is sensitive tokustomizeversion.)Kubernetes: v1.29+
-
Computing resources:
- 16GB RAM
- 8 CPUs
Example
Step 1: Install Kubeflow v1.7-branch
-
This example installs Kubeflow with the v1.9-branch.
-
Install all Kubeflow official components and all common services using one command.
- If you do not want to install all components, you can comment out KNative, Katib, Tensorboards Controller, Tensorboard Web App, Training Operator, and KServe from example/kustomization.yaml.
Step 2: Install KubeRay operator
We never ever break Kubernetes standards and do not use the "default" namespace, but a proper one, in our case "kubeflow" for the ray operator.
# Install a KubeRay operator and custom resource definitions.
kustomize build kuberay-operator/overlays/kubeflow | kubectl apply --server-side -f -
# Check KubeRay operator
kubectl get pod -l app.kubernetes.io/component=kuberay-operator -n kubeflow
# NAME READY STATUS RESTARTS AGE
# kuberay-operator-5b8cd69758-rkpvh 1/1 Running 0 6m23s
Step 3: Install RayCluster
# Create a RayCluster CR, and the KubeRay operator will reconcile a Ray cluster
# with 1 head Pod and 1 worker Pod.
# $MY_KUBEFLOW_USER_NAMESPACE is a proper Kubeflow user namespace with istio sidecar injection and never ever the wrong "default"
export MY_KUBEFLOW_USER_NAMESPACE=development
kubectl apply -f raycluster_example.yaml -n $MY_KUBEFLOW_USER_NAMESPACE
# Check RayCluster
kubectl get pod -l ray.io/cluster=kubeflow-raycluster -n $MY_KUBEFLOW_USER_NAMESPACE
# NAME READY STATUS RESTARTS AGE
# kubeflow-raycluster-head-p6dpk 1/1 Running 0 70s
# kubeflow-raycluster-worker-small-group-l7j6c 1/1 Running 0 70s
raycluster_example.yamlusesrayproject/ray:2.23.0-py311-cpuas its OCI image. Ray is very sensitive to the Python versions and Ray versions between the server (RayCluster) and client (JupyterLab) sides. This image uses:- Python 3.11
- Ray 2.23.0
Step 4: Forward the port of Istio's Ingress-Gateway
- Follow the instructions to forward the port of Istio's Ingress-Gateway and log in to Kubeflow Central Dashboard.
Step 5: Create a JupyterLab via Kubeflow Central Dashboard
- Click "Notebooks" icon in the left panel.
- Click "New Notebook"
- Select
kubeflownotebookswg/jupyter-scipy:v1.9.0as OCI image (or any other with the same python version) - Click "Launch"
- Click "CONNECT" to connect into the JupyterLab instance.
Step 6: Use Ray client in the JupyterLab to connect to the RayCluster
- As I mentioned in Step 3, Ray is very sensitive to the Python versions and Ray versions between the server (RayCluster) and client (JupyterLab) sides.
# Check Python version. The version's MAJOR and MINOR should match with RayCluster (i.e. Python 3.11.9) python --version # Python 3.11.9 pip install -U ray[default]==2.23.0 - Connect to RayCluster via Ray client.
# Open a new .ipynb page. import ray # For other namespaces use ray://${RAYCLUSTER_HEAD_SVC}.${NAMESPACE}.svc.cluster.local:${RAY_CLIENT_PORT} # But we use of course our per namespace ray cluster to have multi-tenancy and # We never ever use "default" as namespace since this would violate Kubernetes standards ray.init(address="ray://kubeflow-raycluster-head-svc:10001") print(ray.cluster_resources()) # {'node:10.244.0.41': 1.0, 'memory': 3000000000.0, 'node:10.244.0.40': 1.0, 'object_store_memory': 805386239.0, 'CPU': 2.0} # Try Ray task
@ray.remote def f(x): return x * x
futures = [f.remote(i) for i in range(4)] print(ray.get(futures)) # [0, 1, 4, 9]
Try Ray actor
@ray.remote class Counter(object): def init(self): self.n = 0
def increment(self):
self.n += 1
def read(self):
return self.n
counters = [Counter.remote() for i in range(4)] [c.increment.remote() for c in counters] futures = [c.read.remote() for c in counters] print(ray.get(futures)) # [1, 1, 1, 1] ```
Upgrading
See UPGRADE.md for more details.