如何搭建私有jupyter在线平台

1 基础环境搭建
1.1 运行环境
首先需要一个vps,可以自购阿里云、腾讯云等,有能力也可以gcp、awz等,具体需要什么配置根据jupyter运行需求自定义即可。教程系统安装使用了debain10,其他系统自行搜索修改对应的命令行
1.2 网站准备(可选)
自行注册一个域名,并进行必要的合规性操作,把dns解析到刚刚购买的vps的ip上,这里使用了cloudflare,网站准备和dns解析太过基础,此处不展开描述,网站主要是给nginx配置反向代理用的,如果直接ip访问可以跳过这一步,一般还是建议配个web地址,便于cdn保护ip避免被攻击
1.3 基础软件
基础配置涉及的代码主要如下,首先是内核升级:
# 大环境更新
apt update
apt upgrade
# (可选)安装bbr(推荐xanmod+bbr2+cake),并开启ipv6和参数优化
# 升级可参考官方网站:https://xanmod.org/
# 参考:https://github.com/ylx2016/Linux-NetSpeed
# 部分厂商的vps可能不支持自定义修改内核,请慎重操作,重要资料请提前备份
wget -O tcpx.sh "https://git.io/JYxKU" && chmod +x tcpx.sh && ./tcpx.sh然后是安装nginx,参考如下:
# 安装nginx,https://nginx.org/en/linux_packages.html#Debian
sudo apt install curl gnupg2 ca-certificates lsb-release debian-archive-keyring
curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor \
| sudo tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null
gpg --dry-run --quiet --import --import-options import-show /usr/share/keyrings/nginx-archive-keyring.gpg
# echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \
# http://nginx.org/packages/debian `lsb_release -cs` nginx" \
# | sudo tee /etc/apt/sources.list.d/nginx.list
# mainline 比 stable 性能稍微好点
echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \
http://nginx.org/packages/mainline/debian `lsb_release -cs` nginx" \
| sudo tee /etc/apt/sources.list.d/nginx.list
sudo apt update
sudo apt install nginx
mkdir /etc/nginx/logs还有安装各类常用软件和用户管理,如下:
# 安装基础软件(如果是windows安装软件包,可能需要手动配置系统环境变量)
# sklearn2pmml需要java环境支持,graphviz的python包也需要graphviz软件支持
# graphviz安装参考:https://graphviz.org/download/
apt install curl sudo git p7zip-full graphviz default-jdk-headless
# 可以编译最新的gcc
apt install gcc
# 安装nodejs,https://github.com/nodesource/distributions/blob/master/README.md#deb
# curl -fsSL https://deb.nodesource.com/setup_20.x | bash - &&\
# apt-get install -y nodejs
# 用户管理
adduser conda_env # 添加用户
passwd conda_env # 修改密码
deluser --remove-home conda_env # 删除用户
# 如有则需要关闭防火墙
ufw disablejupyter是可以通过web面板登录后执行任务的,如果是root对外服务,密码泄露或者jupyter有漏洞,黑客登陆后获取root权限,一把梭rm -rf /*,你机器就没了…,或者拿了你的机器做了什么坏事也会很麻烦其他有需要可自行搜索
1.4 中文字体安装(可选)
当然,如果是windows环境可以跳过这一步,一般linux还是推荐安装一下中文字体,因为很多python的科学计算模块(例如matplotlib)在绘图等场景需要涉及到中文字体渲染,可以直接使用windows的中文字体,我常用的"宋体"的路径是C:\Windows\Fonts\simsun.ttc和C:\Windows\Fonts\simsunb.ttf,也可以直接点击我上传的字体文件simsun.ttc、simsunb.ttf来下载,把这两个文件上传到vps,然后执行以下命令:
# debain提前安装一下字体管理,ubuntu好像不用,其他系统搜索自行安装
apt install xfonts-utils fontconfig -y
# 如果想用win的字体,则创建字体并上传win宋体字体
cd /usr/share/fonts/truetype
mkdir windows-font
cd windows-font
# 自行上传文件到/root/simsun* 并移动文件
# mv /root/simsun* ./
# 或直接下载我上传好的字体文件
wget https://metanoia8295.com/files/如何搭建私有jupyter在线平台/simsun.ttc --header="User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36 Edg/91.0.864.59"
wget https://metanoia8295.com/files/如何搭建私有jupyter在线平台/simsunb.ttf --header="User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36 Edg/91.0.864.59"
# 调整权限其他用户也可以使用
chmod -R 777 /usr/share/fonts/truetype/windows-font
cd /usr/share/fonts/truetype/windows-font
# 建立字体缓存并更新字体缓存
mkfontscale
mkfontdir
fc-cache -fv完成字体安装(并完python安装)后切换用户su conda_env,并打开ipython,输入:
from matplotlib import font_manager
font_manager = {i.name.lower() for i in font_manager.FontManager().ttflist}
print(font_manager)
assert "simsun" in font_manager, "Check simsun config !"检查字体simsun有无出现在列表中,运行完不报错就没问题,当然此处也可以直接安装linux常用的中文字体,
# 直接安装linux的中文字体,比如:文泉驿等宽微米黑
apt install fonts-wqy-microhei2 jupyter环境搭建
2.1 anaconda安装
我笔记本的开发环境也是使用anaconda,主要考虑到包管理和环境全面,还能装一些非python体系的包简化配置流程(例如libpython、graphviz),安装时首先在anaconda官网找到下载的链接地址,
切换用户,然后根据下载链接地址运行:
su conda_env
cd
# xxx替换为需要的版本su
wget https://repo.anaconda.com/archive/Anaconda3-xxx-Linux-x86_64.sh
bash Anaconda3-xxx-Linux-x86_64.sh
# (推荐)空间不够可以改用 miniconda
rm -rf miniconda3 # 如果之前已经安装过
wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh
bash Miniconda3-latest-Linux-x86_64.sh
# 如果失败了可尝试用sudo来
# root下执行:usermod -aG sudo conda_env
# 再切到conda_env:sudo bash Miniconda3-latest-Linux-x86_64.sh -u
# 注意安装时要把路径写对:/home/conda_env/miniconda3
# 取消权限:sudo deluser conda_env sudo
# 安装完成后需要自行安装jupyterlab
pip install jupyterlab ipywidgets
# 如果环境变量加载有问题,则:nano ./.bashrc
# 最后添加:export PATH="/home/conda_env/miniconda3/bin:$PATH"一路回车安装,最后会显示: Do you wish the installer to initialize Anaconda3 by running conda init? [yes|no] 默认是no,第一次建议选yes,然后exit退出到root,再su conda_env切换用户即可,如果vps硬盘小可以删除sh安装包节约机器空间
anaconda商用会涉及到收费的问题,如果涉及可以使用miniforge来替换,网上教程很多2.2 jupyter登录密码生成
首先在conda_env用户下打开python(ipython都可以),输入以下命令:
from notebook.auth import passwd; passwd()
# notebook 或 lab
from jupyter_server.auth import passwd; passwd()输入两次后复制粘贴获取到的一长串string备用
2.3 jupyter配置文件修改
主要操作如下:
# 首先生成配置
# jupyter notebook --generate-config
jupyter lab --generate-config
# 版本不同,配置文件路径可能不同:
# /home/conda_env/.jupyter/jupyter_notebook_config.py
# /home/conda_env/.jupyter/jupyter_lab_config.py
# 移动并下需编辑,按可能的版本不同二选一
mv /home/conda_env/.jupyter/jupyter_notebook_config.py /home/conda_env
mv /home/conda_env/.jupyter/jupyter_lab_config.py /home/conda_env
# 编辑完后移动回去,按可能的版本不同二选一
mv /home/conda_env/jupyter_notebook_config.py /home/conda_env/.jupyter
mv /home/conda_env/jupyter_lab_config.py /home/conda_env/.jupyter网上常见教程的notebook的配置文件如下:
# 允许机器IP访问,如果直接ip提供服务这里修改参数为"*"
c.NotebookApp.ip = 'localhost'
# 这里的密码就是粘贴2.2步骤中生成的string
c.NotebookApp.password = 'sha1:........'
# 服务器上并没有浏览器可以供Jupyter打开
c.NotebookApp.open_browser = False
# 监听端口设置为8888或xxxx等自己喜欢的端口
c.NotebookApp.port = xxxx
# 我们可以修改jupyter的工作目录,也可以保持原样不变,如果修改的话,要保证这一目录已存在
c.NotebookApp.notebook_dir = '/home/conda_env'
# 禁止通过ui修改密码
c.NotebookApp.allow_password_change = False
# 禁止远程访问
c.NotebookApp.allow_remote_access = False
# 配置域名访问
c.NotebookApp.local_hostnames = ['localhost', 'jupyterlab.metanoia8295.com']
# 配置无活动自动关闭,设置大一点可以持续保留内存,但内存小则设置少点
c.NotebookApp.shutdown_no_activity_timeout = 3600试用中发现,如果是版本比较新的anaconda而且是lab,一般是这样的配置文件修改方式:
# 内容和上面一致,参数名微调
c.ServerApp.ip = "localhost"
c.ServerApp.password = 'sha1:........'
c.ServerApp.open_browser = False
c.ExtensionApp.open_browser = False
c.LabServerApp.open_browser = False
c.LabApp.open_browser = False
c.ServerApp.port = 8866
c.ServerApp.root_dir = '/home/conda_env'
c.ServerApp.allow_password_change = False
c.ServerApp.allow_remote_access = False
c.ServerApp.local_hostnames = ['localhost', 'jupyterlab.metanoia8295.com']
c.ServerApp.shutdown_no_activity_timeout = 3600如果要用别的cdn反代,需要加入这一句,不需要可以不加,安全考虑不建议直接放'*':
c.ServerApp.allow_origin = 'https://jupyterlab-cn.metanoia8295.com'
# 或者删除上面这行,改成下面这行更好
c.ServerApp.allow_origin_pat = 'https://jupyterlab.*.metanoia8295.com'日志也建议都打开,有些奇奇怪怪的bug比较容易观测到:
c.Application.log_level = 0
c.JupyterApp.log_level = 0
c.ExtensionApp.log_level = 0
c.LabServerApp.log_level = 0
c.LabApp.log_level = 0
c.ServerApp.log_level = 0如果运行后发现前端页面能打开,后端服务起不了,可以先尝试打开这个参数,会通过nodejs编译后运行,有一次后端挂了我debug了一天,最后加了这个参数终于能跑了,一看日志才发现是环境配置错了。。能成功运行后记得注释这个参数,实测这个参数开启后特别费内存。。
c.LabApp.watch = True如果web服务不在根目录开启,或配合nginx进行路径分流,可另外修改以下几行,意思是在web路径/online-lab/下启用jupyterlab服务:
# Leading and trailing slashes can be omitted, and will automatically be added.
# Default: '/'
c.ServerApp.base_url = '/online-lab/'
# This option is intended to be used when the URL to display to the user cannot
# be determined reliably by the Jupyter server (proxified or containerized
# setups for example).
# Default: ''
c.ServerApp.custom_display_url = '/online-lab/'编辑完保存后退出,如果不熟悉vim,可以把文件下载到本地修改好后覆盖远程服务器配置
2.4 jupyter常驻服务
单次使用直接登录后允许jupyter或jupyter-lab即可,但断开vps后会停止服务,这里介绍一下如何配置为系统服务并设置开机自启,在root用户下新建服务文件sudo vim /lib/systemd/system/jupyter.service,vim编辑填入:
[Unit]
Description=jupyter notebook
After=network.target
[Service]
Type=simple
# 这里填用户名,下同
User=conda_env
# 确定jupyter-lab的位置
# whereis jupyter-lab
# EnvironmentFile=/home/conda_env/anaconda3/bin/jupyter-lab
WorkingDirectory=/home/conda_env
ExecStart=/home/conda_env/anaconda3/bin/jupyter-lab
ExecStop=/usr/bin/pkill /home/conda_env/anaconda3/bin/jupyter-lab
KillMode=process
# 搭配实现自动关闭后重启
Restart=on-failure
RestartSec=30s
[Install]
WantedBy=multi-user.target保存后退出,systemctl的使用方式如下:
# 保存并运行,多用户可用不同服务名称表示
sudo systemctl daemon-reload
sudo systemctl enable jupyter.service
sudo systemctl start jupyter.service
# 查看有无错误
systemctl status jupyter
# 其他可用参数
#重启jupyter服务
sudo systemctl restart jupyter.service
#停止jupyter服务
sudo systemctl stop jupyter.service
#移除jupyter服务
sudo systemctl disable jupyter.service
# 查看日志
journalctl -e如果是miniconda或jupyter,可修改jupyter.service文件的对应参数,多用户时不同用户需配置不同路径
3 nginx反向代理(可选)
3.1 ssl证书申请
这一步可以搞一个自签名证书,或者用acme.sh脚本,或者cloudflare直接生成一个15年的证书,在不使用cdn的情况下建议acme.sh生成证书,命令如下:
# 安装acme
apt install socat
curl https://get.acme.sh | sh
# 重新载入
source ~/.bashrc
# 签发证书
systemctl stop nginx # 先关闭nginx如果已经安装了
acme.sh --issue -d jupyterlab.metanoia8295.com --standalone
# 或配合nginx签发证书
acme.sh --issue -d jupyterlab.metanoia8295.com --nginx /etc/nginx/conf.d/jupyter.conf
# 安装证书
acme.sh --installcert -d jupyterlab.metanoia8295.com \
--key-file /etc/nginx/jupyterlab.metanoia8295.com.key \
--fullchain-file /etc/nginx/jupyterlab.metanoia8295.com.crt \
--reloadcmd "systemctl reload nginx"
# 或配置自签名证书
openssl genrsa -des3 -out none.key 2048
mv none.key x.key
openssl rsa -in x.key -out none.key
rm x.key
openssl req -new -key none.key -out none.csr
sudo openssl x509 -req -days 3650 -in none.csr -signkey none.key -out none.crt
# 建议配置
systemctl stop nginx # 先关闭nginx如果已经安装了
openssl dhparam -out /etc/ssl/certs/dhparam.pem 4096
# 吊销与删除证书
acme.sh --revoke -d domain.com # 撤销一个证书
acme.sh --remove -d domain.com # 删除一个证书
acme.sh --uninstall # 卸载acme.sh这一步可以等到配置文件jupyter.conf写完后来进行,或使用standalone模式,需要注意如果是在nginx运行之后申请证书,需要配置nginx的80端口允许访问well-known路径,而且如果开了cf的cdn,需要在ssl栏关闭always https选项,不然在http验证的时候cf会把http升级为https导致证书签发验证失败
3.2 nginx配置文件
nginx的配置文件主要在/etc/nginx/nginx.conf,一般把常用配置写在里面,
# 调整jupyter文件上传大小上限避免nginx413错误
client_max_body_size 100m;
# 隐藏nginx版本信息
server_tokens off;
# 绕过cf获取真实访问ip并记录日志
map $HTTP_CF_CONNECTING_IP $clientRealIp
{
"" $remote_addr;
~^(?P<firstAddr>[a-z0-9.:]+),?.*$ $firstAddr;
}
log_format access '$clientRealIp [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" $remote_addr $request_time'
'"$http_host" $remote_user $upstream_status'
'$ssl_protocol $ssl_cipher $upstream_addr';
access_log /etc/nginx/logs/access.log access;3.3 jupyter配置文件
一般位置是/etc/nginx/conf.d/*.conf,常用来放网站server配置,在conf.d中新建jupyter.conf,写入的配置直接参考这个链接,类似:
# top-level http config for websocket headers
# If Upgrade is defined, Connection = upgrade
# If Upgrade is empty, Connection = close
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
# HTTP server to redirect all 80 traffic to SSL/HTTPS
server {
listen 80;
server_name HUB.DOMAIN.TLD;
# Tell all requests to port 80 to be 302 redirected to HTTPS
return 302 https://$host$request_uri;
}
# HTTPS server to handle JupyterHub
server {
listen 443;
ssl on;
server_name HUB.DOMAIN.TLD;
ssl_certificate /etc/letsencrypt/live/HUB.DOMAIN.TLD/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/HUB.DOMAIN.TLD/privkey.pem;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_dhparam /etc/ssl/certs/dhparam.pem;
ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_stapling on;
ssl_stapling_verify on;
add_header Strict-Transport-Security max-age=15768000;
# Managing literal requests to the JupyterHub front end
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# websocket headers
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header X-Scheme $scheme;
proxy_buffering off;
}
# Managing requests to verify letsencrypt host
location ~ /.well-known {
allow all;
}
}把HUB.DOMAIN.TLD替换为你准备的域名,证书替换为你准备的证书,其他按个人需求修改,最后重启服务systemctl restart nginx不报错即可,如果报错可以使用nginx -t检查配置文件情况,注意如果考虑到http验证acme证书,则location ~ /.well-known也应该配置到80端口
另外,有些版本anaconda的jupyterlab的前端javascript在使用cf的Rocket Loader会出现加载异常,关闭cf的Rocket Loader即可
4 在线服务
4.1 效果展示
首次部署完的域名为:https://jupyterlab.metanoia8295.com,效果如下:
另为优化国内体验,用aws的服务另外做了个一个cdn,域名是:https://jupyterlab-cn.metanoia8295.com,这个地址只能中国地区的ip访问,怎么做cdn又是另外一类教程了,后面有空再写
4.2 多用户登录
当然了,这样的方式仅适合个人使用,如果涉及到多用户,可以参考一下jupyterhub的实现方式,当然我这里懒得折腾,直接延用jupyterlab的安装模式,通过创建第二个用户conda_env2,环境重新来一套,同时使用nginx的分流和autoindex实现了一个样例,效果如图:
需要大量修改nginx、jupyter配置文件等,自定义index页面还需要动态编译nginx模块,这里先不展开
4.3 python环境管理
这里简要介绍如何使用conda管理python环境,比如anaconda默认装了python3.7,然后我需要python3.8的环境,可以使用如下的命令:
# 新建环境命名python38并指定安装python版本为3.8
conda create -n python38 python=3.8
# 删除环境
conda remove -n python38 --all
# 安装时如果有些包太大,而网络环境不好下载经常中断,可以提前下载并手动提前安装
# 根据requirements.txt安装python环境依赖,单用户可以不用conda只用python的pip管理
pip install -r requirements.txt
conda install --yes --file requirements.txt
# 有些包可能conda没有,也可以使用conda和pip混合安装
# linux
while read requirement; do conda install --yes $requirement; done < requirements.txt
# win
FOR /F "delims=~" %f in (requirements.txt) DO conda install --yes "%f" || pip install "%f"
# 个人常用环境配置文件安装
# pip install -r https://metanoia8295.com/files/%e5%a6%82%e4%bd%95%e6%90%ad%e5%bb%ba%e7%a7%81%e6%9c%89jupyter%e5%9c%a8%e7%ba%bf%e5%b9%b3%e5%8f%b0/requirements.txt
# 有些模块使用conda安装可能更好,比如bottleneck
pip install bottleneck
conda install bottlenect
# 网络环境较差的话也可以配置一下国内的pip源
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple # 清华
pip config set global.index-url https://mirrors.aliyun.com/pypi/simple/ # 阿里云
pip config set global.index-url https://pypi.douban.com/simple/ # 豆瓣
pip config set global.index-url https//mirrors.cloud.tencent.com/pypi/simple # 腾讯
# 或指定源安装某个模块
# pip install --upgrade 模块 -i https://pypi.org/simple
# conda 添加源
conda config --add channels conda-forge
# conda 源管理也可以考虑使用清华源,按说明修改./.condarc文件
# https://mirrors.tuna.tsinghua.edu.cn/help/anaconda/
# 切回默认源
# conda config --remove-key channels
# win切换环境
activate python38
# linux切换环境
source activate python38这里附一个我常用的requirements.txt文件,如果需要给断网的机器离线安装,则可以使用pip的download下载到一台机器,拷贝安装包到另一台机器后用pip离线安装,这里附上两个脚本供参考:auto_download.py、auto_install.py,python执行时需要和requirements.txt文件在同一路径
4.4 安全策略
安全方面分几块,一是vps的ip的保护,sshd防暴力破解可以考虑使用类似fail2ban的工具,二是上cdn避免ip泄露和攻击,三是可以自签证书在nginx单独配置给ip防止全网ip暴力扫描泄露ip对应的web地址,还有常用通过chmod控制权限或隐藏ip的,样例如下:
安全策略部分的细节较为敏感且非重点固不展开详述,未涉及的至少还有速率限制、防CC、只允许cf的ip访问,多用户还需要考虑带宽、CPU、内存、硬盘使用限制等,有需求可以自行搜索后配置
4.5 试用服务
- 网络限制:
jupyterlab中不支持外网访问,不能使用wget或curl等涉及网络请求的服务,可以通过页面上传建模数据或下载模型文件 - 网速限制:每天会有分段限速和动态限速,应该足够正常使用了,另外有防火墙和安全策略,还会开启随机域名,请不要进行浪费时间的操作
- 内存限制:在线资源有限,每个用户内存上限有限,请不要读取大文件,单脚本轻度测试足够,但开很多窗口一起跑肯定不行,请每次只进行一类案例的测试
- 处理器限制:在线资源有限,演示教程中的案例均在性能限制下执行,第一次
import时是单核会稍慢一些,后面的代码自带并发正常轻度使用性能足够 - 硬盘限制:除安装
miniconda、依赖包和样例报告总共占不到6G空间外,剩余空间足够正常测试,请不要上传大文件或不合理编写代码,每个用户存在空间限制,额外空间一般不超过10G


