# User Image Stacks ## Overview Our Docker image stack hierarchy is inspired by [jupyter/docker-stacks](https://jupyter-docker-stacks.readthedocs.io/en/latest/index.html), but customized for our specific needs. This document outlines our image hierarchy, key features, and customizations. ## Image Hierarchies ### jupyter/docker-stacks Reference Hierarchy ```{mermaid} graph TD A[ubuntu LTS with point release] --> B[docker-stacks-foundation] B --> C[base-notebook] C --> D[minimal-notebook] D --> E[scipy-notebook] D --> F[r-notebook] D --> G[julia-notebook] E --> H[tensorflow-notebook] E --> I[pytorch-notebook] E --> J[datascience-notebook] E --> K[pyspark-notebook] K --> L[all-spark-notebook] ``` ### Our Custom Hierarchy ```{mermaid} graph TD A["ubuntu
(LTS with point release)"] --> B[docker-stacks-foundation] C["ubuntu
+ CUDA"] --> B["polusai/notebooks-hub-stacks-foundation
"] B --> D["polusai/notebook
"] B --> E["polusai/dashboard-base
(add jhsingle-native-proxy)"] E --> F["polusai/vscode
"] E --> G["polusai/rstudio
"] E --> H["polusai/rshiny
"] E --> I["polusai/pshiny
"] E --> J["polusai/dash
"] E --> K["polusai/solara
"] E --> L["polusai/streamlit
"] E --> M["polusai/voila
"] ``` ## Base Images ### polusai/notebooks-hub-stacks-foundation This image is based on [`jupyter/docker-stacks-foundation`](https://jupyter-docker-stacks.readthedocs.io/en/latest/using/selecting.html#jupyter-docker-stacks-foundation) with additional customizations. #### Features from jupyter/docker-stacks-foundation: - Package managers: - [conda](https://github.com/conda/conda): Cross-platform, language-agnostic binary package manager - [mamba](https://github.com/mamba-org/mamba): Faster reimplementation of conda in C++ (default package manager) - Unprivileged user `jovyan` (uid=1000, configurable) in group `users` (gid=100) - `tini` and `start.sh` script as container entry point - `run-hooks.sh` script for sourcing/running files in a given directory - Options for passwordless sudo - Common system libraries (bzip2, ca-certificates, locales) - `wget` for downloading external files - No preinstalled scientific computing packages #### Additional customizations: - Lmod for managing environment modules across Notebooks Hub - `apt-fast` for faster .deb package installation - Additional tools: `gdebi-core`, `vim`, `nano`, `jq` - Symbolic link to shared directory: `/home/jovyan/shared` -> `/opt/shared` ### `polusai/notebook` Based on the foundation image, this adds: - JupyterLab - Related packages and extensions ### `polusai/dashboard-base` This image adds: - `jhsingle-native-proxy` for making any web-based application compatible with JupyterHub by presenting itself as a Jupyter Server. - A wrapper script for starting applications ## Application-specific Images The following images are based on the `dashboard-base` image: 1. `polusai/vscode`: VS Code development environment based on [Coder](https://coder.com/) 2. `polusai/rstudio`: RStudio IDE for R development 3. `polusai/rshiny`: R Shiny for interactive web applications 4. `polusai/pshiny`: Python-based Shiny alternative 5. `polusai/dash`: Plotly Dash for analytical web applications 6. `polusai/solara`: Streamlit-like framework for data apps 7. `polusai/streamlit`: Streamlit for data applications 8. `polusai/voila`: Voilà for converting Jupyter notebooks to standalone applications ## Usage ### Building a Dashboard Image To build a custom dashboard image, navigate to the directory containing your Dockerfile and run the following command: ```sh docker build . -t ``` This command will use the latest `dashboard-base` image by default to create your custom dashboard image. ### Running the Dashboard Container To run the dashboard container, you need to set the `DASHBOARD_PORT` environment variable and ensure that the necessary port (default is 8888) is open. You can use the following `docker run` command to start your container: ```sh docker run -d -p 8888:8888 -e DASHBOARD_PORT=8888 ``` Here's a breakdown of the command: - `docker run -d`: Run the container in detached mode. - `-p 8888:8888`: Map the container's port 8888 to the host's port 8888. - `-e DASHBOARD_PORT=8888`: Set the `DASHBOARD_PORT` environment variable to 8888. - ``: Replace this with the name of your custom dashboard image. After running this command, your dashboard application should be accessible via `http://localhost:8888`. ### Example Suppose you have built an image named `my-custom-dashboard`. You would start it with: ```sh docker run -d -p 8888:8888 -e DASHBOARD_PORT=8888 my-custom-dashboard ``` This will launch your custom dashboard and make it accessible at `http://localhost:8888`. By following these steps, you can easily build and run your custom dashboard images within the Notebooks Hub environment. ## Customization The `polusai/dashboard-base` image is designed to simplify the adoption of new web-based applications. This flexibility allows you to easily add and integrate various applications into your Notebooks Hub environment. ### Prerequisites for New Applications To successfully integrate a new application using the `dashboard-base` image, ensure that the application meets these two key requirements: 1. **Command-line Startup**: The application must be capable of starting from the command line in a Linux environment. 2. **Web Application Port**: The application should serve its web interface on a port, ideally one that is configurable. ### Steps to Integrate a New Application 1. **Create a New Dockerfile**: Start with the `polusai/dashboard-base` image as your base: ```dockerfile ARG ROOT_CONTAINER=polusai/dashboard-base:latest FROM $ROOT_CONTAINER USER root COPY start-dashboard.sh /usr/bin RUN chmod +x /usr/bin/start-dashboard.sh USER $NB_USER # Start the dashboard CMD ["/usr/bin/dashboard-wrapper.sh"] ``` 2. **Create a start-dashboard.sh script**: This script will contain the specific logic to start your application. Here's an example based on the Streamlit implementation: ```bash #!/bin/bash # run dashboard based on type if [ "$DASHBOARD_TYPE" == "file" ]; then $PYTHON_EXEC_PATH -m pip install your-application==1.0.0 dependency1 dependency2 $JHSINGLE_COMMAND --destport 8080 $PYTHON_EXEC_PATH {-}m your-application run $DASHBOARD_PATH {--}port 8080 {--}other-options elif [ "$DASHBOARD_TYPE" == "folder" ]; then echo "ERROR: Your application requires file path to be provided" >&2 exit 1 elif [ "$DASHBOARD_TYPE" == "none" ]; then echo "ERROR: Your application requires file path to be provided" >&2 exit 1 fi ``` 3. **Understand the dashboard-wrapper.sh script**: The `dashboard-wrapper.sh` script in the `dashboard-base` image sets up the environment and calls your `start-dashboard.sh` script. Key points: - It activates Lmod and loads environment modules. - Sets up the `JHSINGLE_COMMAND` with appropriate options. - Sources your `start-dashboard.sh` script. 4. **Configure jhsingle-native-proxy**: The `JHSINGLE_COMMAND` in `dashboard-wrapper.sh` handles the proxying of your application. Ensure your `start-dashboard.sh` uses this command correctly. 5. **Port Configuration**: Use the `$DASHBOARD_PORT` environment variable (set in `dashboard-wrapper.sh`) for your application's port. 6. **Build and Test**: Build your new image and test it to ensure the application starts correctly and is accessible through the proxy. ### Example: Integrating a Hypothetical Web App Let's say you want to integrate a hypothetical web application called "DataViz". Here's how you might set it up: Dockerfile: ```dockerfile ARG ROOT_CONTAINER=polusai/dashboard-base:latest FROM $ROOT_CONTAINER COPY start-dashboard.sh /usr/bin CMD ["/usr/bin/dashboard-wrapper.sh"] ``` start-dashboard.sh: ```bash #!/bin/bash if [ "$DASHBOARD_TYPE" == "file" ]; then $PYTHON_EXEC_PATH -m pip install dataviz==1.0.0 required-dependency1 required-dependency2 $JHSINGLE_COMMAND --destport 8080 $PYTHON_EXEC_PATH {-}m dataviz run $DASHBOARD_PATH {--}port 8080 {--}server.address=0.0.0.0 {--}server.headless True elif [ "$DASHBOARD_TYPE" == "folder" ]; then echo "ERROR: DataViz requires a file path to be provided" >&2 exit 1 elif [ "$DASHBOARD_TYPE" == "none" ]; then echo "ERROR: DataViz requires a file path to be provided" >&2 exit 1 fi ``` This setup installs the DataViz application, sets its port, and uses `jhsingle-native-proxy` (via `$JHSINGLE_COMMAND`) to make it available through the standard Notebooks Hub interface. ### Separation of dashboarding tools from the environment In Notebooks Hub, there are broadly two types of dashboarding tools: those that can be separated from the environment they use and those that cannot. #### Tools that can run independently Examples of tools that can run independently: - Voila. It has a flag `--VoilaExecutor.kernel_name` which allows to point it to a separate environment. - RStudio. It has a flag `--rsession-which-r` which allows to point it to R executable in a separate environment. - RShiny. It implicitly uses `R` executable which can be remapped to a separate environment. #### Tools That Need Runtime Installation of Dashboarding Library These tools normally import Python packages from the same environment they are installed in and don't have any configuration flags to point them to a separate environment: - PyShiny - Streamlit - Solara - Dash We install packages at runtime in the start-dashboard.sh script because we combine the user-provided environment with Shiny dependencies, and thus cannot install the packages during the Docker build. ### Best Practices ✅ Use environment variables like `$DASHBOARD_TYPE`, `$DASHBOARD_PATH`, and `$PYTHON_EXEC_PATH` provided by the `dashboard-base` image. ✅ Include necessary dependencies and their versions in your `start-dashboard.sh` script. ✅ Handle different `DASHBOARD_TYPE` scenarios appropriately. ✅ Use the `$JHSINGLE_COMMAND` for starting your application to ensure proper integration with the proxy. ✅ Test thoroughly to ensure compatibility with the Notebooks Hub environment. ✅ Consider adding health checks or additional error handling in your `start-dashboard.sh` script. ✅ Consider the [type of dashboarding application](#separation-of-dashboarding-tools-from-the-environment) when adding a new one to ensure proper configuration and integration. By following these guidelines and the provided example, you can easily extend the functionality of your Notebooks Hub environment with new web-based applications, leveraging the flexibility provided by the `dashboard-base` image.