侧边栏壁纸
博主头像
包包博主等级

talk is cheap,show me the code

  • 累计撰写 25 篇文章
  • 累计创建 59 个标签
  • 累计收到 54 条评论

Docker容器网络通信机制

包包
2021-06-17 / 0 评论 / 0 点赞 / 1,463 阅读 / 2,608 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2022-04-19,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

原理

当Docker启动时,会自动在主机上创建一个docker虚拟网桥,实际上是 Linux 的一个 bridge,可以理解为一个软件交换机,它会在挂载到它的网口之间进行转发。同时,Docker 随机分配一个本地未占用的私有网段中的一个地址给 docker 接口。比如典型的 172.17.0.1,掩码为 255.255.0.0此后启动的容器内的网口也会自动分配一个同一网段(172.17.0.0/16)的地址

我们可以通过命令来查看docker虚拟网桥的信息

docker network ls  # 查看docker的网络列表
docker inspect 网络名称  # 查看某个网络的详细信息

也可以通过查看容器详细信息,得到其连接哪个网络

当创建一个 Docker 容器的时候,同时会创建了一对 veth pair接口当数据包发送到一个接口时,另外一个接口也可以收到相同的数据包)。这对接口一端在容器内,即 eth0;另一端在宿主机本地并被挂载到 docker 网桥,名称以 veth 开头(例如 vethAQI2QT)。通过这种方式,宿主机可以跟容器通信,容器之间也可以通过docker网桥相互通信。Docker就创建了在宿主机和所有容器之间一个虚拟共享网络

  • 安装docker时创建的默认docker网桥,用docker network ls查看其名称是bridge,如果在宿主机上用ifconfig查看,实际上是对应了docker0虚拟网卡

  • 我们启动的所有容器,如果不做特殊的网络指定,默认都是连接到docker网桥上。每启动一个容器,都会在宿主机上创建一个veth开头的虚拟网卡,用来进行容器与宿主机、容器与容器间的通信。容器通过DHCP获取一个与docker0同网段的IP地址

  • 我们启动的所有容器,默认都是连接到docker网桥上,但是这不是一个好的实践。假设我们有多个应用,每个应用启动了自己独有的mysqlredistomcat等容器,那么默认情况下所有容器都连接到同一个网桥,此时应用1的tomcat发送给应用1自己的mysql的数据,应用2和应用3的所有容器也都能收到,这增加了不必要的网络流量压力:

所以最佳的方式是:以应用为单位划分网络,同个应用对应的容器连接到1个网络,不同应用之间的容器做到网络隔离

docker默认的网络除了bridge外,还有hostnone

  • host:如果指定容器网络为host,那么表示容器与宿主机共享网络,即容器的ip与宿主机相同。这样做的好处是访问宿主机就相当于访问容器,提升网络性能,不需要宿主机转发给容器。坏处是容器与宿主机共享端口,不能冲突。我们可以在启动容器时通过--network host指定为host网络

    docker run -d --network host --name redis redis:5.0.8
    

    当指定网络为host之后,进入容器可以发现容器拥有与宿主机相同的ip配置

    而默认bridge模式的容器只有自己的ip

    host模式可以用于所有应用和中间件部署在同一台机器的情况下,应用在容器中连接中间件只需指定ip为localhost即可,因为此时localhost就是代表了宿主机

  • none:如果指定容器的网络为none,那么将无法通过网络访问容器,容器只有一个本地回环地址。如果禁止容器联网,为了安全可以采用这种方式

网络相关命令

1.查看网络

docker network ls  # 查看docker的网络列表
docker inspect 网络名称  # 查看某个网络的详细信息

2.创建网络

docker network create 网络名称

每创建一个网络,宿主机上都会生成类似于docker0的虚拟网卡,作为该网络容器之间通信的桥梁

3.启动容器时指定网络

docker run -d -p 6380:6379 --name redis01 --network 网络名称 redis:5.0.8

可以发现,我们创建的网络baobao,默认网段是172.18.0.1,与默认docker172.17.0.1刚好区分开

启动容器指定网络时,必须保证网络已经存在

4.容器间的网络通信

连接到同一个网络的容器可以互相通信,容器启动时docker网络会为其分配ip,我们可以利用ip与其他容器通信

首先我们启动2个redis容器,指定一个自定义网络

docker run -d -p 6380:6379 --name redis01 --network baobao redis:5.0.8
docker run -d -p 6381:6379 --name redis02 --network baobao redis:5.0.8

然后通过查看网络的详细信息可以知道网络下所有容器的ip

然后我们进入到redis01的内部,尝试ping redis02的ip。没有ping命令可以先用下列命令安装

apt-get update
apt install iputils-ping

虽然用ip可以完成容器间通信,但是如果容器ip发生变化,通信将会失败。从Docker 1.10版本开始,docker daemon实现了一个内嵌的DNS server,使容器可以直接通过容器名通信。使用默认的bridge网络,不能通过DNS server实现通过容器名通信,但是使用自定义bridge网络可以做到通过容器名互相通信。所以在自定义网络的情况下,更推荐的方式是用容器名称进行通信。我们尝试在redis01中ping redis02的容器名称

注意:

  • 采用容器名称互相通信的方式,必须在创建容器时指定自定义网络才会生效。如果容器连接到默认的docker0网络是无法用容器名通信的
  • 如果没有给容器指定自定义网络,那么会连接到默认的docker0网段,此时可以用ip进行容器互联,但是这个ip是通过DHCP动态分配的,所以这种情况下更推荐直接用宿主机ip+容器映射端口进行容器间通信
  • 如果指定了自定义网络,那么推荐用容器名称进行通信

5.将容器连接到另一个网络

假如我们要将网络1的某个容器同时挂到网络2下,可以使用下列命令

docker network connect 网络名称 容器名称

执行完成后该容器与网络2就连通了,可以与网络2下的所有容器通信。其原理很简单,就是docker会给该容器再分配一个网络2网段的ip,相当于容器有2个ip,一个是网络1网段的,另一个是网络2网段的

如果想让容器与网络再断开连接,可以用下列命令

docker network disconnect 网络名称 容器名称
0

评论区