派玩python之Jupyter使用远程kernel(3)- WSL环境

派玩python之Jupyter使用远程kernel(3)- WSL环境

经过上一期《派玩python之Jupyter使用远程kernel(2)》的介绍也有一段时间了。 这次继续介绍如何使用WSL环境(运行端)作为主要Python运行环境,满足如机器学习,显卡硬件设备等运行要求,并且连接到远程树莓派部署的Jupyter编辑器(开发端)上开发写代码。

WSL2环境设置

WSL是Microsoft开发的Linux子系统,可以方便地在Windows操作系统上进行类Linux系统开发。本例使用WSL2上安装的Ubuntu子系统进行演示。

WSL安装和SSH安装设置

我们假设你已经安装好了WSL,如果没有,请自行查看WSL安装文档

安装完成后, 执行wsl -l, 输出类似以下内容,就是安装成功了。

适用于 Linux 的 Windows 子系统分发版:
docker-desktop-data (默认)
Ubuntu-20.04
docker-desktop

如果有多个WSL子系统,比如我这个是3个,并且默认不是你想要的WSL,那么接下来所有wsl命令都需要指定。 为了不这么麻烦, 务必执行类似以下命令切换默认系统就可以了。我这里默认使用Ubuntu-20.04这个。

wsl -s Ubuntu-20.04

接下来参考网上,进行安装配置SSH相关内容:

  1. 在wsl2下重装ssh
sudo apt-get remove openssh-server
sudo apt-get install openssh-server
  1. 编辑sshd_config文件,修改几处配置
sudo vi /etc/ssh/sshd_config
    Port 222
    PermitRootLogin
    yesPasswordAuthentication yes
  1. 编辑sudo vim /etc/hosts.allow,添加一行
sudo vim /etc/hosts.allow
添加的内容为 sshd: ALL
  1. sudo visudo 然后加入
%sudo   ALL=(ALL) NOPASSWD: /etc/init.d/ssh
  1. 重启ssh服务
sudo service ssh --full-restart

宿主Windows的SSH转发WSL设置

Windows作为宿主机,本身是也可以安装SSH服务的。 这样就可以远程ssh登录Windows。我们在上一篇文章中就是这种方式。 本次的WSL也是可以安装SSH服务的。这时候就需要区分,ssh到Windows,还是ssh到WSL子系统。 我们存在两个SSH服务。 我们采取3)方式进行转发,来达到两个SSH服务都可以使用的目的。

IP         端口      SSH服务
宿主机      22       Windows的SSH服务    1)
WSL内部IP   222       WSL子系统的SSH服务   2)
宿主机    222->222   WSL子系统的SSH服务   3)

转发命令一句话就可以搞定,类似

netsh interface portproxy add v4tov4 listenport=222 connectport=22 connectaddress=<WSL的内部IP>

但是这个WSL子系统每次启动IP可能会变,或者宿主机启动,以上转发配置没有了。 因此我推荐使用自动化脚本(附录中的脚本1,来自网络),并且添加到Windows自动启动的任务计划程序中。

注意脚本1中的$Ports = (222)部分,可以按自己情况修改, 之后仍然可以执行netsh interface portproxy show v4tov4确认脚本执行后的情况。

建立jupter的kernel连接

这部分可以直接按照上一篇文章的利用remoteikernel自动远程连接章节即可。 当然是要在jupter环境中设置好对于WSL的SSH免密码登录, 可以参考SSH免密登录

关键的添加kernel命令大概是这个样子:

remote_ikernel manage --add     --kernel_cmd="ipython kernel -f {connection_file}"   --name="Remote WSL" --interface=ssh     --host=user@host:222 --workdir="~/" --verbose

注意修改--host=user@host:222 部分。如果使用了conda环境, 可以把--kernel_cmd="ipython kernel -f {connection_file}" 修改成 --kernel_cmd="$CONDA_PREFIX/bin/python -m ipykernel_launcher -f {connection_file}"

可能需要把conda安装位置和jupyter环境上conda保持一致。

sudo ln -s  /path/to/miniconda3 /opt/conda

到这里,如果能在jupyterlab上能够选择并且正常使用这个kernel,就证明一切OK了。

总结

本文介绍了WSL和jupyterlab进行远程连接的方法。 非常适合在jupyter上进行开发和测试,但是需要在WSL环境中运行的场景。 当然了, 本文介绍的方法同样也适用于在同一台主机的情况,即jupyter在Windows宿主机上,运行在WSL子系统。 这个时候不需要安装remote_ikernel, 其他都是一样的。

全文完。

附录

脚本1

# Start SSH Service.
#wsl sudo service ssh start
wsl sudo /etc/init.d/ssh start

# WSL2 network port forwarding script v1
#   for enable script, 'Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope CurrentUser' in Powershell,
#   for delete exist rules and ports use 'delete' as parameter, for show ports use 'list' as parameter.
#   written by Daehyuk Ahn, Aug-1-2020

# Display all portproxy information
If ($Args[0] -eq "list") {
    netsh interface portproxy show v4tov4;
    exit;
} 

# If elevation needed, start new process
If (-NOT ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator))
{
  # Relaunch as an elevated process:
  Start-Process powershell.exe "-File",('"{0}"' -f $MyInvocation.MyCommand.Path),"$Args runas" -Verb RunAs
  exit
}

# You should modify '$Ports' for your applications 
#$Ports = (2222,80,443,8080)
$Ports = (222)

# Check WSL ip address
wsl hostname -I | Set-Variable -Name "WSL"
$found = $WSL -match '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}';
if (-not $found) {
  echo "WSL2 cannot be found. Terminate script.";
  exit;
}

# Remove and Create NetFireWallRule
Remove-NetFireWallRule -DisplayName 'WSL 2 Firewall Unlock';
if ($Args[0] -ne "delete") {
  New-NetFireWallRule -DisplayName 'WSL 2 Firewall Unlock' -Direction Outbound -LocalPort $Ports -Action Allow -Protocol TCP;
  New-NetFireWallRule -DisplayName 'WSL 2 Firewall Unlock' -Direction Inbound -LocalPort $Ports -Action Allow -Protocol TCP;
}

# Add each port into portproxy
$Addr = "0.0.0.0"
#$Addr = "*"
Foreach ($Port in $Ports) {
    iex "netsh interface portproxy delete v4tov4 listenaddress=$Addr listenport=$Port | Out-Null";
    if ($Args[0] -ne "delete") {
        iex "netsh interface portproxy add v4tov4 listenaddress=$Addr listenport=$Port connectaddress=$WSL connectport=$Port | Out-Null";
    }
}

# Display all portproxy information
netsh interface portproxy show v4tov4;

# Give user to chance to see above list when relaunched start
If ($Args[0] -eq "runas" -Or $Args[1] -eq "runas") {
  Write-Host -NoNewLine 'Press any key to close! ';
  $null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown');
}