本文发自 http://www.binss.me/blog/the-way-to-use-host-proxy-in-docker-containers-or-parallels-desktop-machines/,转载请注明出处。

问题

Docker容器内,apt-get无法更新,报错:

Err http://archive.ubuntu.com trusty Release.gpg
  Cannot initiate the connection to 6152:80 (0.0.24.8). - connect (22: Invalid argument)

经过检查,原来是Docker在容器中也使用了我的系统全局代理设置,通过env可以发现设置了HTTP和HTTPS代理为127.0.0.1:6152。而显然容器localhost是没有开启代理服务的,导致出错。

最简单的解决方法是unset掉环境变量:

$ unset no_proxy http_proxy https_proxy HTTP_PROXY HTTPS_PROXY

此时apt-get能够正常更新,然而速度缓慢,时不时抛个缓存错误。这是因为只是摘掉了错误的代理,相当于直连,受运营商劫持到Cache等影响导致,最好的解决方法当然是代理出去。经过一番折腾,发现Docker网络流量皆由com.docker.slirp这个进程发出,在宿主机上通过Proxifier将之劫持到代理端口即可。

然而又发现了一个问题,我发现代理软件上收到的请求的目标是IP而不是域名,而我的代理规则又主要是基于域名的,虽然可以把IP写死到规则里,再用个掩码划一个网段,但总是不嗨森。

这时候突然发现思路错了。解决问题的point不是如何在宿主机中使容器走代理,应该是如何在容器内连接宿主机的代理。

方案

宿主机上跑个Web Server,容器内尝试wget,能成功代表可以成功访问宿主机。

解决

先查看容器的接口信息:

root@88fbd6e341ec:/# ifconfig
eth0      Link encap:Ethernet  HWaddr 02:42:ac:11:00:02
          inet addr:172.17.0.2  Bcast:0.0.0.0  Mask:255.255.0.0
          inet6 addr: fe80::42:acff:fe11:2/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:14 errors:0 dropped:0 overruns:0 frame:0
          TX packets:7 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:1156 (1.1 KB)  TX bytes:578 (578.0 B)

于是猜想宿主机的IP为172.17.0.1,ping一下发现可以ping通,然而wget却失败了。这时想起在Mac上,Docker本身也是跑在一个虚拟机内的,172.17.0.1可能就是该虚拟机的IP。

由于我用的是Docker for MAC,使用的是Xhyve,而我对此没什么了解,只能上官方论坛一阵搜索,发现可通过Screen命令来访问虚拟机:

$ screen ~/Library/Containers/com.docker.docker/Data/com.docker.driver.amd64-linux/tty

输完命令后敲回车出现登录提示,输入root登入。

登入后查看接口信息:

Welcome to the Moby alpha, based on Alpine Linux.
moby:~# ifconfig
docker0   Link encap:Ethernet  HWaddr 02:42:00:CF:6F:AD
          inet addr:172.17.0.1  Bcast:0.0.0.0  Mask:255.255.0.0
          inet6 addr: fe80::42:ff:fecf:6fad%32719/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:77 errors:0 dropped:0 overruns:0 frame:0
          TX packets:58 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:4151 (4.0 KiB)  TX bytes:28040 (27.3 KiB)

eth0      Link encap:Ethernet  HWaddr C0:FF:EE:C0:FF:EE
          inet addr:192.168.65.2  Bcast:192.168.65.7  Mask:255.255.255.248
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:109 errors:0 dropped:0 overruns:0 frame:0
          TX packets:112 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:45044 (43.9 KiB)  TX bytes:8691 (8.4 KiB)

果然,这台虚拟机的IP为172.17.0.1,并通过此和容器共享网络。看这台虚拟机的网卡eth0,很明显是对外的了,本机IP为192.168.65.2,猜测宿主机IP为192.168.65.1。wget一下:

moby:~# wget 192.168.65.1:7777
Connecting to 192.168.65.1:7777 (192.168.65.1:7777)
index.html           100% |*******************************| 12068   0:00:00 ETA

成功。到容器中试试:

root@88fbd6e341ec:/# wget 192.168.65.1:7777
--2016-07-21 17:13:37--  http://192.168.65.1:7777/
Connecting to 192.168.65.1:7777... connected.
HTTP request sent, awaiting response... 200 OK
Length: 12068 (12K) [text/html]
Saving to: ‘index.html’

100%[====================================================================>] 12,068      --.-K/s   in 0s

2016-07-21 17:13:37 (254 MB/s) - ‘index.html’ saved [12068/12068]

成功。因此到Docker的设置中设置成相应的IP即可:

注意容器的环境变量在创建时就被定下了,要改需要重新创建容器.....

搞定后发现apt-get终于走了宿主机的代理了:

P.S. 向screen发送命令的快捷键和我们习惯的不一样,比如quit的快捷键为 Ctrl+a Ctrl+\ ,然后按y。具体可以查阅man。

2018.01.11 更新

心血来潮将 Docker 更新到 17.12.0-ce-mac46 (21698) ,结果发现代理失效了。

重复以上方法发现宿主机 ip 变为 192.168.65.2 ,在 Docker 的设置中修改后发现不会被新建容器所继承,还会导致 pull image 时尝试走该代理而无法连接,因此在设置处设置为 Use System Proxy 就好。

然后记得在新建容器时指定环境变量:

-e HTTP_PROXY=http://192.168.65.2:6152/ -e http_proxy=http://192.168.65.2:6152/ -e HTTPS_PROXY=https://192.168.65.2:6152/ -e https_proxy=https://192.168.65.2:6152/

同时流量也不再是由 com.docker.slirp 发出而是 VPNkit,这和 Docker 的架构变化有关。

Parallels Desktop虚拟机代理方案

虽然我的虚拟机都是拿来玩游戏跑迅雷的,但偶尔也是需要访问宿主机的,比如说访问Docker容器映射到宿主机端口上的游戏服务端(有点绕)。

这里宿主机IP就比较容易看了,打开Parallels desktop的Preferences-Network,选择虚拟机正在使用的网络,勾选Show in System Preferences,然后就能够在MAC的Network中看到宿主机的IP:

拿到之后在IE里填上:

于是原本不能访问外网(Host-Only)的虚拟机通过访问宿主机的代理后能够上网了:

题外话:我对访问百度过程中访问的123.60.63.137很好奇,查了下,貌似是微软在HK的服务器?是用来验证的还是用来分析用户行为的?害怕.jpg。

总结

折腾了一晚上,累感不爱。以文记之,希望能帮到大家。