DEV Community

Priyab Dash
Priyab Dash

Posted on • Edited on • Originally published at Medium

Setting JupyterLab With Docker & Raspberry Pi 4

Originally posted here

Date: 25-Jan-2022

Background

I have been trying to find a suitable Web IDE for my projects which I can use directly from browser. I found some websites and some truly cloud IDEs but nothing that can run out of docker on my PC or on Raspberry pi.

Some examples of popular pure cloud IDEs are:

  • ShiftEdit
  • Cloud9
  • Eclipse Che
  • replit
  • Github codespaces

Also I found IceCoder, which is more of a Web editor, but it was not a real IDE. I wanted an IDE which was not only a web editor but also share a terminal and support executing multiple backend. In this only jupyterlab fit the bit. While I have use jupyter notebook and jupyter lab in docker earlier but ws not sure if it will run on my raspberry pi 4. While I expected it to be few hour work, getting the right Dockerfile setup itself took me a whole day and was a frustrating and learning experience. I am writing this blog to capture my experience and also share some of the challenges I faced in setting up the environment.

Envionment Setup

Installing Docker

For this POC I already had installed Docker on my Raspberry Pi 4, you can get the instructions in below link.

https://docs.docker.com/engine/install/debian/

I use docker compose to manage the environment dependencies, I had installed docker-compose directly from pip using the below command https://pypi.org/project/docker-compose/

pip install docker-compose
Enter fullscreen mode Exit fullscreen mode

Note: The New compose is being integrated into the docker command and that may change things in future

The Folder Structure

I used the following folder structure as I felt I may be installing other dependencies my docker-compose file in future. Feel free to change to your needs


- docker-compose.yml
- docker
    - jupyter
        - Dockerfile
        - entrypoint.sh
- notebooks
Enter fullscreen mode Exit fullscreen mode

docker-compose.yml -> the compose file to note the services, volumes and dependencies to setup my docker environment
Dockerfile -> This contains the reference to base image the steps for installing Jupyterlab and running it.
entrypoint.sh -> This is the entry point script that contains the logic to start Jupyterlab process

Dockerfile

The Dockerfile looks as below

FROM balenalib/raspberry-pi-debian-python:3-buster

RUN apt-get update && apt-get -y update
RUN apt-get install -y build-essential python3-dev libffi-dev

RUN pip3 install --no-cache-dir jupyterlab

RUN mkdir /work

COPY ./docker/jupyter/entrypoint.sh /work/entrypoint.sh

RUN mkdir /work/notebook

WORKDIR /work/notebook

EXPOSE 8081
RUN chmod -R 765 /work/*.sh
ENTRYPOINT ["/work/entrypoint.sh"]
CMD ["server"]
Enter fullscreen mode Exit fullscreen mode

The most important part was selecting the base image balenalib/raspberry-pi-debian-python:3-buster I tried multiple base images from Ubuntu, to python3-slim and even the latest Debian version, but there seems to be a continuing problem is Debian Bullseye version which seems to break pip.

The second most important part was to install build-essentials and python3-dev as there are many platform specific components in jupyterlab for which gcc and g++ will be required during the build process.

I had setup the install folder as /work and created a new folder called /work/notebook where all notebooks can be kept and exposed as a volume

entrypoint.sh

The entrypoint.sh looks as below, this is a format I have used from my earlier formats, its simple and works for me

#!/bin/bash
set -e

echo "Container's IP address: `awk 'END{print $1}' /etc/hosts`"

if [ "$1" = 'server' ]; then
   jupyter lab --port=8081 --no-browser --ip=0.0.0.0 --allow-root
else
    exec "$@"
fi
Enter fullscreen mode Exit fullscreen mode

The main part in this script is to point --ip to 0.0.0.0, this is to make sure the Http connections from outside the container can hit the Jupyterlab process. While this may be obvious to other developers, it was a pain to me when it was pointing to localhost by default and connections were not hitting Jupyterlab

docker-compose yml file

The docker-compose.yml is also very simple where only one service jupyterlab is defined.

version: '3'
services:
  jupyterlab:
    build:
      context: .
      dockerfile: ./docker/jupyter/Dockerfile
    image: "bobquest33/jupyterlab:latest"
    restart: always
    user: root
    volumes:
      - /path/to/jupyterlab/notebooks:/work/notebook
    ports:
      - 8081:8081
    container_name: jupyterlab-container
Enter fullscreen mode Exit fullscreen mode

Under build tag, the context folder path and the dockerfile path are important as they point to the current path from where you will be executing the docker-compose.

The section under volumes is important as there I map my local notebooks folder to the path /work/notebook under the container so that any files or notebooks created or modified under jupyter container are persisted on the host system disk.

Running Jupyterlab

Once we are ready, we can run the Jupyterlab with the below command from the root directory where docker-compose.yml is present

docker-compose up --build -d
Enter fullscreen mode Exit fullscreen mode

The -d option makes the Jupyterlab service run in the background and the --build flag ensures that if any changes are made to the Dockerfile the Jupyterlab container image is built.

As a next step you will have to run docker-compose logs to check the URL for Jupyterlab , it will look as below

 To access the server, open this file in a browser:                                      
     file:///root/.local/share/jupyter/runtime/jpserver-9-open.html                      
 Or copy and paste one of these URLs:                                                    
     http://1a84add9fdd7:8081/lab?token=3078938b3dbe06114e11bfd53b91568bbaaf20cb7bc382cd 
  or http://127.0.0.1:8081/lab?token=3078938b3dbe06114e11bfd53b91568bbaaf20cb7bc382cd    
Enter fullscreen mode Exit fullscreen mode

URL format to put in web browser will be http://<raspberry ip address>:8081/lab?token=<url token value>

e.g. http://192.168.11.21:8081/lab?token=3078938b3dbe06114e11bfd53b91568bbaaf20cb7bc382cd

Challenges & Solution

Base Docker Image

When I started this POC, I thought this will be a quick 1 hour thing, but soon it turned to a whole night and day's worth of work. First big challenge was selecting the base image for Jupyterlab, first I tried many base docker images from Belina, but one of the most toughest part was getting any additional library installed, installing wget was getting challenging.

First I tried installing using Jupyterlab using conda, but installing Miniconda, proved challenging on many of the base images. Finally I settled to installing balenalib/raspberry-pi-debian-python base image, but again its latest version which points to Debian "Bullseye" where pip broke with time.time() with error PermissionError: [Errno 1] Operation not permitted, detailed description below:

from pip._internal.utils import _log
File "/usr/local/lib/python3.10/site-packages/pip/_internal/utils/_log.py", line 8, in 
<module>
import logging
File "/usr/local/lib/python3.10/logging/__init__.py", line 57, in <module>
_startTime = time.time()
PermissionError: [Errno 1] Operation not permitted
Enter fullscreen mode Exit fullscreen mode

More details in the given link.

As a solution I pointed to the Debian buster version balenalib/raspberry-pi-debian-python:3-buster of the base image, things proceeded fine. After the Docker file was updated and simplified, and the Jupyterlab was compiled.

Installing pip dependencies

The next step that took longest time for me was figuring out what Debian packages to install, earlier I did the mistake upgrading pip in the Dockerfile, this prevented even to install Jupyterlab, then I have to install buildessentials and python3-dev to make sure the native dependencies of the dependent packages of pip gets installed.

Bash script format

Lastly, the container did not start, I finally realised that the entrypoint.sh that I have developed was have CRLF which is DOS format, it should be in Unix format converting the file to Unix using dos2unix helped.

dos2unix entrypoint.sh
Enter fullscreen mode Exit fullscreen mode

Next Steps

Overall this was a great experience, I learnt many things about how package a web product on Raspberry pi and how its not as straight forward as installing on a x86 Windows or even a Mac machine and how I need to spend more time improving my docker expertise.

References

Top comments (0)