长文讲述Docker的4大网络模型(hostcontainernonebridge)

Posted 董哥的黑板报

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了长文讲述Docker的4大网络模型(hostcontainernonebridge)相关的知识,希望对你有一定的参考价值。

  • docker在安装后会默认生成三种网络,none、bridge及host

  • 我们在使用docker run创建Docker容器时,可以用--net选项指定容器的网络模式,Docker有以下4种网络模式:
    • host模式,使用--net=host指定
    • container模式,使用--net=container:NAME_or_ID指定
    • none模式,使用--net=none指定
    • bridge模式,使用--net=bridge指定,默认设置

一、host模式

  • 众所周知,Docker使用了Linux的Namespaces技术来进行资源隔离,如PID Namespace隔离进程,Mount Namespace隔离文件系统,Network Namespace隔离网络等。一个Network Namespace提供了一份独立的网络环境,包括网卡、路由、Iptable规则等都与其他的Network Namespace隔离。一个Docker容器一般会分配一个独立的Network Namespace
  • 但如果启动容器的时候使用host模式,那么这个容器将不会获得一个独立的Network Namespace,而是和宿主机共用一个Network Namespace。容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口
  • 例如,我们在10.10.101.105/24的机器上用host模式启动一个含有web应用的Docker容器,监听tcp80端口。当我们在容器中执行任何类似ifconfig命令查看网络环境时,看到的都是宿主机上的信息。而外界访问容器中的应用,则直接使用10.10.101.105:80即可,不用任何NAT转换,就如直接跑在宿主机中一样。但是,容器的其他方面,如文件系统、进程列表等还是和宿主机隔离的
  • 例如,下面运行一个host模式容器:
docker run -it --network=host busybox

二、container模式

  • 在理解了host模式后,这个模式也就好理解了。这个模式指定新创建的容器和已经存在的一个容器共享一个Network Namespace,而不是和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享IP、端口范围等。同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。两个容器的进程可以通过lo网卡设备通信

三、none模式

  • 这个模式和前两个不同。在这种模式下,Docker容器拥有自己的Network Namespace,但是,并不为Docker容器进行任何网络配置。也就是说,这个Docker容器没有网卡、IP、路由等信息。需要我们自己为Docker容器添加网卡、配置IP等
  • 例如,下面运行一个none模式容器:
docker run -it --network=none busybox

四、bridge模式

  • bridge模式是Docker默认的网络设置,此模式会为每一个容器分配Network Namespace、设置IP等,并将一个主机上的Docker容器连接到一个虚拟网桥上。下面着重介绍一下此模式
  • 工作原理:
    • 当Docker server启动时,会在主机上创建一个名为docker0的虚拟网桥,此主机上启动的Docker容器会连接到这个虚拟网桥上。虚拟网桥的工作方式和物理交换机类似,这样主机上的所有容器就通过交换机连在了一个二层网络中
    • 接下来就要为容器分配IP了,Docker会从RFC1918所定义的私有IP网段中,选择一个和宿主机不同的IP地址和子网分配给docker0,连接到docker0的容器就从这个子网中选择一个未占用的IP使用。如一般Docker会使用172.17.0.0/16这个网段,并将172.17.42.1/16分配给docker0网桥(在主机上使用ifconfig命令是可以看到docker0的,可以认为它是网桥的管理接口,在宿主机上作为一块虚拟网卡使用)。单机环境下的网络拓扑如下,主机地址为10.10.101.105/24

宿主机的“docker0”接口(虚拟的以太网桥)

  • 在宿主机中安装完Docker之后,会在宿主机中创建一个新的网络接口,名为docker0
  • 在宿主机中输入下面的命令查看接口情况,如下所示:
    • 可以看到,docker0接口有符合RFC1918的私有IP地址,范围是172.16~172.30
    • 此docker0接口的地址是172.18.0.1,是这个Docker网络的网关地址,也是当前宿主机中所有Docker容器的网关地址
    • 备注:Docker会默认使用172.17.x.x作为子网地址。如果这个子网被占用了,Docker会在172.16~172.30这个范围内尝试创建别的子网(下图是我们的一台云服务器,其172.17.x.xIP被占用了,因此使用了172.18.x.x的地址)
ip a show docker0

  • 接口docker0是一个虚拟的以太网桥,用于连接容器和本地宿主机网络

宿主机的“vethxxx”接口

  • 在宿主机中每使用“docker run”命令运行一个容器,就会在宿主机中创建一个以“veth”开头的网络接口
  • 如下图所示:
    • 可以看到当前宿主机中运行着两个docker容器
    • 通过ifconfig命令可以查看到有两个以“veth”开头的网络接口
sudo docker ps

ifconfig

Docker容器的“eth0”接口

  • 容器创建成功之后,容器内部会创建一个名为eth0的接口,用于与宿主机之间进行通信
  • 例如,下面我们启动一个容器,并查看其网络接口。可以看到Docker给容器分配的IP地址为172.18.0.4
sudo docker run -t -i ubuntu /bin/bash

# 第一次创建容器,没有ifconfig、ip这样的命令,需要输入下面的命令安装
apt-get update
apt-get install net-tools

ifconfig

  • 详情参阅下面的工作原理

Docker内部网络工作原理

  • 上面已经介绍了这么多的接口,我们来介绍一下Docker内部网络是如何工作的
  • 第一步:Docker每创建一个容器就会创建一组互联的网络接口,这组接口就像管道的两端(一端发送,另一端接收)
    • 在宿主机中叫“vethxxx”接口
    • 在Docker容器中叫eth0接口

  • 第二步:然后宿主机中还有一个名为docker0的虚拟子网,这个子网由宿主机和所有Docker容器共享
    • 可以把docker0看成是虚拟网线,其一端连接宿主机的“veth”接口,另一端连接容器的eth0接口
    • 因此可以进行通信

路由演示案例

  • 上面我们介绍了工作原理,现在我们从容器内跟踪对外通信的路由,看看是如何建立连接的
  • 第一步:启动一个容器,在容器中输入下面的命令安装traceroute路由工具
apt-get -yqq update

apt-get install -yqq traceroute

  • 第二步:输入下面的命令让容器访问百度。可以看到容器第一跳的地址是宿主机网络上docker0接口的网络IP 172.18.0.1
traceroute baidu.com

Docker的防火墙规则和NAT配置

  • 不过Docker网络还有一部分配置才能允许建立连接:防火墙规则和NAT配置。这些配置允许Docker在宿主机网路和容器间路由
  • 在宿主机中输入下面的命令可以查看宿主机的IPTables NAT配置,如下所示:这里有几个值得注意的IPTables规则。首先,我们注意到,容器默认是无法访问的。从宿主网络与容器通信时,必须明确指定打开的端口
sudo iptables -t nat -L -n

  • 下面我们以DNAT(即目标NAT)为例,这个规则把容器里的访问路路由到宿主机的32774端口上
  • 第一步:我们宿主机中当前有一个运行着redis服务的容器,如下所示:
    • 容器的4379端口映射到了宿主机的32774端口上

  • 第二步:查看redis容器的网络,命令如下:
    • 可以看到redis容器的IP地址为172.18.0.3
    • 并且使用了docker0接口作为网关地址
    • 容器的6379端口被映射到了本地宿主机的32774端口
# 查看所有配置
sudo docker inspect redis

# 过滤只查看IP地址
sudo docker inspect -f ' .NetworkSettings.IPAddress ' redis

  • 第三步:因为容器的6379端口被映射到了本地宿主机的32774端口,所以我们可以在宿主机中通过下面的命令来访问容器中的redis服务
redis-cli -h 127.0.0.1 -p 32774

  • 第四步:但是,因为运行在本地的Docker宿主机上,所以可以不使用映射后的端口,可以直接使用172.18.0.3和4379端口对容器的redis进行访问,如下所示:
redis-cli -h 172.18.0.3 -p 6379

这种联网方式不适用于实际开发

  • 这种联网方式有两大问题:
    • 第一:要在应用程序里对容器的IP地址做硬编码
    • 第二:如果重启容器,Docker可能会改变容器的IP地址
  • 因此,在实际开发中,如果容器提供服务,其重启后IP地址改变,那么就需要不断的更新,这种方式不是很灵活

一个完整的演示案例

  • 第一步:当前系统中没有运行任何容器,查看docker0网卡上没有任何网络设备
bridge link show dev docker0 | grep docker0

  • 第二步:运行一个busybox容器,之后发现一个新的网络接口veth1144baa state UP @if7挂载到了docker0,这个就是busybox的虚拟网卡
sudo docker run -itd --name busybox busybox

bridge link show | grep docker0

  • 第三步:但是查看busybox的网络配置却没有找到这个veth1144baa state UP @if7设备
sudo docker exec -it busybox ip l

  • 这里的eth0与veth1144baa state UP @if7就是一对veth pair设备。可以把veth设备看做是一对网卡,一块网卡(eth0)在容器内,另一块(veth1144baa state UP @if7)挂载在docker0上。目的就是把容器的eth0挂到docker0上
  • 第四步:查看容器的网关,这个网关就是docker0的ip地址
sudo docker exec -it busybox ip route

sudo docker network inspect bridge | jq '.[0].IPAM.Config[0].Gateway'


  • 我是小董,V公众点击"笔记白嫖"解锁更多【Docker】资料内容。

以上是关于长文讲述Docker的4大网络模型(hostcontainernonebridge)的主要内容,如果未能解决你的问题,请参考以下文章

nmap探测大网络空间中的存活主机

虚拟化大网络时代工程师必备技能

如何用python实现网络图节点权重的添加以及如何把一个非连通的大网络图分成多个小网络图

读论文七步走,CV资深博客专家长文:一篇论文需要读4遍

嵌入式AI基础知识介绍

万字长文系统讲解模型特征选择方法,这一篇就够了!