Docker—7章(Docker网络)


Docker 网络

1. Docker 网络理论基础

Docker 网络中的相关命令非常少,但需要掌握的底层原理相对较多。

1.1 Network Namespace

Docker 网络的底层原理是 Linux Network Namespace,所以对于 Linux Network Namespace 的理解对 Docker 网络底层原理的理解非常重要。

1.1.1 简介

Network NamespaceLinux 内核提供的用于实现网络虚拟化的重要功能,它能创建多个隔离的网络空间,每个独立的网络空间内的防火墙、网卡、路由表、邻居表、协议栈都是独立的。不管是虚拟机还是容器,当运行在独立的命名空间时,就像是一台单独的主机一样。

1.1.2 需求

下面要通过手工方式创建两个 Network Namespace,并最终让它们相互连通,即可以通过 ping 命令测试成功。以使大家能够理解 Docker 网络的底层原理。

1.1.3 创建两个命名空间

分别创建两个命名空间 ns1ns2

因为每个网络空间都是独立的,所以每个Network Namespace都具有一个回环网络适配器 lo

1.1.4 创建网络接口 veth pair

如果要让两个命名空间连通,则需要用到虚拟设备接口技术 veth pair。该技术需要一对网络接口分别置于两个命名空间中。

以下命令用于创建一对网络接口 veth-ns1 veth-ns2

此时通过 ip link 查看当前的网络地址情况,可以看到新增了两个相互连通的 veth pair,它们都具有 MAC 地址,但它们的状态都是 DOWN,且都不具有 IP

1.1.5 命名空间添加 veth 接口

通过 ip link set 命令,将这两个网络接口分别分配给两个命名空间。

此时分别在两个命名空间中执行 ip link 命令,可以查看到,它们中分别新增了前面指定的一个网络接口

此时再在主机中查看 ip link,发现原来的那两个网络接口已经消失了。

1.1.6 为 veth 接口分配 IP

前面创建的两个网络接口是没有 IP 的。下面要通过 ip netns exec 命令,为每个指定的命名空间执行IP添加命令 ip addr add [ip] dev [网络接口]

ns1 veth-ns1 网络接口分配的 IP 192.168.1.1,掩码为 24;为 ns2 veth-ns2 网络接口分配的 IP192.168.1.2,掩码为 24

此时通过在 ns1 ns2 中运行 ip a 命令,便可看到为接口分配的 IP 了。

1.1.7 启动 veth 接口

以上两个命名空间中的 veth 接口已经具有了 IP,但其状态仍为 DOWN,还没有开启。下面要通过 ip link set dev [接口] up 来启动指定的网络接口。

此时再通过 ip a 命令查看两接口的状态,已经变为了 UP

1.1.8 相互 ping

此时可以通过在两个命名空间中执行 ping 命令来与对方进行连通性测试了。

1.2 CNM

Docker 网络架构由三个主要部分构成:CNMLibnetwork Driver

CNMContainer Network Model,容器网络模型,其是一种网络连接的解决方案,是一种设计规范、设计标准,其规定了 Docker 网络的基础组成要素。

CNM 中定义了三个基本要素:沙盒 Sandbox,终端 Endpoint 与网络 Network

  • 沙盒:一个独立的网络栈,其中包括以太网接口、端口号、路由表、DNS 配置等。Linux Network Namespace 是沙盒的标准实现。
  • 终端:虚拟网络接口,主要负责创建连接,即将沙盒连接到网络上。一个终端只能接入某一个网络。
  • 网络:802.1d 网桥的软件实现,是需要交互的终端的集合。

1.3 Libnetwork

CNM 是设计规范,而 Libnetwork 是开源的、由 Go 语言编写的、跨平台的 CNM 的标准实现。

Libnetwork 除了实现了 CNM 的三个组件,还实现了本地服务发现、容器负载均衡,以及网络控制层与管理层功能。

1.4 Driver

每种不同的网络类型都有对应的不同的底层 Driver,这些 Driver 负责在主机上真正实现需要的网络功能,例如创建 veth pair 设备等。

不过,无论哪种网络类型,其工作方式都是类似的。通过调用 Docker 引擎的 API 发出请求,然后由 Libnetwork 做出框架性的处理,然后将请求转发给相应的 Driver

通过 docker network ls 命令可以查看当前主机所连接的网络及网络类型。

2. bridge 网络

bridge 网络,也称为单机桥接网络,是 Docker 默认的网络模式。该网络模式只能存在于单个 Docker 主机上,其只能用于连接所在 Docker 主机上的容器。

2.1 docker0网桥

2.1.1 查看 docker0 网桥

bridge 网络模式中具有一个默认的虚拟网桥 docker0,通过 ip aifconfig 命令都可查看到。

当然,通过 docker network inspect bridge 也可以查看到网络名称为 bridge 的网络的详情

可以看到该网络的驱动为 bridge,其网桥名称为 docker0。只不过,目前该网络上还没有连接任何容器。

2.1.2 docker0 网桥工作原理

Linux 主机上,Docker bridge 网络由 Bridge 驱动创建,其在创建时会创建一个默认的网桥 docker0。容器与网桥间是通过 veth pair 技术实现的连接,网桥与外网间是通过“网络地址转换 NAT 技术”实现的连接,即将通信的数据包中的内网地址转换为外网地址

Bridge 驱动的底层是基于 Linux 内核的 Linux Bridge 技术。该技术已经经历了近 20 年的考验,这就意味着该模式是高性能且非常稳定的。

2.2 查看网络连接详情

2.2.1 查看 bridge 网络整体连接

现在通过 docker network inspect 命令查看当前 bridge 网络的整体连接情况。

Containers 中可以查看到当前名称为 bridge 的网络中连接的 myubuntu2myubuntumyvols三个容器。这三个容器及宿主机,其实就是四个完全独立的 Network Namespace

2.2.2 查看宿主机接口

此时在宿主机上通过 ip a 命令查看当前主机的网络接口情况。

发现除了回环地址 lo,本地网卡 ens33,网桥 docker0 外,还有两个 veth 网络接口。这两个 veth 就是由 Libnetwork 生成的 veth pair 中的宿主机中的 EndPoint

  • 9: vethxxx@if8 表示这是第 9 个接口,其用于连接外部的第 8 个接口
  • 27: vethxxx@if26 表示这是第27 个接口,其用于连接外部的第 26 个接口

2.2.3 查看容器接口

出现这个错误表示容器中没有IP这个命令

此时你需要先安装iproute2来解决问题

#首先进入容器
docker exec -it  bash/sh
#更新软件包列表
apt-get update

如果出现下面错误:

bash: apt-get: command not found

你可以使用yum来安装

yum install -y iproute

安装完毕就可以使用了

在这个容器中使用ip a命令查看它的地址情况,可以看到包含 eth0 的接口。其中 myvols中的接口 18,即 eth0@if19,其用于连接宿主机的第 19个接口

2.2.4 查看网桥连接

这里要使用一个专门用于网桥控制管理的命令 brctl。由于该命令默认在 Linux 中没有安装,所以需要首先安装一个网桥的工具包 bridge-utils

yum -y install bridge-utils

使用brctl show命令可以查看本机当前所有网桥及其连接情况。可以看到,当前宿主机中只有一个网桥 docker0,其上连接着 vethxxx 的接口,就是前面连接 myvolseth0 的接口。

brctl show

2.2.5 查看容器详情

通过 docker inspect 查看容器的详情,可以看到,其网络连接中的网关 Geteway IP 地址就是 docker0 网桥的地址。

2.3 网络创建

理解图

2.3.1 创建网络

通过 docker network create 命令可以创建指定名称与类型的网络。

-d 选项用于指定要创建网络时所使用的驱动,即创建的网络类型。最后的 bridge2 则是新创建网络的名称。

2.3.2 查看宿主机支持网络

此时通过 docker network ls 可查看到新创建的网络。

2.3.3 查看宿主机网桥

通过 brctl show 命令可以查看到新增了一个网桥,只不过该网桥暂时还没有任何连接的网络接口。该网桥就是在创建新的网络时自动创建的

2.4 创建容器指定网络

2.4.1 创建容器

现在要创建一个新的 myvols容器 myvols1,其连接在新建的 bridge2 网络上。

在创建容器时通过--network 指定要连接到的网络,如果不指定,默认连接到默认bridge 网络。

2.4.2 查看新建网络详情

此时查看 bridge2 的网络详情,可以看到容器 myvols1 已经连接到了上面。

2.4.3 查看宿主机网络接口

此时查看当前宿主机的网络接口情况,发现多出了两个接口。其中一个是 20 号接口,其为网桥 br-xxx,一个是 30号接口,其是连接 myvols1的接口 vethxxx@if29。

2.4.4 查看新增容器网络接口

此时查看容器 myvols1的网络接口,发现其 29 号接口正好是宿主机 30 号接口的 pair 接口

2.4.5 查看宿主机网桥

此时再查看网桥情况,发现新增网桥上增加了一个接口,而该接口正好就是宿主机上的30号接口。并且,该接口的 IP 为 172.18.xx.xx/16,与 bridge 网络 172.17.xx.xx/16 不是同一网段,所以它们之间是不能相互通信的。

2.5 容器连接到指定网络

2.5.1 连接到指定网络

现在要将容器 myvols连接到新建的 bridge2 网络上。可以使用 docker network connect 命令。

2.5.2 查看两个网络详情

此时查看 bridge2 的网络详情中的容器情况,发现 myvolsmyvols1都在该网络上

然后再查看原来的 bridge 网络详情的容器情况,发现 myvols 仍连接在其上。即,myvols容器同时连接在了两个网络上。

2.5.3 查看容器接口

此时查看 myvols的网络接口情况,发现其同时具有两个网络接口,分别连接在两个不同的网络上。

2.5.4 查看容器详情

查看 myvols容器详情,可以看到其连接在两个网络上,具有两个 IP。

2.5.5 容器互 ping

容器 myvolsmyvols1ping 是可以 ping 通的。

myvols1要 ping 容器 myvols的另一网段的 IP 是 ping 不通的。

2.5.6 容器互 ping 容器名

除了可以直接 ping 通指定的 IP 外,还可以直接去 ping 对方的容器名称

该方式在生产中非常重要。因为生产中容器的 IP 可能会发生变化,但容器名称一般是不会变的。如果某服务总是直接通过 IP 与容器相连接,那么一旦容器 IP 变化,则该服务将连接不上容器。但如果是通过容器名称相连接的,那么无论容器 IP 如何变化,都将不影响服务与容器的连接。

2.5.7 创建定向连接容器

对于自定义bridge 网络,其具有一个特性:该网络上的容器可以通过容器名ping。但默认的 bridge 网络是不行的。如果在默认的 bridge 网络上实现通过容器名进行的连接,则需要创建容器时通过--link 选项指定。

注意我这里的有一个镜像和容器都是myvols名称;这里的--link myvols后面接的容器名称 ,而后的myvols是镜像名称

此时容器 myvols2直接通过 myvol的容器名称就是连接上 myvol

但容器 myvols2是无法通过容器名称来连接 myvols1的。然后 myvols也无法通过容器名称连接 myvols2

所以,--link 指定的连接是一种定向连接,是带有指向性与方向性的。

2.5.8 创建共享网络命名空间容器

在创建容器时可以指定其与某已经存在的容器共享 Network Namespace,但要求该已经存在的容器采用的是 bridge 网络模式。

上面的命令创建了一个myvols-1 的容器,其共享了 myvols容器的 Network Namespace。查看两个容器的接口情况,发现完全相同。

查看容器 myvols-1 的详情,可以发现,其没有自身的网络设置。因为其共享的 myvols容器的网络设置。

3. none 网络

none 网络,即没有网络。容器仍是一个独立的 Network Namespace,但没有网络接口,没有 IP。

3.1 创建none网络容器

docker run 命令中,通过--network none 选项指定创建的容器没有网络功能。

通过 docker inspect 命令查看该容器的详情,发现其没有 IP,没有网关,没有 MAC 地址。

3.2 查看容器网络接口

通过ip a命令查看容器的网络接口,发现其只有一个回环地址 lo,没有其它接口。

4. host 网络

host 网络,即与宿主机 host 共用一个 Network Namespace。该网络类型的容器没有独立的网络空间,没有独立的 IP,全部与 host 共用。

4.1 创建host网络容器

docker run 命令中,通过--network host 选项指定创建的容器为 host 网络。

4.2 查看网络详情

通过 docker network inspect host 命令查看网络详情,发现容器 myvols4连接在该网络上,但容器 myvols4却没有 IP、MAC,并且该网络模式中居然没有网关 Gateway。因为该网络模式实际相当于没有网络,容器与宿主机共用 Network Namespace,根本就不需要网络连接。

4.3 查看host与容器网络接口

通过 ip adocker exec myvols4 ip a 命令分别查看宿主机与容器 myvols4 的网络接口,发现是一样的。因为它们共用一个 Network Namespace,所以也就共用了所有网络接口。

4.4 关于端口映射7.4.4

由于容器与宿主机共用一个 Network Namespace,所以无论是 IP 还是应用程序的 Port,容器与宿主机的都是相同的,所以对于容器中应用程序的 Port 不存在映射的问题,host 中的 Port 与容器中的 Port 相同。

上面的 tomcat 容器由于指定了网络模式为 host,在启动时指定的端口映射不会起作用。系统给出的 WARNING 指出,当使用 host 网络模式时,已发布的端口号被丢弃。

此时,通过仍需通过 8080 端口访问。

也正因为 host 与容器中的应用使用的是相同的端口号,所以当采用 host 网络模式时,在一个宿主机中只能启动一个应用的一个容器,否则会出现端口号冲突问题。

5. 总结

5.1 Docker网络理论基础

一个 Network Namespece 就代表一个独立的主机。一个容器就对应一个 Namespece,所以一个容器就代表了网络中的一个独立主机。

CNM 是规范,Libnetwork 是 CNM 规范的实现,Driver Libnetwork 中不同网络模式的实现。

5.2 bridge网络

加入网络的容器具有独立的 namespace,具有自己独立的网络接口与 IP。默认的网络模式,是使用最多的网络模式。

5.3 none网络

加入网络的容器具有独立的 namespace,但其根本就没有连接外网的网络接口,也就不可能会有 IP 了。

5.4 host网络

加入网络的容器没有自己独立的 namespace,没有自己独立的网络接口与 IP,全部与宿主机共享。


文章作者: 念心卓
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 念心卓 !
  目录