docker network —— 网络基础

发布时间 2023-06-16 10:15:57作者: PanPan003

course:

Managing Docker Networking | Pluralsight

 

CNM: Container Network model

网络 抽象层级结构

 

 

 

 Sandbox: a network namespace

沙箱:share 主机操作系统 的 环形围栏区域

 

container share:机器内核,操作系统。 

similar: VM (虚拟CPUs,虚拟RAM, 虚拟硬盘,虚拟网卡……)

 

a container 包含一系列的namespace(一些列根文件 namespace,进程 namespace,网络 namespace……)

namespace: linux world的术语

 

 

endpoint: 连接 container的sandbox 到 外部网络世界

含:virtual Ethernet interface

 

网络:一组可供 通信的 endpoints 

 

 

 

 

Q:

linux network namespace

容器化大行其道的今天,Docker 可谓是容器界的宠儿。比起笨重的虚拟机,Docker 可谓是身轻如燕。当然,本文不是介绍虚拟机与 Docker 之间的优缺点,而是介绍 Docker 网络中重要的组成部分之一:

虚拟网络设备对:veth

在介绍 veth 前,我们先来介绍一下 网络命名空间(network namespace)

网络命名空间

网络命名空间 是 Linux 内核用来隔离不同容器间的网络资源(每个 Docker 容器都拥有一个独立的网络命名空间),网络命名空间主要隔离的资源包括:

  • iptables规则表
  • 路由规则表
  • 网络设备列表

如下图所示,当系统中拥有 3 个网络命名空间:

由于不同的网络命名空间之间是相互隔离的,所以不同的网络命名空间之间并不能直接通信。比如在 网络命名空间A 配置了一个 IP 地址为 172.17.42.1 的设备,但在 网络命名空间B 里却不能访问,如下图所示:

就好比两台电脑,如果没有任何网线连接,它们之间是不能通信的。所以,Linux 内核提供了 虚拟网络设备对(veth) 这个功能,用于解决不同网络命名空间之间的通信。

virtual Ethernet interface(veth)

如何让一个独立的网络命名空间和主机的网络互通,这里我们需要用到linux虚拟网络设备veth。

 

veth设备

veth是linux的一种虚拟网络设备,它有点类似于两张网卡中间用一条网线连着,veth设备总是成对出现,通常用来连接不同网络命名空间(下面开始简称NS),一端连着NS1的内核协议栈,另一端连着NS2的内核协议栈,一端发送的数据会立刻被另一端接收。

 

虚拟网络设备对(veth)

虚拟网络设备对 用于解决不同网络命名空间之间的通信,可以将其看成是两块有网线连接的网卡。只要将其中一块网卡放置到网络命名空间A,另外一块网卡放置到网络命名空间B,那么两个不同的网络命名空间就能够通信,如下图所示:

如上图所示,veth0veth1 组成一个虚拟网络设备对。虚拟网络设备对 就像管道一样,只要向其中一端发送数据,就可以从另外一端接收到数据。

Docker 就是使用 虚拟网络设备对 来实现不同容器之间的通信,其原理如下图:

从上图可以看出,每个容器之间并不是直接通过 虚拟网络设备对 来进行连接的,而是在主机上创建一个名为 docker0网桥,然后通过 虚拟网络设备对 来将各个容器连接到 网桥 上。网桥 有将多个 网络设备 连接起来的能力,就如现实中的 交换机 一样。

 

 

第一回是最简单的veth。

先说说什么是虚拟网络接口。与之对应的是真实的物理网络接口,即真实的网卡。而虚拟网络接口,则是没有实际的物理设备,而是通过软件“模拟”的网络接口。对于内核的来说,只要实现了net_device规定的几个必要接口,并且成功调用register_netdevice注册了该netdev,则内核就会认为这个netdev是一个网络接口。至于怎么能不能接收或者发送数据包,就看你自己个儿的了:)

其实虚拟网络接口没有那么神秘,vlan,bridge等,都是虚拟网络接口。

真实的网络接口连在实际的物理链路上,自然有正常的收发报文,并且一般通过中断进行通知。而对于虚拟接口来说,内核也早已设计好了框架,只要进行适当的接口调用,内核就会把数据包传递给你创建的虚拟接口。这里引用之前画的skb在内核的flow图的一个片断。

 

 

veth设备的特点

  • veth和其它的网络设备都一样,一端连接的是内核协议栈。
  • veth设备是成对出现的,另一端两个设备彼此相连
  • 一个设备收到协议栈的数据发送请求后,会将数据发送到另一个设备上去。

下面这张关系图很清楚的说明了veth设备的特点:

+----------------------------------------------------------------+
|                                                                |
|       +------------------------------------------------+       |
|       |             Newwork Protocol Stack             |       |
|       +------------------------------------------------+       |
|              ↑               ↑               ↑                 |
|..............|...............|...............|.................|
|              ↓               ↓               ↓                 |
|        +----------+    +-----------+   +-----------+           |
|        |   eth0   |    |   veth0   |   |   veth1   |           |
|        +----------+    +-----------+   +-----------+           |
|192.168.1.11  ↑               ↑               ↑                 |
|              |               +---------------+                 |
|              |         192.168.2.11     192.168.2.1            |
+--------------|-------------------------------------------------+
               ↓
         Physical Network

上图中,我们给物理网卡eth0配置的IP为192.168.1.11, 而veth0和veth1的IP分别是192.168.2.11和192.168.2.1。

 

 

Veth

Veth缩写是Virtual ETHernet。veth设备是在linux内核中是成对出现(所以也叫veth-pair),两个设备彼此相连,一个设备从协议栈读取数据后,会将数据发送到另一个设备上去。这个设备其实是专门为container所建的,作用就是把一个network namespace发出的数据包转发到另一个namespace(通常就是宿主机)。

 

 

Bridge

Bridge(桥)是 Linux 上用来做 TCP/IP 二层协议交换的设备,与现实世界中的交换机功能相似。Bridge 设备实例可以和 Linux 上其他网络设备实例连接,既 attach 一个从设备,类似于在现实世界中的交换机和一个用户终端之间连接一根网线。当有数据到达时,Bridge 会根据报文中的 MAC 信息进行广播、转发、丢弃处理。

 

 

 

 

 

2. bridge网络模式

2.1 介绍

bridge模式是docker默认的网络方式,当Docker 进程启动时,会自动在主机上创建一个 docker0 虚拟网桥,默认分配网段172.17.0.0/16,实际上是 Linux 的一个 bridge,可以理解为一个软件交换机,附加在其上的任何网卡之间都能自动转发数据包。

创建一个容器时,容器从docker0中的子网分配一个IP地址(也可以使用--ip指定),并创建一对veth虚拟网络设备,一个设备在容器中作为容器的网卡名叫eth0,另一个设备在宿主机上叫做名称为vethXXX并桥接在docker0上,可通过命令brctl show 查看,通过这样的桥接方法宿主机上的所有容器都处于同一个二层网络中,这样使得容器与容器以及容器与宿主机之间能够互相通信,以下介绍其通信细节。(ps:这里的通信是单主机上容器通讯)

2.2 关于veth

veth 是 Virtual ETHernet 的缩写,是一种虚拟网络设备。它的特点是:当它被创建以后,总是以两张虚拟网卡(Veth peer)形式成对出现,并且在一个网卡上的数据包可直接转发给另一个与之对应的网卡上,即使这两个网卡不在同一个namespace中。

这里推荐一篇文章介绍veth: https://segmentfault.com/a/1190000009251098。

 

 

2.4 容器与外部网络通讯

https://blog.csdn.net/m0_45406092/article/details/118281804?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_baidulandingword~default-0-118281804-blog-122514017.235^v38^pc_relevant_sort&spm=1001.2101.3001.4242.1&utm_relevant_index=3

同样的,介绍了容器与容器间的通讯,容器与主机间的通讯就容易理解了,如果在容器bs1 ping 另一个主机10.168.0.3,它发出去的数据包经过docker0网桥流向eth0,eth0在将数据包转发给与之相通的10.168.0.3,于是bs1就能和其主机节点进行通讯了。当然如果这个地址不是其他主机节点而是公网地址,只要eth0能到达,容器bs1都能与之通讯。以下是其示意图:

 

2.5 宿主机与容器通讯

当宿主机访问容器时,数据包从docker0流入到与容器对应的veth设备,在经容器的eth0到达到主机中,示意图如下:
 

 

2.6 外部访问容器

默认情况,其他外部网络(宿主机以外)无法访问到容器内的端口,通常的做法是使用-p或者-P选项(使用方法参考docker基础篇)来暴露容器中的端口到宿主机上,外部网络通过访问宿主机的端口从而访问到容器端口。

 

2.6 外部访问容器

 

默认情况,其他外部网络(宿主机以外)无法访问到容器内的端口,通常的做法是使用-p或者-P选项(使用方法参考docker基础篇)来暴露容器中的端口到宿主机上,外部网络通过访问宿主机的端口从而访问到容器端口。

 

本质上来说,该方式是通过iptables规则转发实现的,例如以下启动nginx容器并使用宿主机的8080映射到容器内部80端口:

 


查看其对应的iptables规则:

 

 


上述规则表明当访问宿主机的8080端口时,NAT到容器中的172.17.0.4的80中。

 

3. host网络模式

Host模式使用是在容器启动时候指明--network host,此时容器共享宿主机的Network Namespace,容器内启动的端口直接是宿主机的端口,并且容器不会创建网卡和IP,直接使用宿主机的网卡和IP,但是容器内的其他资源是隔离的,如文件系统、用户和用户组。这种模式的好处就是效率高,因为不需要额外的网络开始,直接使用宿主机网络。同样启动一个nginx,此时共享主机网络:

 

问题来了:

  • 如果是容器A内发送消息给宿主
  • 如果是容器A内发送消息给其他容器,比如容器B
  • 如果外部往容器A怎么发送?

4. container网络模式

理解了host模式就很容易理解container模式,host模式是容器共享宿主机的Network Namespace,而container模式即共享已存在的容器的Network Namespace,此时这两容器共同使用同一网卡、主机名、IP地址,容器间通讯可直接通过lo回环接口通讯,但是其他名称空间是隔离的,例如User、Mount。如果你了解kubernetes中的pod的话,pod内的网络也是靠这样的共享方式来实现的。

 

5. none网络模式

使用--network none选项指定其网络模式,在该模式下虽然容器有着自己的Network Namespace,但是容器内没有网卡、IP、路由信息,只有一个lo回环接口。如果需要网络配置则需要用户手动进行配置网卡、ip以及路由信息,通常这样的容器用于承担某些无需网络介入的工作,如离线任务、备份等。启动一个none网络模式的容器如下:

 

 

6. 总结

本文着重对docker 单主机网络中默认bridge网络模式做了较多介绍,其核心是通过docker0网桥+Veth Pair设备实现,而host、container模式都是“共享”方式实现,host网络模式是容器共享宿主机Network Namespace,continer网络模式是容器共享已存在的容器的Network Namespace,none模式则是网络封闭状态,容器内使用lo接口通讯。