layout: post title: "Docker网络模型" categories: Docker tags: swarm network overlay
还记得 docker 单机部署时的 run -p port:port 吗?这样做的目的是将 docker 容器内的端口映射到宿主机的端口上,以便能够通过外网 ip 访问到 docker 容器,这时我们就想,如果我们把所有容器的接口都暴露在宿主机中,通过访问外网 ip 来达到容器间通信,这不是万事大吉了吗?
但是在集群中如果这样暴露容器的端口,是有问题的,如果其中一个容器监听了宿主机 8080 端口,那么其他容器只能映射到其它端口了,因为端口并不能被共享,而且映射到宿主机的端口上,意味着容器也就暴露到外网了,如果要限制访问,那么就需要做一些安全配置。
在介绍跨主机网络模型前,先来看看单机网络模型,在安装 docker 之后,docker 就会有 4 种网络模型,分别是:
但这四种网络模式都仅限于单机,其中 bridge 网络模型是 docker 的默认单机网络模型,它会将一个主机上的 docker 容器连接到一个虚拟网桥上,这个虚拟桥名称为 docker0,如下图:
单机中的容器之间就可以通过 docker0 互相通信了,但是如果容器被分布在不同主机上,在没有跨主机网络模型前,只能通过映射端口的形式来通信了。
如上图,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 网络模型中了。
如上图可知,hello 服务的两个实例都在同一台服务器,都是 8080 端口,且没有映射到宿主机的端口上。
查看 docker 默认网络:
$ docker network ls
其中 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 网络驱动去做就行了,而且在同一台服务器,不会引起端口冲突,最重要的一点是可以实现集群容器间的负载均衡。
正如它的名字一样,在所有容器的上面一层,覆盖了一层网络,该网络可以使在集群中的容器像本地通信一样,所以 orverlay 网络模型也称之为覆盖网络。
$ 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 集群的内部会为容器的各个节点之间负责负载均衡的管理,无需我们去操心了,如上如图三台服务器,无论我们访问的哪台服务器,都可以访问到 docker 各个可用节点中,比如访问 172.16.1.11:8080,也可以通过 swarm 集群的负载均衡转发到 172.16.1.12:8080。