☰
服务器远程运行Jupyter Notebook:虚拟环境配置与SSH端口转发实战
2026/10/24 7:14:14 网站建设 项目流程

在实验室里,经常会有人拎着笔记本过来问:服务器上怎么开 Jupyter Notebook?最好还能直接用我自己配好的那个虚拟环境。这个问题听起来简单,真正上手才发现至少有四道坎:远程连接要通、Jupyter 要在没显示屏的服务器上跑起来、本地浏览器要能正常打开页面、最后虚拟环境还得能被 Notebook 识别,不然一新建文件又回到默认的 base,前几分钟全白折腾。我见过不少人在前两步就卡住了,更别说最后一步——环境装了一堆,打开 Notebook 却死活找不到。

这篇文章我就把整套流程完整捋一遍。假设你已经在某个实验室的服务器上有账号,服务器是 Linux 系统,你平时主要通过 SSH 登录。看完之后你不仅能跑起 Jupyter,还能把自定义环境加到内核列表里,彻底告别"嗯?怎么还是 base"的尴尬。

1. 先看运行链路:浏览器只是个遥控器

1.1 Jupyter 到底跑在哪里

很多刚接触服务器 Jupyter 的人会误以为 Notebook 运行在自己电脑上。实际上,你在本地浏览器里看到的网页只是前端界面,真正执行 Python 代码的是服务器上的 kernel 进程。也就是说,所有的 import、计算、模型训练都发生在服务器上,你的笔记本只负责展示页面、收集输入和显示结果。

拆开看这条链路是这样的:

  • 服务器上启动 Jupyter Server,监听某个端口(默认 8888)。
  • 本地浏览器通过 HTTP 访问服务器的端口,Jupyter 把 Notebook 页面推送到浏览器。
  • 你在单元格里按 Ctrl+Enter,代码被发送回服务器,交给 kernel 执行。
  • 执行结果(包括 print、图片、表格)再通过网络返回浏览器显示。

理解这一点非常关键,因为你之后遇到的所有问题,比如"连不上""打开慢""内核断连",本质上都在上面这条链路的不同环节出了问题。只要定位到是哪一环断掉,排查思路就清晰了。

1.2 动手前必须确认的三件事

我建议在安装任何东西之前,先检查三件事,避免后面白忙活:

  1. 服务器 IP 或主机名是否可用。用ssh username@server_ip能正常登录。
  2. 你的账号是否有写权限,至少在家目录里有空间装包、建环境。
  3. Python 是否已安装。一般服务器都会装,但版本可能旧一点。

再补充一个我踩过的真实小坑:有一次某同事让我帮他在服务器上配 Jupyter,折腾半天发现他根本连不上服务器,最后查出来是 SSH 登录时把端口写错了。如果 SSH 这一步都不通,后面所有步骤都无从谈起。所以别急着装软件,先确认"能连上"这个前提。

可以用下面命令快速确认 Python 环境:

which python3 python3 --version

如果系统里有多个 Python,建议记下路径。等会儿建虚拟环境时要用。

2. 创建虚拟环境:把依赖隔离在自己的小房间

2.1 为什么不能直接装在系统 Python 里

实验室服务器通常是多人共用的,系统 Python 环境往往已经被折腾得惨不忍睹。A 研究组需要 pytorch 2.x,B 研究组还在用 1.x,C 同学装了个不知名的包把 numpy 直接升级到不兼容的版本,结果大家写代码的时候全都一脸懵。

虚拟环境就是为了解决这种"一个环境装所有东西导致的混乱"。你可以把它理解成合租房里的独立房间:公共区域(系统 Python)保持不变,每个房间(虚拟环境)里的软件随便折腾,互不干扰。你只需要在干活的时候进自己的房间,干完再出来就行。

2.2 使用 conda 创建环境

如果你的服务器已经装了 Anaconda 或 Miniconda,最简单的方式就是用 conda。Miniconda 更轻量,Anaconda 自带很多常用库。没有的话可以用 Miniconda 安装脚本装一个。

创建环境:

conda create -n myenv python=3.10

这里myenv是环境名,python=3.10指定解释器版本。conda 的优点是能管理 Python 版本本身,比如系统默认 Python 是 3.8,但你项目需要 3.10,conda 可以直接给你一个独立的 3.10。

激活环境:

conda activate myenv

激活后命令行前面会出现(myenv)标记,这时候which python指向的就是环境内的解释器了。

如果提示conda activate不能用,可能需要先跑一次:

conda init bash

然后重新登录 SSH,或者重新加载 shell 配置。这个坑很常见,尤其在新装的 Miniconda 上。

2.3 使用 venv 创建环境

如果不想装 Anaconda,用系统自带的venv也完全够用。它更轻量,但只能基于系统当前的 Python 版本创建环境,不能随意切换 Python 大版本。

cd ~ python3 -m venv myenv source myenv/bin/activate

激活后(myenv)前缀同样会出现。venv 的好处是不依赖额外工具,干净利落;缺点是如果你需要多个 Python 版本,它没法直接解决。

两种方式对比:

对比项condavenv
Python 版本切换支持,可指定任意版本不支持,跟随系统 Python
底层库管理可管理部分非 Python 依赖只管理 Python 包
安装体积较大,Miniconda 也有几百 MB轻量
适用场景深度学习、需要不同 Python 版本普通项目、只需要隔离 Python 包

我在实验室实际用得最多的还是 conda,因为深度学习项目经常需要指定 CUDA 版本配合的 Python 版本,venv 做不到这一点。如果只是写脚本做数据分析,venv 就够了。

3. 在服务器上安装并配置 Jupyter

3.1 安装 Jupyter Notebook

创建好环境之后,先激活环境,然后在环境内部安装 Jupyter:

conda activate myenv pip install jupyter notebook

如果你用的是 JupyterLab,也可以:

pip install jupyterlab

为什么强调"激活后再安装"?因为这样 Jupyter 相关包会装进当前环境,而不是污染 base。后面启动时用当前环境的jupyter命令,路径才不会搞混。

注意区分一下版本:现在 Jupyter Notebook 有 7.x 和旧版 6.x。7.x 基于 Jupyter Server,配置项名字和旧版略有差异。如果你看到网上教程写的c.NotebookApp.xxx不生效,大概率是因为你在 7.x 上找了旧配置项。后面我再详细说配置差异。

安装完成后,可以先跑一下:

jupyter --version

确认安装成功,并输出版本信息。

3.2 生成配置文件

Jupyter 启动时会读一个配置文件,通常是~/.jupyter/jupyter_notebook_config.py。第一次运行需要手动生成:

jupyter notebook --generate-config

然后编辑这个文件。对于 Jupyter Notebook 7.x,主要是这一段:

c.ServerApp.ip = '127.0.0.1' # 只监听本机地址 c.ServerApp.port = 8888 # 使用端口 8888 c.ServerApp.open_browser = False # 服务器上不自动打开浏览器 c.ServerApp.allow_remote_access = True # 允许远程访问(配合端口转发)

如果是旧版 6.x,对应的是:

c.NotebookApp.ip = '127.0.0.1' c.NotebookApp.port = 8888 c.NotebookApp.open_browser = False c.NotebookApp.allow_remote_access = True

我个人建议新装机器直接用 7.x,因为它内部切换到 Jupyter Server 之后,扩展机制和稳定性都更现代。旧版配置虽然兼容但迟早要迁。

ip设置成127.0.0.1而不是0.0.0.0,是为了安全考虑。除非你明确知道自己在做什么,否则不要直接把服务暴露到所有网卡上。后续远程访问用 SSH 端口转发就够了,没必要监听公网。后面第 4 节我会展开讲。

3.3 设置登录密码

由于 Jupyter 自带认证机制,最好用一个密码保护。旧版 Jupyter 提供命令:

jupyter notebook password

新版同样支持这个命令,它会提示你输入并确认密码。执行后密码会被哈希写入~/.jupyter/jupyter_server_config.json,以后启动 Notebook 时只需要输入这个密码。

为什么不推荐启动时用 token?因为每次重启 token 都会变,然后你得去日志里找那一长串字符,非常麻烦。设一个固定密码,一劳永逸。密码设置好之后,网页登录只会要求输这个密码,不会再弹 token 框。

4. 远程访问:两种常用方案对比

4.1 方案一:SSH 端口转发(我最推荐)

这个方案安全,也最容易排查。原理很简单:你在本地电脑和服务器之间建立一条 SSH 加密通道,把服务器上的某个端口"映射"到本地的一个端口。这样浏览器访问本地端口,数据通过 SSH 隧道转发到服务器上的 Jupyter。

命令格式:

ssh -N -f -L 8888:localhost:8888 username@server_ip

各个参数的含义:

  • -N表示不执行远程命令,只做转发
  • -f表示后台运行
  • -L 8888:localhost:8888表示把本机的 8888 端口映射到服务器的 8888 端口

命令执行完之后,在你本地浏览器打开:

http://localhost:8888

就能看到 Jupyter 登录页面了。

这个方法的好处是:服务器上的 Jupyter 只监听127.0.0.1,外部网络根本访问不到它,数据在传输过程中也被 SSH 加密。即使所有人共用服务器,也不用担心别人在网络上抓到你的密码。

如果 SSH 连接容易断开,可以加个保活参数:

ssh -N -f -o ServerAliveInterval=60 -L 8888:localhost:8888 username@server_ip

ServerAliveInterval=60的意思是每 60 秒发一次心跳包,防止长时间没流量被防火墙掐断。我实测下来,加了这项之后连续挂一两天都没问题。

Windows 用户如果用的是 PowerShell 或者 Windows Terminal,直接敲同样的命令即可。如果你用的是 MobaXterm 这类工具,它自带隧道配置界面,图形化操作也可以。

4.2 方案二:直接开放端口访问

还有一种方式是让 Jupyter 监听0.0.0.0,然后浏览器直接访问服务器的 IP 加端口:

http://服务器IP:8888

需要改配置:

c.ServerApp.ip = '0.0.0.0'

改完之后别忘了检查防火墙。很多服务器的防火墙默认没开 8888 端口,需要放行:

sudo ufw allow 8888

某些集群环境还有安全组设置,得在管理页面里放行端口。

这个方法适合什么场景?适合你确定服务器处于可信内网、且你能方便地开放端口的情况。但说实话风险比较大:Jupyter 有执行任意命令的能力,一旦有人猜到密码或利用漏洞,等于直接把服务器控制权交出去。所以我基本只在自己有完全掌控权的机器上用,实验室共用服务器我建议大家走 SSH 端口转发。

4.3 启动 Jupyter 并在后台运行

一切配置好之后,启动命令很简单:

jupyter notebook --no-browser --port=8888

--no-browser告诉服务器不要尝试打开浏览器,毕竟服务器上根本没有图形界面。

问题是,如果直接在前台运行,SSH 窗口一关,Jupyter 也就停了。所以要让它一直在后台跑,两个办法比较靠谱:

方法一,用 nohup:

nohup jupyter notebook --no-browser --port=8888 > ~/jupyter.log 2>&1 &

这样 Jupyter 在后台跑,日志输出到~/jupyter.log,以后想看错误就去翻这个文件。

方法二,用 tmux,更灵活:

tmux new -s jupyter jupyter notebook --no-browser --port=8888

进入 tmux 后跑 Jupyter,按Ctrl-b d退出会话,SSH 断开也不影响。下次回来:

tmux attach -t jupyter

就能回到 Jupyter 的会话界面。如果你想关掉 Jupyter,在 tmux 里按Ctrl-c即可。

我个人现在基本只用 tmux,因为除了跑 Jupyter,我平时在服务器上跑训练脚本也靠它。如果你完全不想学新工具,nohup 也能满足 90% 的需求。

另一个小习惯:启动后别急着关终端,先看日志里是否有报错。正常时日志最后会显示类似:

http://127.0.0.1:8888/

如果这里显示的不是127.0.0.1而是别的地址,说明配置里 IP 没有改对。要是启动失败,日志里一般会直接告诉你原因,十有八九是端口被占了。

5. 关键一步:让 Jupyter 认得出你的虚拟环境

5.1 为什么 Notebook 里还是默认环境

很多人在服务器上装好了虚拟环境,也激活了环境,启动 Jupyter,然后新建 Notebook 一看,sys.executable指向的还是系统的 Python 路径,装了一堆包的环境好像根本没生效。

原因在于:Jupyter 启动时会根据它自己的运行环境去调用 Python kernel。你虽然在 shell 里激活了myenv,但 Jupyter Server 进程本身的 Python 解释器可能还是 base 环境的。更关键的是,Notebook 的 kernel 是一个独立进程,你在 shell 里 activate 的环境对它没有任何直接影响。

就好比你订了酒店房间,但酒店前台给你的房卡却是公共区域的卡,你得单独告诉 Jupyter"现在我要用这个环境的 Python 作为 kernel"。

5.2 使用 ipykernel 注册环境

解决办法是让每个虚拟环境把自己"注册"成 Jupyter 的一个可选内核。这样启动 Notebook 之后就能在下拉菜单里看到它,点一下就能切换。

操作分两步:

第一步,在虚拟环境里安装 ipykernel:

conda activate myenv pip install ipykernel

第二步,注册内核:

python -m ipykernel install --user --name=myenv --display-name="MyEnv Python 3.10"

参数解释:

  • --name=myenv是内核的内部名字,用来唯一标识。
  • --display-name是你在 Jupyter 网页端下拉菜单里看到的显示名,可以随便起,建议起得让自己一眼能认出来。
  • --user表示把内核配置安装到当前用户目录,通常在~/.local/share/jupyter/kernels/下,不需要 root 权限。

注册之后,刷新 Jupyter 网页。在新建 Notebook 的时候,Kernel 菜单里就能看到 "MyEnv Python 3.10" 了。已打开的 Notebook 也可以在菜单栏Kernel -> Change Kernel里切换。

注册完可以验证一下,新建选这个内核的 Notebook,运行:

import sys print(sys.executable)

如果输出的是~/miniconda3/envs/myenv/bin/python或者类似的路径,说明环境已经正确穿透到 kernel 里了。这个验证习惯我强烈建议养成,每次配完环境都看一眼,能省不少事。

5.3 多个环境怎么管理

如果你经常给不同项目建环境,会发现每次都要重复一遍pip install ipykernel和python -m ipykernel install。这个过程可以写成一个简单脚本:

#!/bin/bash conda activate $1 pip install ipykernel python -m ipykernel install --user --name=$1 --display-name="$1"

调用时bash add_kernel.sh myenv就行。有段时间我实验室里帮好几个同学配环境,有了这个脚本,一套流程 30 秒搞定。

还有一个更省事的思路:直接装一个nb_conda_kernels包,它会在启动 Jupyter 时自动扫描所有的 conda 环境,把它们全部列成可选内核。装一次,以后建新环境直接出现在列表里,不用挨个注册。

conda activate base conda install -c conda-forge nb_conda_kernels

注意要去 base 环境装,然后 Jupyter 启动时它才会自动扫描。缺点是如果环境特别多,内核列表会很长,选起来有点眼花。我个人的选择是:共用服务器上装一个nb_conda_kernels,大家都能顺手选环境;自己日常用的机器还是手动注册,干净可控。

6. 常见问题与排查实录

6.1 问题速查表

下面这些是我实际见过的案例整理成的一份速查表,建议先收藏。

现象可能原因排查方法
浏览器打不开 localhost:8888SSH 隧道没建立成功检查 SSH 命令是否报错,用ssh -v看调试输出
浏览器显示无法连接服务器端口号不一致确认启动 Jupyter 的端口和转发命令里的端口一致
初次访问弹出 token 框尚未设置固定密码执行jupyter notebook password后重启服务
Notebook 新建后还是 base 环境kernel 未注册在对应环境执行ipykernel install
端口被占用有人或之前实例占用 8888用lsof -i:8888查看进程,或换端口启动
Jupyter 启动后马上退出配置项写错看日志,确认ServerApp和NotebookApp配置与版本匹配
服务器重启后 Jupyter 消失没有使用后台守护方式改用 tmux 或 systemd 方式
打开 Notebook 一直显示 Connectingipykernel 缺失或内核崩溃在对应环境重装 ipykernel,看服务器日志
conda activate 无效shell 未初始化执行conda init bash后重新登录

6.2 几个真实案例复盘

先说端口占用。又一次我的 Jupyter 突然启动报错,提示端口被占用。排查过程很简单:

lsof -i:8888

看到是一个残留的旧进程占着,直接kill掉再启动就好。大多数端口问题的本质就是这个,偶尔也可能是你上次的 nohup 进程还没退出导致的幽灵进程。启动前养成查端口的习惯,能少踩不少坑。

再说内核连接失败。某个师妹反馈,点开 Notebook 之后一直转圈,状态是 Connecting。我看了一下,她的环境里没装 ipykernel,只是在 conda 环境里启动了 jupyter。虽然 Jupyter 进程本身能跑,但新建的就是旧内核,环境里找不到对应的启动脚本,自然就连不上。补装一次 ipykernel 之后就恢复了。这其实就是第 5 节讲的注册问题的变体,很多 New User 都会漏掉。

最后说一个容易忽略的:有些实验室服务器对登录节点有限制,要求只有调度节点能跑计算任务。这时候你把 Jupyter 跑在登录节点上,重负载任务会拖垮整台机器,影响所有同学。这种情况下的正解是申请一段交互式作业,在里面启动 Jupyter,然后同样用 SSH 端口转发访问。具体命令要看实验室的调度系统,但思路是一致的:把 Jupyter 放到有资源的节点上,而不是长期占着登录节点。遇到这类问题,最好先问一下实验室管理员有没有相关约定,别自己埋头配。

6.3 被低估的安全事项补漏

说几个容易被忽略的安全细节:

  • 不要用 root 账号跑 Jupyter。Jupyter 一旦出问题,root 权限意味着整台机器被接管。
  • 密码尽量用强密码,不要设空密码。有些同学贪方便不设密码,结果服务又开着公网,这等于把服务器大门敞开。
  • 日志文件会记录访问来源和 token,不要随意分享给别人。
  • 配置里c.ServerApp.token = ''这种写法强烈不建议,尤其在生产或多人共用环境里。

这些经验不是书上教的,都是我或者同事实际踩过之后总结出来的。有一次某同学图省事,直接开了0.0.0.0监听,结果日志里出现了大量来自陌生地址的访问尝试,吓得我赶紧把服务改成 SSH 转发模式。安全这个东西,一次疏忽可能就是整台服务器的灾难。

7. 我对这套方案的个人补充建议

最后分享一点个人体会。如果你打算在实验室服务器上长期用 Jupyter 工作,我建议一上来就别用老式 Notebook 界面,直接用 JupyterLab。命令相同,配置大同小异,但界面更适合同时开多个 Notebook、终端、文件管理,效率高不少。启动 JupyterLab 只需要把启动命令改成:

jupyter lab --no-browser --port=8888

SSH 端口转发和 kernel 配置不用变,直接复用。

另外,如果你经常需要在本地传代码、看结果,可以考虑把 Jupyter 的密码放到~/.jupyter/jupyter_server_config.json里备份一份,或者写一个一键启动脚本。我在服务器上一般放一个start_jupyter.sh:

#!/bin/bash nohup jupyter lab --no-browser --port=8888 > ~/jupyter.log 2>&1 & echo "Jupyter started, check ~/jupyter.log"

以后每次连上服务器,只需要bash start_jupyter.sh然后本地跑 SSH 转发,两条命令搞定。这套组合我用了一年多,中间没出过什么大问题。

折腾服务器的过程就是这样——一开始觉得乱,等你把链路各个环节都摸清楚了,之后再遇到问题基本就是"看日志、查端口、对配置"三步走的事。希望这篇记录能帮你少绕点弯路。毕竟环境配置这种事,永远是第一次最痛苦,配好了就一劳永逸。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询