安装Jupyter Notebook并配置用于多用户的反向代理(Nginx)

虽然code-server已经能够满足很多需求, 比如用来写C/C++, Java, Tex等, 但是, 当遇到需要做数据分析的时候, 在code-server上运行Python和R就不是那么方便. 而当下非常流行的Python发行包Anaconda中自带了Jupyter Notebook, 是一种集科学计算和撰写文本为一体的"IDE".
当然, Anaconda的优点不止于此, 它预置了许多常用的科学计算包, 比如NumPy, pandas, 同时对R语言也有非常好的支持, 这些特性对用于学习的数据分析已经完全足够了.
PS: 若是嫌Anaconda太大的话, 可以选择Miniconda, 只自带了python和conda, 别的软件包可以通过pip或者conda进行安装.
Jupyter Notebook也是基于web的服务, 所以除了部署之外只要简单地做一下反代和注册service就好了.

本文主要介绍在Ubuntu Server 18.04下全局安装Jupyter Notebook,并且通过反向代理的方式实现多个用户账户通过不同的地址来访问自己的Jupyter Notebook

Step1: 安装Anaconda

去Anaconda的官网或镜像上下载对应系统,位数和Python版本的安装文件, 我选用的是TUNA镜像站. 由于在服务器上有多个用户需要使用, 所以安装时提了权限: sudo sh Anaconda3-5.3.1-Linux-x86_64.sh.

接下来Anaconda会让你看一段特别长的License, 想不想看都行, 用Enter翻到最后时, 会问是否同意, 哪有不同意的份, 输yes就完事了.

接下来Anaconda会询问安装位置, 默认是在用户目录下: ~/anaconda3, 但由于要多用户, 我选择安装在/usr/local/anaconda3下. 然后就是不太漫长的等待, 最后还会问是否需要将环境变量加入.bashrc, 选不选都可以, 不选的话之后在终端输入/path2anaconda/bin/conda init, 会与之前选添加环境变量有着相同的效果(因为既没添加环境, 也没自动添加PATH, 所以直接输入conda大概率会报command not found的错误).

最后还有个VScode的广告, 介于我们这个服务器甚至没有装x-server, 就没装了.

如果选了添加环境, .bashrc的末尾会多出来一段东西, 大概像这样:

# added by Anaconda3 5.3.1 installer
# >>> conda init >>>
# !! Contents within this block are managed by 'conda init' !!
__conda_setup="$(CONDA_REPORT_ERRORS=false '/usr/local/anaconda3/bin/conda' shell.bash hook 2> /dev/null)"
if [ $? -eq 0 ]; then
    \eval "$__conda_setup"
else
    if [ -f "/usr/local/anaconda3/etc/profile.d/conda.sh" ]; then
        . "/usr/local/anaconda3/etc/profile.d/conda.sh"
        CONDA_CHANGEPS1=false conda activate base
    else
        \export PATH="/usr/local/anaconda3/bin:$PATH"
    fi
fi
unset __conda_setup
# <<< conda init <<<

由于我用的是zsh, 所以这些东西写在.bashrc里面自然起不了效果, 需要提出来用. 虽然一堆判断看上去复杂, 但是它们于我何与也? 全都复制出来, 把里面的bash改成zsh, 再放在一个新的脚本文件里面, 就大功告成了. 最后这个文件除了把bash换成了zsh, 跟上面这段完全一致.

我把它起名为conda_activate.sh, 需要激活环境的时候输入source conda_activate.sh就行了, 退出终端重进环境就失效了.

Step2: 配置Jupyter Notebook

安装好之后, 理论上只要在激活环境的情况下输入jupyter notebook就可以启动了, 像这样:

但是, 可以看到, 此时Notebook运行在8888端口, 访问需要通过token, 而token是随每次启动会变化的, 很不方便, 所以需要创建一个配置文件进行修改.

首先, 要生成一个配置文件: jupyter notebook --generate-config, 生成的配置文件在~/.jupyter/jupyter_notebook_config.py.

然后, 开始修改一些参数:

  1. 在48行(左右)可以找到c.NotebookApp.allow_origin, 将其前面的#(也就是注释符)去掉, 并改其值为'*'. (反代若没挂在根目录下就需要)
  2. 在93行(左右)可以找到c.NotebookApp.base_url, 取消注释, 并改其值为'/path/'. (这个path就是反代挂载的路径)
  3. 在139行(左右)可以找到c.NotebookApp.custom_display_url, 取消注释, 并改其值为'/path/'. (就是与上面那个保持一致)
  4. 在204行(左右)可以找到c.NotebookApp.ip, 取消注释, 并改其值为'*'. (意思是任何ip都可以访问Notebook)
  5. 在252行(左右)可以找到c.NotebookApp.open_browser, 取消注释, 并改其值为False.
  6. 在287行(左右)可以找到c.NotebookApp.port, 取消注释, 并改其值为22333. (就是指定Notebook的端口)

保存后, 再启动jupyter notebook, 在浏览器里把除了token之外的地址输进去(大概像http://localhost:22333), 会发现提示输入token后进入. 在页面的下面还有设定密码的地方, 只要把token和新的密码输进去就行了. (若现在无法访问也可以选择在配置完反代后再设置)

或者, 还有一种方法也可以设置密码: 执行jupyter notebook password, 输完两遍密码后会生成一个json文件, 保存在~/.jupyter/jupyter_notebook_config.json, 找到那个以sha1开头的字符串, 并将其拷贝到~/.jupyter/jupyter_notebook_config.py的276行(左右)的c.NotebookApp.password的值处, 并在前面加上u, 大概就像:
c.NotebookApp.password = u'sha1:7dc5d4f1796f:b04e59cf83a50fedf24fb2691f1bf1ebd5218484'
哈希值别抄成上面那个就行了, 如果写成上面那个, 那你的密码就会变成1111.

Step3: Nginx配置反代

要修改配置文件/etc/nginx/nginx.conf.
首先还是SSL. 远程访问的话安全还是很重要的.

http {
  #前面的东西省略

  ssl on
  ssl_certificate     /path/to/crt/chain.crt
  ssl_certificate_key /path/to/key/key.key

  #后面的也省略
}

然后是反代服务器:

http {
  #前面的东西省略

  #SSL的东西也省略

  #中间可能还有东西, 继续省略

  server {
    listen 8080; #这个是客户端访问反代服务器时的端口
    listen [::]:8080; #这个应该也是, 但是是IPv6的配置
    server_name myjupyter.com itsjupyter.com;
    #填访问时用的域名, 可填多个, 空格分隔
    location /somepath/ {
      #可能有的别的反代, 省略
    }
    location /path/ {
      proxy_pass http://localhost:22333/;
      #这是反代访问Notebook的地址,
      #所以端口也是填Notebook的端口
      proxy_http_version 1.1;
      #等会细讲
      proxy_redirect http:// https://;
      #把外部的https转成内部的http
      proxy_set_header Host $host:443/path;
      #这里端口填外部访问的端口
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection upgrade;
      proxy_set_header Accept-Encoding gzip;
      #最后三行说不清楚, 照抄就完事了
    }
    #可能存在的第二个第三个location模块, 反正和上面那个差不多
  }
}

等会现在细讲: proxy_http_version 1.1显然意思是指定http版本为1.1, 但是为什么呢? 你自己试试不就知道了如果不加的话, 在Notebook中将无法连接到内核, 一直会显示disconnected.

Step4: 注册为service

创建一个以service为后缀的文件, 比如jupyter-notebook.service. 里面这么填:

[Unit]
Description=Jupyter Notebook NO.1
After=network.target

[Service]
Type=simple
User=username
EnvironmentFile=/home/username/.jupyter_env
WorkingDirectory=/home/username/
Restart=on-failure
RestartSec=10

ExecStart=/usr/local/anaconda3/bin/jupyter notebook

StandardOutput=file:/path2output/output.log
StandardError=file:/path2error/error.log

[Install]
WantedBy=multi-user.target

这个和写code-server的那个几乎一模一样, 只有环境文件, 启动命令和log文件的位置不一样.

.jupyter_env这个环境文件中, 把conda_activate.sh里面的东西再拷过来, 顺便在它的前面加上其余的PATH变量.

把这个文件丢到/etc/systemd/system/下, 并执行systemctl start jupyter-notebook.service, 将start换成enable以使其开机自启.

如果前面没有配置密码的话, 现在可以在把service启动之后, 去StandardOutput所在的地方, 找到token, 然后再做前面的操作以设置密码.

Step5: 维护Anaconda环境

要更新conda中的包就用conda update --all, 安装是conda install <package>, 卸载是conda remove --all. 如果装的是Anaconda, 估计主要用的还是更新, 毕竟Anaconda里面常用的大多都在里面.

如果嫌更新慢的话, 可以在用户目录新建一个.condarc, 也可以在安装目录下(比如/usr/local/anaconda3/)新建这个文件, 要是用户目录和安装目录下都有, 则会优先读取用户目录下的文件.

新建好之后, 在里面写上:

channels:
  - defaults
show_channel_urls: true
channel_alias: https://mirrors.tuna.tsinghua.edu.cn/anaconda
default_channels:
  - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main
  - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free
  - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/r
  - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/pro
  - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/msys2
custom_channels:
  conda-forge: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud
  msys2: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud
  bioconda: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud
  menpo: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud
  pytorch: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud
  simpleitk: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud

这是清华的镜像, 速度蛮快的, 也可以自行换成别的镜像.

如果是全局安装的话, conda install等指令可能会报错, 大概率是权限不够的错误, 但是conda又会告诉你, 不建议提权执行conda, 所以需要一些操作.

简单粗暴的方法是直接从安装目录开始, 改权限为777: chmod 777 -R /usr/local/anaconda3. 虽然确实好使, 但是总觉得不是太好. 于是有了下一种方法.

  1. 创建用户组anaconda: groupadd anaconda.
  2. 把Anaconda的安装目录改为用户组anaconda所有: chgrp -R anaconda /usr/local/anaconda3.
  3. 修改安装目录的权限: chmod 770 -R /usr/local/anaconda3.
  4. 把自己也加入用户组anaconda: usermod -a -G username anaconda.

虽然也是修改了文件夹权限, 但是比粗暴的777大概还是好那么一点点吧.

BTW, 上述两种方法大概率需要sudo. 啊? 有没有不提权的方法? 又要全局又不提权, 那不可能

附录: 杂谈以及可能的常见报错(问题)的解决方法

  1. 除了conda有镜像, pip也有, 搜索PyPI镜像, 一搜一大把, 清华的也有. 如果真的出现了镜像也不好使的pip包, 可以先用pip安装: pip install <package>, 会出现一个下载链接, 拷出来, 再用有代理的设备下下来, 然后丢回去, 执行本地安装: pip install <package>.whl. 但是需要注意的是, 这样操作的话绝不能改文件的名字, 否则安装会报错.
  2. 在conda中安装r-essentials便可以在Notebook中调用R内核, 安装Kotlin内核(conda install kotlin-jupyter-kernel或者pip install kotlin-jupyter-kernel)就可以调用Kotlin内核这难道不是废话, 但是不同于r-essentials, kotlin的Notebook内核只能在Jupyter Notebook中被调用, 和真正的Kotlin编译器是分开的, 要想编译运行kt文件, 可以用snap install --classic kotlin kotlin-native, 或者直接从Github的Release Kotlin 1.3.61上下载二进制包(截至2020.2.13的最新版本). u1s1我还没见过真有人用kotlin干数据科学的活
  3. chmod 777 (-R) <path>或者chmod +x (-R) <path>整日从各种地方抄下来, 但是它具体代表了什么意思呢? -R表示递归操作, 也就是对这个目录下的子目录/子文件都执行这个命令. 那个三位数的三位分别表示了文件(夹)拥有者, 与拥有者同用户组的其他用户, 其余用户的操作权限. 0-7有2^3种可能, 就是4(读取), 2(写入), 1(执行)的组合, 或者说是把一个三位二进制数转换成了0-7的十进制数. +x表示所有用户增加执行权限, 把x换成r,w, 加号换成减号, 都有着类似的意思. 有兴趣的话可以参阅chmod --help.

Q: Anaconda全局安装之后用conda安装/卸载/更新包出现权限问题?
A: 把Anaconda的目录改成由用户组anaconda所有, 自己也进用户组, 最后组内用户权限7, 或者安装目录直接777.

Q: conda/pip下载极慢?
A: 用镜像, TUNA上什么镜像都有, 或者pip玩家可以把下载链接窃来直接下.

Q: 反代之后在Notebook里面连不上内核?
A: 指定http版本为1.1, Nginx里面就是proxy_http_version 1.1;.

Q: 反代后创建笔记本/别的操作失败? 后台报Blocking Cross Origin API request?
A: 在jupyter_notebook_config.py中找到c.NotebookApp.allow_origin(48行左右), 取消注释并将值改为'*'.

Q: 无法访问Notebook, 后台报non localhost?
A: 在jupyter_notebook_config.py中找到c.NotebookApp.ip(204行左右), 取消注释并将值改为'*'.

Q: 无法访问Notebook, 访问到别的东西? 或者没访问到啥, 并且后台一点反应都没?
A: 可能端口被占用了, Jupyter的端口会尝试自动+1, 试试看+1的端口.

Q: R用户用Anaconda, Python版本选啥好?
A: 其实无所谓, 可以买新不买旧选个Python3.

Q: Jupyter Notebook都可以远程访问, 那我R用户最喜欢的RStudio能不能也远程访问?
A: GUI界面远程访问, 可以考虑远程X Server(Xming/VcXsrv)/VNC/或者别的奇技淫巧, 否则, 那不可能.

Q: Jupyter Notebook还有啥好使的内核?
A: 官方维护的有IPython, IJulia, IRkernel, 还有不少社区支持的内核, 可以参见Jupyter kernels. 甚至能看到Wolfram Language和MATLAB/Octave的内核(其实这三个都只是转接头, 白嫖Mathematica和MATLAB是几乎8可能的).

Q: 我遇到的问题在搜索引擎上搜不到想要的结果/发在论坛上没有想看到的回复?
A: 建议大家读读How To Ask Questions The Smart Way, 由巨佬Eric S. Raymond编写, 读了8成会有收获.

注: 所有的操作在Ubuntu Server 18.04下实测有效, 一些文件的位置和命令可能随环境的不同而失效.

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 162,050评论 4 370
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,538评论 1 306
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 111,673评论 0 254
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,622评论 0 218
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,047评论 3 295
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,974评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,129评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,893评论 0 209
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,654评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,828评论 2 254
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,297评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,619评论 3 262
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,326评论 3 243
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,176评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,975评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,118评论 2 285
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,909评论 2 278

推荐阅读更多精彩内容