# 在AWS EC2上部署用于深度学习的Jupyter Notebook环境 ## 0. 前言 为了个人学习用途,我试图在云端部署一个深度学习的环境,整个过程中收获非常大,特此记录,以供将来参考。 EC2的操作系统是Ubuntu 24.04,本地环境是Windows11下的WSL Ubuntu 24.04。 --- ## 1. 基础版:本地部署 ### a. Python虚拟环境和Jupyter部署 ``` bash #使用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的服务器就正常启动了,命令行返回 ``` console [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权限,指定用这个私钥登录实例 ``` bash 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。这种转发称为本地转发。 ``` bash 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`,然后, ``` python # 首先生成一个密码的哈希值,把它存在Jupyter的配置里。 # 产生密码的哈希值的功能在Jupyter历代版本中都略有改动,如有报错以当前版本官方文档为准。 from jupyter_server.auth import passwd passwd() ``` 输入密码后,命令行会返回密码的哈希值。 接下来要生成一个Jupyter的配置文件 ``` bash jupyter notebook --generate-config vi ~/.jupyter/jupyter_notebook_config.py ``` 在配置文件中修改这几行 ``` python 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。 ``` python c.ServerApp.allow_remote_access = True ``` 在EC2的管理界面需要增加一条inbound rule,允许8888端口的流量。 这样在本地就可以直接访问EC2机器的8888端口上的Jupyter了。 ``` link 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: ``` bash sudo apt-get install nginx service nginx status ``` certbot会自动认证域名、签发证书和自动续期。安装certbot: ``` bash 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规则。有两条规则一定要设: 1. A类规则,把域名解析到某个IP地址,这里填EC2实例的IP 2. CNAME类规则,建立alias,这里把www填进去,效果是使www.signalxceed.xyz和signalxceed.xyz等价 EC2的管理界面还需要打开80端口,否则certbot的认证会不通过,显示连接不到这台服务器。 现在可以用certbot签发证书了: ``` bash sudo certbot --nginx ``` 根据提示输入域名并签署即可。 ### c. 反向代理配置 最后还需要设定反向代理,把HTTPS的请求重定向到localhost:8888。编辑nginx配置文件/etc/nginx/sites-enabled/default ``` nginx 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的配置: ``` bash sudo nginx -t && sudo nginx -s reload ``` 在后台启动Jupyter,这样SSH断开之后仍会在运行。 ``` bash nohup jupyter notebook &> /dev/null & ``` 我们打算给Jupyter分配的路径是signalxceed.xyz/jupyter/,所以还需要修改一下Jupyter配置文件里的的默认url: ``` python c.ServerApp.base_url = '/jupyter/' ``` 在本机上,现在可以用HTTPS访问服务器了。EC2的8888端口也可以关掉了,因为所有流量都会走HTTPS(443)端口。 ``` link 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。这种转发称为远程转发。 ``` bash 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。 ``` bash sudo vi /etc/ssh/sshd_config ``` 然后, ``` GatewayPorts yes ``` 此外,EC2需要打开8987端口,电脑A上的Jupyter需要被配置成允许远程访问。电脑B现在可以访问电脑A上的Jupyter了。 ``` link http://signalxceed.xyz:8987 ``` ### b. 反向代理配置 同理,我们还是想通过HTTPS访问电脑A,那么在nginx的配置里再加一条匹配规则,假设分配给电脑A的路径是signalxceed.xyz/jupyter2。 ``` nginx 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的默认路径也需要修改: ``` python c.ServerApp.base_url = '/jupyter2/' ``` 电脑B现在可以通过HTTPS访问电脑A了,EC2的8987端口和网关功能也可以关掉了。 ``` link https://signalxceed.xyz/jupyter2/ ``` --- ## 参考资料 Jupyter HTTPS SSH DNS Certbot nginx vim Port