type
status
date
slug
summary
tags
category
icon
password

1、基础知识

在介绍 Docker 的网络之前,我们先介绍一些 Linux 的网络基础知识,这些是构建 Docker 网络的基石。
  • network namespace:Docker 实现网络隔离的基础。
  • veth pair:一对虚拟网卡。
  • Linux bridge:网桥,类似于虚拟交换机的作用。

1.1 network namespace

前面已经介绍过 Linux 的 namespace 技术,他的作用是用于内核资源隔离。全局资源被 namespace 分割到一个个抽象的独立空间里面,处于 namespace 里面的进程有两个错觉:1)它是系统里唯一的进程;2)它独享系统的所有资源。
namespace 有六种不同类型,network namespace 是其中一种类型。它在 Linux 内核 2.6 版本引入,作用是隔离 Linux 系统的设备,以及 IP 地址、端口、路由表、防火墙规则等网络资源。因此,每个 network namespace 里都有自己的网络设备(如 IP 地址、路由表、端口范围、 /proc/net 目录等)。
从网络的角度看,network namespace 使得容器非常有用,一个直观的例子就是:由于每个容器都有自己的(虚拟)网络设备,并且容器里的进程可以放心地绑定在端口上而不必担心冲突,这就使得在一个主机上同时运行多个监听 80 端口的 Web 服务器变为可能。
network namespace 的增删改查功能已经集成到 Linux 的 ip 工具的 netns 子命令中。下面先介绍几条简单的网络 namespace 管理的命令。
1、创建一个名为 netns1 的network namespace。当 ip 命令创建了一个 network namespace 时,系统会在 /var/run/netns 路径下面生成一个挂载点。挂载点的作用一方面是方便对 namespace 的管理,另一方面是使 namespace 即使没有进程运行也能继续存在。
2、查看系统中有哪些 network namespace。
3、删除 network namespace。但实际上并没有删除 netns1 这个 network namespace,它只是移除了这个 network namespace 对应的挂载点。只要里面还有进程运行着,network namespace 便会一直存在。
4、进入 network namespace,做一些网络查询/配置的工作
5、实现 netns1 与主机上的网卡通信。仅有一个本地回环设备是没法与外界通信的。如果我们想与外界(比如主机上的网卡)进行通信,就需要在 namespace 里再创建一对虚拟的以太网卡,即所谓的 veth pair(下面会介绍)。

1.2 veth pair

在安装了 Docker 或 Kubernetes 在主机上输入 ifconfigip addr 命令查询网卡信息的时候,总会出来一大堆 eth0vethxxxx之类的网卡名。eth 开头的是物理网卡名,而 veth 开头的是 Docker/Kubernetes 为容器而创建的虚拟网卡。
veth 是虚拟以太网卡(Virtual Ethernet)的缩写。veth 设备总是成对的,因此我们称之为 veth pair。veth pair 设备的原理较简单,就是向 veth pair 设备的一端输入数据,数据通过内核协议栈后从 veth pair 的另一端出来。根据这一特性,veth pair 常被用于跨 network namespace 之间的通信,即分别将 veth pair 的两端放在不同的 namespace 里
notion image
下面是 veth pair 的一些常用命令

1.3 Linux bridge

两个 network namespace 可以通过 veth pair 连接,但要做到两个以上 network namespace 相互连接,veth pair 就显得捉襟见肘了,这就需要 Linux bridge 了。Linux bridge 类似于一台虚拟的网络交换机,任意的真实物理设备(例如 eth0)和虚拟设备( veth pairtap设备)都可以连接到 Linux bridge 上。Linux bridge 有多个端口,数据可以从任何端口进来,进来之后从哪个口出去取决于目的 MAC 地址
下面介绍一些 Linux bridge 的基本命令。Linux bridge 可以使用 iproute2 软件包的 ip 工具管理,也可以使用 bridge-utils 软件包的 brctl 工具管理。
1、创建 Linux bridge。刚创建一个 bridge 时,它是一个独立的网络设备,只有一个端口连着协议栈,其他端口什么都没连接,这样的 bridge 其实没有任何实际功。
notion image
2、把 veth pair 连接到 bridge 上。假设已经创建了一对 veth pair(veth0 和 veth1),并且都是已激活的状态。
3、查看当前网桥及连接的网络设备

2、Docker的网络类型

从网络的角度看容器,就是 network namespace + 容器的组网方案。利用 network namespace,可以为 Docker 容器创建隔离的网络环境。容器具有完全独立的网络栈,与宿主机隔离。用户也可以让 Docker 容器共享主机或者其他容器的 network namespace
我们在使用 docker run 命令创建 Docker 容器时,可以使用 --network 选项指定容器的网络模式。Docker 有以下 4 种网络模式:
  • bridge 模式,通过 --network=bridge 指定。
  • host 模式,通过 --network=bridge指定。
  • container 模式,通过 --network=bridge 指定。
  • none 模式,通过 --network=bridge 指定。
安装 Docker 以后,Docker Daemon 会在宿主机上自动创建三个网络,分别是 bridge 网络、host 网络和 none 网络,可以通过 docker network ls 查看。

2.1 bridge模式

Docker 的默认网络模式。Docker 安装时会创建一个命名为 docker0 的 Linux 网桥。如果创建容器的时候不指定 --network,Docker 会为每一个容器分配network namespace、设置IP等,并将 Docker 容器连接到 docker0 网桥上。严谨的表述是,创建的容器的 veth pair 中的一端桥接到 docker0 上。我们可以通过 brctl 命令查看该网桥信息。
notion image
当前 docker0 上没有任何其他网络设备,我们创建一个容器看看有什么变化,如图所示。
notion image
一个新的网络接口 veth28c57df 被挂到了 docker0 上,veth28c57df 就是新创建容器的虚拟网卡。下面看一下容器的网络配置。
notion image
实际上 eth0@if34veth28c57df 是一对 veth pair。veth pair 是守护进程会创建的一对对等的虚拟设备接口,可以把它们想象成由一根虚拟网线连接起来的一对网卡,网卡的一头( eth0@if34)在容器中,另一头(veth28c57df)挂在网桥 docker0 上,其效果就是将 eth0@if34 也挂在了 docker0 上
我们还看到 eth0@if34 已经配置了IP 172.17.0.2。为什么是这个 ip 地址呢?让我们通过 docker network inspect bridge 看一下 bridge 网络的配置信息。
notion image
在默认情况下,docker0 的 IP 地址均为 172.17.0.1(除非在 Docker Daemon 启动时自行配置),而接到 docker0 上的 Docker 容器的 IP 地址范围是 172.17.0.0/16
  • 守护进程还会从地址段中 172.17.0.0/16 分配一个 IP 地址和子网给该容器,第一个连接到 docker0 上的容器被分配地址 172.17.0.2,第二个连接到 docker0 上的容器被分配地址 172.17.0.3,以此类推。
  • 连接在 docker0 上的所有容器的默认网关均为 docker0,即默认网关地址都为 172.17.0.1,访问非本机容器网段要经过 docker0 网关转发。
总结一下,Bridge 桥接模式的实现步骤主要如下:
notion image
  • Docker Daemon 利用 veth pair 技术,在宿主机上创建一对对等虚拟网络接口设备,假设为 veth0veth1。veth pair 技术的特性可以保证无论哪一个 veth 接收到网络报文,都会将报文传输给另一方。
  • Docker Daemon 将 veth0 附加到 Docker Daemon 创建的 docker0 网桥上。保证宿主机的网络报文可以发往 veth0;
  • Docker Daemon 将 veth1 添加到 Docker Container 所属的 namespace 下,并被改名为 eth0。如此一来,宿主机的网络报文若发往 veth0,则立即会被 Container 的 eth0 接收,实现宿主机到 Docker Container 网络的联通性;同时,也保证 Docker Container 单独使用 eth0,实现容器网络环境的隔离性。
bridge 模式为 Docker 容器创建独立的网络栈,保证容器内的进程使用独立的网络环境,使容器和容器、容器和宿主机之间能实现网络隔离。

2.2 host模式

连接到 host 网络的容器共享 Docker host 的网络栈,容器的网络配置与 host 完全一样。host 模式下容器将不会获得独立的 network namespace,而是和宿主机共用一个 network namespace。容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的 IP 和端口。
host 模式下的容器可以看到宿主机的所有网卡信息,甚至可以直接使用宿主机 IP 地址和主机名与外界通信,无须额外进行 NAT,也无须通过 Linux bridge 进行转发或者进行数据包的封装。原理如下所示:
notion image
创建容器时通过参数 --net host 或者 --network host 指定使用 host 网络。如下所示,在 alpine 容器中可以看到 host 的所有网卡,并且连 hostname 也是 host 的。
notion image
直接使用 Docker host 的网络最大的好处就是性能,如果容器对网络传输效率有较高要求,则可以选择 host 网络。Docker host 的另一个用途是让容器可以直接配置 host 网路,比如某些跨 host 的网络解决方案,其本身也是以容器方式运行的,这些方案需要对网络进行配置,比如管理 iptables.
缺点也很明显:
  • 容器没有隔离、独立的网络栈:容器因与宿主机共用网络栈而争抢网络资源,并且容器崩溃也可能使主机崩溃,导致网络的隔离性不好。
  • 端口资源冲突:宿主机上已经使用的端口就不能再用了。

2.3 container模式

在创建容器时通过参数 --net container:NAME_or_ID 或者 --network container:NAME_or_ID 指定容器的网络和一个已经存在的容器共享一个 network namespace,这样新创建的容器不会创建自己的网卡,配置自己的 IP,而是和一个指定的容器共享 IP、端口范围等,这样两个容器之间可以使用 localhost 高效快速通信。但两个容器除了网络方面相同之外,其他的如文件系统、进程列表等还是隔离的。
notion image
处于 Container 网络模式下的容器,如果所依赖的容器停止运行了,本容器就只剩下 lo 接口了。Kubernetes 的 Pod 网络采用的就是 Docker 的 container 模式网络。

2.4 none模式

none 模式下的容器只有 lo 回环网络,没有其他网卡。也就是说,这个 Docker 容器没有网卡、IP、路由等信息,这种类型的网络没有办法联网,属于完全封闭的网络。
在创建容器时通过参数 --net none 或者 --network none 指定使用 none 网络。
notion image
这样一个封闭的网络有什么用呢?封闭意味着隔离,一些对安全性要求高并且不需要联网的应用可以使用 none 网络。比如某个容器的唯一用途是生成随机密码,就可以放到 none 网络中避免密码被窃取。

3、容器间的通信

容器之间可通过 IP、Docker DNS Server 或 joined 容器三种方式通信。
docker run --link 可以用来链接两个容器,使得源容器(被链接的容器)和接收容器(主动去链接的容器)之间可以互相通信,并且接收容器可以获取源容器的一些数据,如源容器的环境变量。这种方式官方已不推荐使用,并且在未来版本可能会被移除。

3.1 IP通信

处于同一网络下的两个容器可以直接通过 IP 地址进行通信。具体做法是在容器创建时通过 --network 指定相应的网络,或者通过 docker network connect 将现有容器加入到指定网络。
但是 IP 地址可能是不固定的,有被更改的情况发生,那容器内所有通信的 IP 地址也需要进行更改。

3.2 Docker DNS Server和自定义网络

从 Docker 1.10 版本开始,Docker daemon 实现了一个内嵌的 DNS server,使容器可以直接通过容器名称通信。方法很简单,只要在创建容器时使用 –name 为容器命名即可。
但是使用 Docker DNS 有个限制:只能在 user-defined 网络中使用。也就是说,默认的 bridge 网络是无法使用 DNS 的
为了启用容器名称到 IP 地址的自动 DNS 解析,以及提高容器的安全性,我们就需要自定义网络。

3.2.1 创建网络

创建一个基于 bridge 网络模式的自定义网络模式 custom_network。
查看网络模式,确认生成 custom_network 模式。

3.2.2 使用自定义网络创建容器

这时候进入容器 custom_bbox01,直接执行
通过容器名字已经可以 ping 通。

3.2.3 加入自定义网络模式

已经存在的容器 custom_bbox03,可以加入自定义网络模式 custom_network

3.2.4 断开自定义网络模式

custom_bbox03也可以退出自定义网络模式

3.2.5 删除自定义网络模式

注意:如果通过某个自定义网络模式创建了容器,则该网络模式无法删除。

3.3 joined容器

joined 容器非常特别,它可以使两个或多个容器共享一个网络栈,共享网卡和配置信息,joined 容器之间可以通过 127.0.0.1 直接通信。
先创建一个httpd容器,名字为web1。
然后创建 busybox 容器并通过 --network=container:web1 指定 joined 容器为 web1。
notion image
这样 busybox 和 web1 的网卡 mac 地址与 IP 完全一样,它们共享了相同的网络栈。busybox 可以直接用 127.0.0.1 访问 web1 的 http 服务。
joined 容器非常适合以下场景:
  1. 不同容器中的程序希望通过 loopback 高效快速地通信,比如 Web Server 与 App Server。
  1. 希望监控其他容器的网络流量,比如运行在独立容器中的网络监控程序。

4、将容器与外部世界连接

前面我们已经解决了容器间通信的问题,接下来讨论容器如何与外部世界通信。这里涉及两个方向:
  1. 容器访问外部世界。
  1. 外部世界访问容器。

4.1 容器访问外部世界

容器默认就能访问外网。

4.2 外部世界访问容器

下面我们来讨论另一个方向:外网如何访问到容器?答案是:端口映射。
docker 可将容器对外提供服务的端口映射到 host 的某个端口,外网通过该端口访问容器。容器启动时通过 -p 参数映射端口。
notion image
容器启动后,可通过 docker ps 或者 docker port 查看到 host 映射的端口。在上面的例子中,httpd 容器的 80 端口被映射到 host 32773上,这样就可以通过 <host ip>:<32773> 访问容器的 Web 服务了。
每一个映射的端口,host 都会启动一个 docker-proxy 进程来处理访问容器的流量,如图所示。
notion image
Docker系列:Docker存储Docker系列:Docker容器的底层技术
Loading...