docker网络(单机版)

发布时间 2023-03-30 15:32:51作者: 我是一个大废物

引入

image-20230308114006250

image-20230308114051967

网络命名空间(Network Namespace)

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

简介

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

需求

image-20230322091610544

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

创建两个命名空间

分别创建两个命名空间 ns1 与 ns2

image-20230322092615435

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

image-20230322093345674

创建网络接口 veth pair

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

image-20230322093649928

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

image-20230322093758346

命名空间添加 veth 接口

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

image-20230322094252212

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

image-20230322094512598

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

image-20230322094643864

为 veth 接口分配 IP

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

image-20230322095347831

image-20230322095635864

启动 veth 接口

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

image-20230322100044423

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

image-20230322100020618

相互 ping

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

image-20230322100310292

CNM与Libnetwork

CNM

image-20230322103752912

Docker 网络架构由三个主要部分构成:CNM、Libnetwork 与 Driver。

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

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

 沙盒:一个独立的网络栈,其中包括以太网接口、端口号、路由表、DNS 配置等。Linux Network Namespace 是沙盒的标准实现。

 终端:虚拟网络接口,主要负责创建连接,即将沙盒连接到网络上。一个终端只能接入 某一个网络。

 网络:802.1d 网桥的软件实现,是需要交互的终端的集合。

Libnetwork

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

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

Driver

image-20230322111129577

每种不同的网络类型都有对应的不同的底层 Driver,这些 Driver 负责在主机上真正实现 需要的网络功能,例如创建 veth pair 设备等。 不过,无论哪种网络类型,其工作方式都是类似的。通过调用 Docker 引擎的 API 发出 请求,然后由 Libnetwork 做出框架性的处理,然后将请求转发给相应的 Driver。 通过 docker network ls 命令可以查看当前主机所连接的网络及网络类型。

image-20230322111207734

bridge

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

docker0网桥

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

image-20230322140108690

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

image-20230322140302002

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

docker0网桥工作原理

image-20230322140433136

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

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

查看网络连接详情

查看 bridge 网络整体连接

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

image-20230322143500088

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

查看宿主机接口

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

image-20230322143825538

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

 7: veth161e288@if6 表示这是第 7个接口,其用于连接外部的第 6 个接口

 9: veth7b28689@if8 表示这是第 9 个接口,其用于连接外部的第 8 个接口

查看容器接口

image-20230322145548915

在两个容器中分别使用 ip a 命令查看它们的地址情况,可以看到均包含 eth0 的接口。

其中bb1中的接口6,即6eth0@if7,其用于连接宿主机的第7个接口;

bb2的接口8,即eth0@if9:, 其用于连接宿主机的第 9个接口。

它们的接口正好与宿主机中的接口构成两对 pair。

查看网桥连接

image-20230322150358465

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

网络创建

image-20230322151920549

创建网络

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

image-20230322152636130

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

查看宿主机支持网络

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

image-20230322152856230

查看宿主机网桥

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

image-20230322153007906

创建容器指定网络

创建容器

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

image-20230322161035455

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

查看新建网络详情

image-20230322161225285

查看宿主机网络接口

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

image-20230322161619009

查看新增容器网络接口

此时查看容器 bb3 的网络接口,发现其 9 号接口正好是宿主机 10 号接口的 pair 接口。

image-20230322161807802

查看宿主机网桥

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

image-20230322161907866

容器连接到指定网络

连接到指定网络

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

image-20230322162453883

查看两个网络详情

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

image-20230322162704227

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

image-20230322162801293

查看容器接口

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

image-20230322162958551

查看容器详情

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

image-20230322163116529

image-20230322163057631

容器互 ping

容器 bb2 与 bb3 互 ping 是可以 ping 通的

image-20230322172213671

但 bb3 要 ping 容器 bb2 的另一网段的 IP 是 ping 不通的。

image-20230322172422152

image-20230322172451906

容器互 ping 容器名

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

这里出现了问题,猜测是镜像问题换回之前的tomcat解决问题

容器名互通只存在于自定义网络。

image-20230323094107894

创建定向连接容器

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

image-20230323095211233

但容器 bb4 是无法通过容器名称来连接 bb2 的。然后 bb1 也无法通过容器名称连接 bb4。

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

image-20230323095258872

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

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

image-20230323101810446

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

image-20230323101931568

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

image-20230323102358853

image-20230323102343416

none 网络

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

创建 none 网络容器

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

image-20230323103213067

查看容器详情

image-20230323103358126

image-20230323103334848

查看容器网络接口

image-20230323103508618

host 网络

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

创建 host 网络容器

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

image-20230323103737418

查看网络详情

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

image-20230323103900894

查看 host 与容器网络接口

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

image-20230323104559248

关于端口映射

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

image-20230323110630600

image-20230323110653584

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

总结

Docker 网络理论基础

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

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

bridge 网络

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

none 网络

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

host 网络

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

加入网络的容器无需再暴露端口号了,其端口号直接就在宿主机的 namespce 中