目录
- 一、Docker 容器网络通信的基本原理
- 1、查看 Docker 容器网络
- (1)新建一个 Dockerfile文件,内容如下:
- (2)使用以下命令创建镜像
- (3)基于 debian 的镜像创建一个容器,并进入该容器中。
- (4)在宿主机上打开一个命令窗口,执行以下命令查看宿主机的 docker0 网桥信息。
- (5)在容器内执行以下命令查看容器网络信息。如图所示:
- 2、宿主机与 Docker 容器建立网络通信的过程
- 二、使用命令查看 Docker 的网络配置信息
- 1、利用以下命令查看 Docker 的网络通信模式,如图所示:
- 2、查看 bridge 模式的额详细信息,如图所示:
- 三、Docker 的 4 种网络通信模式
- 1、bridge 模式
- (1)使用 bridge 模式创建容器(使用 busybox 镜像)。
- (2)在容器内执行 “ifconfig” 命令查看容器内的网络信息,如图所示:
- (3)执行以下命令自定义 bridge 网络。
- (4)查看 Docker网络,可以看到新创建的 bridge2,如图所示:
- (5)使用 bridge2 创建一个容器,这里通过参数--ip 制定了容器的 IP 地址。
- (6)在容器内执行 “ifconfig” 命令查看网络信息,如图所示:
- 2、host 模式
- 3、container 模式
- (1)使用 busybox 的镜像创建一个容器A,并查看容器的网络信息,如图所示:
- (2)开启一个新的命令行窗口查看容器 A 的 ID。
- (3)开启一个新的命令行窗口,并使用 container 模式创建一个新的容器 B。
- (4)查看容器 B 的网络信息。
- (5)对比两个容器的网络信息。
- 4、none 模式
- 四、容器间的通信
- 1、通过 IP 地址进行通信
- (1)利用上面创建的自定义网络 bridge2 创建两个容器,并指定他们的 IP 地址。
- (2)在其中一个容器内执行 “ping” 命令。
- 2、通过 Docker DNS Server 进行通信
- (1)利用上面创建的自定义网络 bridge2 创建两个容器,并指定他们的 IP 地址。
- (2)在其中一个容器内执行 “ping” 命令。
- 3、通过 joined 方式进行通信
- (1)基于 httpd 的镜像创建一个容器,命名为“box1”
- (2)基于busybox 的镜像创建一个新的容器“box2” ,并通过参数--network=container:box1 指定与box1 容器进行通信。
- (3)在box2 容器中,通过 “wget 127.0.0.1” 命令可以直接访问 box1容器的 HTTP 服务,而在 box1 容器中会打印出一条日志,表中从 127.0.0.1 地址接收到了一个HTTP 的get 请求。
- 4、容器间的跨节点通信
- 五、容器的网络访问控制
- 1、容器内的应用访问外部网络
- (1)开启 IP 数据包的转发功能
- (2)实例
- - 基于busybox的镜像创建一个容器。
- - 在容器内部访问百度的首页,可以看到 ping 命令正常返回信息,如图所示:
- 2、外部网络访问容器内的应用
- (1)基于 Nginx 的镜像创建一个容器,并将容器的 80 端口映射到宿主机的 1234 端口。
- (2)在宿主机上,通过以下命令来查看 iptable 表中的路由转发规则,如图所示:
Docker 的容器运行在宿主机的虚拟机上。这些虚拟机彼此独立,彼此之间没有任何接口,即容器彼此之间是逻辑隔离的。
那么,如何实现容器的相互通信?容器又如何访问外部的网络呢?外部的网络如何才能访问部署在容器内的应用呢?本章节将详细了解这些问题。
一、Docker 容器网络通信的基本原理
Docker 容器中的网络接口默认都是虚拟接口。虚拟接口最大优势是转发效率极高。这是因为,Linux 通过在内核中进行数据复制来实现虚拟接口之间的数据转发,即发送接口缓存中的数据包会被直接辅助到接收接口的缓存中,而无需通过外部的物理网络设备进行交换。
虚拟接口和一个正常的以太网并无太大区别,只是它的速度比以太网卡快的多
Docker 的网络很好的利用了 Linux 虚拟网络技术,在宿主机的物理网卡和容器内分别创建一个虚拟接口(veth),并让他们通过宿主机的 docker0 网桥进行连接,我们把这样的一对 veth 叫做 veth pair。如下图所示:
1、查看 Docker 容器网络
(1)新建一个 Dockerfile文件,内容如下:
FROM debian
RUN apt-get update --fix-missing && apt-get install -y net-tools
(2)使用以下命令创建镜像
sudo docker build -t debian .
(3)基于 debian 的镜像创建一个容器,并进入该容器中。
sudo docker run -it debian /bin/bash
(4)在宿主机上打开一个命令窗口,执行以下命令查看宿主机的 docker0 网桥信息。
(5)在容器内执行以下命令查看容器网络信息。如图所示:
对比两张图可以发现,容器内的网络地址与宿主机的网络地址具有相同的flag,即:flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
这说明了在创建容器时,会成对的创建veth pair,并且他们通过宿主机的 docker0 网桥进行通信
2、宿主机与 Docker 容器建立网络通信的过程
Docker 默认采用的是 bridge 网络通信模式。
二、使用命令查看 Docker 的网络配置信息
“sudo docker help network” 命令用于查看 Docker 的网络配置信息。如图所示:展示了该命令的帮助信息。
1、利用以下命令查看 Docker 的网络通信模式,如图所示:
通过输出信息可以看到,Docker 有3种网络通信模式:bridge、host和none。在默认情况下,Docker 使用 bridge 模式。
除这里列出的3种网络通信模式外,Docker 还提供了 container 网络通信模式用于容器间的相互通信。
2、查看 bridge 模式的额详细信息,如图所示:
从图中可以看出,bridge 模式默认的子网地址是 “172.17.0.0/16” ,容器的 IP 地址是 172.17.0.2,属于 bridge 网络的子网地址。
三、Docker 的 4 种网络通信模式
由于 Docker 容器彼此之间是逻辑隔离的,所以,在安装 Docker 时会在容器中创建隔离的网络环境。在该隔离的网络模式环境中,运行在宿主机上的各个容器具有完全独立的网络栈,并且 Docker 容器的网络环境和宿主机相互隔离。通过使用 Docker 的不同网络模式,可以使 Docker 容器共享宿主机的网络命名空间,也可以实现 Docker 容器间的相互访问。
Docker 一共提供四种网络通信模式:bridge、container、host和 none。
网络通信模式 | 是否支持多机 | 纵向通信机制 | 横向通信机制 |
---|---|---|---|
bridge | 否 | 绑定宿主机端口 | 通过Linux桥接进行通信 |
container | 否 | 绑定宿主机端口 | 通过Linux连接进行通信 |
host | 是 | 通过宿主机网络进行通信 | 通过宿主机网络进行通信 |
none | 否 | 无法通信 | 只能通过Linux连接进行通信 |
下面分别介绍四种模式。
1、bridge 模式
bridge 模式是 Docker 默认的网络通信模式,是开发者最常用的模式。
在 bridge 模式下,Docker 引擎会创建独立的网络命名空间。这样就可以保证运行在每一个命名空间中的容器具有独立的网卡等网络资源。
利用 bridge 模式,可以非常方便地实现容器与容器之间、容器与宿主机之间的网络隔离。使用宿主机上的 docker0 网桥,可以实现 Docker 容器与宿主机(乃至外部网络)的网络通信。
(1)使用 bridge 模式创建容器(使用 busybox 镜像)。
sudo docker pull busybox
sudo docker run -it --network=bridge busybox /bin/sh
这里的 --network=bridge 可以不写,默认就是 bridge 模式。
busybox 是一个集成了一百多个最常用的Linux 命令和工具的软件工具箱。
(2)在容器内执行 “ifconfig” 命令查看容器内的网络信息,如图所示:
(3)执行以下命令自定义 bridge 网络。
sudo docker network create -d bridge --ip-range=192.168.11.0/24 --gateway=192.168.11.1 --subnet=192.168.11.0/24 bridge2
# 其中参数说明如下:
# -d:指定网络通信模式,默认是bridge。
# --ip-range:指定子网IP 地址的范围
# --gateway:指定网关的 IP 地址。
# --subnet:指定子网的 IP 地址。
# bridge2:指定bridge 网络的名字。
(4)查看 Docker网络,可以看到新创建的 bridge2,如图所示:
(5)使用 bridge2 创建一个容器,这里通过参数–ip 制定了容器的 IP 地址。
sudo docker run -it --network=bridge2 --ip=192.168.11.98 busybox
(6)在容器内执行 “ifconfig” 命令查看网络信息,如图所示:
2、host 模式
在使用 host 模式时,容器与宿主机共享同一个网络命名空间,容器的 IP 地址与宿主机的 IP 地址相同。如果宿主机具有公网 IP 地址,则容器也拥有这个公网的 IP 地址。即这时容器可以直接使用宿主机的 IP 地址与外界进行通信,且容器内服务的端口也可以直接使用宿主机的端口,无需进行任何转换。
由于在 host 模式下不在需要宿主机的转发,因此器性能得到了极大的提高。下图是 host 模式的工作机制。
使用 host 模式来创建容器,如以下命令所示:
sudo docker run -it --network=host busybox /bin/sh
对比一下容器内的网络信息和宿主机的网络信息,可以发现,容器与宿主机共享了同一个网络命名空间,即容器使用了宿主机的网络配置信息。
尽管使用 host 模式可以很方便地通过 localhost 或者127.0.0.1 实现容器与宿主机的相互访问,并且性能也比较好。但是这种模式也存在两个问题。
• 由于容器使用了宿主机的网络环境,因此网络环境的隔离性功能被减弱,从而造成宿主机和容器争用网络资源。容器本身也不再拥有所有的网络资源,而是与宿主机共享网络资源。
• 宿主机和容器使用了相同的 IP 地址,这不利于网络的配置和管理。
3、container 模式
在 container 模式下,容器之间会共享网络环境。即一个容器会使用另一个容器的网络命名空间。因此,在这种模式下,容器之间可以通过 localhost 或者 127.0.0.1 进行相互间的访问,从而提高了传输的效率。
container 模式节约了网络资源,但是运行在这种模式下的容器不存在网络隔离。Container 网络的隔离性处于 bridge网络与 host 网络之间。
下面演示如何使用 container 模式。
(1)使用 busybox 的镜像创建一个容器A,并查看容器的网络信息,如图所示:
sudo docker run -it busybox /bin/sh
ifconfig
(2)开启一个新的命令行窗口查看容器 A 的 ID。
(3)开启一个新的命令行窗口,并使用 container 模式创建一个新的容器 B。
sudo docker run -it --network=container:d8ae81df1803 busybox /bin/sh
# 其中,参数--network 用于指定新容器使用哪一个容器的网络信息,这里指定的是容器A。
(4)查看容器 B 的网络信息。
(5)对比两个容器的网络信息。
容器 A 和容器 B 使用了相同的网络命名空间。这是因为,在创建容器B 时使用了container 模式,是的容器 B 不再创建自己的网络命名空间,而直接使用容器 A 的网络命名空间。
4、none 模式
none模式下的容器具有独立的网络命名空间,但不包含任何网络配置,只能通过 Local Loopback 网卡与容器进行通信,即只能使用 localhost 或者 127.0.0.1 访问容器。
在 none 模式下需要手动进行网络配置,下面使用 none 模式来创建一个容器。
sudo docker run -it --network=none busybox /bin/sh
ifconfig
由于 none 模式不包含任何网络配置,所以在其网络配置信息中就只包含一个 127.0.0.1 的 IP 地址。
四、容器间的通信
1、通过 IP 地址进行通信
在宿主机上创建一个容器时, Docker 守护进程会为每一个新创建的容器自动分配一个虚拟的IP 地址。但是,外部的网络时无法通过这个虚拟的 IP 地址访问容器内的应用的。
下面对容器间的虚拟 IP 地址通信方式进行测试。
(1)利用上面创建的自定义网络 bridge2 创建两个容器,并指定他们的 IP 地址。
sudo docker run -it --network=bridge2 --ip=192.168.11.97 busybox
sudo docker run -it --network=bridge2 --ip=192.168.11.98 busybox
(2)在其中一个容器内执行 “ping” 命令。
可以发现,通过容器的虚拟 IP 地址能够实现容器间的相互通信,但是这种通信方式存在一个问题就是不利于管理和维护。那有没有解决的办法呢?答案当然是有的——通过 Docker DNS Server 进行通信。
2、通过 Docker DNS Server 进行通信
下面对容器减的 DNS Server 通信方式进行测试。
(1)利用上面创建的自定义网络 bridge2 创建两个容器,并指定他们的 IP 地址。
sudo docker run -it --network=bridge2 --name box1 busybox
sudo docker run -it --network=bridge2 --name box2 busybox
(2)在其中一个容器内执行 “ping” 命令。
要使用 Docker DNS Server 进行容器间的通信,有一个限制条件:这种方式只能在用户自定义的 bridge 网络中使用,在 Docker 默认的 bridge 网络中是无法使用的。
3、通过 joined 方式进行通信
Joined 是 Docker 引擎提供的一个种特殊的容器间通信方式,其本质上使用了 container 模式。因为在 container 模式下,多个容器共享同一个网络环境,也共享网卡的配置。因此,在 container 模式下,容器之间可以直接通过 localhost 或者 127.0.0.1 进行通信。
下面演示如何使用 Joined 方式实现容器间的通信。
(1)基于 httpd 的镜像创建一个容器,命名为“box1”
sudo docker pull httpd
sudo docker run -it --name box1 httpd
httpd 是 Apache HTTP服务器,可以把它看成是web服务器。
(2)基于busybox 的镜像创建一个新的容器“box2” ,并通过参数–network=container:box1 指定与box1 容器进行通信。
sudo docker run -it --network=container:box1 --name box2 busybox
(3)在box2 容器中,通过 “wget 127.0.0.1” 命令可以直接访问 box1容器的 HTTP 服务,而在 box1 容器中会打印出一条日志,表中从 127.0.0.1 地址接收到了一个HTTP 的get 请求。
4、容器间的跨节点通信
(感兴趣的可以自己研究一下,需要的给我留言)
五、容器的网络访问控制
从 Docker 的网络通信模式可以看出,在默认情况下,运行在宿主机上的容器可以与宿主机及外部的网络进行通信。即使是在同一个宿主机上,容器也能够通过 bridge 模式的 docker0 网桥进行相互通信。
但是,由于宿主机上的网络与容器内的网络不属于同一网段,因此仅仅依靠 bridge 模式的虚拟接口无法让宿主机以外的系统访问宿主机上的容器。为了解决这个问题,Docker 采用端口绑定的方式让外部系统可以访问宿主机上的容器的内部,即利用 Linux 的 iptable 表将宿主机上的端口映射到容器内的端口。
下面介绍容器内的应用如何访问外部网络,以及从外部网络如何访问容器的应用。
1、容器内的应用访问外部网络
在默认情况下,容器内的应用访问外部网络是通过宿主机上的 docker0 网桥来实现的。当容器内的应用需要访问外部网络时,需要宿主机进行转发。
(1)开启 IP 数据包的转发功能
执行以下指定可以确定宿主机的 Linux 是否开启了 IP 数据包转发功能。
sysctl net.ipv4.ip_forward
或者
cat /proc/sys/net/ipv4/ip_forward
• 如果返回值是1,则表示已经开启了 Linux 的 IP 数据包转发功能。
• 如果返回值是0,则表示没有开启此功能。
可以通过执行以下语句开启此功能。
sysctl -w net.ipv4.ip_forward=1
开启此功能的另一种方式是,在启动 Docker 服务时指定参数 “–ip-forward=true” 。这样 Docker 守护进程会自动将宿主机的 ip_forward 参数设置为 1。
(2)实例
下面对容器内的应用访问外部网络进行测试。
- 基于busybox的镜像创建一个容器。
sudo docker run -it busybox
- 在容器内部访问百度的首页,可以看到 ping 命令正常返回信息,如图所示:
2、外部网络访问容器内的应用
运行在宿主机上的容器,允许从外部网络访问器内部的应用,这主要是通过 -p 参数来实现的。
通过在创建容器时指定 -p 参数,可以将容器内的某个端口与宿主机绑定,来完成宿主机与容器的端口映射。其本质是:在宿主机 iptable 表中添加相应的路由转发规则,对访问外部 IP 地址的数据包进行转换,将其访问的目标地址修改为容器的 IP 地址和容器内的端口。
下面通过一个简单的示例来说明这个过程。
(1)基于 Nginx 的镜像创建一个容器,并将容器的 80 端口映射到宿主机的 1234 端口。
sudo docker run -d -p 1234:80 nginx
(2)在宿主机上,通过以下命令来查看 iptable 表中的路由转发规则,如图所示:
sudo iptables -t nat -L -n | grep 1234