当项目大规模使用 Docker 时,容器通信的问题也就产生了。要解决容器通信问题,必须先了解很多关于网络的知识。Docker 作为目前最火的轻量级容器技术,有很多令人称道的功能,如 Docker 的镜像管理。然而,Docker 同样有着很多不完善的地方,网络方面就是 Docker 比较薄弱的部分。因此,我们有必要深入了解 Docker 的网络知识,以满足更高的网络需求。
[root@localhost ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
688d1970f72e bridge bridge local
885da101da7d host host local
f4f1b3cf1b7f none null local
常见网络模式 | 使用命令 | 作用 | 业务场景 |
none | 使用--network none指定 | 不得使用任务的网络 | 处理一些保密数据,对一些安全考虑,需要将其隔离网络环境执行一些纯计算任务 |
bridge | 使用--network bridge指定,默认使用docker0 | 使得容器与容器之间可以相互互通 | 容器需要实现网络通信或者提供网络服务 |
host主机模式 | 使用--network host指定 | 让容器程序可以使用主机网络 | 容器需要控制主机网络或者用主机网络提 |
container模式 | 使用--network container:NAME或者容器ID指定 | 将两个容器放到统一网络空间中,可以直接通过localhost访问 | 两个容器之间需要直接通过localhost通一般用户网络 |
bridge网络模式是一中基于NAT模式下的网络通信方式。此驱动为Docker的默认设置驱动,使用这个驱动的时候,libnetwork将创建出来的Docker容器连接到Docker网桥上。作为最常规的模式,bridge模式已经可以满足Docker容器最基本的使用需求了。然而其与外界通信使用NAT,增加了通信的复杂性,在复杂场景下使用会有诸多限制。
在该模式中,Docker 守护进程创建了一个虚拟以太网桥 docker0
,新建的容器会自动桥接到这个接口,附加在其上的任何网卡之间都能自动转发数据包。默认情况下,守护进程会创建一对对等虚拟设备接口 veth pair
,将其中一个接口设置为容器的 eth0
接口(容器的网卡),另一个接口放置在宿主机的命名空间中,以类似 vethxxx
这样的名字命名,从而将宿主机上的所有容器都连接到这个内部网络上。
Bridge 桥接模式的实现步骤主要如下:
运行一个基于 busybox 镜像构建的容器 bbox01,查看 ip addr:
docker run -it --name bbox01 busybox
然后宿主机通过 ip addr 查看信息如下:
ip addr
通过以上的比较可以发现,证实了之前所说的:守护进程会创建一对对等虚拟设备接口 veth pair
,将其中一个接口设置为容器的 eth0
接口(容器的网卡),另一个接口放置在宿主机的命名空间中,以类似 vethxxx
这样的名字命名。
同时,守护进程还会从网桥 docker0
的私有地址空间中分配一个 IP 地址和子网给该容器,并设置 docker0 的 IP 地址为容器的默认网关。也可以安装 yum install -y bridge-utils
以后,通过 brctl show
命令查看网桥信息。对于每个容器的 IP 地址和 Gateway 信息,我们可以通过 docker inspect 容器名称|ID
进行查看,在 NetworkSettings
节点中可以看到详细信息。
我们可以通过 docker network inspect bridge
查看所有 bridge
网络模式下的容器,在 Containers
节点中可以看到容器名称。
容器内的网络并不是希望永远跟主机是隔离的,有些基础业务需要创建或更新主机的网络配置,我们的程序必须以主机网络模式运行才能够修改主机网络,这时候就需要用到 Docker 的 host 主机网络模式。因此可以认为host驱动适用于对于容器集群规模不大的场景。
采用 host 网络模式的 Docker Container,可以直接使用宿主机的 IP 地址与外界进行通信,若宿主机的 eth0 是一个公有 IP,那么容器也拥有这个公有 IP。同时容器内服务的端口也可以使用宿主机的端口,无需额外进行 NAT 转换;host 网络模式可以让容器共享宿主机网络栈,这样的好处是外部主机与容器直接通信,但是容器的网络缺少隔离性。host 网络模式需要在创建容器时通过参数 --net host
或者 --network host
指定;
比如我基于 host 网络模式创建了一个基于 busybox 镜像构建的容器 bbox02,查看 ip addr:
docker run -it --name bbox02 --net host busybox
ip addr
你没有看错,返回信息一模一样,我也可以肯定我没有截错图,不信接着往下看。我们可以通过 docker network inspect host
查看所有 host
网络模式下的容器,在 Containers
节点中可以看到容器名称。
none 网络模式即不为 Docker Container 创建任何的网络环境,容器内部就只能使用 loopback 网络设备,不会再有其他的网络资源。可以说 none 模式为 Docke Container 做了极少的网络设定,但是俗话说得好“少即是多”,在没有网络配置的情况下,作为 Docker 开发者,才能在这基础做其他无限多可能的网络定制开发。这也恰巧体现了 Docker 设计理念的开放。
有时候,我们需要处理一些保密数据,出于安全考虑,我们需要一个隔离的网络环境执行一些纯计算任务。这时候 null 网络模式就派上用场了,这时候我们的容器就像一个没有联网的电脑,处于一个相对较安全的环境,确保我们的数据不被他人从网络窃取。
比如我基于 none 网络模式创建了一个基于 busybox 镜像构建的容器 bbox03,查看 ip addr:
docker run -it --name bbox03 -=net none busybox
ip addr
我们可以通过 docker network inspect none
查看所有 none
网络模式下的容器,在 Containers
节点中可以看到容器名称。
container网络模式允许一个容器共享另一个容器的网络命名空间。当两个容器需要共享网络,但其他资源仍然需要隔离时就可以使用 container 网络模式,例如我们开发了一个 http 服务,但又想使用 nginx 的一些特性,让 nginx 代理外部的请求然后转发给自己的业务,这时我们使用 container 网络模式将自己开发的服务和 nginx 服务部署到同一个网络命名空间中。Container 网络模式是 Docker 中一种较为特别的网络的模式,处于这个模式下的 Docker 容器会共享一个网络栈,这样两个容器之间可以使用 localhost 高效快速通信。
在创建容器时通过参数 --net container:已运行的容器名称|ID
或者 --network container:已运行的容器名称|ID
指定。Container 网络模式即新创建的容器不会创建自己的网卡,配置自己的 IP,而是和一个指定的容器共享 IP、端口范围等。同样两个容器除了网络方面相同之外,其他的如文件系统、进程列表等还是隔离的。
比如我基于容器 bbox01 创建了 container 网络模式的容器 bbox04,查看 ip addr:
docker run -it --name bbox04 --net container:bbox01 busybbox
ip addr
通过以上测试可以发现,Docker 守护进程只创建了一对对等虚拟设备接口用于连接 bbox01 容器和宿主机,而 bbox04 容器则直接使用了 bbox01 容器的网卡信息。这个时候如果将 bbox01 容器停止,会发现 bbox04 容器就只剩下 lo 接口了。
然后 bbox01 容器重启以后,bbox04 容器也重启一下,就又可以获取到网卡信息了。
虽然 Docker 提供的默认网络使用比较简单,但是为了保证各容器中应用的安全性,在实际开发中更推荐使用自定义的网络进行容器管理,以及启用容器名称到 IP 地址的自动 DNS 解析。从 Docker 1.10 版本开始,docker daemon 实现了一个内嵌的 DNS server,使容器可以直接通过容器名称进行通信。方法很简单,只要在创建容器时使用 --name
为容器命名即可。但是使用 Docker DNS 有个限制:只能在 user-defined 网络中使用。也就是说,默认的 bridge 网络是无法使用 DNS 的,所以我们就需要自定义网络。
通过 docker network create 命令可以创建自定义网络模式,命令提示如下:
docker netwwork --help
进一步查看 docker network create 命令使用详情,发现可以通过 --driver 指定网络模式且默认是 bridge 网络模式,提示如下:
创建一个基于 bridge 网络模式的自定义网络模式 custom_network,完整命令如下:
docker network create custom_network
通过 docker network ls 查看网络模式:
[root@localhost ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
b3634bbd8943 bridge bridge local
062082493d3a custom_network bridge local
885da101da7d host host local
f4f1b3cf1b7f none null local
通过自定义网络模式 custom_network 创建容器:
docker run -di --name bbox05 --net custom_network busybox
通过 docker inspect 容器名称|ID 查看容器的网络信息,在 NetworkSettings 节点中可以看到详细信息。
docker inspect b356565632561d
通过 docker network connect 网络名称 容器名称 为容器连接新的网络模式。
docker network connect bridge bbox05
通过 docker network disconnect 网络名称 容器名称 命令断开网络。
docker network disconnect custom_network bbox05
通过 docker inspect 容器名称|ID 再次查看容器的网络信息,发现只剩下默认的 bridge。
可以通过 docker network rm 网络名称 命令移除自定义网络模式,网络模式移除成功会返回网络模式名称。
docker network rm custom_network
注意:如果通过某个自定义网络模式创建了容器,则该网络模式无法删除。
接下来我们通过所学的知识实现容器间的网络通信。首先明确一点,容器之间要互相通信,必须要有属于同一个网络的网卡。
我们先创建两个基于默认的 bridge 网络模式的容器。
docker run -di --name default_bbox01 busybox
docker run -di --name default_bbox02 busybox
通过 docker network inspect bridge 查看两容器的具体 IP 信息。
经过测试,从结果得知两个属于同一个网络的容器是可以进行网络通信的,但是 IP 地址可能是不固定的,有被更改的情况发生,那容器内所有通信的 IP 地址也需要进行更改,能否使用容器名称进行网络通信?继续测试。
经过测试,从结果得知使用容器进行网络通信是不行的,那怎么实现这个功能呢?
从 Docker 1.10 版本开始,docker daemon 实现了一个内嵌的 DNS server,使容器可以直接通过容器名称通信。方法很简单,只要在创建容器时使用 --name
为容器命名即可。 但是使用 Docker DNS 有个限制:只能在 user-defined 网络中使用。也就是说,默认的 bridge 网络是无法使用 DNS 的,所以我们就需要自定义网络。 我们先基于 bridge
网络模式创建自定义网络 custom_network
,然后创建两个基于自定义网络模式的容器。
docker run -di --name custom_bbox01 --net custom_network busybox
docker run -di --name custom_bbox02 --net custom_network busybox
通过 docker network inspect custom_network 查看两容器的具体 IP 信息。
# 创建网络
docker network create
实例:docker network create -d bridge my-net
# 查看查看网络列表
docker network ls
实例:docker network ls
# 查询网络的模型详细信息
docker network inspect
# 网络连接
docker network connect
# 网络断开
docker network disconnect
# 网络删除
docker network rm
# 自定义网络固定IP
docker network create --subnet=172.18.0.0/16 mynetwork
docker run -itd --name test2 --net mynetwork --ip 172.18.0.100 centos:7 /bin/bash
docker inspect 1effd1f15ef3
# 在宿主机环境执行容器内命令
docker exec -it 容器ID /bin/bash -c 'nginx'
docker容器内部的ip是有可能会发生改变的,如何保证容器之间的通信?
如果没有使用自定义网络可能出现:按照IP地址ping是OK的,但是当服务出现宕机的时候服务iP地址发生变化,导致的服务请求的地址出现错误,导致服务不可用。
docker run -d -p 8081:8080 --name tomcat81 billygoo/tomcat8-jdk8
docker run -d -p 8082:8080 --name tomcat82 billygoo/tomcat8-jdk8
使用自定义网络解决当服务出现宕机的时候服务的IP地址发生变化,使用服务名依然可以访问。
docker run -d -p 8081:8080 --network zzyy_network --name tomcat81 billygoo/tomcat8-jdk8
docker run -d -p 8082:8080 --network zzyy_network --name tomcat82 billygoo/tomcat8-jdk8
docker的四种网络模式(Bridge、Host、Container、None 网络模式),自定义网络(创建自定义网桥、使两个不同网桥的容器通信、跨主机容器容器通讯等)_那人如此可好的博客-CSDN博客_docker host 网络模式
Docker之overlay网络模式 - 简书
docker-从原理到实战深度讲解_哔哩哔哩_bilibili
“深入浅出”来解读Docker网络核心原理_DevOps(甘兵)的技术博客_51CTO博客_docker原理_docker原理与架构