docker 和 uu 的冲突

uu / docker / lsp / winsock

之前写了一个迷你系列,来分析 LSP 的 AppId_Catalog 是如何计算的, 但是却忘了提 docker 和 uu 的冲突是如何解决的了,这次我们把这个问题修掉。

原因

我想看过上个系列 的朋友们应该知道了问题的原因,不过我觉得这里再放一个懒人包罗列一下要点也未尝不可:

TLDR

  • docker 在 windows 上使用 wsl 作为 linux 的虚拟机。
  • uu 使用 LSP 技术来接管所有网络流量。
  • LSP 在 windows 8 的时候被微软淘汰。
  • 目前的 wsl 并不支援这一技术
  • 而为了方便用户,uu 默认会接管所有应用的网络,包括 wsl

因此,在安装了 uu 的机器上,你会发现,与 wsl 有关的应用,比如我们这次的 docker, 都会无法启动。

解决

解决这个问题,我们需要让 LSP 放行 wsl,确保 docker 可以正常开启 linux 虚拟机, 然后还要让 LSP 放行 docker 服务, 因为 docker 容器和外部环境的联通是由 docker 控制的,这个默认也会被 uu 接管。

放行 wsl

放行 wsl,推荐的做法还是使用 github @mariotaku 的脚本

#Requires -RunAsAdministrator
# Fix for https://github.com/microsoft/WSL/issues/4177

$MethodDefinition = @'
[DllImport("ws2_32.dll", CharSet = CharSet.Unicode)]
public static extern int WSCSetApplicationCategory([MarshalAs(UnmanagedType.LPWStr)] string Path, uint PathLength, [MarshalAs(UnmanagedType.LPWStr)] string Extra, uint ExtraLength, uint PermittedLspCategories, out uint pPrevPermLspCat, out int lpErrno);
'@

$Ws2Spi = Add-Type -MemberDefinition $MethodDefinition -Name 'Ws2Spi' -PassThru

$WslLocation = Get-AppxPackage MicrosoftCorporationII.WindowsSubsystemForLinux | Select-Object -expand InstallLocation

$Executables = ("wsl.exe", "wslservice.exe");

foreach ($Exe in $Executables) {
    $ExePath = "${WslLocation}\${Exe}";
    $ExePathLength = $ExePath.Length;

    $PrevCat = $null;
    $ErrNo = $null;
    if ($Ws2Spi::WSCSetApplicationCategory($ExePath, $ExePathLength, $null, 0, [uint32]"0x80000000", [ref] $PrevCat, [ref] $ErrNo) -eq 0) {
        Write-Output "Added $ExePath!";
    }
}

这个脚本会找到商店安装的 wsl,然后调用 WinSock 的 WSCSetApplicationCategory 接口来对程序进行配置。之前我们说过,商店安装的 wsl 每次更新后路径会变, 所以每次更新都要执行一下这个脚本。简单一点的话,执行后重启即可。

放行 docker

我们根据这个脚本依样画葫芦,就可以给 docker 放行, 由于 docker 的安装位置是固定的,所以还更简单一点:

$DockerLocation = "C:\Program Files\Docker\Docker"
$DockerExes = ("Docker Desktop.exe", "DockerCli.exe", "courgette64.exe", "com.docker.service");

foreach ($Exe in $DockerExes) {
    $ExePath = "${DockerLocation}\${Exe}";
    $ExePathLength = $ExePath.Length;

    $PrevCat = $null;
    $ErrNo = $null;
    if ($Ws2Spi::WSCSetApplicationCategory($ExePath, $ExePathLength, $null, 0, [uint32]"0x80000000", [ref] $PrevCat, [ref] $ErrNo) -eq 0) {
        Write-Output "Added $ExePath!";
    }
}

docker 目录下有三个 exe,我全放进去了,还是不成功,后来发现, docker 的后台服务并不是个 exe:

docker service

我的理解看来已经过时了,所以 $DockerExes 才有了最后一个 service,再重启。搞定!

总结

解决 docker 与 uu 的冲突,需要同时放行 wsl 和 docker, 方法都是调用 WinSock 的 WSCSetApplicationCategory。由于 Category 这个 id 是只与程序路径有关,所以对于 docker 来说,设定一次就好, 不过由于 docker 在 windows 上使用的还是 wsl,所以首先要解决 wsl 的问题。