wslc拉取镜像设置代理

· 1441字 · 3分钟

wslc 用独立于 wsl 发行版的虚拟机承载容器,session 外的代理设置影响不到它内部的 dockerd,所以 wslc pull 依然会直连镜像仓库。本文探索 wslc 的架构后给出一个非持久化方案:带上 –http-proxy 等参数重启 session 内的 dockerd,并延长 session 的空闲回收时间。

关键字: wslc proxy http_proxy wslc代理

太长不看版

# 设置wslc更长的自动关闭
"session:`n  idleTimeout: 300`n" | Set-Content -Path "$env:LOCALAPPDATA\wslc\settings.yaml" -Encoding ascii
# 杀死dockerd并携带proxy选项重启
wslc system session run /usr/bin/sh -c '/usr/bin/killall dockerd -9; /usr/bin/nohup /usr/bin/dockerd --http-proxy=http://127.0.0.1:18088 --https-proxy=http://127.0.0.1:18088 --no-proxy=localhost,127.0.0.1 --containerd=/run/containerd/containerd.sock >/dev/null 2>&1 &'

探索与思路 🔗

wsl2发布了3.0.1版本,提供了wslc命令代替docker。根据微软debblogs的文章WSLC Architecture deep dive,wslc使用了独立于wsl发行版的另一个虚拟机实例装载容器实例。并且使用session隔离。

简单探索发现,wslc依然在session内使用标准的容器技术栈。甚至session的shell里有docker命令。 这也解释了为何session外部的wslc命令环境下的代理无法影响wslc pull。 毕竟在linux下也需要设置/etc/docker/daemon.json然后重启daemon。

PS C:\Users\Bo> wslc system session shell
root@ [ / ]# ps -Af | grep -v '\['
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  3 05:07 hvc0     00:00:00 /init
root       171     1  0 05:07 hvc0     00:00:00 /gns --socket 6
root       173     1  0 05:07 hvc0     00:00:00 /init
root       181     1  1 05:07 hvc0     00:00:00 /usr/bin/containerd --address /run/containerd/containerd.sock --root /var/lib/docker/containerd/daemon --state /run/docker/containerd/daemon
root       182     1  4 05:07 hvc0     00:00:00 /usr/bin/dockerd --containerd /run/containerd/containerd.sock
root       385     1  0 05:07 hvc0     00:00:00 /init
root       386   385  0 05:07 pts/0    00:00:00 /bin/sh --login
root       394   386  0 05:07 pts/0    00:00:00 ps -Af

查阅源码,dockerd进程应该直接由init创建。

// WSL\src\windows\wslcsession\WSLCSession.cpp
void WSLCSession::StartDockerd()
{
    std::vector<std::string> args{"/usr/bin/dockerd", "--containerd", c_containerdSocket};

    if (WI_IsFlagSet(m_featureFlags, WslcFeatureFlagsDebug))
    {
        args.emplace_back("--debug");
    }

    m_runtime.SetDockerdProcess(StartProcess("/usr/bin/dockerd", args, "dockerd", std::bind(&WSLCSession::OnDockerdExited, this)));
    WSL_LOG("DockerdStarted");
}

同时在wsl distro中的system distro中也可以找到dockerd。wslc应该与wslg共享同一个基础镜像。

PS C:\Users\Bo> wsl --system -d debian -- bash -c 'ls /usr/bin/{docker,dockerd} -lahd'
-rwxr-xr-x 1 root root 29M May 28 21:47 /usr/bin/docker
-rwxr-xr-x 1 root root 89M May 31 12:47 /usr/bin/dockerd

同时在新版本的consomme网络在wsl内对127.0.0.1的访问会直接转发到host中,所以可以直接设置让dockerd使用host的http代理。

所以思路有两个:

  • 在wslg的镜像中持久化/etc/docker/daemon.json配置,副作用是升级wsl后要重新配置镜像。
  • 非持久化的临时设置。副作用是wslc默认30s空闲关闭虚拟机。可能每次需要重新设置。

代理步骤 🔗

这里不折腾,选择非持久化的方案。只要wslc还基于dockerd这个方法就不会失效。

步骤如下

  • killall dockerd
  • dockerd本身有代理的命令行选项,所以使用–http-proxy和–https-proxy参数启动新的dockerd
  • 由于没有常规的init系统,使用nohup屏蔽SIGHUP信号,并设置dockerd后台进程形式,让pid1自动接管dockerd。
PS C:\Users\Bo> wslc system session shell
root@ [ / ]# killall dockerd
root@ [ / ]# nohup dockerd --http-proxy=http://127.0.0.1:18088 --https-proxy=http://127.0.0.1:18088 --no-proxy=localhost,127.0.0.1 --containerd=/run/containerd/containerd.sock &
[1] 399
root@ [ / ]# nohup: ignoring input and appending output to 'nohup.out'
root@ [ / ]#
logout
/bin/sh 退出,返回值: 0

PS C:\Users\Bo> wslc system session run sh -c 'ps -Af | grep -v \\['
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 05:49 hvc0     00:00:00 /init
root       172     1  0 05:49 hvc0     00:00:00 /gns --socket 6
root       173     1  0 05:49 hvc0     00:00:00 /init
root       181     1  0 05:49 hvc0     00:00:00 /usr/bin/containerd --address /run/containerd/containerd.sock --root /var/lib/docker/containerd/daemon --state /run/docker/containerd/daemon
root       399     1  0 05:52 ?        00:00:00 dockerd --http-proxy=http://127.0.0.1:18088 --https-proxy=http://127.0.0.1:18088 --no-proxy=localhost,127.0.0.1 --containerd=/run/containerd/containerd.sock
root       550   549  0 05:53 hvc0     00:00:00 ps -Af

可以看到退出shell后,成功的让init接管了dockerd。

所以方法变得明确起来。

首先设置$env:LOCALAPPDATA\wslc\settings.yaml,延长wslc空闲回收时间。然后在有需要拉取镜像前重启dockerd即可。

# 设置wslc更长的自动关闭
"session:`n  idleTimeout: 300`n" | Set-Content -Path "$env:LOCALAPPDATA\wslc\settings.yaml" -Encoding ascii
# 杀死dockerd并携带proxy选项重启
wslc system session run /usr/bin/sh -c '/usr/bin/killall dockerd -9; /usr/bin/nohup /usr/bin/dockerd --http-proxy=http://127.0.0.1:18088 --https-proxy=http://127.0.0.1:18088 --no-proxy=localhost,127.0.0.1 --containerd=/run/containerd/containerd.sock >/dev/null 2>&1 &'

也可以持久化到powershell的$PROFILE里,需要时直接开启。

function global:cprx {
    wslc system session run /usr/bin/sh -c '/usr/bin/killall dockerd -9; /usr/bin/nohup /usr/bin/dockerd --http-proxy=http://127.0.0.1:18088 --https-proxy=http://127.0.0.1:18088 --no-proxy=localhost,127.0.0.1 --containerd=/run/containerd/containerd.sock >/dev/null 2>&1 &'
}