外部主机无法访问宿主机内容器是怎么回事

TL;DR

是因为创建的容器采用的网络类型是 bridge,而宿主机没有启用 ip 转发,所以,外部主机请求没有转发到对应容器。需要两个步骤启用 ip 转发功能。(来自 Docker 官方文档

  1. 配置 Linux 内核允许 ip 转发。
$ sysctl net.ipv4.conf.all.forwarding=1
  1. 将 iptables FORWARD 规则从 DROP 改为 ACCEPT。
$ sudo iptables -P FORWARD ACCEPT

短话长说

背景

因为测试需要,在一台 centos 7 服务器上用 Docker 部署了一个 nginx 服务。部署好之后,在宿主机上使用 curl 命令可以访问 nginx 服务,但是外部机访问却提示 HTTP ERROR 503,服务不可用。查看了容器中 nginx 的 access 日志,发现也没有访问日志,双方都表示不是自己的责任。那就看看是不是中间商(宿主机)的责任吧。

排查问题

首先想到的是宿主机的防火墙可能没有开放端口,把请求给拦掉了。不过,使用 systemctl status firewalld查看防火墙并没有开启。那就看看是不是 Docker 自身的网络配置的问题。
首先使用 docker inspect docker-name查看容器的网络配置信息:

            "Networks": {
                "root_default": {
                    "IPAMConfig": null,
                    "Links": null,
                    "Aliases": [
                        "survey-nginx",
                        "dd6d4af0b1f0"
                    ],
                    "NetworkID": "9781d8e96e344e662a0397a7b25f82ef6bce7e650b035b46404557438af0b5fb",
                    "EndpointID": "e62f1ce9da0fb9c0668986cfab01c1edc825f1c95cdfa042a6d5becb005cd15e",
                    "Gateway": "172.19.0.1",
                    "IPAddress": "172.19.0.2",
                    "IPPrefixLen": 16,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "MacAddress": "02:42:ac:13:00:02"
                }
            }

可以看到使用的网络类型是 root_default,一个自定义的网络类型,继续使用命令 docker network ls查看该网络具体的网络类型:

NETWORK ID          NAME                DRIVER              SCOPE
5934ab852fe2        bridge              bridge              local
057efb45b2a8        host                host                local
8bb8b2aa1a26        kind                bridge              local
a99c8640a808        none                null                local
9781d8e96e34        root_default        bridge              local

如上所示,自定义网络类型 root_defualt 是网桥,那么网桥为什么会导致数据没有转发呢?我的 Docker 知识比较匮乏,不过可以到官方文档看看有没有相应的描述。(文档传送
在文档中,看到了这么一段内容:

In terms of Docker, a bridge network uses a software bridge which allows containers connected to the same bridge network to communicate, while providing isolation from containers which are not connected to that bridge network. The Docker bridge driver automatically installs rules in the host machine so that containers on different bridge networks cannot communicate directly with each other.
Bridge networks apply to containers running on the same Docker daemon host. For communication among containers running on different Docker daemon hosts, you can either manage routing at the OS level, or you can use an overlay network.

从文档中,我们可以了解到,Docker 的网桥是软件网桥,它只允许使用了同一个网桥的容器之间互相通信,Docker 会自动在宿主机上创建转发规则,避免不同网桥上的容器相互直接通信。不过,如果是不同宿主机上的容器,需要在系统层面增加路由,或者将网络类型改为 overlay。似乎已经破案了,虽然文档中说的是不同主机间容器的通信,不同主机和当前宿主机的容器间的通信可能也是因为现在这台主机上可能没有开启系统层面的路由。
文档继续往下看,我们找到了开启系统方法,即本文开始的两条命令:

$ sysctl net.ipv4.conf.all.forwarding=1
$ sudo iptables -P FORWARD ACCEPT

因为,宿主机没有开启防火墙,所以只执行了第一条命令,然后通过浏览器访问服务,访问成功。

上一篇:Adobe Bridge/Br 2020软件安装包(附安装教程)


下一篇:UITableView 或 UIScrollView 点击状态栏列表回到顶部