1. 为什么要在局域网里折腾 Jupyter Notebook 远程访问
很多人第一次听到"内网远程访问 Jupyter Notebook"这个需求,脑子里冒出来的第一个疑问是:我直接在本机跑不就行了吗,为什么非要远程?这个问题问得对,因为如果只是自己写两行 Python 脚本,本机跑确实最省事。但真实场景往往不是这样。
我遇到这个需求,最早是因为一台放在机房的深度学习工作站。那台机器装了两块显卡,跑模型训练的时候风扇声音跟吹风机一样,坐在旁边根本没法安心写代码。更现实的问题是,数据全在那台机器上,几十个 G 的数据集不可能每次都拷到笔记本上。于是最自然的想法就是:让那台机器当"计算服务器",我在自己的笔记本上通过浏览器连过去,用它的算力、用它的数据,但界面在我这边。
这就是 Jupyter Notebook 远程访问的核心价值——把计算和交互分离。计算跑在性能强的机器上,交互界面在你手边的设备上。Anaconda 在这里扮演的角色是环境管理器,它把 Python 解释器、Jupyter 以及一大堆科学计算库打包在一起,省去了手动配环境的麻烦。
但这里有个关键前提:内网(局域网)。为什么强调内网?因为内网环境相对封闭,机器之间通过交换机或路由器直连,不经过公网,配置起来简单、速度快、延迟低,而且不涉及任何对外暴露的问题。你不需要考虑域名、证书、端口映射这些让人头大的东西,只要两台机器在同一个网段里能互相 ping 通,事情就成了一半。
这篇文章适合谁看?三类人最对口。第一类是有多台机器(比如一台台式机加一台笔记本)的开发者,想把重活丢给性能好的那台。第二类是实验室或小团队里共用一台计算服务器的同学,需要多人各自连上去用。第三类是刚接触 Anaconda 和 Jupyter、想搞明白"服务端和客户端到底怎么分工"的新手。不管你属于哪一类,下面的内容都会从最基础的原理讲起,一步步带你跑通,并且把我踩过的坑都摊开说清楚。
需要提前说明的是,本文所有操作都限定在局域网内部,不涉及任何跨网络访问的配置。你只需要保证两台机器在同一个路由器或交换机下,能互相通信即可。
2. 搞懂 Jupyter 的服务端与客户端分工再动手
2.1 Jupyter Notebook 本质上是一个 Web 服务
很多人用了很久 Jupyter 都没意识到一件事:你平时在浏览器里打开的那个localhost:8888,其实是一个跑在本地的 Web 服务器。Jupyter Notebook 的架构非常清晰,它由两部分组成:一个是内核(Kernel),负责真正执行 Python 代码;另一个是 Web 服务器,负责把代码、输出、Markdown 渲染成网页给你看。
你平时双击图标启动 Jupyter,它做的事情就是:在本地的 8888 端口起一个 HTTP 服务,然后自动帮你打开浏览器访问这个地址。浏览器和这个服务之间的通信,走的是标准的 HTTP 协议加上 WebSocket(用于实时推送执行结果)。
理解这一点非常关键,因为它直接决定了远程访问的思路:既然它是个 Web 服务,那我只要让这个服务监听在一个别的机器能访问到的地址上,不就能远程连了吗?答案是对的,但默认配置下它只监听localhost,也就是只接受本机连接。我们要做的,就是把它改成监听所有网络接口,再加上一层访问控制。
2.2 为什么默认只监听 localhost
这是很多人第一次配置时最困惑的地方:我明明启动了 Jupyter,为什么同事的电脑就是连不上?
原因在于 Jupyter 的默认安全策略。它默认绑定127.0.0.1,这个地址是回环地址,只有本机能访问。这样设计是出于安全考虑——如果默认就监听所有接口,那同一网络下任何人只要知道你的 IP 和端口,就能直接打开你的 Notebook,执行任意代码。这在共享网络环境下是相当危险的。
所以我们要做的改动,本质上是主动打开这扇门,同时配上一把锁。打开门的方式是修改绑定地址,配锁的方式是设置密码和 token。这两件事必须一起做,只做前者不做后者,等于把家门敞开还贴个告示说"欢迎光临"。
2.3 Anaconda 环境下的 Jupyter 到底装在哪
还有一个容易被忽略的细节:Anaconda 里的 Jupyter 和你用 pip 单独装的 Jupyter,配置文件位置可能不一样。Anaconda 会把 Jupyter 装在它自己的环境目录下,配置文件默认生成在用户主目录的.jupyter文件夹里。
在 Windows 上,这个路径通常是C:\Users\你的用户名\.jupyter\;在 Linux 或 macOS 上,是~/.jupyter/。这个目录下的jupyter_notebook_config.py就是我们要改的核心文件。如果你从来没生成过这个文件,需要先运行一次生成命令,这个后面会讲。
搞清楚这三个基础认知,后面的操作就不会是"照着敲命令但不知道在干嘛"了。你知道每一步在改什么、为什么改,出问题的时候才能自己排查。
3. 服务端配置:让 Jupyter 愿意接受外部连接
3.1 生成配置文件这一步别跳过
假设你的服务端机器已经装好了 Anaconda,打开终端(Windows 用 Anaconda Prompt,Linux/macOS 用普通终端),先确认 Jupyter 能正常启动。然后执行下面这条命令生成配置文件:
jupyter notebook --generate-config执行完你会看到类似这样的输出,告诉你配置文件生成到了哪个路径:
Writing default config to: /home/yourname/.jupyter/jupyter_notebook_config.py注意:如果你之前已经生成过这个文件,这条命令会提示文件已存在,问你是否覆盖。这时候一定要选否(输入 n),否则你之前的配置就全没了。如果确实想重新生成,先把旧文件备份一下。
这个文件内容很长,全是注释,默认所有配置项都是被注释掉的。我们不需要全部看懂,只改几个关键项就行。
3.2 设置访问密码而不是裸奔
在改绑定地址之前,先把密码设好。Jupyter 提供了一个专门的命令来生成密码哈希:
jupyter notebook password它会提示你输入两次密码,然后自动把加密后的哈希写进配置文件。这个密码就是你之后在浏览器里打开 Notebook 时需要输入的。为什么要用哈希而不是明文?因为配置文件是纯文本,如果直接写明文密码,任何能读到这个文件的人都能看到你的密码。哈希值即使被看到,也无法反推出原始密码。
设置完之后,你可以打开配置文件确认一下,会看到类似这样一行:
c.NotebookApp.password = 'argon2:$argon2id$v=19$m=10240,t=10,p=8$...'看到这串东西就说明密码设置成功了。这里用的是 argon2 算法,比早期的 sha1 安全得多,Anaconda 较新版本默认都用这个。
3.3 修改绑定地址与端口
接下来是核心改动。用任意文本编辑器打开jupyter_notebook_config.py,找到下面这几行(它们默认都是注释状态),改成我们需要的值:
c.NotebookApp.ip = '0.0.0.0' c.NotebookApp.port = 8888 c.NotebookApp.open_browser = False c.NotebookApp.allow_remote_access = True逐条解释一下为什么这么改。
c.NotebookApp.ip = '0.0.0.0'是让 Jupyter 监听所有网络接口。0.0.0.0不是一个真实地址,而是一个通配符,意思是"本机所有网卡上的地址都接受连接"。这样无论是通过 127.0.0.1 还是通过局域网 IP,都能访问到。
c.NotebookApp.port = 8888是端口号。8888 是 Jupyter 的默认端口,你也可以改成别的,比如 8890,只要不和其他服务冲突就行。如果 8888 被占用了,启动时会报错,换个端口即可。
c.NotebookApp.open_browser = False是让服务端启动时不要自动弹浏览器。因为服务端机器可能根本没有图形界面,或者你根本不需要在它上面看,关掉能省点资源,也避免报错。
c.NotebookApp.allow_remote_access = True是明确允许远程访问。有些版本即使设了0.0.0.0,如果这项是 False,仍然会被拦下来。
提示:不同版本的 Jupyter 配置项名称可能略有差异。较新的版本里,
NotebookApp可能被ServerApp替代。如果你改了没生效,先确认一下你的 Jupyter 版本,用jupyter --version查看。如果版本较新,把c.NotebookApp.xxx换成c.ServerApp.xxx试试。
3.4 启动并验证服务端
配置改完,保存文件,然后在服务端启动 Jupyter:
jupyter notebook如果配置正确,你会看到类似这样的输出:
[I 10:23:45.123 NotebookApp] Serving notebooks from local directory: /home/yourname [I 10:23:45.123 NotebookApp] Jupyter Notebook 6.4.5 is running at: [I 10:23:45.123 NotebookApp] http://your-hostname:8888/注意看它打印出来的地址,如果显示的是0.0.0.0:8888或者你的主机名,而不是localhost:8888,说明绑定成功了。
这时候在服务端本机用浏览器访问http://localhost:8888应该能打开,输入密码后进入。但这只证明本机能用,还不能证明远程能连。真正的验证要在客户端机器上做,这个放到下一节。
3.5 防火墙这道坎必须过
服务端配置好了,客户端却连不上,十有八九是防火墙在拦。这是新手最容易卡住的地方,而且报错信息往往很模糊,浏览器只会显示"无法访问此网站",不会告诉你具体原因。
在 Linux 上,如果用的是 ufw,需要放行 8888 端口:
sudo ufw allow 8888/tcp如果用的是 firewalld:
sudo firewall-cmd --permanent --add-port=8888/tcp sudo firewall-cmd --reload在 Windows 上,第一次启动 Jupyter 监听外部地址时,系统通常会弹出一个防火墙提示框,问你是否允许该程序通过防火墙。这时候一定要勾选"专用网络"并允许,否则外部连接会被直接挡掉。如果当时手滑点了取消,需要手动去"Windows Defender 防火墙"的"允许应用通过防火墙"里把 Python 或 Jupyter 加进去。
注意:这里只放行局域网需要的端口即可,不要图省事把防火墙整个关掉。关防火墙虽然能快速解决问题,但会让机器完全暴露在局域网中,得不偿失。
4. 客户端连接:从另一台机器打开 Notebook
4.1 先确认两台机器能互相通信
在客户端机器上,第一步不是打开浏览器,而是先确认网络通不通。打开终端或命令行,ping 一下服务端的局域网 IP:
ping 192.168.1.100这里的 IP 要换成你服务端的实际局域网地址。怎么查服务端的 IP?Linux 上用ip addr或ifconfig,Windows 上用ipconfig。找那个以 192.168 或 10 开头的地址,那就是局域网 IP。
如果 ping 不通,说明两台机器根本不在一个可通信的网络里,后面的都不用试了。常见原因有两个:一是两台机器连的不是同一个路由器或交换机;二是服务端开了防火墙把 ICMP 也拦了(这种情况少见,但存在)。先解决网络连通性,再谈 Jupyter。
4.2 浏览器访问与 token 处理
ping 通了之后,在客户端浏览器地址栏输入:
http://192.168.1.100:8888把 IP 换成你的服务端地址。正常情况下,你会看到一个登录页面,要求输入密码。输入你在服务端设置的密码,就能进入 Notebook 界面了。
但有时候你会遇到另一种情况:页面提示需要 token。这是因为某些版本的 Jupyter 在首次启动时会生成一个一次性 token,即使你设了密码,它也可能要求先输入 token。token 在哪里找?就在服务端启动 Jupyter 时终端打印的那段输出里,有一行类似:
http://your-hostname:8888/?token=abc123def456...把token=后面那串字符复制出来,粘贴到客户端的 token 输入框里即可。输入一次之后,后续就只需要密码了。
如果你觉得每次找 token 太麻烦,可以在配置文件里把 token 验证关掉,只保留密码验证:
c.NotebookApp.token = ''提示:关掉 token 之后,密码就成了唯一的防线,所以密码一定要设得复杂一点。局域网虽然相对封闭,但同一网络下可能有其他设备,不能假设绝对安全。
4.3 用主机名代替 IP 的可行性
如果你觉得记 IP 太麻烦,也可以尝试用主机名访问,比如http://my-workstation:8888。但这需要局域网内有 DNS 解析,或者你在客户端的 hosts 文件里手动加了映射。对于临时用用,直接记 IP 更省事;如果是长期使用,配个 hosts 映射会舒服很多。
在 Windows 上编辑C:\Windows\System32\drivers\etc\hosts,在 Linux/macOS 上编辑/etc/hosts,加一行:
192.168.1.100 my-workstation之后就能用http://my-workstation:8888访问了。这个改动只影响你自己的机器,不影响别人。
4.4 连接成功后的实际体验
连上之后,你会发现操作体验和本机几乎没区别。新建 Notebook、写代码、运行、看输出,全都正常。唯一的差别是执行速度取决于服务端的性能,而不是你客户端机器的性能。如果你的服务端有 GPU,那在 Notebook 里用torch.cuda.is_available()检查一下,能返回 True 就说明 GPU 可用,接下来就能在远程环境里跑训练了。
文件管理方面,Notebook 里看到的目录是服务端的工作目录,不是你客户端的目录。你在 Notebook 里上传文件,文件是传到服务端去的;你下载文件,也是从服务端下载。这一点要心里有数,别搞混了。
5. 那些让我折腾半天的坑与排查思路
5.1 改了配置却不生效
这是最让人抓狂的情况:明明配置文件改对了,重启 Jupyter 后行为却和没改一样。我遇到过好几次,后来发现原因通常是这几个。
第一个原因是改错了配置文件。Anaconda 环境下可能存在多个 Jupyter 配置文件,比如你之前用 pip 装过一个 Jupyter,它的配置在另一个位置。Jupyter 启动时会按优先级读取,可能读的不是你改的那个。排查方法是启动时加--debug参数,看它到底加载了哪个配置文件:
jupyter notebook --debug输出里会有一行Loading config file: ...,那个路径才是真正生效的文件。
第二个原因是配置项名称过时。前面提过,新版本把NotebookApp改成了ServerApp。如果你用的是 Jupyter Notebook 7 或 JupyterLab,配置项名称可能已经变了。这时候要么降级到旧版本,要么按新版本的名称改。
第三个原因是没重启服务。配置文件是启动时读取的,改完必须完全停掉 Jupyter 再重新启动,热重载是不存在的。
5.2 端口被占用怎么办
启动时报Address already in use,说明 8888 端口已经被别的程序占了。可能是你之前启动的 Jupyter 没关干净,也可能是别的服务在用这个端口。
先查是谁占用了端口。Linux 上用:
lsof -i :8888Windows 上用:
netstat -ano | findstr :8888找到进程号后,要么把它关掉,要么给 Jupyter 换个端口。换端口很简单,改配置文件里的c.NotebookApp.port,或者启动时直接指定:
jupyter notebook --port 88905.3 能 ping 通但浏览器打不开
这种情况最迷惑人:网络明明是通的,ping 有回应,但浏览器就是连不上。可能的原因有几个。
一是防火墙只放行了 ICMP 没放行 TCP。ping 用的是 ICMP 协议,而浏览器访问用的是 TCP。防火墙可能允许 ping 但拦住了 8888 端口的 TCP 连接。回到服务端检查防火墙规则,确认 8888 端口确实放行了。
二是Jupyter 实际没监听在 0.0.0.0。虽然你改了配置,但可能没生效(见上一节)。在服务端用netstat -tlnp | grep 8888看一下,如果显示的是127.0.0.1:8888而不是0.0.0.0:8888,说明绑定没改成功。
三是客户端和服务端不在同一网段。比如服务端是 192.168.1.x,客户端是 192.168.0.x,虽然都能上网,但互相之间可能被路由规则隔离。确认两台机器的 IP 前三段是否一致。
5.4 多人同时使用时的资源冲突
如果是一台服务器多人共用,会碰到一个现实问题:每个人启动 Jupyter 都想用 8888 端口,但端口只能被一个进程占用。解决办法是每个人用不同的端口。比如第一个人用 8888,第二个人用 8889,第三个人用 8890,以此类推。
启动时指定端口即可:
jupyter notebook --port 8889但这样每个人都要记自己的端口,容易乱。更好的做法是每个人在自己的用户目录下配置不同的端口,写进各自的配置文件里,启动时自动生效。
另外,多人共用时还要注意工作目录的隔离。默认情况下 Jupyter 启动在哪个目录,就只能访问那个目录及其子目录。每个人在自己的 home 目录下启动,就天然隔离了,不会互相看到对方的文件。
5.5 长时间运行断开连接的问题
远程连接有个常见现象:你开着 Notebook 去干别的事,过一段时间回来发现连接断了,需要重新登录。这是因为浏览器和 Jupyter 之间的 WebSocket 连接在空闲时可能被中断。
如果只是偶尔断一下,重新连上就行,Notebook 里的代码和输出不会丢,因为它们是保存在服务端的。但如果频繁断开影响使用,可以检查一下是不是网络不稳定,或者服务端的 Jupyter 进程被系统休眠策略影响了。
对于需要长时间跑的任务,建议不要依赖浏览器的连接状态。把训练脚本写成.py文件,用nohup或tmux在服务端后台跑,Notebook 只用来做交互式开发和调试。这样即使浏览器断了,任务照样在跑。
6. 让远程访问更顺手的几个进阶配置
6.1 配置默认工作目录
每次启动 Jupyter 都跳到 home 目录,然后手动一层层点进项目文件夹,时间长了很烦。可以在配置文件里指定默认目录:
c.NotebookApp.notebook_dir = '/home/yourname/projects'Windows 上路径要写成r'D:\projects'这种形式,注意用原始字符串避免反斜杠转义问题。改完之后启动 Jupyter,直接就在项目目录里了。
注意:这个目录必须存在,否则 Jupyter 启动会报错。另外要确保运行 Jupyter 的用户对这个目录有读写权限。
6.2 用 nohup 让 Jupyter 在后台常驻
默认情况下,你关掉终端,Jupyter 进程就跟着结束了。如果希望它一直在后台跑,可以用 nohup:
nohup jupyter notebook > jupyter.log 2>&1 &这条命令的意思是:把 Jupyter 放到后台运行,标准输出和错误输出都重定向到jupyter.log文件,&表示后台执行。这样即使你退出 SSH 或关掉终端,Jupyter 依然在跑。
想停掉它的时候,先找到进程号:
ps aux | grep jupyter然后 kill 掉即可。日志文件jupyter.log里会记录启动信息和访问日志,出问题的时候可以翻一翻。
6.3 用 tmux 管理多个会话
如果你经常需要在服务端同时跑多个任务,tmux 是个好东西。它能在服务端创建多个独立的终端会话,每个会话里可以跑不同的东西,而且断开连接后会话依然保留。
基本用法很简单:
tmux new -s jupyter # 新建一个叫 jupyter 的会话 # 在里面启动 jupyter notebook # 按 Ctrl+B 然后按 D 脱离会话 tmux attach -t jupyter # 重新连接会话这样你可以一个会话跑 Jupyter,另一个会话跑训练脚本,互不干扰。比起 nohup,tmux 的好处是你能随时回去看实时输出,交互性更好。
6.4 关于安全性的几句实在话
虽然本文限定在局域网内,但有些话还是得说。局域网不等于绝对安全,尤其是在办公室、实验室这种多人共享的网络里。同一网络下的其他设备理论上是可以扫描到你的开放端口的。
所以几件事必须做到:密码要复杂,不要用123456这种;不要图省事关掉密码验证;如果长期不用,把 Jupyter 停掉而不是一直挂着;定期看看访问日志,有没有陌生的 IP 连过。
另外,c.NotebookApp.allow_remote_access = True这项虽然方便,但也意味着任何能访问到你 IP 的人都能尝试连接。如果你的局域网环境比较复杂,可以考虑用 SSH 隧道的方式,只在本机开一个转发端口,这样 Jupyter 根本不需要对外监听。不过 SSH 隧道的配置稍微复杂一些,属于另一个话题了,这里不展开。
7. 我实际用下来的一些体会
这套配置我在不同环境下搭过好几次,从 Windows 服务端到 Linux 服务端都试过。最大的感受是:配置文件那几行改动本身不难,难的是排查为什么没生效。新手卡住的地方往往不是"不知道怎么改",而是"改了没用,又不知道去哪找原因"。
我的建议是,每改一步就验证一步。改完绑定地址,先在服务端本机用局域网 IP 访问一下,确认监听生效了;再去客户端 ping 一下,确认网络通了;最后才在客户端浏览器访问。这样出问题的时候,你能快速定位是哪一环断了,而不是面对一个"连不上"的结果干瞪眼。
还有一个体会是关于版本的。Anaconda 更新挺频繁的,不同版本之间 Jupyter 的配置项名称、默认行为都可能有变化。网上搜到的教程如果年份比较早,配置项名称可能已经过时了。遇到对不上的情况,第一反应应该是去查你当前版本的官方文档,而不是硬套旧教程。
最后说个小事。远程访问配好之后,我习惯在服务端的 Jupyter 里装一个jupyterlab或者nbextensions,前者界面更现代,后者能加一些实用的小功能比如代码折叠、目录导航。这些都属于锦上添花,核心的远程访问跑通了,剩下的按自己喜好折腾就行。