基于 Docker18.09.0
建议使用用户定义的网桥来控制哪些容器可以相互通信,以及启用容器名称到IP地址的自动DNS
解析。
创建自定义的网络
Docker提供用于创建这些网络的默认网络驱动程序。
默认可以创建如下几种网络:
- bridge network
- overlay network
- MACVLAN network
我们可以根据需要创建任意数量的网络,并且可以在任何给定时间将容器连接到零个或多个这些网络中。
此外,还可以在不重新启动容器的情况下把正在运行的容器从一个网络中连接和断开。
桥接网络 bridge network
这个网桥类似于默认网络中的 bridge
创建 网桥类型的自定义网络
$ docker network create --driver bridge my-net
67b19436a9e361c8b9da4034c5903c4903444cecd3ecdbde62bbaf70828d49d9
查看网络
$ docker network ls
NETWORK ID NAME DRIVER SCOPE
8a5f470ffe77 bridge bridge local
10470df7f76f host host local
67b19436a9e3 my-net bridge local
3ba807431203 none null local
查看源数据
$ docker network inspect my-net
[
{
"Name": "my-net",
"Id": "67b19436a9e361c8b9da4034c5903c4903444cecd3ecdbde62bbaf70828d49d9",
"Created": "2018-12-10T07:22:53.2694852Z",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.23.0.0/16",
"Gateway": "172.23.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {},
"Options": {},
"Labels": {}
}
]
可以看出来 Docker 为这个自定义的网络分配了一个
172.23.0.0/16
的子网。
使用自定义网络中的网桥创建容器
创建网络后,可以使用该 docker run --network=
选项在其上启动容器 。
$ docker run --network=my-net -itd --name=container3 busybox
59d4343bbbaa0b6c6aaedef445bf6d17bba4210899dab4cac990a3d5528bf4bb
查看网桥
$ docker network inspect my-net
[
{
"Name": "my-net",
"Id": "67b19436a9e361c8b9da4034c5903c4903444cecd3ecdbde62bbaf70828d49d9",
...略...
"Containers": {
"59d4343bbbaa0b6c6aaedef445bf6d17bba4210899dab4cac990a3d5528bf4bb": {
"Name": "container3",
"EndpointID": "63fa53249f21708669e7d998cb3ae12edacf323bd5161e87df67c102f190f0f9",
"MacAddress": "02:42:ac:17:00:02",
"IPv4Address": "172.23.0.2/16",
"IPv6Address": ""
}
},
"Options": {},
"Labels": {}
}
]
我们在此网络中启动的容器必须位于同一个Docker主机上。网络中的每个容器都可以立即与网络中的其他容器通信。但是,网络本身是将容器与外部网络隔离开来的。
如下图:
在用户定义的网桥中,不支持链接。
但是, 可以在此网络中的容器上公开和发布容器端口。
如果要使bridge
网络的一部分可用于外部网络,这将是非常有用的方案。具体后面最后点部分会提到。
可以看出,如果要在单个主机上运行相对较小的网络,桥接网络是非常合适的。但是,您可以通过创建 network
来创建更大的 overlay
网络。后面会专门文章讨论。
测试容器间使用容器名进行互相通信
- 运行另一个容器,并加入到 自定义网络中
$ docker run --network=my-net -itd --name=container4 busybox
6b61b5707c1660f8a66fbe98d5ad569c4ccf4b72703ea54621395e2fa3c84f18
- 进入到其中的一个容器中,利用
ping
另外一个容器的容器名
# shark @ SharkAir in ~/docker_files/elk/filebeat on git:master x [17:23:34]
$ docker exec -it container3 /bin/sh
/ # ping container4
PING container4 (172.23.0.3): 56 data bytes
64 bytes from 172.23.0.3: seq=0 ttl=64 time=0.181 ms
64 bytes from 172.23.0.3: seq=1 ttl=64 time=0.538 ms
^C
--- container4 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.181/0.359/0.538 ms
- 查看容器本地的
hosts
文件。
/ # cat /etc/hosts
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.23.0.2 59d4343bbbaa
可以看出,在使用自定义网络的桥接类型时,容器内的
hosts
文件内容没啥不一样的。
- 查看容器内容使用的是哪个 DNS 服务器
/ # vi /etc/resolv.conf
/ # cat /etc/resolv.conf
nameserver 127.0.0.11
options ndots:0
/ #
上面我看查看
hosts
文件内容时,并没有发现容器 IP 到 容器名之间有对应的关系。哪容器直接怎么能够通过容器名互相通信呢?就是借助了这个 DNS 服务器来进行解析的。这个是 Dockers 嵌入式的 DNS 服务器
嵌入式 DNS 服务器
任何使用变量 name
或 net-alias
创建的容器,Docker 的守护进程都为此提供了内置的服务发现,就是所说的 嵌入式DNS服务器。
至于 文件 /etc/hosts
,/etc/resolv.conf
在容器内 Docker 是如何管理的,我们先不用去关心它,我们通常建议使用如下的选项进行管理:
运行容器时的选项 | 具体的含义 |
---|---|
--name=CONTAINER-NAME | --name用于发现用户定义的docker网络中的容器。嵌入式DNS服务器维护容器名称与其IP地址之间的映射(在容器所连接的网络上)。 |
--network-alias=ALIAS | 除了 --name 之外, 还可以使用此选项参数给正在创建的容器起一个别名。嵌入式DNS服务器会在一个指定的用户定义的网络上维护所有容器别名与其IP地址之间的映射。使用 docker network connect 命令时,使用 --alias 可以让容器在不同的网络中具有不同的别名。 |
--dns=[IP_ADDRESS...] | 如果嵌入式DNS服务器无法从容器解析名称解析请求,则嵌入式DNS服务器将使用通过该选项传递的IP地址转发DNS查询。这些--dns 指定的IP地址由嵌入式DNS服务器管理,不会在容器的/etc/resolv.conf 文件中更新。 |
关于自定义网络的相关操作请查看网络命令使用的文章(目前还发布_)
用户定义网络中的网桥与默认网络中的网桥之间的差异
1. 用户定义的桥接器可在容器化应用程序之间提供更好的隔离和互操作性。
连接到同一个用户定义的网桥的容器会自动将所有端口相互暴露,而不会向外界显示任何端口。这使得容器化应用程序可以轻松地相互通信,而不会意外地打开对外界的访问。
想象一下具有Web前端和数据库后端的应用程序。外部世界需要访问Web前端(可能在端口80上),但只有后端本身需要访问数据库主机和端口。使用用户定义的网桥,只需要打开Web端口,并且数据库应用程序不需要打开任何端口,因为Web前端可以通过用户定义的网桥访问它。
如果在默认桥接网络上运行相同的应用程序堆栈,则需要打开Web端口和数据库端口,并使用 每个的标记-p
或--publish
标记。这意味着Docker主机需要通过其他方式阻止对数据库端口的访问。
2. 用户定义的桥接器在容器之间提供自动DNS解析。
默认网桥上的容器只能通过IP地址相互访问,除非您使用被认为是遗留的 --link
选项。
在用户定义的桥接网络上,容器可以通过名称或别名相互解析。
如果在默认桥接网络上运行需要相互通信的应用程序容器,则需要在两个容器之间手动创建链接(使用旧--link
标志)。这些链接需要在两个方向上创建,因此您可以看到这对于需要通信的两个以上容器而言变得复杂。或者,您可以操作/etc/hosts
容器中的文件,但这会产生难以调试的问题。
3. 容器可以在运行中与用户定义的网络连接和分离。
在容器的生命周期中,您可以动态地将其与用户定义的网络连接或断开连接。要从默认桥接网络中彻底删除容器,您需要停止容器并使用不同的网络选项重新创建容器。
4. 每个用户定义的网络都会创建一个可配置的网桥。
如果容器使用默认桥接网络,则可以对其进行配置,但所有容器都使用相同的设置,例如MTU和iptables
规则。此外,这些对默认桥接的网络进行配置的行为,是发生在Docker本身之外的,所以需要重新启动Docker。
使用 docker network create
创建和配置用户定义的网桥 。如果不同的应用程序组具有不同的网络要求,则可以在创建时单独配置每个用户定义的网桥。
5. 默认桥接网络上的链接容器共享环境变量。
最初,在两个容器之间共享环境变量的唯一方法是使用--link
标志链接它们。用户定义的网络无法实现这种类型的变量共享
。但是,有更好的方法来共享环境变量。比如下面一些想法:
多个容器可以使用Docker卷装入包含共享信息的文件或目录。
docker-compose
可以一起启动多个容器,并且compose
文件可以定义共享变量。您可以使用swarm服务而不是独立容器,并利用共享Docker secrets和 Docker Configs。
连接到同一用户定义的网桥的容器可以有效地将所有端口暴露给对方。对于不同网络上或非此Docker主机的容器想访问的这些端口,必须使用 -p
or --publish
标志发布该端口。