Copy
heat_template_version: 2021-04-16
description: >
NeevCloud: JupyterLab AI Workspace with Python data science stack.
Flavor minimum: StellarSurge (4 vCPU, 16 GB RAM).
JupyterLab accessible on port 8888.
parameters:
image:
type: string
default: Ubuntu 22.04 Updated
flavor:
type: string
default: StellarSurge
volume_size:
type: number
description: Boot volume size in GB
default: 60
public_network:
type: string
default: Public
key_name:
type: string
default: stack-test
resources:
net:
type: OS::Neutron::Net
properties:
name: jupyter-net
subnet:
type: OS::Neutron::Subnet
properties:
network: { get_resource: net }
cidr: 10.200.0.0/24
dns_nameservers: [8.8.8.8, 8.8.4.4]
router:
type: OS::Neutron::Router
properties:
external_gateway_info:
network: { get_param: public_network }
router_interface:
type: OS::Neutron::RouterInterface
properties:
router: { get_resource: router }
subnet: { get_resource: subnet }
sg:
type: OS::Neutron::SecurityGroup
properties:
name: jupyter-sg
rules:
- { direction: ingress, protocol: tcp, port_range_min: 22, port_range_max: 22, remote_ip_prefix: 0.0.0.0/0 }
- { direction: ingress, protocol: tcp, port_range_min: 8888, port_range_max: 8888, remote_ip_prefix: 0.0.0.0/0 }
- { direction: ingress, protocol: icmp, remote_ip_prefix: 0.0.0.0/0 }
server:
type: OS::Nova::Server
properties:
name: jupyterlab-server
flavor: { get_param: flavor }
key_name: { get_param: key_name }
networks:
- network: { get_resource: net }
security_groups:
- { get_resource: sg }
block_device_mapping_v2:
- boot_index: 0
delete_on_termination: true
image: { get_param: image }
volume_size: { get_param: volume_size }
user_data_format: RAW
user_data: |
#!/bin/bash
set -e
export DEBIAN_FRONTEND=noninteractive
JUPYTER_PASS=$(openssl rand -hex 12)
apt-get update -y
apt-get install -y python3 python3-pip python3-venv
# Create dedicated user and venv
useradd -m -s /bin/bash jupyter
su - jupyter -c "python3 -m venv /home/jupyter/venv"
su - jupyter -c "/home/jupyter/venv/bin/pip install --upgrade pip"
su - jupyter -c "/home/jupyter/venv/bin/pip install jupyterlab numpy pandas scikit-learn matplotlib seaborn transformers torch --extra-index-url https://download.pytorch.org/whl/cpu"
# Generate hashed password
HASH=$(su - jupyter -c "/home/jupyter/venv/bin/python3 -c \"from jupyter_server.auth import passwd; print(passwd('${JUPYTER_PASS}'))\"")
# JupyterLab config
mkdir -p /home/jupyter/.jupyter
cat > /home/jupyter/.jupyter/jupyter_lab_config.py <<JCFG
c.ServerApp.ip = '0.0.0.0'
c.ServerApp.port = 8888
c.ServerApp.open_browser = False
c.ServerApp.password = '${HASH}'
c.ServerApp.allow_remote_access = True
JCFG
chown -R jupyter:jupyter /home/jupyter/.jupyter
# Systemd service
cat > /etc/systemd/system/jupyterlab.service <<SVC
[Unit]
Description=JupyterLab
After=network.target
[Service]
User=jupyter
WorkingDirectory=/home/jupyter
ExecStart=/home/jupyter/venv/bin/jupyter lab --config=/home/jupyter/.jupyter/jupyter_lab_config.py
Restart=always
[Install]
WantedBy=multi-user.target
SVC
systemctl daemon-reload
systemctl enable jupyterlab && systemctl start jupyterlab
cat > /root/.jupyter_credentials <<EOF
JupyterLab Password: ${JUPYTER_PASS}
URL: http://<FLOATING_IP>:8888
EOF
chmod 600 /root/.jupyter_credentials
fip:
type: OS::Neutron::FloatingIP
properties:
floating_network: { get_param: public_network }
fip_assoc:
type: OS::Neutron::FloatingIPAssociation
properties:
floatingip_id: { get_resource: fip }
port_id: { get_attr: [server, addresses, { get_resource: net }, 0, port] }
outputs:
jupyterlab_url:
description: JupyterLab URL
value:
str_replace:
template: http://HOST:8888
params:
HOST: { get_attr: [fip, floating_ip_address] }
floating_ip:
description: Public IP
value: { get_attr: [fip, floating_ip_address] }
credentials_note:
description: JupyterLab password location
value: "SSH into server and run: cat /root/.jupyter_credentials"