仓库源文站点原文


layout: post title: "Docker网络模型" categories: Docker tags: swarm network overlay

author: 张乘辉

容器的端口映射

还记得 docker 单机部署时的 run -p port:port 吗?这样做的目的是将 docker 容器内的端口映射到宿主机的端口上,以便能够通过外网 ip 访问到 docker 容器,这时我们就想,如果我们把所有容器的接口都暴露在宿主机中,通过访问外网 ip 来达到容器间通信,这不是万事大吉了吗?

但是在集群中如果这样暴露容器的端口,是有问题的,如果其中一个容器监听了宿主机 8080 端口,那么其他容器只能映射到其它端口了,因为端口并不能被共享,而且映射到宿主机的端口上,意味着容器也就暴露到外网了,如果要限制访问,那么就需要做一些安全配置。

单机网络模型

在介绍跨主机网络模型前,先来看看单机网络模型,在安装 docker 之后,docker 就会有 4 种网络模型,分别是:

但这四种网络模式都仅限于单机,其中 bridge 网络模型是 docker 的默认单机网络模型,它会将一个主机上的 docker 容器连接到一个虚拟网桥上,这个虚拟桥名称为 docker0,如下图:

brige

单机中的容器之间就可以通过 docker0 互相通信了,但是如果容器被分布在不同主机上,在没有跨主机网络模型前,只能通过映射端口的形式来通信了。

brige

如上图,net1 和 net2 都代表一台主机中的 docker0 网络,在同主机下的容器通过 docker0 网络互相通信,但是在不同主机中却又是隔离的。

跨主机网络模型

docker 1.9 版本之后,加入了一个默认的 overlay 的网络模型,它是 docker swarm 内置的跨主机通信方案,这是一个基于 vxlan 协议的网络实现,其作用是虚拟出一个子网,让处于不同主机的容器能透明地使用这个子网。所以跨主机的容器通信就变成了在同一个子网下的容器通信,看上去就像是同一主机下的 bridge 网络通信。

关于详细的 vxlan 协议原理,请移步:vxlan 协议原理简介

在 swarm 管理节点发布的服务想要监听端口,只需要在 像 docker run 一样在后缀加 -p 8080:8080 就可以了,如下:

$ docker service create --replicas 2 -p 8080:8080 --name hello \
chenghuizhang/helloword:0.0.2

但是跟 docker run 的 -p 又有本质的区别,实际上面那条命令并没有将 8080 端口直接暴露出去,而是将 8080 端口托付给 docker 的 overlay 网络模型中了。

docker ps

如上图可知,hello 服务的两个实例都在同一台服务器,都是 8080 端口,且没有映射到宿主机的端口上。

查看 docker 默认网络:

$ docker network ls

docker network

其中 ingress 为 docker 默认的 overlay 网络。

查看 ingress 网络信息:

$ docker network inspect ingress
[
    {
        "Name": "ingress",
        "Id": "x58u3pdo4z4qooriloi82l58k",
        "Created": "2018-08-15T12:54:22.080771222+08:00",
        "Scope": "swarm",
        "Driver": "overlay",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "10.255.0.0/16",
                    "Gateway": "10.255.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Containers": {
            "aef3e18d73b1db723aab0f162600a805aa7c50b5f51ec31d82d80b7eb3438c08": {
                "Name": "hello.1.tzk61cn6d0jjat2w4mu5x8bbf",
                "EndpointID": "40e9352419b4eccea739be3a6ab7e6327ee28a2220ce185750f028d74e2e05c8",
                "MacAddress": "02:42:0a:ff:00:05",
                "IPv4Address": "10.255.0.5/16",
                "IPv6Address": ""
            },
            "ef4a7f16567d3b51d0dff629b3b6252f50d06ec77a24b36dcd8d40b6ab9afc2d": {
                "Name": "hello.2.lcvumjyd19tymzankbjwita1w",
                "EndpointID": "d5b9a2e41654097337d6f22b139e1501b253e260d2ac3ac52e8eaf491a70340d",
                "MacAddress": "02:42:0a:ff:00:06",
                "IPv4Address": "10.255.0.6/16",
                "IPv6Address": ""
            },
            "ingress-sbox": {
                "Name": "ingress-endpoint",
                "EndpointID": "b610f32940e6066751a6c7b8d87f11874077f779a00d28855d8d3329d15783e9",
                "MacAddress": "02:42:0a:ff:00:03",
                "IPv4Address": "10.255.0.3/16",
                "IPv6Address": ""
            }
        },
        "Options": {
            "com.docker.network.driver.overlay.vxlanid_list": "4096"
        },
        "Labels": {},
        "Peers": [
            {
                "Name": "VM_0_10_centos-8ef37c047944",
                "IP": "172.16.0.10"
            }
        ]
    }
]

由于 orverlay 网络模型是基于 vxlan 协议的网络实现,所以根据上面的网络信息可知,它是要在三层网络中虚拟出二层网络,即跨网段建立虚拟子网,也就是把 docker 要发送的信息先发送到虚拟子网地址 10.255.0.1,再由虚拟子网包装为宿主机的真实网网址 172.16.0.10,这样做的好处就是不会公开暴露容器的端口,让这些事情交给 overlay 网络驱动去做就行了,而且在同一台服务器,不会引起端口冲突,最重要的一点是可以实现集群容器间的负载均衡。

overlay network

正如它的名字一样,在所有容器的上面一层,覆盖了一层网络,该网络可以使在集群中的容器像本地通信一样,所以 orverlay 网络模型也称之为覆盖网络。

构建自定义 overlay 网络集群

$ docker network create -d overlay mynet

-d 指定 mynet 网络驱动为 overlay 类型。

$ docker service create \
--name etcd \
--replicas 1 \
--network mynet \
-p 2379:2379
$ docker service create \
--name mysql-galera \
--replicas 3 \
--network mynet \
-p 3306:3306
$ docker service create \
--name hello chenghuizhang/helloword:0.0.2 \
--replicas 2 \
--network mynet \
-p 8080:8080

到这里,我们已经构建了一个名为 mynet 的网络集群了,集群网络模型如下:

swarm network model

swarm 集群的内部会为容器的各个节点之间负责负载均衡的管理,无需我们去操心了,如上如图三台服务器,无论我们访问的哪台服务器,都可以访问到 docker 各个可用节点中,比如访问 172.16.1.11:8080,也可以通过 swarm 集群的负载均衡转发到 172.16.1.12:8080。