参考文档
网络理论
容器网络实质是在由docker为程序所创建的虚拟环境的一部分,让应用从宿主机操作系统的网络环境独立出来,形成容器自有的网络设备、IP协议栈、端口套接字、IP路由表、防火墙等网络相关模块
Docker 为实现容器网络,主要采用的架构由三部分组成:CNM、Libnetwork 和驱动
CNM
Docker网络架构采用的设计规范是CNM(Container Network Model):CNM 中规定了 Docker 网络的基础组成要素:Sandbox、Endpoint、Network。如图所示
Sandbox,提供了容器的虚拟网络栈,也即端口套接字、IP 路由表、防火墙、DNS 配置等内容。主要用于隔离容器网络与宿主机网络,形成了完全独立的容器网络环境。
Network,Docker 内部的虚拟子网,网络内的参与者相互可见并能够进行通讯。Docker 的虚拟网路和宿主机网络是存在隔离关系的,其目的主要是形成容器间的安全通讯环境。
Endpoint,就是虚拟网络的接口,就像普通网络接口一样,Endpoint 的主要职责是负责创建连接。在 CNM 中,终端负责将沙盒连接到网络。个人理解:Endpoint 与常见的网络适配器类似,也就意味着 Endpoint 只能接入某一个网络。因此,如果容器需要接入到多个网络,就需要多个 Endpoint。
Libnetwork
Libnetwork 是 CNM 的标准实现。Libnetwork 是开源库,采用 Go 语言编写(跨平台的),也是 Docker 所使用的库,Docker 网络架构的核心代码都在这个库中。Libnetwork 实现了 CNM 中定义的全部三个组件,此外它还实现了本地服务发现、基于 Ingress 的容器负载均衡,以及网络控制层和管理层功能。
驱动
如果说 Libnetwork 实现了控制层和管理层功能,那么驱动就负责实现数据层。比如网络的连通性和隔离性是由驱动来处理的。驱动通过实现特定网络类型的方式扩展了 Docker 网络栈,例如桥接网络和覆盖网络。
Docker 内置了若干驱动,通常被称作原生驱动或者本地驱动。比如 Bridge Driver、Host Driver、Overlay Driver、MacLan Driver、None Driver 等等。第三方也可以编写 Docker 网络驱动,这些驱动被叫做远程驱动,例如 Calico、Contiv、Kuryr 以及 Weave 等。每个驱动负责创建其上所有网络资源的创建和管理。
其中 Bridge 和 Overlay 在开发过程中使用频率较高。
Bridge,Docker 容器的默认网络驱动,通过网桥来实现网络通讯。
Overlay,借助 Docker 集群模块 Docker Swarm 搭建的跨 Docker Daemon 网络。通过它可以搭建跨物理网络主机的虚拟网络,进而让不同物理机中运行的容器感知不到多个物理机的存在。
在 Docker 安装时,会自动安装一块 Docker 网卡称为 docker0,用于 Docker 各容器及宿主机的网络通信。
docker0 Link encap:Ethernet HWaddr 02:42:be:6b:61:dc
inet addr:172.17.0.1 Bcast:172.17.255.255 Mask:255.255.0.0
inet6 addr: fe80::42:beff:fe6b:61dc/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:332 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:0 (0.0 B) TX bytes:30787 (30.7 KB)
★个人理解:CNM 就是一个设计文档,指导你怎么去实现容器网络,而 Libnetwork 和驱动则是其具体实现,从而确保容器网络的通信
桥接网络
原理
Docker 的 bridge 网络采用内置的 bridge 驱动,而 bridge 的底层采用的是 Linux 内核中 Linux bridge 技术(这意味着 bridge 是高性能并且是非常稳定的)。
容器运行在自己单独的 network namespace 中,所以有单独的协议栈。容器中配置网关为 172.17.0.1,发出去的数据包先到达 br0,然后交给主机的协议栈,由于目的 IP 是外网 IP,且主机会开启 IP forward 功能,于是数据包通过主机的 eth0 发出去。由于 172.17.0.1 是内网 IP ,所以一般发出去之前会做 NAT 转换。由于要进过主机的协议栈并且要做 NAT 转换,所以性能上可能会差点,但是优点就是容器处于内网中,安全性相对要高点。
备注:
veth(虚拟网桥)和 eth0(网络接口控制器)都是 Linux 网络的组成部分
br0
是 Linux 中用于创建虚拟网桥的一种例子,它是一种逻辑构建物,通过连接来自不同网络接口、命名空间或容器的数据包而实现广泛通讯能力。
- veth:veth 设备主要用于创建网络命名空间(network namespace)中的一对链接,以连接两个不同的网络命名空间内的互联设备。例如,在 Docker 容器之间通信时,可以使用 veth 创建一个两端口通道来充当容器中的网络设备。
- eth0:eth0 是计算机中现实世界的网络适配器,它管理着物理硬件设备的输入/输出并与网络进行通信。 简而言之:
- veth 设备构建于内核内存,并由应用程序通过包括
ip
和ifconfig
命令等工具在用户态中进行配置、操作和删除; - eth0 则是物理硬件设备,由系统引导时自动检测到,然后作为其固有资源初始化,只能受到限制地受到修改或禁用。
- 基本上,一个虚拟桥由
bridge-utils
包提供支持。 使用此软件包可轻松安装和配置虚拟网桥,并允许您控制所有通过其进行的数据流量。以下是使用 vim 创建 br0 的常规步骤:
$ sudo apt-get install bridge-utils
# 不带任何扩展参数调用 ip 命令并将结果输出到终端:
$ ip addr
# 在全局范围内为网桥(例如 br0)分配 IP 地址
$ vim /etc/network/interfaces
auto eth0
iface eth0 inet manual
auto eth1
iface eth1 inet manual
# Add the following at the end of your configuration file to create the virtual bridge within Ubuntu networking:
auto br0
iface br0 inet dhcp
bridge_ports eth0 eth1
$ ifup br0
示例
默认情况下,创建的容器在没有使用 --network 参数指定要加入的 docker 网络时,默认都是加入 Docker 默认的单机桥接网络,也就是下面的 name 为 bridge 的网络。
⚡ root@daixin-dev-machine ~ docker network ls
NETWORK ID NAME DRIVER SCOPE
75b2cfd39ae0 bridge bridge local
e16f511866fc dataplatform_default bridge local
3cf756a7e6b6 dicta_default bridge local
287854308d9a host host local
54b8cbf5ebea none null local
0265d21b3c56 storage_default bridge local
默认的 bridge 网络是被映射到内核中为 docker0 的网桥上。
⚡ root@daixin-dev-machine ~ ip link show docker0
4: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN mode DEFAULT group default
link/ether 02:42:bc:c7:32:d1 brd ff:ff:ff:ff:ff:ff
⚡ root@daixin-dev-machine ~ docker network inspect bridge|grep bridge.name
"com.docker.network.bridge.name": "docker0",
Docker 默认的 bridge 网络和 Linux 内核中的 “docker0” 网桥是一个对应关系,如图所示。bridge 是 Docker 中对网络的命名,而 docker0 是内核中网桥的名字。(个人理解:你就可以把 bridge 和 docker0 当成 Linux 网桥的两个名字,两个都是代表同一个东西。docker 为了管理网络,又给 docker0 这个网桥取名为 bridge)。
创建单机桥接网络
红色就是代表的是网桥和网段
同一网络内容器通信
docker container run -d --name demo1 --network localnet alpine sleep 3600
1. 在这个容器中直接 ping demo1 发现可以的 ping 通的。这是因为,demo2 运行了一个本地 DNS 解析器,该解析器会将该请求转发到 Docker 内部 DNS 服务器中。DNS 服务器中记录了容器启动时通过 --name 或者 --net-alias 参数指定的名称和容器之间的和映射关系。
2. ★Docker 默认的 bridge 网络是不支持通过 Docker DNS 服务进行[域名解析]
端口
端口暴露
同一个网络中的容器之间虽然可以互相 ping 通,但是并不意味着可以任意访问容器中的任何服务。Docker 为容器增加了一套安全机制,只有容器自身允许的端口,才能被其他容器所访问。如下所示,我们可以通过 docker container ls
命令可以看到容器暴露给其他容器访问的端口是 80,那么我们只能容器的 80 端口进行访问,而不能对没有开放的 22 端口进行访问。
我们可以在镜像创建的时候定义要暴露的端口,也可以在容器创建时定义要暴露的端口,使用 --expose
$ docker container run -d --name web --expose 22 --expose 20 nginx
$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
4749dac32711 nginx "/docker-entrypoint.…" 12 seconds ago Up 10 seconds 20/tcp, 22/tcp, 80/tcp web
端口映射
上面提到的桥接网络中的容器只能与位于相同网络中的容器进行通信,假如一个容器想对外提供服务的话,需要进行端口映射。端口映射将容器的某个端口映射到 Docker 主机端口上。那么任何发送到该端口的流量,都会被转发到容器中。
评论区