Skip to content

Instantly share code, notes, and snippets.

@arvati
Last active December 14, 2023 16:03
Show Gist options
  • Save arvati/072f9fda5d6bc8e2f0ef16e4a3285fb3 to your computer and use it in GitHub Desktop.
Save arvati/072f9fda5d6bc8e2f0ef16e4a3285fb3 to your computer and use it in GitHub Desktop.
Install Jupyter Hub on LXC container in OpenWrt

Install Jupyter Hub on LXC container in OpenWrt

Config LXC Containers

Config fstab btrfs subvolume /containers to hold all LXC Containers
nano /etc/config/fstab

config mount 'lxc'
        option target '/srv/lxc'
        option uuid '68706ead-a626-4209-b3d0-1187b835f803'
        option fstype 'btrfs'
	option btrfs_raid '1'
	option options 'subvol=/containers,noatime,nodiratime,noacl,nossd'
#        option options 'subvol=/containers,noatime,nodiratime,noacl,degraded,nossd,device=/dev/sda,device=/dev/sdb,device=/dev/sdc,device=/dev/sdd' 
        option enabled '1'

Config LXC to use /srv/lxc

Create or Modify config file to use /srv/lxc path in extroot of openwrt
nano /etc/lxc/lxc.conf

lxc.lxcpath = /srv/lxc

nano /etc/lxc/default.conf

lxc.net.0.type = veth
lxc.net.0.link = br-lan
lxc.net.0.flags = up

Debootstrap Debian into Container

First Clear any previous container

lxc-ls --fancy
LXC_CACHE_PATH=/srv/lxc/cache lxc-destroy --snapshots --force --name jupyter

Create debian buster container with name jupyter within btrfs subvolume

LXC_CACHE_PATH=/srv/lxc/cache lxc-create --bdev btrfs --name jupyter -t debian -- -r buster -a amd64

Patch Debian Container

Patch debian container to work on openwrt. At this moment need to uninstall systemd

chroot /srv/lxc/jupyter/rootfs bash
apt-get update --allow-releaseinfo-change
apt-get remove --purge --auto-remove systemd
apt-get install sysvinit-core sysvinit-utils
apt-get purge systemd*
echo -e 'Package: *systemd*\nPin: release *\nPin-Priority: -1\n' > /etc/apt/preferences.d/systemd
echo -e 'Package: libsystemd0\nPin: version *\nPin-Priority: 500\n' >> /etc/apt/preferences.d/nosystemd
rm /etc/systemd/system/default.target
ln -s /dev/null /etc/systemd/system/udev.service
ln -s /dev/null /etc/systemd/system/systemd-udevd.service
ln -s /lib/systemd/system/multi-user.target /etc/systemd/system/default.target
exit

Config Jupyter Container

Config lxc config file container
nano /srv/lxc/jupyter/config

lxc.net.0.hwaddr = 00:16:3e:b1:75:ba
lxc.net.0.type = veth
lxc.net.0.link = br-lan
lxc.net.0.flags = up
lxc.rootfs.path = btrfs:/srv/lxc/jupyter/rootfs
lxc.mount.entry=/mnt/data/media/Server/jupyter home/ none bind 0 0
# Common configuration
lxc.include = /usr/share/lxc/config/debian.common.conf
# Container specific configuration
lxc.tty.max = 4
lxc.uts.name = jupyter
lxc.arch = amd64
lxc.pty.max = 1024

Create home folder inside LXC Host
mkdir -p /mnt/data/media/Server/jupyter

Test Container

Test Jupyter LXC Container and create root password

lxc-start --name jupyter
lxc-attach --name jupyter
passwd

Initial Container OS Config

Install some basic packages

apt-get dist-upgrade
apt-get install -yq --no-install-recommends \
    apt-transport-https \
    nano sudo locales wget curl gpg \
    apt-utils \
    bzip2 \
    git \
    ca-certificates

Config SSH server

nano /etc/ssh/sshd_config

#PermitRootLogin yes
PasswordAuthentication yes
PermitEmptyPasswords no
ChallengeResponseAuthentication no
UsePAM yes
X11Forwarding no
PrintMotd yes
#Banner none
AcceptEnv LANG LC_*
Subsystem	sftp	/usr/lib/openssh/sftp-server

Config Timezone and Languages

Configuring Timezone

ln --force --symbolic '/usr/share/zoneinfo/America/Sao_Paulo' '/etc/localtime'
echo "America/Sao_Paulo" | tee /etc/timezone
dpkg-reconfigure --frontend=noninteractive tzdata

Configuring Languages

locale-gen pt_BR.UTF-8
locale-gen --purge en_US.UTF-8
sed -i -e 's/# pt_BR.UTF-8 UTF-8/pt_BR.UTF-8 UTF-8/' /etc/locale.gen
sed -i -e 's/en_US.UTF-8 UTF-8/# en_US.UTF-8 UTF-8/' /etc/locale.gen
echo 'LANG=pt_BR.UTF-8' > /etc/default/locale
dpkg-reconfigure --frontend=noninteractive locales
update-locale LANG='pt_BR.UTF-8'
update-locale LC_ALL='pt_BR.UTF-8'
update-locale LANGUAGE='pt_BR.UTF-8'

Sudo without passwords

sudo visudo /etc/sudoers.d/nopasswd

%sudo ALL = (ALL) NOPASSWD: ALL
Defaults exempt_group = sudo

Creating Users

Creating user manager and allow it to sudoers

adduser manager
usermod -aG sudo manager

Check sudoers users
getent group sudo
Now log into container using user and password

exit
lxc-stop -n jupyter
lxc-start -n jupyter
lxc-console --name jupyter

Create ssh login credentials

exit
#cat ~/.ssh/id_rsa.pub | ssh root@jupyter "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"
cat ~/.ssh/id_rsa.pub | ssh manager@jupyter "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"

You may Log in using ssh now

#ssh root@jupyter
ssh manager@jupyter

Jupyter Installation

Conda Repositories

curl https://repo.anaconda.com/pkgs/misc/gpgkeys/anaconda.asc | gpg --dearmor > conda.gpg
sudo install -o root -g root -m 644 conda.gpg /usr/share/keyrings/conda-archive-keyring.gpg
rm conda.gpg
sudo bash -c 'echo "deb [arch=amd64 signed-by=/usr/share/keyrings/conda-archive-keyring.gpg] https://repo.anaconda.com/pkgs/misc/debrepo/conda stable main" > /etc/apt/sources.list.d/conda.list'

Nodejs Repositories

sudo bash -c 'curl -sL https://deb.nodesource.com/setup_10.x | bash -'

Install apt packages into server

Run apt instalation

sudo apt update
sudo apt-get install -yq --no-install-recommends \
    fonts-liberation \
    python3 python3-dev \
    python3-pip \ 
    python3-venv \
    python3-setuptools \ 
    libzmq3-dev \
    libssl-dev \
    libcurl4-openssl-dev \
    build-essential \
    nodejs \
    conda \
    luarocks \
    libczmq4 \ 
    ruby ruby-dev \ 
    libtool libffi-dev

Install npm packages

sudo npm install -g configurable-http-proxy
sudo npm config set prefix /opt/jupyterhub/
sudo npm config set python /opt/jupyterhub/bin/python3
sudo npm install -g --unsafe-perm tslab

Install Gem packages

sudo bash -c 'export GEM_HOME=/opt/jupyterhub/ && gem install cztop'
sudo bash -c 'export GEM_HOME=/opt/jupyterhub/ && gem install ffi-rzmq'
sudo bash -c 'export GEM_HOME=/opt/jupyterhub/ && gem install iruby --pre'

Setup the JupyterHub and JupyterLab in a virtual environment

sudo python3 -m venv /opt/jupyterhub/
sudo /opt/jupyterhub/bin/python3 -m pip install --upgrade pip
sudo /opt/jupyterhub/bin/python3 -m pip install wheel
sudo /opt/jupyterhub/bin/python3 -m pip install jupyterhub jupyterlab notebook
sudo /opt/jupyterhub/bin/python3 -m pip install ipywidgets
sudo /opt/jupyterhub/bin/python3 -m pip install pycurl jupyterhub-dummyauthenticator jupyterhub-firstuseauthenticator jupyterhub-nativeauthenticator jupyterhub-tmpauthenticator oauthenticator
sudo /opt/jupyterhub/bin/python3 -m pip install ilua bash_kernel

Create jupyterhub config file

sudo mkdir -p /opt/jupyterhub/etc/jupyterhub/
cd /opt/jupyterhub/etc/jupyterhub/
sudo /opt/jupyterhub/bin/jupyterhub --generate-config

Create jupyterhub server folder

sudo mkdir -p /opt/jupyterhub/srv/jupyterhub/

Jupyterhub Config

sudo nano /opt/jupyterhub/etc/jupyterhub/jupyterhub_config.py

c.JupyterHub.data_files_path = '/opt/jupyterhub/share/jupyterhub'
c.Spawner.default_url = '/lab' 
c.Authenticator.admin_users = { 'manager' }
c.Authenticator.whitelist = { 'manager' }
c.JupyterHub.ssl_key = '/opt/jupyterhub/etc/jupyterhub/certs/jupyter.key'
c.JupyterHub.ssl_cert = '/opt/jupyterhub/etc/jupyterhub/certs/jupyter.crt'
c.JupyterHub.cookie_secret_file = '/opt/jupyterhub/srv/jupyterhub/jupyterhub_cookie_secret'
c.JupyterHub.db_url = 'sqlite:///opt/jupyterhub/srv/jupyterhub/jupyterhub.sqlite'

Install a default conda environment for all users

Conda was installed into the folder /opt/conda/, with the conda command available at /opt/conda/bin/conda

sudo ln -s /opt/conda/etc/profile.d/conda.sh /etc/profile.d/conda.sh
sudo mkdir /opt/conda/envs/
sudo /opt/conda/bin/conda create --prefix /opt/conda/envs/python python=3.7 ipykernel
sudo /opt/conda/envs/python/bin/python -m ipykernel install --prefix=/opt/jupyterhub/ --name 'python' --display-name "Python (default)"
sudo /opt/conda/envs/python/bin/python -m ipykernel install --prefix /usr/local/ --name 'python' --display-name "Python (default)"

Install Bash Kernel

sudo /opt/jupyterhub/bin/python3 -m bash_kernel.install --prefix=/opt/jupyterhub/

Install Javascript Kernel

sudo /opt/jupyterhub/bin/tslab install --prefix=/opt/jupyterhub/ --python=/opt/jupyterhub/bin/python3

Install Ruby Kernel

sudo bash -c 'export GEM_HOME=/opt/jupyterhub/ && /opt/jupyterhub/bin/iruby register --force --prefix=/opt/jupyterhub/'
sudo cp -r /root/.local/share/jupyter/kernels/ruby /opt/jupyterhub/share/jupyter/kernels/
sudo /opt/jupyterhub/bin/jupyter kernelspec remove ruby
sudo bash -c 'echo export GEM_HOME=\"/opt/jupyterhub/\${GEM_HOME:+:\${GEM_HOME}}\" > /etc/profile.d/iruby.sh'

List Kernels Installed
sudo /opt/jupyterhub/bin/jupyter kernelspec list

Available kernels:
  bash       /opt/jupyterhub/share/jupyter/kernels/bash
  jslab      /opt/jupyterhub/share/jupyter/kernels/jslab
  lua        /opt/jupyterhub/share/jupyter/kernels/lua
  python3    /opt/jupyterhub/share/jupyter/kernels/python3
  tslab      /opt/jupyterhub/share/jupyter/kernels/tslab

Add /opt/jupyterhub/bin folder to $PATH for all users

sudo bash -c 'echo export PATH=\"/opt/jupyterhub/bin\${PATH:+:\${PATH}}\" > /etc/profile.d/jupyterhub.sh'
sudo bash -c 'echo export PATH=\"/opt/jupyterhub/bin\${PATH:+:\${PATH}}\" >> /root/.profile'

Setting up users’ own conda environments

There is relatively little for the administrator to do here, as users will have to set up their own environments using the shell. On login they should run conda init or /opt/conda/bin/conda.
They can then use conda to set up their environment, although they must also install ipykernel. Once done, they can enable their kernel using:

/path/to/kernel/env/bin/python -m ipykernel install --name 'python-my-env' --display-name "Python My Env"

Install Extensions

sudo /opt/jupyterhub/bin/jupyter labextension install --no-build @jupyter-widgets/jupyterlab-manager
sudo /opt/jupyterhub/bin/jupyter lab build --minimize=False --dev-build=False

Make SSL certificate

Making certificate files:

  • keyfile=/root/certs/jupyter.key
  • certfile=/root/certs/jupyter.crt
su root
sudo mkdir -p /opt/jupyterhub/etc/jupyterhub/certs
cd /opt/jupyterhub/etc/jupyterhub/certs
sudo openssl req -x509 -newkey rsa:4096 -sha256 -days 3650 -nodes -keyout jupyter.key -out jupyter.crt -subj /CN=example.com -addext subjectAltName=DNS:jupyter.casa,DNS:jupyter,IP:192.168.1.77

Init Scripts

sudo nano /etc/init.d/jupyterhub
sudo chmod +x /etc/init.d/jupyterhub
sudo update-rc.d jupyterhub defaults
sudo service jupyterhub start
sudo cat /var/log/jupyterhub.err
sudo cat /var/log/jupyterhub.log

Default Values

sudo nano /etc/default/jupyterhub

DIR="/opt/jupyterhub/"
CONFIG_FILE="/opt/jupyterhub/etc/jupyterhub/jupyterhub_config.py"

Running Server Manually

sudo /opt/jupyterhub/bin/jupyterhub -f /opt/jupyterhub/etc/jupyterhub/jupyterhub_config.py

Generate Password hash

python3
>>> from notebook.auth import passwd; passwd(); exit();
'sha1:efc3bc450b78:d299ead28c08f11029e28791308f8431d6e18778'
#!/bin/sh
### BEGIN INIT INFO
# Provides: jupyterhub
# Required-Start: $local_fs $network $named $time $syslog
# Required-Stop: $local_fs $network $named $time $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Description: code-server - VS Code on a remote server
### END INIT INFO
user="root"
name=`basename $0`
[ -f /etc/default/$name ] && . /etc/default/$name
if [ -z "$DIR" ] ; then
DIR="/opt/jupyterhub"
fi
if [ -z "$CONFIG_FILE" ] ; then
CONFIG_FILE="/opt/jupyterhub/etc/jupyterhub/jupyterhub_config.py"
fi
pid_file="/var/run/$name.pid"
stdout_log="/var/log/$name.log"
stderr_log="/var/log/$name.err"
PATH="$DIR/bin:/opt/conda/condabin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
cmd="env PATH=$PATH $DIR/bin/$name -f $CONFIG_FILE"
get_pid() {
cat "$pid_file"
}
is_running() {
[ -f "$pid_file" ] && ps -p `get_pid` > /dev/null 2>&1
}
case "$1" in
start)
if is_running; then
echo "Already started"
else
echo "Starting $name"
cd "$dir"
if [ -z "$user" ]; then
sudo $cmd >> "$stdout_log" 2>> "$stderr_log" &
else
sudo -u "$user" $cmd >> "$stdout_log" 2>> "$stderr_log" &
fi
echo $! > "$pid_file"
if ! is_running; then
echo "Unable to start, see $stdout_log and $stderr_log"
exit 1
fi
fi
;;
stop)
if is_running; then
echo -n "Stopping $name.."
kill `get_pid`
for i in 1 2 3 4 5 6 7 8 9 10
# for i in `seq 10`
do
if ! is_running; then
break
fi
echo -n "."
sleep 1
done
echo
if is_running; then
echo "Not stopped; may still be shutting down or shutdown may have failed"
exit 1
else
echo "Stopped"
if [ -f "$pid_file" ]; then
rm "$pid_file"
fi
fi
else
echo "Not running"
fi
;;
restart)
$0 stop
if is_running; then
echo "Unable to stop, will not attempt to start"
exit 1
fi
$0 start
;;
status)
if is_running; then
echo "Running"
else
echo "Stopped"
exit 1
fi
;;
*)
echo "Usage: $0 {start|stop|restart|status}"
exit 1
;;
esac
exit 0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment