Skip to content

Run Jupyter on a UbiOps deployment

When you're still experimenting with your code and it's not yet completely production ready, you might want to use Jupyter notebooks to help you iterate more quickly. It can also help you develop your code in the runtime environment of your deployments. By using the port forwarding functionality of deployments it is possible to run Jupyter directly in a deployment instance in UbiOps. This how-to will describe how to do that step by step.

Beware of security vulnerabilities in dev environments with Jupyter

While it is really powerful to run a Jupyter environment in a deployment, it does open up some security vulnerabilities, which you should be aware of.

  • Secret environment variables of that deployment will be visible inside the notebook environment via os.environ. So be careful with using credentials.
  • Any permission management in UbiOps does not propagate to the Jupyter environment

To run Jupyter inside a deployment, you need to perform the following steps:

  1. Create a deployment that spawns a Jupyter environment
  2. Turn on the deployment
  3. Open Jupyter URL in browser

Creating a deployment that spawns a Jupyter environment

To create a deployment that spawns a Jupyter environment, we need to put together a deployment.py, our environment specifications and then deploy it to UbiOps with the right configuration.

Note that the deployment.py will only contain code that spawns the Jupyter notebook. It is not recommended to include a deployment.py that runs your own code. Rather, the set-up can be used to develop your final deployment.py or train.py that serves your solution.

Creating the deployment.py

To spin up a Jupyter environment from Python code we can use:

import subprocess
import uuid

token = str(uuid.uuid4())
proc = subprocess.Popen(['jupyter', 'notebook', '--ip', '0.0.0.0', '--IdentityProvider.token', token],
                        stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)

This starts a Jupyter environment in the deployment. We need to obtain the IP address of the deployment to be able to access the Jupyter interface, which can be done in the following way:

# Get the IP address and print to the logs
http_request = urllib.request.urlopen("https://whatismyipv4.ubiops.com")
ip_address = http_request.read().decode("utf8")
http_request.close()

print(f"Notebook URL: http://{ip_address}:8888/tree?token={token}")

Let's put this together in a deployment.py with some error handling. The deployment will print the Jupyter link to the logs, but it will also return it in its request output for easy accessibility.

import subprocess
import urllib.request
import uuid


# This class allows us to have custom error messages when the deployment fails
class UbiOpsError(Exception):
    def __init__(self, error_message):
        super().__init__()
        self.public_error_message = error_message


class Deployment:

    def __init__(self):

        token = str(uuid.uuid4())

        try:
            self.proc = subprocess.Popen(['jupyter', 'notebook', '--ip', '0.0.0.0', '--IdentityProvider.token', token],
                                         stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
        except FileNotFoundError:
            raise UbiOpsError("Unable to start a Jupyter notebook, are the packages jupyterlab and notebook installed?")

        # Get the IP address and print to the logs
        http_request = urllib.request.urlopen("https://whatismyipv4.ubiops.com")
        ip_address = http_request.read().decode("utf8")
        http_request.close()

        self.notebook_url = f"http://{ip_address}:8888/tree?token={token}"
        print(f"Notebook URL: {self.notebook_url}")

    def request(self, data):
        return {"notebook_url": self.notebook_url}

    def stop(self):
        # Stop the Jupyter environment when the deployment is shutting down
        if self.proc is not None:
            self.proc.kill()

Preparing the environment specifications

To be able to run the deployment.py from the previous section, we need Jupyter to be available in the deployment environment. We can ensure that with the following requirements.txt:

jupyterlab==4.0.11
notebook==7.0.7
ipywidgets==8.1.2

Creating the deployment in UbiOps

Now that we have our files ready, we can create a deployment in UbiOps. You can use the following settings:

Deployment configuration
Name jupyter
Description Runs Jupyter inside a deployment
Input fields: leave empty
Output fields: name = notebook_url, datatype = string

Once you click Next: create a version you can fill in the form with the following settings:

Deployment version configuration
Version name v1
Environment python 3-11
Upload code Upload a zip file containing the deployment.py and requirements.txt from the previous sections
Instance type group 4096 MB dedicated
Port forwarding deployment port = 8888, public port = 8888, protocol = TCP
Scaling & resource settings minimum instances = 1, maximum instances = 1, idle time = 300

Any settings that aren't mentioned in the table can be left however you prefer. We set the minimum number of instances to one to ensure that an instance will start running directly after creation of the deployment version.

This deployment requires a dedicated instance

Since this deployment uses the port forwarding functionality, it requires a dedicated instance type.

Click Create and your deployment will start building!

Accessing the Jupyter Environment

In order to access the environment, we need to acquire the Jupyter URL. We can retrieve this by checking the 'Active Instances' tab at the deployment version level in the WebApp, by navigating to the logs and copying the URL from there or by sending a request to the deployment, which will return the URL in the request results.

With this url, you will have access to all the functionalities of Jupyter, with the compute power of your selected instance type! You can thus use the environment to start developing and iterating over your code.

The Jupyter environment is stateless, make sure to save your code externally

Once your code is ready, do make sure to save it somewhere where you can access it later (i.e. local or push the code to git). The deployment is completely stateless, so once you shut it down, any progress will be lost if it's not saved in an external place. The 'remote kernel' integration allows you to manage your files within the server of your IDE, while developing your code in the runtime environment of your deployment instance.

Accessing the Jupyter notebook of an instance in an internal network

When your instance is only accessible within an internal network, then the 'Active Instances' tab is the place to find the internal IP. This how-to helps you find the external IP address (using https://whatismyip4.ubiops.com). In case that this does not work because the instance can only be reached within an internal network, replace the external IP with the internal IP from the 'Active instances' tab.

There are multiple ways in which we can interact with the Jupyter environment. We will describe two of them here, namely with a browser and a remote kernel with an IDE (e.g. Visual Code).

Browser

To access the Jupyter environment from your browser is simple. Just copy the URL from the logs or the request results and visit it in your browser. You will be greeted with the Jupyter interface, where you can start creating notebooks and running code.

Resulting Jupyter environment

A remote kernel via your IDE (e.g. Visual Code)

To access the Jupyter environment from an IDE, you first need to have a Jupyter-compatible IDE installed. For most IDEs, you can install the Jupyter extension to enable this functionality. In this section, we will however focus on Visual Code, but the steps will be mostly similar for other IDEs.

Visual Code

For Visual Code, you can install the Jupyter extension by going to the extensions tab and searching for Jupyter. Once installed, we can connect to a remote Jupyter server. The official documentation provides a good and extensive guide on how to do this, it is highly recommended to follow that guide.

However, a small summary is provided below:

  1. Open a Jupyter notebook file in Visual Code
  2. Open the Kernel Picker button in the top right corner
  3. Click on Select Another Kernel ... button
  4. Click on Existing Jupyter Server ... button
  5. Now paste the Jupyter URL in the input field and press enter

You are now connected to the Jupyter environment and can start developing your code inside the UbiOps deployment!

Cleanup

To shut down the Jupyter deployment, you can set the minimum number of instances back to zero.