这个章节详细讨论Docker网络含义操作语法,解释了当前Docker网络范例的优缺点。
主要包含了一下的主题
1. 配置Docker的IP协议
- IPv4支持
- IPv4地址管理
- IPv6支持
2. 配置DNS
- DNS基础
- 组播DNS
3. 配置Docker网桥
4. Overlay网络和Underlay网络
- 两个网络的概念?
- Docker如何使用它们?
- 它们的优势有哪些?
配置Docker的IP协议
Docker uses the Ip stack to interact with the outside world using TCP or UDP. 它支持IPv4和IPv6协议,在以下的章节将解释它们的含义。
IPv4支持
默认情况下,Docker给每个容器分配IPv4地址,这些地址附加到默认的docker0网桥上。在开启Docker daemon时候能指定
IP地址的范围通过使用 --fixed-cidr 标志位,就像在下面代码中展示的:
$ sudo docker -d --fixed-cidr=192.168.1.0/25
我们将讨论更多关于这个在配置Docker网桥章节,除了Unix socket之外,还可以在一个IPv4 TCP端点监听Docker daemon进程。
$ sudo docker -H tcp://127.0.0.1:2375 -H unix:///var/run/docker.sock -d &
IPv6支持
IPv4和IPv6能同时运行;这个被称作双协议栈。通过设置Docker daemon的ipv6标志,使能Docker双协议栈。Docker将设置docker0网桥的IPv6本地连接地址fe80::1.容器之间的所有的数据包都通过这个网桥。
为了给容器分配全局可达的IPv6地址,必须指定一个IPv6子网来选择地址。
以下的命令通过--fixed-cidr-v6参数设置IPv6子网,当启动Docker容器的时候同时也增加了一个新的路由规则到路由表中。
$ docker -d --ipv6 --fixed-cidr-v6="1553:ba3:2::/64"
$ docker run -t -i --name c0 ubuntu:latest /bin/bash
下图展示了Docker网桥配置一个IPv6地址的范围:
如果在容器中使用ifconfig命令查看IP地址的范围,你将注意到恰当的子网已经被分配到eth0接口,就像下面代码展示的:
全部到1535:bas:2::/64子网的通信都将通过docker0接口。
前面的容器使用fe80::42:acff:fe11:1/64作为链接本地地址,1553:ba3:2::242:ac11:1/64作为全局可路由IPv6地址分配。
注意:
链路本地地址和环回地址具有链路本地作用域,这意味着它们将在直接连接的网络(链接)中使用。所有其他地址都具有全局(或通用)作用域,这意味着它们是全局可达的,可以用于连接到任何具有全局作用域的地址。
配置一个DNS服务
Docker给每个容器提供hostname和DNS配置,而不用我们自己构建镜像。它通过虚拟文件覆盖容器内的/etc文件,并在其中写入新的信息。
这能通过在容器中运行mount命令查看。容器在初始创建的时候接收和主机相同的resolv.conf文件。如果主机的resolv.conf文件被修改,当容器被重启时,会在容器的 /resolv.conf文件会被更新。
在Docker,你能用以下的两种方式设置DNS选项:
- 使用 docker run --dns=<ip-address>
- 增加 DOCKER_OPTS="-dns ip-address" 在Docker的守护进程文件中
你也能指定这个检索域使用 --dns-search=<DOMAIN>
下面的图片展示了一个配置在一个容器中的域名服务器(nameserver),它使用Docker daemon文件中设置的DOCKER_OPTS参数。
主要的DNS文件如下所示:
- /etc/hostname
- /etc/resolv.conf
- /etc/hosts
如下的命令增加一个DNS服务器:
docker run --dns=8.8.8.8 --net="bridge" -i -t ubuntu:latest /bin/bash
修改容器的hostname
docker run --dns=8.8.8.8 --hostname=docker-vm1 -i -t ubuntu:latest /bin/bash
容器和外网之间通信
只有服务器的ip_forward参数设置成1时,容器之间的数据包才能够相互传输。通常,你只需要保持Docker的默认设置,--ip-forward=true,同时当服务启动时Docker将设置ip_forward的值为1。
使用下面的命令,查看设置或者手动打开IP转发:
# cat /proc/sys/net/ipv4/ip_forward
0
# echo 1 > /proc/sys/net/ipv4/ip_forward
# cat /proc/sys/net/ipv4/ip_forwar
1
通过设置ip_forward,用户在容器与外网之间的通信成为可能;如果你设置了多桥接模式,那么容器之间的通信也需要使用它。下图显示ip_forward = false如何将所有数据包从容器转发到外部网络。
Docker不会从Docker过滤器链中删除或修改任何已存在的规则。这允许用户创建规则来限制对容器的访问。
在单个主机上,Docker使用docker0桥接器来转发数据包。它添加了一个规则来使用IPTables转发链路,以便数据包在两个容器之间传输。设置--icc=false将删除所有数据包。
如果设置Docker daemon参数 --icc=false以及iptables=true同时docker run 调用参数--link,Docker服务将创建一对IPTables规则,新创建的容器能够暴露端口给其他容器,容器暴露的端口是在镜像的Dockerfile文件中。下图展示了 ip_forward = false来自/去往容器中的包以及从外网/到外网中数据的包如何传输:
默认情况下,Docker允许所有的外部IP访问,为了允许指定的IP或者网络访问容器,在Docker的过滤器链路的顶端插入一个否定的规则。
举个例子,使用下面的命令,你可以限制外部的网络而仅仅允许源IP是10.10.10.10访问容器:
# iptables -I DOCKER -i ext_if I -s 10.10.10.10 -j DROP
限制从一个容器到另一个容器的SSH登陆
下面的步骤可以限制从一个容器到另一个容器的SSH登陆功能:
1.运行两个容器,c1和c2.
使用下面的命令创建c1:
# docker run -i -t --name c1 ubuntu:latest /bin/bash
生成的输出如下:
使用下面的命令创建c2:
# docker run -i -t --name c2 ubuntu:latest /bin/bash
生成的输出如下:
我们可以使用刚刚发现的IP地址测试容器之间的连接。现在让我们使用ping工具来看看:
2.在两个容器中安装 openssh-server功能:
# apt-get install openssh-server
3.启动iptables在宿主机上
1.最初,你能通过SSH功能从一台服务器登陆另一台服务器。
2.停止Docker服务并且添加参数DOCKER_OPTS="--icc=false --iptables=true"在宿主机的默认Dockerfile文件(Stop the Docker service and add DOCKER_OPTS="--icc=false --iptables=true" to the default Dockerfile of the host machine.)(总觉得这里有问题)。这个选项将启动iptables并且关闭在容器之间的端口。
默认情况下,iptables是没有开启的,下面的命令将能够开启iptables功能:
3.Docker运行在宿主机以Upstart或者SysVinit作为初始化程序的配置文件。这个Docker二进制文件的指定的位置(特别是对于开发测试):
#DOCKER="/usr/local/bin/docker"
4.使用DOCKER_OPTS去修改进程的启动选项:
#DOCKER_OPTS="--dns 8.8.8.8" --dns 8.8.4.4
#DOCKER_OPTS="-icc=false --iptables=true"
5.重启Docker服务:
# service docker start
6.查看iptables:
在宿主机的iptables规则中增加了DROP规则,规则阻止了容器之建立联系,此时,无法通过在容器之间使用SSH功能。
4.我们也能通过使用--link参数在容器之间通信或者建立连接,下面的部署将会有帮助:
1.创建第一个容器,作为server端, sshserver:
root@ubuntu: ~# docker run -it -p 2222:22 --name sshserver ubuntu bash
2.执行iptables命令,可以查看增加的Docker链路规则:
下图说明了使用--link参数在容器之间建立通信:
5. 你能检查你的被链接的容器使用docker inspect 命令:
root@ubuntu:~# docker inspect -f "{{.HostConfig.Links}}"
sshclient
[/sshserver:/sshclient/sshserver]
现在你能通过它的IP成功的ssh登录到sshserver。
# ssh root@172.17.0.3 -p 22
使用 --link参数,Docker创建在容器之间创建了一个安全的通道,而不需要在容器的外部暴露任何端口。
配置Docker网桥
Docker服务在Linux内核中默认创建了一个名为docker0网桥,它能在物理机或者虚拟网络接口之间来回的传递数据包,使它们表现的就像一个以太网网络。在一个虚拟机上运行下面的命令可以发现一系列的接口以及他们链接的IP地址。
一旦你有一个或多个容器是启动且运行的,你能在宿主机上运行brctl命令通过观察interfaces列的输出来保证Docker已经连接到docker0网桥上。
在配置docker0网桥之前,安装桥接工具:
# apt-get install bridge-utils
这个宿主机上连接了两个不同的容器:
当一个容器是被创建时,Docker使用docker0网桥的设置,从桥接的网段中分配一个新的IP地址,如下所示:
默认情况下,Docker提供一个名字是docker0的虚拟网桥,它的IP地址是192.17.42.1。Docker中容器的IP地址段在172.17.0.0/16。
为了修改Docker中默认的设置,修改文件 /etc/default/docker。
下面的步骤能修改默认的网桥从docker0到br0:
下面的命令展示新的网桥名称以及Docker的IP地址段:
Overlay 网络和underlay 网络
一个Overlay网络是建立在底层网络之上的虚拟网络设施。其目的是实现物理网络中不可用的网络服务。
Overlay网络显著的增加了创建在物理网络之上的虚拟子网的数量,而物理网络又支持多租户和虚拟化。。
Docker中每个容器都分配了一个IP地址,用来和其它的容器通信。如果容器必须与外部网络通信,则在主机系统中设置网络,并将端口从容器暴露或映射到主机。这样,在容器内运行的应用程序将不能显示它们的外部IP和端口,因为这些信息对他们是不可用的。
这个解决办法是通过某种方式给集群中的宿主机上Docker容器分配不一样的IP地址以及有网络产品建立路由通路在宿主机之间。
这有不同的方案来处理Docker网络,如下所示:
- Flannel
- Weave
- Open vSwitch
Flannel提供了一种方案给每个容器一个IP地址,用来容器之间的通信。通过数据包封装,它在主机网络上创建一个虚拟覆盖网络。默认情况下,Flannel在集群上提供了一个 a/24子网,Docker守护进程从子网中给容器分配IP。下图展示了使用Flannel网络容器之间的通信:
Flannel在每个宿主机上运行一个flanneld客户端,它的主要职责是从预配置的地址空间中分配子网租约。Flannel使用etcd存储网络配置、分配子网、辅助的数据(宿主机IP)。
Flannel使用通用TUN/TAP设备,并创建一个覆盖网络使用UDP封装IP数据包。子网分配是在etcd的帮助下完成的,它维持着子网和主机的映射。
Weave创建一个虚拟的网络,连接跨主机/虚拟机部署的Docker容器,同时启用自动发现功能。下图战术了Weave网络:
Weave可以穿过防火墙,在部分连接的网络中运行。流量可以选择性加密,允许物理主机/虚拟主机通过不可信的网络连接。
Weave增强了Docker现有的网络(单个主机)的能力,比如这个docker0网桥,因此容器能继续使用这些功能。
Open vSwitch是一种开源的软件自定义的虚拟交换机,它通常和虚拟机管理软件一起使用,以便实现在同主机中不同虚拟机以及跨主机不同网络的通信。Overlay网络需要使用支持的隧道封装(VXLAN、GRE)创建一个虚拟的数据路径。
这个覆盖数据数据路径驻留在Docker主机中的隧道端点之间进行配置,在宿主机外部看了就是宿主机之间直接通信。
当生成一个新的容器时,在路由协议中更改前缀,通过隧道端点确定其位置。当其他Docker主机接收更新时,转发规则被安装到主机所在隧道端点的OVS中。
当主机被驱逐时,会发生类似的过程,隧道端点Docker主机会删除已解除供应的容器的转发。下图显示了通过基于OVS的VXLAN隧道在多个主机上运行的容器之间的通信:
总结
这个章节,我们讨论了Docker内部网络架构。我们学到了关于Docker中IPv4、IPv6、DNS配置方法。在后部分,我们了Docker网桥以及宿主机内部和跨主机的容器的通信。
我们还讨论了Docker网络的叠加隧道和不同的实现方法,如OVS、Flannel和Weave。
在下一章中,我们将学习实际操作的Docker网络,以及各种框架。