docker网络(单机版)
引入
网络命名空间(Network Namespace)
Docker 网络的底层原理是 Linux 的 Network Namespace,所以对于 Linux Network Namespace 的理解对 Docker 网络底层原理的理解非常重要。
简介
Network Namespace 是 Linux 内核提供的用于实现网络虚拟化的重要功能,它能创建多 个隔离的网络空间,每个独立的网络空间内的防火墙、网卡、路由表、邻居表、协议栈都是 独立的。不管是虚拟机还是容器,当运行在独立的命名空间时,就像是一台单独的主机一样。
需求
下面要通过手工方式创建两个 Network Namespace,并最终让它们相互连通,即可以通 过 ping 命令测试成功。以使大家能够理解 Docker 网络的底层原理。
创建两个命名空间
分别创建两个命名空间 ns1 与 ns2
因为每个网络空间都是独立的,所以每个 Network Namespace 都具有一个回环网络适配 器 lo。
创建网络接口 veth pair
如果要让两个命名空间连通,则需要用到虚拟设备接口技术 veth pair。该技术需要一对 网络接口分别置于两个命名空间中。 以下命令用于创建一对网络接口 veth-ns1 与 veth-ns2。
此时通过 ip link 查看当前的网络地址情况,可以看到新增了两个相互连通的 veth pair, 它们都具有 MAC 地址,但它们的状态都是 DOWN,且都不具有 IP。
命名空间添加 veth 接口
通过 ip link set 命令,将这两个网络接口分别分配给两个命名空间。
此时分别在两个命名空间中执行 ip link 命令,可以查看到,它们中分别新增了前面指定 的一个网络接口。
此时再在主机中查看 ip link,发现原来的那两个网络接口已经消失了。
为 veth 接口分配 IP
前面创建的两个网络接口是没有 IP 的。下面要通过 ip netns exec 命令,为每个指定的命 名空间执行 IP 添加命令 ip addr add [ip] dev [网络接口]。
启动 veth 接口
以上两个命名空间中的 veth 接口已经具有了 IP,但其状态仍为 DOWN,还没有开启。 下面要通过 ip link set dev [接口] up 来启动指定的网络接口。
此时再通过 ip a 命令查看两接口的状态,已经变为了 UP。
相互 ping
此时可以通过在两个命名空间中执行 ping 命令来与对方进行连通性测试了
CNM与Libnetwork
CNM
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
每种不同的网络类型都有对应的不同的底层 Driver,这些 Driver 负责在主机上真正实现 需要的网络功能,例如创建 veth pair 设备等。 不过,无论哪种网络类型,其工作方式都是类似的。通过调用 Docker 引擎的 API 发出 请求,然后由 Libnetwork 做出框架性的处理,然后将请求转发给相应的 Driver。 通过 docker network ls 命令可以查看当前主机所连接的网络及网络类型。
bridge
bridge 网络,也称为单机桥接网络,是 Docker 默认的网络模式。该网络模式只能存在 于单个 Docker 主机上,其只能用于连接所在 Docker 主机上的容器。
docker0网桥
bridge 网络模式中具有一个默认的虚拟网桥 docker0,通过 ip a 或 ifconfig 命令都可查看 到。
当然,通过 docker network inspect bridge也可以查看到网络名称为 bridge 的网络的详情。
可以看到该网络的驱动为 bridge,其网桥名称为 docker0。只不过,目前该网络上还没 有连接任何容器。
docker0网桥工作原理
在 Linux 主机上,Docker 的 bridge 网络由 Bridge 驱动创建,其在创建时会创建一个默认 的网桥 docker0。容器与网桥间是通过 veth pair 技术实现的连接,网桥与外网间是通过“网 络地址转换 NAT 技术”实现的连接,即将通信的数据包中的内网地址转换为外网地址。
Bridge 驱动的底层是基于 Linux 内核的 Linux Bridge 技术。该技术已经经历了近 20 年的 考验,这就意味着该模式是高性能且非常稳定的。
查看网络连接详情
查看 bridge 网络整体连接
现在通过 docker network inspect 命令查看当前 bridge 网络的整体连接情况。
在 Containers 中可以查看到当前名称为 bridge 的网络中连接的 bb1 与 bb2 两个容器。这两个容器及宿主机,其实就是三个完全独立的 Network Namespace。
查看宿主机接口
此时在宿主机上通过 ip a 命令查看当前主机的网络接口情况。
发现除了回环地址 lo,本地网卡 ens33,网桥 docker0 外,还有两个 veth 网络接口。这 两个 veth 就是由 Libnetwork 生成的 veth pair 中的宿主机中的 EndPoint。
7: veth161e288@if6 表示这是第 7个接口,其用于连接外部的第 6 个接口
9: veth7b28689@if8 表示这是第 9 个接口,其用于连接外部的第 8 个接口
查看容器接口
在两个容器中分别使用 ip a 命令查看它们的地址情况,可以看到均包含 eth0 的接口。
其中bb1中的接口6,即6eth0@if7,其用于连接宿主机的第7个接口;
bb2的接口8,即eth0@if9:, 其用于连接宿主机的第 9个接口。
它们的接口正好与宿主机中的接口构成两对 pair。
查看网桥连接
使用 brctl show 命令可以查看本机当前所有网桥及其连接情况。可以看到,当前宿主机 中只有一个网桥 docker0,其上连接着两 vethxxx 的接口,就是前面连接 bb1 与 bb2 上两个 eth0 的两个接口
网络创建
创建网络
通过 docker network create 命令可以创建指定名称与类型的网络。
-d 选项用于指定要创建网络时所使用的驱动,即创建的网络类型。最后的 bridge2 则是 新创建网络的名称。
查看宿主机支持网络
此时通过 docker network ls 可查看到新创建的网络。
查看宿主机网桥
通过 brctl show 命令可以查看到新增了一个网桥,只不过该网桥暂时还没有任何连接的 网络接口。该网桥就是在创建新的网络时自动创建的。
创建容器指定网络
创建容器
现在要创建一个新的 BusyBox 容器 bb3,其连接在新建的 bridge2 网络上。
在创建容器时通过--network 指定要连接到的网络,如果不指定,默认连接到默认的 bridge 网络。
查看新建网络详情
查看宿主机网络接口
此时查看当前宿主机的网络接口情况,发现多出了两个接口。其中一个是 10号接口,其 为网桥 br-xxx,一个是 102号接口,其是连接 bb3 的接口 vethxxx@if11
查看新增容器网络接口
此时查看容器 bb3 的网络接口,发现其 9 号接口正好是宿主机 10 号接口的 pair 接口。
查看宿主机网桥
此时再查看网桥情况,发现新增网桥上增加了一个接口,而该接口正好就是宿主机上的 10 号接口。并且,该接口的 IP 为 172.18.xx.xx/16,与 bridge 网络 172.17.xx.xx/16 不是同一 网段,所以它们之间是不能相互通信的。
容器连接到指定网络
连接到指定网络
现在要将容器 bb2 连接到新建的 bridge2 网络上。可以使用 docker network connect 命令
查看两个网络详情
此时查看 bridge2 的网络详情中的容器情况,发现 bb2 与 bb3 都在该网络上。
然后再查看原来的 bridge 网络详情的容器情况,发现 bb2 仍连接在其上。即,bb2 容器 同时连接在了两个网络上
查看容器接口
此时查看 bb2 的网络接口情况,发现其同时具有两个网络接口,分别连接在两个不同的 网络上。
查看容器详情
查看 bb2 容器详情,可以看到其连接在两个网络上,具有两个 IP。
容器互 ping
容器 bb2 与 bb3 互 ping 是可以 ping 通的
但 bb3 要 ping 容器 bb2 的另一网段的 IP 是 ping 不通的。
容器互 ping 容器名
除了可以直接 ping 通指定的 IP 外,还可以直接去 ping 对方的容器名称。
这里出现了问题,猜测是镜像问题换回之前的tomcat解决问题
容器名互通只存在于自定义网络。
创建定向连接容器
对于自定义的 bridge 网络,其具有一个特性:该网络上的容器可以通过容器名互 ping。 但默认的 bridge 网络是不行的。如果在默认的 bridge 网络上实现通过容器名进行的连接, 则需要创建容器时通过--link 选项指定。
但容器 bb4 是无法通过容器名称来连接 bb2 的。然后 bb1 也无法通过容器名称连接 bb4。
所以,--link 指定的连接是一种定向连接,是带有指向性与方向性的。
创建共享网络命名空间容器
在创建容器时可以指定其与某已经存在的容器共享 Network Namespace,但要求该已经 存在的容器采用的是 bridge 网络模式。
上面的命令创建了一个 bb1-1 的容器,其共享了 bb1 容器的 Network Namespace。 查看两个容器的接口情况,发现完全相同。
查看容器 bb1-1 的详情,可以发现,其没有自身的网络设置。因为其共享的 bb1 容器的 网络设置。
none 网络
none 网络,即没有网络。容器仍是一个独立的 Network Namespace,但没有网络接口, 没有 IP。
创建 none 网络容器
在 docker run 命令中,通过--network none 选项指定创建的容器没有网络功能。
查看容器详情
查看容器网络接口
host 网络
host 网络,即与宿主机 host 共用一个 Network Namespace。该网络类型的容器没有独立 的网络空间,没有独立的 IP,全部与 host 共用。
创建 host 网络容器
在 docker run 命令中,通过--network host 选项指定创建的容器为 host 网络。
查看网络详情
通过 docker network inspect host 命令查看网络详情,发现容器 bb6 连接在该网络上,但 容器 bb6 却没有 IP、MAC,并且该网络模式中居然没有网关 Gateway。因为该网络模式实际 相当于没有网络,容器与宿主机共用 Network Namespace,根本就不需要网络连接。
查看 host 与容器网络接口
通过 ip a 与 docker exec bb6 ip a 命令分别查看宿主机与容器 bb6 的网络接口,发现是一 样的。因为它们共用一个 Network Namespace,所以也就共用了所有网络接口。
关于端口映射
由于容器与宿主机共用一个 Network Namespace,所以无论是 IP 还是应用程序的 Port, 容器与宿主机的都是相同的,所以对于容器中应用程序的 Port 不存在映射的问题,host 中 的 Port 与容器中的 Port 相同。
也正因为 host 与容器中的应用使用的是相同的端口号,所以当采用 host 网络模式时, 在一个宿主机中只能启动一个应用的一个容器,否则会出现端口号冲突问题。
总结
Docker 网络理论基础
一个 Network Namespece 就代表一个独立的主机。一个容器就对应一个 Namespece,所 以一个容器就代表了网络中的一个独立主机。
CNM 是规范,Libnetwork 是 CNM 规范的实现,Driver 是 Libnetwork 中不同网络模式的 实现。
bridge 网络
加入网络的容器具有独立的 namespace,具有自己独立的网络接口与 IP。默认的网络模 式,是使用最多的网络模式。
none 网络
加入网络的容器具有独立的 namespace,但其根本就没有连接外网的网络接口,也就不 可能会有 IP 了。
host 网络
加入网络的容器没有自己独立的 namespace,没有自己独立的网络接口与 IP,全部与宿 主机共享。
加入网络的容器无需再暴露端口号了,其端口号直接就在宿主机的 namespce 中
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义