一个在 OSX 环境下下通过 IP 访问 Docker 容器的解决方案

不建议使用这种方式,它实现起来不算方便,且实现后还需要配置终端的代理设置,在这里推荐我写(抄)的项目,虽然需要在本机安装额外的依赖,但是配置完后就基本无感知了!

如果是 VSCode 或 JetBrain 家的 IDE,也可以将项目部署到 container 内。

MacOS(以及 Windows)上的 Docker 由于实现方式的原因,在网络配置上有许多限制,最麻烦的地方是无法通过容器的 IP 直接访问 IP,这在搭建分布式系统环境时会造成很多麻烦。我经过一些研究,发现可以使用 Docker 提供的(实验性的)socksProxyPort配置和系统的 proxy 配置来使对特定网段的请求路由到 container。

原理

配置 Docker 的socksProxyPort后,对该端口的请求将会被路由到 Container 中,因此我们可以通过配置代理让对特定网段如172.172.0.0/16的请求代理到127.0.0.1:socketProxyPort,让 Docker 引擎帮我们路由到具体的容器中。

这里的代理配置我们使用 Mac 的 PAC 配置进行实现,考虑到 Mac 暂时不支持从文件系统读取 PAC 文件,我们需要某种渠道将该文件托管到特定服务器中,这里使用 Mac 自带的 apache 的服务器(使用 nginx,nodejs 的 http-server 等也行)。

材料

  • Docker 环境
  • apache HttpServer(Mac 自带)

配置 Docker

我们首先需要配置 Docker 中的socksProxyPort配置项,该配置文件位于~/Library/Group\ Containers/group.com.docker/settings.json,可通过搜索找到socksProxyPort项,其默认值为 0,修改其为想要使用的端口,这里使用8888

编写 PAC 文件

然后我们编写一个 PAC 文件 来配置代理信息,PAC 文件使用 js 编写,下面的代码是一个可直接使用的例子,它假设要使用的 Docker 容器占据的网段为172.172.0.0/16

1
2
3
4
5
6
function FindProxyForURL(url, host) {
if (isInNet(host, '172.172.0.0', '255.255.0.0')) {
return 'SOCKS5 127.0.0.1:8888'
}
return 'DIRECT'
}

isInNet(host, '172.172.0.0', '255.255.0.0')检查请求的地址是否满足172.172.*.*,如果满足(说明是对 Docker 容器的访问),则将其通过 SOCKS5 代理路由到127.0.0.1:8888,即 Docker 中配置的socksProxyPort端口。

如果不满足,则将该请求直接“放行”。

编写完成 PAC 文件后,将其保存在/Library/WebServer/Documents目录下,假设文件名称为docker.pac

配置 HttpServer 的端口和 type

HttpServer 默认使用 80 端口,这对我们来说可能会造成一些麻烦,所以我们应修改该端口。

只需编辑/etc/apache2/httpd.conf,搜索Listen,把那一堆奇怪的 XML 标签删掉,改成Listen 你想用的端口就行,这里使用11451(可惜只有 5 位!)。

这里同时还需为 pac 文件配置 type,如果没有该配置,该文件可能会不被识别,最终配置见下。

1
2
3
4
5
6
7
# ...

Listen 11451
# PAC files e.g proxy.pac
AddType application/x-ns-proxy-autoconfig .pac

# ...

启动 HttpServer

我们首先使用如下命令启动 HttpServer 服务——

1
$ sudo apachectl start

然后使用curl测试一下是否启动了,如果这个命令正确返回了我们编写的 pac 文件的信息就说明启动成功。

如果没启动,请百度😂。

1
$ curl localhost:11451/docker.pac

配置系统的 PAC 配置

前往System Preferences -> Network -> Advanced -> Proxy,勾选Automatic Proxy Configuration,在 URL 中填入地址localhost:11451/docker.pac

测试

我们首先创建一个 Docker 的子网,再在该子网中创建一个 nginx 容器来检查能否通过 IP 进行访问。

1
2
$ docker network create --subnet 172.172.0.0/16 --gateway 172.172.0.1 my_network
$ docker run -d --network my_network --ip 172.172.0.101 nginx

在浏览器中访问172.172.0.101(或者使用终端),如果出现 nginx 界面则配置成功。

https_proxy=http://127.0.0.1:7890
http_proxy=http://127.0.0.1:7890
all_proxy=socks5://127.0.0.1:7890