之前就发现了 mac 下 docker 本地部署若依分离版,nginx 反向代理无效问题解决,当时还疑惑 docker 网络分配的 ip 为什么之前使用 proxy_pass 还能识别,到了 fastcgi_pass 就不认识了。原来之前是在 ubuntu 服务器上,现在是在 Mac 系统里,环境发生了变化。

在了解并理解这个问题的过程中查了很多相关的文章,关于mac 系统docker 使用桥接网络ip 宿主机无法访问容器的问题,其中提到容器中可以互相 ping 通,只有宿主机与容器无法互相 ping 通。但我发现安装的大部分的镜像运行后都是不认识 ping 指令的,也就是说并没有安装 ping 指令。参考这里 安装 ping 指令

ping 之前需要先获取 docker 中相关服务的 ip 地址,我是安装了 portainer,通过 http://localhost:9000/ 查看 local 容器列表,里面是有一列 IP Address 的。也可以通过命令行 docker inpect container_name 来查看,IP Address 在最后的 Networks 中。网上查询过程中也看到了一个指令,直接输出相关容器的 IP Adrress:

通过 inspect 返回的信息检索
docker inspect --format='{{.NetworkSettings.IPAddress}}' nginx
# 或者
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' nginx
# 或者
docker inspect php80 | grep "IPAddress\":"

使用 ping 指令测试,其他服务的 IP 地址是通的。安装 curl 访问 nginx 服务,可以拿到信息,说明各个容器之间是通的。但本地 ping docker 的服务全都超时。

对此,docker 宿主机无法访问容器 里给的解释:

mac docker 实现的桥接网络是通过了一个 linux 虚拟机实现,并不是直接在 mac 宿主机上创建虚拟网卡,导致无法 ping 通

给到的官网文档说明:there is no docker0 bridge on the host,中文翻译:macOS 上没有 docker0 网桥。原文还提到一个除了正常的端口映射以外的 OpenVPN 的解决方案,没继续跟。

可能这个 docker0 网桥是什么都不知道,docker0 是 docker 默认创建的一个虚拟网卡,用以连通宿主机与各个容器。在 linux 中网桥(docker0)是直接安装在环境中的,所以两者者可以直接访问;到了 Mac 环境下,Docker Desktop 实际是 Hypervisor 虚拟技术启动了一个 VM 虚拟机来跑 docker 服务的,所以网桥(docker0)是安装在 VM 虚拟机中的,与宿主机并不连通。

Docker 的(Linux/Mac OS)网络结构 里有两种环境的 docker 网络结构图,一目了然。

另外有的文章里提到了一个 github issue 2670,里面有个回答提到了一个待发布的版本:

https://download-stage.docker.com/mac/bysha1/52ea5bcc410a8b62f03f09aa04ad4b7ffb9eed0c/Docker.dmg
Version 18.03.0-ce-rc2-mac56 (23206)
Channel: pr
52ea5bcc41

当前查询下载地址已过期,并且是一个 18 的版本,现在已经 20.10.23(当前最新版)了,就算文件存在也没有动力回退到之前的版本。后面部分提到在本地要创建一个 SOCKS 代理,并需要修改 docker 的配置文件,试了一下,没有找到 settings.json 文件。

cd ~/Library/Group\ Containers/group.com.docker/
mv settings.json settings.json.backup
cat settings.json.backup | jq '.["socksProxyPort"]=8888' > settings.json

此方案也作废。

总结目前遇到的几种方案:

  • 端口映射
  • VPN
  • SOCKS 代理

第一种就是正常的端口映射,并没有涉及到解决问题。但如果服务可以通过端口访问到,那么似乎也不在乎是否可以通过 IP 地址访问了。
后两种都需要安装其他的软件,暂时不考虑。

最后提一下之前遇到过的容器互联(docker --link),这是 mac 下 docker 本地部署若依分离版,nginx 反向代理无效问题解决,因为 nginx 中直接 ip:9000 无法访问到 php80 服务而找到的解决方法。它主要针对的是容器之间服务无法访问,在上面测试可以 ping 通,curl 可以拿到 nginx 80 端口页面内容时,我的感觉是困惑的。使用 nmap 扫描相关容器,服务端口比如:80、3306 都是开启的,所以理论上来说是可以连接的。

中间提到过几个域名:

host.docker.internal
gateway.docker.internal
docker.for.mac.host.internal

现在用 ping 指令发现都是同一个 ip 192.168.65.2,这似乎与 Docker Desktop 设置中 Resources 的 Network 部分相关,因为网络的 Docker subnet 是 192.168.65.0/24。从 Portainer 的 Networks 中可以看到当前 docker 的网桥部分为 172.17.0.0/16。尝试修改为 docker 的网桥,结果报错 Invalid value,好了,到此结束,不瞎搞了。

尝试在 nginx 虚拟主机配置中使用 ip 替换 php80(容器互联),重启 nginx 服务竟然可以了。对比上次与这次的操作,印象比较深刻的就是升级 Docker Desktop 到最新版本,然后容器 php80 的 IP 就可以正常访问了。尝试连接 mysql 发现报缺少 driver。后面通过 php80 bin 目录下的 docker-php-ext-install 执行安装 docker-php-ext-install mysql mysqli 在重启 php80 服务好了。安装 docker 的 php 容器扩展参考:docker中php开启mysqli扩展docker完整配置nginx+php+mysql的方法步骤

ok,那么到此宿主机 ip 地址无法访问容器就无所谓了。