在AWS EC2上部署用于深度学习的Jupyter Notebook环境
0. 前言
为了个人学习用途,我试图在云端部署一个深度学习的环境,整个过程中收获非常大,特此记录,以供将来参考。
EC2的操作系统是Ubuntu 24.04,本地环境是Windows11下的WSL Ubuntu 24.04。
1. 基础版:本地部署
a. Python虚拟环境和Jupyter部署
#使用python3.10,因为python3.12在安装d2l时会报错
sudo apt-get install python3.10-venv
mkdir d2l | cd d2l
python3.10 -m venv .venv
source .venv/bin/activate
pip install jupyter
jupyter notebook
这样Jupyter的服务器就正常启动了,命令行返回
[I 2025-02-20 22:33:19.474 ServerApp] Serving notebooks from local directory: /home/dogking/ws_py
0 active kernels
Jupyter Server 2.14.2 is running at:
http://localhost:8888/tree?token=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
http://127.0.0.1:8888/tree?token=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
其中toekn=后面的一串字符就是密钥
在浏览器地址栏中输入localhost:8888
,服务器会要求输入密钥,把密钥复制填入,填入后可以选择自己设定密码,以后就可以用密码登录了。
2. 远程版1:部署在EC2上
a. EC2登录
启动EC2实例后会产生一个只能下载一次的私钥,我们会用这个私钥登录实例。把它放到某个路径下,然后改成400权限,指定用这个私钥登录实例
chmod 400 keypair_ec2.pem
ssh -i "keypair_ec2.pem" ubuntu@ec2-xx-xxx-xxx-xxx.ap-northeast-1.compute.amazonaws.com
登陆后可以像一台普通的linux电脑一样配置Python和Jupyter环境。
b. ssh转发设定
Jupyter运行在EC2服务器上,我们想通过本地机器连接到服务器,需要SSH转发。这个动作是把服务器的localhost:8888端口映射成本地的8889端口,访问本地的8889端口相当于访问服务器的localhost:8888。这种转发称为本地转发。
ssh -i "keypair_ec2.pem" ubuntu@ec2-xx-xxx-xxx-xxx.y.compute.amazonaws.com -L 8889:localhost:8888
在本地的浏览器地址栏中输入localhost:8889
。
c. 允许远程访问
EC2实例是有公网IP的,所以我们也可以不通过SSH转发直接访问EC2的8888端口,但需要使Jupyter允许来自非本地的连接。
进入ipython环境,ipython
,然后,
# 首先生成一个密码的哈希值,把它存在Jupyter的配置里。
# 产生密码的哈希值的功能在Jupyter历代版本中都略有改动,如有报错以当前版本官方文档为准。
from jupyter_server.auth import passwd
passwd()
输入密码后,命令行会返回密码的哈希值。
接下来要生成一个Jupyter的配置文件
jupyter notebook --generate-config
vi ~/.jupyter/jupyter_notebook_config.py
在配置文件中修改这几行
c = get_config()
c.NotebookApp.ip = '0.0.0.0'
c.NotebookApp.password = u'YOUR PASSWORD HASH'
c.NotebookApp.port = 8888
另外有一行默认是False的Allow Remote Access需要设为True。
c.ServerApp.allow_remote_access = True
在EC2的管理界面需要增加一条inbound rule,允许8888端口的流量。 这样在本地就可以直接访问EC2机器的8888端口上的Jupyter了。
http://ec2-xx-xxx-xxx-xxx.ap-northeast-1.compute.amazonaws.com:8888/
3. 把链接变成HTTPS
用浏览器打开Jupyter,发现是通过HTTP协议传输的,大部分主流浏览器都会警告链接不安全,所以我们需要HTTPS协议来保障数据的安全性。
a. nginx和certbot安装
nginx是一个用来处理各种端口请求的Web服务器。在EC2实例上安装nginx:
sudo apt-get install nginx
service nginx status
certbot会自动认证域名、签发证书和自动续期。安装certbot:
sudo apt-get install snap
sudo apt-get upgrade snap
sudo snap install --classic certbot
然而,certbot不会给EC2的公共DNS域名签发证书,我们需要一个真正的域名。
b. 域名和DNS配置
很多服务商都提供域名注册, 比如spaceship,cloudfare, 以及国内的阿里、腾讯,还有专门提供xyz域名的gen.xyz. 我在spaceship上购买了一个.xyz域名。结账之后的开箱步骤中,可以设置DNS规则。有两条规则一定要设:
A类规则,把域名解析到某个IP地址,这里填EC2实例的IP
CNAME类规则,建立alias,这里把www填进去,效果是使www.signalxceed.xyz和signalxceed.xyz等价
EC2的管理界面还需要打开80端口,否则certbot的认证会不通过,显示连接不到这台服务器。
现在可以用certbot签发证书了:
sudo certbot --nginx
根据提示输入域名并签署即可。
c. 反向代理配置
最后还需要设定反向代理,把HTTPS的请求重定向到localhost:8888。编辑nginx配置文件/etc/nginx/sites-enabled/default
location /jupyter {
proxy_pass http://localhost:8888;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection upgrade;
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
/jupyter的意思是匹配到/jupyter就停下,不去管后面的东西。如果是写的/jupyter/,碰到/jupyter/?tree这种地址不能精确匹配,还会继续往下走找别的匹配规则,导致不停地重定向报错。
现在重新加载一次nginx的配置:
sudo nginx -t && sudo nginx -s reload
在后台启动Jupyter,这样SSH断开之后仍会在运行。
nohup jupyter notebook &> /dev/null &
我们打算给Jupyter分配的路径是signalxceed.xyz/jupyter/,所以还需要修改一下Jupyter配置文件里的的默认url:
c.ServerApp.base_url = '/jupyter/'
在本机上,现在可以用HTTPS访问服务器了。EC2的8888端口也可以关掉了,因为所有流量都会走HTTPS(443)端口。
https://signalxceed.xyz/jupyter/
4. 远程版2:利用本地主机的算力
免费的EC2实例只有1G RAM和十分有限的IO性能,运行深度学习很是受限。要是远程也能连接家里电脑,使用家里电脑的GPU的算力就好了。
a. ssh远程转发
Jupyter运行在没有公网IP的家里电脑A上,我们想用另一台电脑B,通过EC2服务器转发一下,连接到A。这个动作是把电脑A的localhost:8888端口映射成EC2实例的8987端口,访问EC2的8987端口相当于访问电脑A的localhost:8888。这种转发称为远程转发。
ssh -i "keypair_ec2.pem" -R 8987:localhost:8888 ubuntu@ec2-xx-xxx-xxx-xxx.ap-northeast-1.compute.amazonaws.com
这时,只有EC2服务器能访问电脑A,还需要打开EC2的网关功能才能让电脑B透过EC2访问电脑A。
sudo vi /etc/ssh/sshd_config
然后,
GatewayPorts yes
此外,EC2需要打开8987端口,电脑A上的Jupyter需要被配置成允许远程访问。电脑B现在可以访问电脑A上的Jupyter了。
http://signalxceed.xyz:8987
b. 反向代理配置
同理,我们还是想通过HTTPS访问电脑A,那么在nginx的配置里再加一条匹配规则,假设分配给电脑A的路径是signalxceed.xyz/jupyter2。
location ^~/jupyter2/ {
proxy_pass http://localhost:8987;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection upgrade;
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
同样地,电脑A的Jupyter的默认路径也需要修改:
c.ServerApp.base_url = '/jupyter2/'
电脑B现在可以通过HTTPS访问电脑A了,EC2的8987端口和网关功能也可以关掉了。
https://signalxceed.xyz/jupyter2/
参考资料
Jupyter
https://jupyter-server.readthedocs.io/en/stable/operators/public-server.html https://dataschool.com/data-modeling-101/running-jupyter-notebook-on-an-ec2-server https://zh.d2l.ai/chapter_appendix-tools-for-deep-learning/aws.html#jupyter
HTTPS
https://dbusteed.github.io/setup-jupyter-lab-on-remote-server
SSH
https://zhuanlan.zhihu.com/p/635001871
DNS
https://phoenixnap.com/kb/dns-record-types
Certbot
https://community.letsencrypt.org/t/solved-another-instance-of-certbot-is-already-running/44690/2 https://community.letsencrypt.org/t/please-help-ensure-the-listed-domains-point-to-this-nginx-server-and-that-it-is-accessible-from-the-internet/166569
nginx
https://blog.csdn.net/yexudengzhidao/article/details/121600603
vim
https://developer.aliyun.com/article/1385040
Port