diff --git a/guidebooks/ml/jupyterlab/start/examples/.gitignore b/guidebooks/ml/jupyterlab/start/examples/.gitignore new file mode 100644 index 00000000..f22a19e6 --- /dev/null +++ b/guidebooks/ml/jupyterlab/start/examples/.gitignore @@ -0,0 +1,2 @@ +.ipynb_checkpoints/ +__pycache__/ diff --git a/guidebooks/ml/jupyterlab/start/examples/example.ipynb b/guidebooks/ml/jupyterlab/start/examples/example.ipynb new file mode 100644 index 00000000..91076345 --- /dev/null +++ b/guidebooks/ml/jupyterlab/start/examples/example.ipynb @@ -0,0 +1,219 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "b61a3cbf-eaa3-49e1-bcd8-2ff7453cc0c6", + "metadata": {}, + "source": [ + "# Jupyter notebook example\n", + "\n", + "Here we demonstrate how to use a Jupyter notebook that may be served locally or on a Kubernetes cluster" + ] + }, + { + "cell_type": "markdown", + "id": "e5d77556-8e98-40c9-9901-17a7aa63cc1c", + "metadata": {}, + "source": [ + "## Plotting using NumPy\n", + "\n", + "This is a simple script to generate and plot a *sine* wave." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "1df92a57-5155-4849-b274-c5c38211606d", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "d3b85b96-3fb7-4e2e-892b-b71d8930f2b0", + "metadata": {}, + "outputs": [], + "source": [ + "n = 10\n", + "x = np.linspace(0.0, 2.0, n)\n", + "y = np.sin(np.pi * x)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "ecec7155-320e-47c1-878e-aa0017d13ca5", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAtYAAAIvCAYAAABQjZwXAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8qNh9FAAAACXBIWXMAAA9hAAAPYQGoP6dpAAComklEQVR4nOzdeVxUZd8G8OvMsCOL7DsIgrijCLjilitqamq5hpnanlZaZmW9ZfVk+1NW5oJrllhqau67GG64gyiygyAgggLDMDPvH8g8EYMCDhxm5vp+Pn7elzNnuc4tPv043Od3CyqVSgUiIiIiInokErEDEBERERHpAxbWRERERERawMKaiIiIiEgLWFgTEREREWkBC2siIiIiIi1gYU1EREREpAUsrImIiIiItICFNRERERGRFrCwJiIiIiLSAhbWREQi6ty5MwRBgKmpKfLz85vsuj4+PhAEASkpKU12zaYSGRkJQRAQFRUldhQiMjAsrImIRHLq1ClcuHABAFBeXo5169aJnIg06devHwRBwKFDh8SOQkTNHAtrIiKRrFixAgDg7u5e7eumsH//fsTHx6uvTUREj46FNRGRCEpKSvDLL78AANauXYsWLVrg4sWLOHXqVJNc38/PD4GBgTA2Nm6S6xERGQIW1kREIti0aROKiorQoUMH9O/fH08++SSAhj+1lslkWLJkCYKDg2FlZQUTExO4uLggJCQE8+fPR0FBQbX9a5tj/c9pD+fOncPYsWPh4OAAU1NTtGvXDl988QVUKlWtOfbv34+xY8fC1dUVJiYmcHJywpgxY3DixIl639M/M/7xxx/o3bs3rK2tYWVlhX79+mHnzp31PicAbNy4EQMHDoSdnR1MTU3h7e2NZ555BomJidX2O3ToEARBwOHDhwEA/fv3hyAI6j+cw01E/8bCmohIBFUF9DPPPFPt/27cuBGlpaX1OpdSqURERATmz5+P69evo0+fPhg3bhw6duyIW7duYcmSJUhLS6vXOXfv3o2wsDAkJCRg0KBB6NGjBxITE/HGG29g7ty5Go9544038Nhjj2Hr1q3w8vLC6NGj4evri61bt6JPnz5YtWpVvTJU+fbbbzF27FjIZDKMGDEC7dq1w+HDhxEREYH//ve/dT6PSqXC008/jYkTJ+LIkSPo0qULxo4dCzMzM6xatQpdunTBrl271Pu7uLjg6aefhrOzMwBgyJAhePrpp9V/Wrdu3aD7ISI9piIioiZ19epVFQCVsbGxKjc3V709MDBQBUC1Zs2aep3v8OHDKgCqLl26qIqKimp8furUKVVeXl61bd7e3ioAquTk5Grb+/btqwKgAqD68ccfq322f/9+lSAIKqlUqkpPT6/22bJly1QAVK1bt1adP3++Rj4rKyuViYmJKjExsc73VZVREATVunXrqn22ceNGlSAIKiMjI9XFixerffb000+rAKhWrVpVbfsPP/ygAqBycHBQxcXFqbcrlUrVokWLVABUtra21f5O/jkmBw8erHN2IjJMfGJNRNTEVq5cCQAYNWoUHB0d1durnlrXdzpITk4OAKBPnz6wsrKq8Xm3bt1gb29fr3OOHTsWs2fPrrZtwIABGDJkCBQKBQ4ePKjerlQq8f777wOofOLeqVOnaseFh4fj3XffRXl5OX766ad65QCAxx9/HJMnT6627cknn8TYsWNRUVGBb7/9tk7n+fzzzwEA7733HoKCgtTbBUHAokWL0KlTJxQWFuLnn3+ud0YiIoBTQYiImlRFRQVWr14N4H+FdJVp06bByMgIR44cQVJSUp3P2bVrV0ilUqxcuRLff/89srOzHznnyJEjNW5v27YtACAzM1O9LS4uDllZWfDz80NwcLDG4/r16wcAiImJqXeWp59++oHb69IGLyMjQz2mms4nCAKmT58OANV+aCAiqg8W1kRETWjHjh24efMm3N3dMWTIkGqfOTs7Y/jw4VCpVOqn2nXh5+eHr776CnK5HC+99BLc3Nzg4+ODiRMnYv369SgvL693Ti8vL43bra2tAQBlZWXqbTdu3AAAJCUlVXu5759/QkNDAQC3bt2qd5ZWrVo9cHtGRsZDz1H1g4C9vb36Hv7Nz8+v2r5ERPVlJHYAIiJDUjXNo6ysDH379q3xeVVRFxUVhf/7v/+DVCqt03lffvllTJgwAdu2bcOxY8dw7NgxbNy4ERs3bsSiRYtw9OhRuLq61jmnRFL35y5KpRJA5ct+//5h4d8cHBzqfN66Uj2gSwkRUVNiYU1E1ESys7PVLeLy8/Nx/PjxWvfNysrCrl27EBERUefzOzs7Y+bMmZg5cyYAICEhAc888wxOnDiBt956Sz0FRds8PT0BVD4NbowWdMnJyejcuXON7VWtAj08PB56jqqFcPLz81FUVKTxqXXVk3cumkNEDcWpIERETSQqKgoKhQJhYWFQqVS1/pk/fz6AR1+JMTAwEG+++SYA4Ny5c48av1YhISFwcHDAlStXcPnyZa2ff+3atRq3r1mzBsD/5m8/iIeHh3qqh6biX6VSqbf379+/2mcmJiYAKufHExE9CAtrIqImUjVvuraX8apMmzYNALB9+/Y6zUk+cOAAdu7cCblcXm27SqXC9u3bAQDe3t4NiVwnxsbGWLRoEVQqFcaMGYNjx47V2EehUODAgQP4+++/633+P/74Axs3bqy2LTo6Gps3b4aRkRFefvnlOp3njTfeAAB8+OGHOH/+vHq7SqXCRx99hHPnzsHW1lb9xL9K1RPxxvihgYj0C6eCEBE1gcOHD+P69eswNTXFU0899cB927dvj65du+Ls2bNYs2YNXn/99Qfuf+HCBcydOxfW1tbo2rUr3NzcUFpairNnzyI1NRU2Njb4v//7P23eTg0vvfQS0tLSsGTJEvTp0wft27dH69atYW5ujps3b+LcuXMoLCzEDz/8gO7du9fr3K+++iomTpyIL7/8Ev7+/khKSkJsbCyAyhZ6/27vV5vZs2cjJiYGa9euRbdu3dC3b184OTnh7NmzuHr1KszNzbFhw4ZqLRAB4IknnsCqVaswf/587Nu3D05OThAEAc888wx69uxZr3shIv3GJ9ZERE2galrHyJEj0bJly4fuX/XUui7TQUaOHIn3338fISEhuHHjBn7//XccOnQINjY2eOutt3Dp0qVqfZsby2effYbjx49j8uTJuHv3Lnbt2oUdO3YgKysL/fr1w/Lly9VLt9fHq6++it9++w1GRkbYtm0bLl26hD59+uDPP/+sdRVITQRBwJo1a7Bhwwb07t0bZ86cQXR0NEpKShAZGYm4uDgMGzasxnERERH4+eef0aFDBxw4cAArV67EihUraiyBTkQkqPg6NRERNUM+Pj5ITU1FcnIyfHx8xI5DRPRQfGJNRERERKQFLKyJiIiIiLSAhTURERERkRZwjjURERERkRbwiTURERERkRawsCYiIiIi0gIuECMipVKJrKwsWFlZQRAEseMQERER0b+oVCoUFxfDzc0NEsmDn0mzsBZRVlYWPD09xY5BRERERA+Rnp4ODw+PB+7DwlpEVlZWACr/oqytrRv9enK5HHv27MHgwYNhbGzc6NfTJRwbzTgutePYaMZx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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plt.figure(figsize = (8, 6))\n", + "plt.title(\"A sine plot\", fontsize = 16)\n", + "plt.xlabel(\"Horizontal axis\", fontsize = 15)\n", + "plt.ylabel(\"Vertical axis\", fontsize = 15)\n", + "plt.plot(x, y)\n", + "plt.xticks(fontsize = 12)\n", + "plt.yticks(fontsize = 12)\n", + "plt.grid()" + ] + }, + { + "cell_type": "markdown", + "id": "9f9c218f-7247-4d50-8009-aa136a44ec75", + "metadata": {}, + "source": [ + "## Ray cluster testing\n", + "\n", + "The following code tests the Ray cluster in which the JupyterLab server is running. The code runs a remote function on all the available workers, and prints their `hostname` on return. This demonstrates that the remote functions are being executed on different work units." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "40e97020-a023-47a1-9080-ba7751ffe82d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "RayContext(dashboard_url='10.217.0.118:8265', python_version='3.7.7', ray_version='1.13.1', ray_commit='da2a91cd34ac58df4c49b2fa65a5bd25bc1e2057', address_info={'node_ip_address': '10.217.0.121', 'raylet_ip_address': '10.217.0.121', 'redis_address': None, 'object_store_address': '/tmp/ray/session_2022-09-26_13-38-36_182086_1/sockets/plasma_store', 'raylet_socket_name': '/tmp/ray/session_2022-09-26_13-38-36_182086_1/sockets/raylet', 'webui_url': '10.217.0.118:8265', 'session_dir': '/tmp/ray/session_2022-09-26_13-38-36_182086_1', 'metrics_export_port': 54840, 'gcs_address': 'mycluster-ray-head:6379', 'address': 'mycluster-ray-head:6379', 'node_id': '204a8ae48e6c0b1c338cd8506b2e86d4376f1fcd7ee67cc40e409cec'})" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import ray\n", + "import time\n", + "import socket\n", + "\n", + "if ray.is_initialized():\n", + " ray.shutdown()\n", + " \n", + "ray.init(\"auto\")\n", + "#ray.init(address = \"ray://mycluster-ray-head:10001\")\n", + "#ray.init(address = \"172.30.186.150:6379\")\n", + "#ray.init(address = \"172.21.103.4:8265\")\n", + "#ray.init(address = \"172.21.103.4:10001\")" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "ed02f67a-b97f-4033-af5f-ed79b8168ddc", + "metadata": {}, + "outputs": [], + "source": [ + "@ray.remote\n", + "def gethostname(worker_id):\n", + " time.sleep(2)\n", + " return \"Worker %d: %s\" % (worker_id, socket.gethostname())" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "208a3d7a-07c4-4267-829d-6f4c0b928325", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Worker 0: mycluster-ray-worker-68c6679546-jkhqf\n", + "Worker 1: mycluster-ray-worker-68c6679546-lp6b6\n", + "Worker 2: mycluster-ray-head-6479567bbd-s9k9k\n", + "Worker 3: mycluster-jupyterlab-58f59b7597-28n78\n", + "Worker 4: mycluster-jupyterlab-58f59b7597-28n78\n", + "Worker 5: mycluster-jupyterlab-58f59b7597-28n78\n", + "Worker 6: mycluster-jupyterlab-58f59b7597-28n78\n", + "Worker 7: mycluster-jupyterlab-58f59b7597-28n78\n" + ] + } + ], + "source": [ + "num_workers = 8\n", + "output = ray.get([gethostname.remote(worker_id) for worker_id in range(num_workers)])\n", + "print(\"\\n\".join(output))" + ] + }, + { + "cell_type": "markdown", + "id": "2af0e93a-8315-49ba-911b-673c9543ac81", + "metadata": {}, + "source": [ + "## Importing custome made modules\n", + "\n", + "In cases a script needs to be broken into parts or some other code is needed, the respective code can be turned into a module and loaded here." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "094cbdfb-75b7-402b-bd5f-e9ee7e6a0d16", + "metadata": {}, + "outputs": [], + "source": [ + "import mymodule\n", + "\n", + "%load_ext autoreload\n", + "%autoreload 2" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "15d87093-d329-427c-9c22-f1d734579a77", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Hi from 'mymodule'\n" + ] + } + ], + "source": [ + "mymodule.say_hi()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/guidebooks/ml/jupyterlab/start/examples/mymodule.py b/guidebooks/ml/jupyterlab/start/examples/mymodule.py new file mode 100644 index 00000000..dfc32431 --- /dev/null +++ b/guidebooks/ml/jupyterlab/start/examples/mymodule.py @@ -0,0 +1,6 @@ +""" +Module function +""" + +def say_hi(): + print("Hi from '%s'" % (__name__)) \ No newline at end of file diff --git a/guidebooks/ml/jupyterlab/start/index.md b/guidebooks/ml/jupyterlab/start/index.md new file mode 100644 index 00000000..46b45e30 --- /dev/null +++ b/guidebooks/ml/jupyterlab/start/index.md @@ -0,0 +1,21 @@ +# Run a JupyterLab Server + +[JupyterLab](https://jupyterlab.readthedocs.io/en/stable/index.html) is an open source platform to manage Jupyter notebooks +from a web console with ease. Jupyter notebooks are interactive Python sessions that allow users to build Python scripts +in a *comparmentalized* way, with the data living throughout the notebook execution. + +=== "Run Locally" + Start a JupyterLab server on your computer + --8<-- "./local" + +=== "Run on a Kubernetes Cluster" + Start a JupyterLab server on a Kubernetes cluster + --8<-- "./kubernetes" + +=== "Shut down a Kubernetes Cluster" + Stop a Kubernetes cluster that maybe running a Jupyter server + :import{ml/ray/stop/kubernetes} + +=== "Shut down a JupyterLab server" + Stop a JupyterLab server + :import{ml/jupyterlab/stop/kubernetes} diff --git a/guidebooks/ml/jupyterlab/start/kubernetes/chart/Chart.yaml b/guidebooks/ml/jupyterlab/start/kubernetes/chart/Chart.yaml new file mode 100644 index 00000000..691ea33a --- /dev/null +++ b/guidebooks/ml/jupyterlab/start/kubernetes/chart/Chart.yaml @@ -0,0 +1,10 @@ +apiVersion: v2 +name: jupyterlab +description: A Helm chart for deployments of JupyterLab on Kubernetes. +type: application + +# Chart version +version: 0.1.0 + +# JupyterLab version +appVersion: "latest" diff --git a/guidebooks/ml/jupyterlab/start/kubernetes/chart/templates/jupyterlab.yaml b/guidebooks/ml/jupyterlab/start/kubernetes/chart/templates/jupyterlab.yaml new file mode 100644 index 00000000..bee68d31 --- /dev/null +++ b/guidebooks/ml/jupyterlab/start/kubernetes/chart/templates/jupyterlab.yaml @@ -0,0 +1,25 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Values.clusterName }}-jupyterlab + labels: + app: jupyterlab-deployment +spec: + replicas: 1 + selector: + matchLabels: + app: jupyterlab-pod + template: + metadata: + labels: + app: jupyterlab-pod + spec: + restartPolicy: Always + containers: + - name: jupyterlab + image: {{ .Values.rayImage }} + command: ["/bin/bash", "-c", "--"] + args: ["conda install -c conda-forge vim; pip install jupyterlab jupyterlab-vim; ray start --address={{ .Values.clusterName }}-ray-head:{{ .Values.rayHeadPort }}; sleep infinity;"] + imagePullPolicy: IfNotPresent + ports: + - containerPort: 8888 diff --git a/guidebooks/ml/jupyterlab/start/kubernetes/chart/templates/service.yaml b/guidebooks/ml/jupyterlab/start/kubernetes/chart/templates/service.yaml new file mode 100644 index 00000000..094d440e --- /dev/null +++ b/guidebooks/ml/jupyterlab/start/kubernetes/chart/templates/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ .Values.clusterName }}-jupyterlab + labels: + app: jupyterlab-deployment +spec: + ports: + - port: 8888 + protocol: TCP + selector: + app: jupyterlab-pod diff --git a/guidebooks/ml/jupyterlab/start/kubernetes/chart/values.yaml b/guidebooks/ml/jupyterlab/start/kubernetes/chart/values.yaml new file mode 100644 index 00000000..7a8b897b --- /dev/null +++ b/guidebooks/ml/jupyterlab/start/kubernetes/chart/values.yaml @@ -0,0 +1,3 @@ +clusterName: mycluster +rayHeadPort: 6379 +rayImage: rayproject/ray:1.13.1-py37 diff --git a/guidebooks/ml/jupyterlab/start/kubernetes/index.md b/guidebooks/ml/jupyterlab/start/kubernetes/index.md new file mode 100644 index 00000000..44f92ca1 --- /dev/null +++ b/guidebooks/ml/jupyterlab/start/kubernetes/index.md @@ -0,0 +1,6 @@ +--- +imports: + - ./ray-server + - ./jupyter-server +--- + diff --git a/guidebooks/ml/jupyterlab/start/kubernetes/install.sh b/guidebooks/ml/jupyterlab/start/kubernetes/install.sh new file mode 100644 index 00000000..17ef5096 --- /dev/null +++ b/guidebooks/ml/jupyterlab/start/kubernetes/install.sh @@ -0,0 +1,24 @@ +# +# Helm deployment of Ray server with JupyterLab +# + +GITHUB=github.com +ORG=${JUPYTERLAB_CHART_ORG-guidebooks} +REPO=${JUPYTERLAB_CHART_REPO-store} +BRANCH=${JUPYTERLAB_CHART_BRANCH} +SUBDIR=${JUPYTERLAB_CHART_SUBDIR-guidebooks/ml/jupyterlab/start/kubernetes/chart} + +JUPYTERLAB_CLONE_TEMPDIR=$(mktemp -d) + +if [ -n "$BRANCH" ]; then + BRANCH_OPT="-b ${BRANCH}" +fi + +git clone --filter=tree:0 --depth 1 --sparse https://${GITHUB}/${ORG}/${REPO}.git ${BRANCH_OPT} ${JUPYTERLAB_CLONE_TEMPDIR} && \ + cd ${JUPYTERLAB_CLONE_TEMPDIR} && \ + git sparse-checkout init --cone && \ + git sparse-checkout set ${SUBDIR} + +helm --kube-context ${KUBE_CONTEXT} --namespace ${KUBE_NS} \ + upgrade --install --wait \ + jupyterlab ${JUPYTERLAB_CLONE_TEMPDIR}/${SUBDIR} diff --git a/guidebooks/ml/jupyterlab/start/kubernetes/jupyter-server.md b/guidebooks/ml/jupyterlab/start/kubernetes/jupyter-server.md new file mode 100644 index 00000000..77071b6b --- /dev/null +++ b/guidebooks/ml/jupyterlab/start/kubernetes/jupyter-server.md @@ -0,0 +1,79 @@ +--- +imports: + - ml/ray/cluster/head +--- + +```shell +export RAY_KUBE_CLUSTER_NAME=$(kubectl get ${KUBE_CONTEXT_ARG} ${KUBE_NS_ARG} pod --no-headers -l ray-node-type=head -o custom-columns=NAME:.metadata.labels.ray-cluster-name | head -1 ) +``` + +# Start the JupyterLab server, create working directory, and copy everything in the local working directory to the server working directory + +```shell +--- +validate: | + if [ -n "${KUBE_CONTEXT}" ] && [ -n "${KUBE_NS}" ]; then kubectl get ${KUBE_CONTEXT_ARG} ${KUBE_NS_ARG} deploy ${RAY_KUBE_CLUSTER_NAME}-jupyterlab; else exit 1; fi +--- + +--8<-- "./install.sh" +``` + +```shell +kubectl wait ${KUBE_CONTEXT_ARG} ${KUBE_NS_ARG} deploy/${RAY_KUBE_CLUSTER_NAME}-jupyterlab --for=condition=Available + +JUPYTERLAB_POD=$(python3 -c "pod = '''$(kubectl get ${KUBE_CONTEXT_ARG} ${KUBE_NS_ARG} pods --selector app=jupyterlab-pod | grep Running)'''.split(); print(pod[0]) if len(pod) > 0 else print()") + +echo -n "Installing JupyterLab..." + +while [ $(kubectl exec ${KUBE_CONTEXT_ARG} ${KUBE_NS_ARG} $JUPYTERLAB_POD -- which jupyter-lab &> /dev/null && echo 1 || echo 0) -eq 0 ] +do + echo -n "." +done + +echo + +# This trick is used because 'kubectl cp /* :' doesn't work for me +kubectl exec ${KUBE_CONTEXT_ARG} ${KUBE_NS_ARG} $JUPYTERLAB_POD -- mkdir -p /home/ray/work +tar -zcvf .work.tar -C $CUSTOM_WORKING_DIR . +kubectl cp ${KUBE_CONTEXT_ARG} ${KUBE_NS_ARG} .work.tar $JUPYTERLAB_POD:/home/ray/work +kubectl exec ${KUBE_CONTEXT_ARG} ${KUBE_NS_ARG} $JUPYTERLAB_POD -- bash -c -- "tar -xvf /home/ray/work/.work.tar -C /home/ray/work; rm /home/ray/work/.work.tar" +``` + +```shell +export JUPYTERLAB_POD=$(python3 -c "pod = '''$(kubectl get ${KUBE_CONTEXT_ARG} ${KUBE_NS_ARG} pods --selector app=jupyterlab-pod | grep Running)'''.split(); print(pod[0]) if len(pod) > 0 else print()") +``` + +# Port-forward to the Jupyter-lab server + +```shell.async +date > /tmp/port-forward-jupyterlab + +NUM_SERVERS=0 + +while [ "${NUM_SERVERS}" = "0" ]; +do + SERVER_LIST=$(kubectl exec ${KUBE_CONTEXT_ARG} ${KUBE_NS_ARG} $JUPYTERLAB_POD -- jupyter server list) + NUM_SERVERS=$(kubectl exec ${KUBE_CONTEXT_ARG} ${KUBE_NS_ARG} $JUPYTERLAB_POD -- python3 -c "response = '''${SERVER_LIST}'''.split('\n'); print(len(response) - 1)") + echo "Waiting for server to start..." >> /tmp/port-forward-jupyterlab + sleep 1 +done + +kubectl port-forward ${KUBE_CONTEXT_ARG} ${KUBE_NS_ARG} service/${RAY_KUBE_CLUSTER_NAME}-jupyterlab 8888:8888 >> /tmp/port-forward-jupyterlab +``` + +# Initiate Jupyter-lab server on the pod + +```shell +kubectl exec ${KUBE_CONTEXT_ARG} ${KUBE_NS_ARG} -it $JUPYTERLAB_POD -- jupyter-lab --port=8888 --no-browser --notebook-dir=/home/ray/work +``` + +# On completion of the Jupyter-lab cluster work, copy everything back to the local working directory + +```shell +# This trick is used because 'kubectl cp :/* ' doesn't work for me +kubectl exec ${KUBE_CONTEXT_ARG} ${KUBE_NS_ARG} $JUPYTERLAB_POD -- tar -zcvf .work.tar -C /home/ray/work . +kubectl cp ${KUBE_CONTEXT_ARG} ${KUBE_NS_ARG} $JUPYTERLAB_POD:/home/ray/.work.tar $CUSTOM_WORKING_DIR/.work.tar +kubectl exec ${KUBE_CONTEXT_ARG} ${KUBE_NS_ARG} $JUPYTERLAB_POD -- rm /home/ray/.work.tar +tar -xvf $CUSTOM_WORKING_DIR/.work.tar -C $CUSTOM_WORKING_DIR . +rm $CUSTOM_WORKING_DIR/.work.tar +``` diff --git a/guidebooks/ml/jupyterlab/start/kubernetes/ray-server.md b/guidebooks/ml/jupyterlab/start/kubernetes/ray-server.md new file mode 100644 index 00000000..377271dd --- /dev/null +++ b/guidebooks/ml/jupyterlab/start/kubernetes/ray-server.md @@ -0,0 +1,14 @@ +--- +imports: + - ml/ray/start/kubernetes + - kubernetes/kubectl + - kubernetes/helm3 + - kubernetes/context + - kubernetes/choose/ns +--- + +# JupyterLab on a Kubernetes cluster + +A JupyterLab server will run on a Kubernetes cluster + +--8<-- "../util/working-directory.md" diff --git a/guidebooks/ml/jupyterlab/start/local/image.md b/guidebooks/ml/jupyterlab/start/local/image.md new file mode 100644 index 00000000..476ec826 --- /dev/null +++ b/guidebooks/ml/jupyterlab/start/local/image.md @@ -0,0 +1,26 @@ +# "Set docker executable" +```shell +export DOCKER=docker +``` + +# If 'docker' is not available use 'podman' +```shell +--- +validate: which docker +--- +export DOCKER=podman +``` + +# If 'docker' is not available use 'podman' +```shell +--- +validate: which podman +--- +echo "No container builder (e.g., docker, podman) was found" +exit 1 +``` + +# "Pulling image" +```bash +$DOCKER pull docker.io/metalcycling/jupyterlab +``` diff --git a/guidebooks/ml/jupyterlab/start/local/index.md b/guidebooks/ml/jupyterlab/start/local/index.md new file mode 100644 index 00000000..a5436f08 --- /dev/null +++ b/guidebooks/ml/jupyterlab/start/local/index.md @@ -0,0 +1,39 @@ +--- +imports: + - ./local/image.md +--- + +# JupyterLab on a local server + +A JupyterLab server will run on your local computer + +--8<-- "../util/working-directory.md" + +```shell.async +$DOCKER run --privileged=true \ + --rm --net=host \ + -v $CUSTOM_WORKING_DIR:/home/ray/code \ + -u root \ + --name jupyterlab \ + docker.io/metalcycling/jupyterlab:latest jupyter-lab \ + --notebook-dir=/home/ray/code --port=8888 --no-browser --allow-root +``` + +```shell +while [ "$($DOCKER ps -q -f name=jupyterlab)" = "" ] +do + sleep 1 +done + +sleep 1 + +server_data=$($DOCKER exec jupyterlab jupyter server list --json) +url=$($DOCKER exec jupyterlab python3 -c "false = False; print(${server_data}['url'])") +token=$($DOCKER exec jupyterlab python3 -c "false = False; print(${server_data}['token'])") +xdg-open ${url}lab?token=${token} + +while true +do + sleep 1 +done +``` diff --git a/guidebooks/ml/jupyterlab/start/util/working-directory.md b/guidebooks/ml/jupyterlab/start/util/working-directory.md new file mode 100644 index 00000000..be4beea2 --- /dev/null +++ b/guidebooks/ml/jupyterlab/start/util/working-directory.md @@ -0,0 +1,4 @@ +=== "What working directory would you like to use? [default: ./]" + ```shell + export CUSTOM_WORKING_DIR=${choice} + ``` diff --git a/guidebooks/ml/jupyterlab/stop/kubernetes/index.md b/guidebooks/ml/jupyterlab/stop/kubernetes/index.md new file mode 100644 index 00000000..519965c3 --- /dev/null +++ b/guidebooks/ml/jupyterlab/stop/kubernetes/index.md @@ -0,0 +1,14 @@ +--- +imports: + - kubernetes/helm3 + - kubernetes/context + - kubernetes/choose/ns +--- + +# Stop The JupyterLab Server in Kubernetes + +Stop The JupyterLab Server in Kubernetes. + +```shell +helm ${KUBE_CONTEXT_ARG_HELM} ${KUBE_NS_ARG} uninstall jupyterlab || exit 0 +``` diff --git a/guidebooks/ml/ray/start/kubernetes/chart/templates/_head-deployment.tpl b/guidebooks/ml/ray/start/kubernetes/chart/templates/_head-deployment.tpl index 64359a7b..2d146c83 100644 --- a/guidebooks/ml/ray/start/kubernetes/chart/templates/_head-deployment.tpl +++ b/guidebooks/ml/ray/start/kubernetes/chart/templates/_head-deployment.tpl @@ -51,7 +51,7 @@ spec: containers: - name: ray-head image: {{ .Values.image }} - imagePullPolicy: IfNotPresent + imagePullPolicy: {{ .Values.imagePullPolicy }} command: [ "/bin/bash", "-c", "--" ] args: - {{ print "ray start --head --port=6379 --redis-shard-ports=6380,6381 --num-cpus=" .Values.podTypes.rayHeadType.CPUInteger " --num-gpus=" .Values.podTypes.rayHeadType.GPU " --object-manager-port=22345 --node-manager-port=22346 --dashboard-host=0.0.0.0 --block" }} diff --git a/guidebooks/ml/ray/start/kubernetes/chart/templates/_head-service.tpl b/guidebooks/ml/ray/start/kubernetes/chart/templates/_head-service.tpl index 48c93167..56f37fca 100644 --- a/guidebooks/ml/ray/start/kubernetes/chart/templates/_head-service.tpl +++ b/guidebooks/ml/ray/start/kubernetes/chart/templates/_head-service.tpl @@ -21,4 +21,4 @@ spec: targetPort: 6379 selector: component: ray-head -{{end}} \ No newline at end of file +{{end}} diff --git a/guidebooks/ml/ray/start/kubernetes/chart/templates/_worker-deployment.tpl b/guidebooks/ml/ray/start/kubernetes/chart/templates/_worker-deployment.tpl index 3e6fbc08..b9d4420e 100644 --- a/guidebooks/ml/ray/start/kubernetes/chart/templates/_worker-deployment.tpl +++ b/guidebooks/ml/ray/start/kubernetes/chart/templates/_worker-deployment.tpl @@ -38,7 +38,7 @@ spec: containers: - name: ray-worker image: {{ .Values.image }} - imagePullPolicy: IfNotPresent + imagePullPolicy: {{ .Values.imagePullPolicy }} command: ["/bin/bash", "-c", "--"] args: - {{ print "ray start --num-cpus=" .Values.podTypes.rayWorkerType.CPUInteger " --num-gpus=" .Values.podTypes.rayWorkerType.GPU " --address=" (include "ray.headService" .) ":6379 --object-manager-port=22345 --node-manager-port=22346 --block" }} diff --git a/guidebooks/ml/ray/start/kubernetes/chart/values.yaml b/guidebooks/ml/ray/start/kubernetes/chart/values.yaml index 2d3119ec..f0c46063 100644 --- a/guidebooks/ml/ray/start/kubernetes/chart/values.yaml +++ b/guidebooks/ml/ray/start/kubernetes/chart/values.yaml @@ -24,6 +24,8 @@ failsafes: # It's recommended to build custom dependencies for your workload into this image, # taking one of the offical `rayproject/ray` images as base. image: rayproject/ray:latest +# Define if images should be pulled or taken if present +imagePullPolicy: IfNotPresent # If a node is idle for this many minutes, it will be removed. idleTimeoutMinutes: 5 # serviceAccountName is used for the Ray head and each Ray worker. diff --git a/guidebooks/ml/ray/start/kubernetes/install-via-helm.sh b/guidebooks/ml/ray/start/kubernetes/install-via-helm.sh index f147075c..3ad0f99f 100644 --- a/guidebooks/ml/ray/start/kubernetes/install-via-helm.sh +++ b/guidebooks/ml/ray/start/kubernetes/install-via-helm.sh @@ -14,7 +14,7 @@ if [ "$KUBE_POD_MANAGER" = mcad ] || [ "$KUBE_POD_MANAGER" = kubernetes ]; then ORG=${RAY_CHART_ORG-guidebooks} REPO=${RAY_CHART_REPO-store} BRANCH=${RAY_CHART_BRANCH} - SUBDIR=guidebooks/ml/ray/start/kubernetes/chart + SUBDIR=${RAY_CHART_SUBDIR-guidebooks/ml/ray/start/kubernetes/chart} if [ "$KUBE_POD_MANAGER" = mcad ] then MCAD_ENABLED=true @@ -77,4 +77,5 @@ cd $REPO/$SUBDIR && \ --set operatorNamespace=${KUBE_NS} \ ${startupProbe} \ --set clusterOnly=${CLUSTER_ONLY-false} ${SKIP_CRDS} \ - --set image=${RAY_IMAGE} + --set image=${RAY_IMAGE} \ + --set imagePullPolicy=${IMAGE_PULL_POLICY} diff --git a/guidebooks/util/jq.md b/guidebooks/util/jq.md index d30670bc..ec8d4314 100644 --- a/guidebooks/util/jq.md +++ b/guidebooks/util/jq.md @@ -12,6 +12,7 @@ ``` === "Linux" + ```shell --- validate: which jq