Docker 容器网络的发展历史

在 Dokcer 发布之初,Docker 是将网络、管理、安全等集成在一起的,其中网络模块可以为容器提供桥接网络、主机网络等简单的网络功能。

从 1.7 版本开始,Docker正是把网络和存储这两部分的功能都以插件化形式剥离出来,允许用户通过指令来选择不同的后端实现。剥离出来的独立容器网络项目叫 libnetwork。

在 1.9 版本时,Docker 又引入了一整套 network 子命令和跨主机网络支持,这允许用户可以根据他们应用的拓扑结构创建虚拟网络并将容器接入其所对应的网络。

什么是 Docker Libnetwork

为了标准化网络的驱动开发步骤和支持多种网络驱动,Docker 将网络部分代码被抽离成为了单独的网络库(Libnetwork),Libnetwork 提供了可以用于开发多种网络驱动的标准化接口和组件。

Docker daemon 通过调用 Libnetwork 对外提供的 API 完成网络的创建和管理等功能,Libnetwork 内置了5种驱动来提供不同类型的网络: bridge driver, host driver, null driver, overlay driver, remote driver

bridge driver

此驱动为Docker的默认设置驱动,使用这个驱动的时候,libnetwork将创建出来的Docker容器连接到Docker网桥上。作为最常规的模式,bridge模式已经可以满足Docker容器最基本的使用需求了。然而其与外界通信使用NAT,增加了通信的复杂性,在复杂场景下使用会有诸多限制。

host driver

使用这种驱动的时候,libnetwork将不为Docker容器创建网络协议栈,即不会创建独立的network namespace。Docker容器中的进程处于宿主机的网络环境中,相当于Docker容器和宿主机共同用一个network namespace,使用宿主机的网卡、IP和端口等信息。

但是,容器其他方面,如文件系统、进程列表等还是和宿主机隔离的。host模式很好地解决了容器与外界通信的地址转换问题,可以直接使用宿主机的IP进行通信,不存在虚拟化网络带来的额外性能负担。但是host驱动也降低了容器与容器之间、容器与宿主机之间网络层面的隔离性,引起网络资源的竞争与冲突。

因此可以认为host驱动适用于对于容器集群规模不大的场景。

null driver

使用这种驱动的时候,Docker容器拥有自己的network namespace,但是并不为Docker容器进行任何网络配置。也就是说,这个Docker容器除了network namespace自带的loopback网卡名,没有其他任何网卡、IP、路由等信息,需要用户为Docker容器添加网卡、配置IP等。

这种模式如果不进行特定的配置是无法正常使用的,但是优点也非常明显,它给了用户最大的自由度来自定义容器的网络环境。

overlay driver

此驱动采用IETE标准的VXLAN方式,并且是VXLAN中被普遍认为最适合大规模的云计算虚拟化环境的SDN controller模式。在使用过程中,需要一个额外的配置存储服务,例如Consul、etcd和zookeeper。还需要在启动Docker daemon的时候额外添加参数来指定所使用的配置存储服务地址。

remote Driver

这个驱动实际上并未做真正的网络服务实现,而是调用了用户自行实现的网络驱动插件,使libnetwork实现了驱动的可插件化,更好地满足了用户的多种需求。用户只需要根据libnetwork提供的协议标准,实现其所要求的各个接口并向Docker daemon进行注册。

什么是 Docker CNM

Docker Libnetwork 中使用了 CNM 的容器网络模式概念,CNM定义了构建容器虚拟化网络的模型,此后容器网络模式也被抽象变成了统一接口的驱动。

CNM 中主要有 sandbox、endpoint 和 network 3 种核心组件CNM 中核心组件的使用模型如下图:

沙盒 (sandbox):一个沙盒包含了一个容器网络栈的信息。沙盒可以对容器的接口(interface)、路由和 DNS 设置等进行管理,沙盒的实现可以是Linux netns、FreeBSD Jail 或者类似的机制,一个沙盒可以有多个端点和多个网络。

端点 (endpoint):一个端点可以加入一个沙盒和一个网络。端点的实现可以是 veth pair、ovs 内部端口或者相似的设备,一个端点只属于一个网络并且只属于一个沙盒。

网络 (network):一个网络是一组可以直接互相联通的端点。网络的实现可以是 Linux bridge、VLAN等,一个网络可以包含多个端点。

Libnetwork Remote driver

kuryr-libnetwork 是 Libnetwork 框架下的一种 remote driver 实现,现在已经成为Docker 官网推荐的一个 remote driver,kuryr-libnetwork 需要做的就是实现 Libnetwork remote driver 需要实现的接口.

常见的 remote driver 要实现的接口如下,格式:HTTP POST + JSON Body

/Plugin.Activate no payload      -- Handshake
/NetworkDriver.GetCapabilities   -- Set capability

/NetworkDriver.DiscoverNew    -- DiscoverNew Notification

/NetworkDriver.DiscoverDelete   -- DiscoverDelete Notification

/NetworkDriver.AllocateNetwork  -- Allocate network specific resources, only called in docker swarm mode

/NetworkDriver.FreeNetwork     -- Free network specific resources, only called in docker swarm mode

/NetworkDriver.CreateNetwork   -- Create network

/NetworkDriver.DeleteNetwork   -- Delete network

/NetworkDriver.CreateEndpoint   -- Create endpoint

/NetworkDriver.EndpointOperInfo -- Endpoint operational info

/NetworkDriver.DeleteEndpoint   -- Delete endpoint

/NetworkDriver.Join            -- Join an endpoint to a sandbox

/NetworkDriver.Leave          -- Remove an endpoint from a sandbox

IPAM Driver

在 Libnetwork 中,CNM 模块通过 IPAM Driver 管理 IP 地址的分配,Libnetwork 内含有一个默认的IPAM驱动,同时它也允许动态地增加第三方IPAM驱动。

在用户创建网络时可以指定 Libnetwork 使用的 IPAM 驱动, Kuryr 项目通过实现了 IPAM 的驱动接口,成为了Docker 的第三方 libnetwork IPAM driver。

常见的 IPAM driver 要实现的接口如下,格式:HTTP POST + JSON Body

/IpamDriver.GetCapabilities   -- provides the IPAM driver capabilities. it's called during the registration of the IPAM driver.

/IpamDriver.GetDefaultAddressSpaces   -- returns the default local and global address space names for this IPAM. it's called after the registration of the IPAM driver

/IpamDriver.RequestPool     -- registering an address pool with the IPAM driver. multiple identical calls must return the same result.

/IpamDriver.RequestAddress   -- allocates the IP address

/IpamDriver.ReleaseAddress   -- deallocates the IP address

/IpamDriver.ReleasePool      -- releasing a previously registered address pool

Docker 网络的生命周期

Docker 用户可以通过与 CNM 的 Object 以及 API 的交互来管理对应容器的网络,下面是一个典型的容器网络生命周期:

1、Driver要向NetworkController注册。内置的Driver在Libnetwork内注册,远程的Driver则通过Plugin mechanism注册。每一个Driver处理特定的networkType。

2、libnetwork.New():NetworkController通过libnetwork.New()创建,用于Network的创建以及通过一些特定的Options配置Driver。

3、controller.NewNetwork():Network通过给这个API提供name和networkType来创建,networkType参数用来选择特定的Driver并且将创建的Network和该Driver相关联。从此以后,对于Network的任何操作都由Driver处理。controller.NewNetwork() 还有一个可选的options参数,用于提供特定Driver的options和Labels。

4、network.CreateEndpoint():可以用于在给定的Network中创建一个新的Endpoint。同时该API还有一个可选的options参数供Driver使用。这个"options"既可以携带已知的labels,也可以携带和特定Driver相关的labels。之后调用相应的Driver的driver.CreateEndpoint,它可以为在一个Endpoint在Network中被创建时,为它们保留IP地址。Driver会通过driverapi中定义的InterfaceInfo进行这些地址的赋值。IP地址将和endpoint暴露的端口用来完善Endpoint作为Service的定义。事实上,Service endpoint不是其他什么东西,仅仅只是一个网络地址以及该应用的容器监听的端口号。

5、endpoint.Join():用于将Endpoint与一个容器相连接。Join操作会先创建一个Sandbox如果对应的容器中还没有的话。Driver可以使用Sandbox Key来识别连接到同一个容器的多个Endpoint。这个API同样接受可选的options参数供Driver使用。

- 虽然这并不是Libnetwork直接的设计要求,但是我们鼓励像Docker这样的用户在执行容器的Start()操作时,即在容器可以操作之前,调用endpoint.Join()。

- 另一个关于endpoint.join()这个API经常被提到的问题是,为什么我们需要一个API创建Endpoint和另一个API来join endpoint。事实上Endpoint代表的是一个Service,它可能有,也可能并没有容器支持。当一个Endpoint被创建的时候,会预留它所需的资源,因此任何容器都能连接该Endpoint并且获得一个一致的网络行为。

6、endpoint.Leave():会在容器停止的时候被调用。Driver可以清除它在调用Join()时获取的状态。Libnetwork会在最后一个Endpoint离开的时候删除Sandbox。但是只要该Endpoint依旧存在,Libnetwork会依然保有IP地址并且在有新的容器加入的时候进行重用。这保证了容器的资源在停止并重启的过程中能够重用。

7、endpoint.Delete():用于从一个Network中删除Endpoint。这将导致Endpoint的删除以及清空缓存的sandbox.Info。

8、network.Delete():用于删除Network。如果还有Endpoint连接到该网络,Libnetwork是不允许对它进行删除的。

Docker 网络命名空间

docker 常常使用 linux netns 实现网络资源隔离,但使用 ip netns 命令却无法查看,这是因为 docker 默认把创建的网络命名空间链接文件隐藏起来了,导致 ip netns 命令无法读取,可以通过下面的方法复现 docker 的 ip netns 命名空间。

# 创建一个带有桥接网络的 docker 容器

$ docker run -it -d --rm --name mytest --network bridge cirros /bin/sh

c093857c756028b4d4f37b16262d017239236bde22a3545f8769fd17366f183a

$ docker ps | grep mytest

c093857c7560    cirros   "/bin/sh"     6 seconds ago    Up 2 seconds   mytest

# 可以通过 inspect 命令查看该容器的 ip 地址和进程号

$ docker inspect mytest |egrep '"IPAddress"|"Pid"'

"Pid": 14908,

"IPAddress": "172.17.0.2",

# 通过进程号参考容器进程

$ ps -fp 14908

UID    PID  PPID  C STIME  CMD

root 14889  1676  0 11:42  containerd-shim -namespace moby \

-workdir

/var/lib/containerd/io.containerd.runtime.v1.linux/moby/c093857c756028b4d4f37b16262d017239236bde22a3545f8769fd17366f183a \

-address /run/containerd/containerd.sock \

-containerd-binary /usr/bin/containerd \

-runtime-root /var/run/docker/runtime-runc

# 通过 nsenter 进入容器网络空间

$ nsenter --target 14908 --net ip a

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN

link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00

inet 127.0.0.1/8 scope host lo

valid_lft forever preferred_lft forever

54: eth0@if55: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP

link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0

inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0

valid_lft forever preferred_lft forever

# 通过软连接容器命名空间实现在 ip netns 下显示

$ ls /proc/14908/ns/net

lrwxrwxrwx 1 root root 0 Jul 25 11:42 /proc/14908/ns/net -> net:[4026532445]

$ ln -s /proc/14908/ns/net /var/run/netns/mytest

# 最后检查一下

$ ip netns

mytest (id: 1)

$ ip netns exec mytest ip a

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN

link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00

inet 127.0.0.1/8 scope host lo

valid_lft forever preferred_lft forever

54: eth0@if55: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP

link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0

inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0

valid_lft forever preferred_lft forever

Docker 主机名与DNS 

一个镜像可以启动多个容器,但是它们的主机名和网络信息并不一样,也即是说主机名和网络信息并非是被写入镜像中的。实际上容器中/etc/目录下有三个文件是容器启动后被虚拟文件覆盖的,分别是/etc/hostname、/etc/hosts、/etc/resolv.conf。对这三个文件的修改不会被docker commit保存,也就是不会保存在镜像中,重启容器也会导致修改失效。

$ docker exec -it mytest mount | grep etc

/dev/mapper/centos-root on /etc/resolv.conf type xfs (rw,relatime,attr2,inode64,noquota)

/dev/mapper/centos-root on /etc/hostname type xfs (rw,relatime,attr2,inode64,noquota)

/dev/mapper/centos-root on /etc/hosts type xfs (rw,relatime,attr2,inode64,noquota)

- 这样能解决主机名的问题,同时也能让DNS及时更新(改变resolv.conf)。

- 由于这些文件的维护方法随着Docker版本演进而不断变化,因此尽量不修改这些文件,而是通过Docker提供的参数进行相关设置。

参考资料

https://github.com/docker/libnetwork/blob/master/docs/design.md

http://dockone.io/article/1306

https://www.oreilly.com/library/view/learning-docker-networking/9781785280955/

https://feisky.gitbooks.io/sdn/container/cnm/

https://www.nuagenetworks.net/blog/container-networking-standards/

Docker容器网络解析相关推荐

  1. Docker容器网络

    Docker的技术依赖于Linux内核的虚拟化技术的发展,Docker使用到的网络技术有Network Namespace.Veth设备对.Iptables/Netfilter.网桥.路由等.接下来, ...

  2. 手工模拟实现 Docker 容器网络!

    大家好,我是飞哥! 如今服务器虚拟化技术已经发展到了深水区.现在业界已经有很多公司都迁移到容器上了.我们的开发写出来的代码大概率是要运行在容器上的.因此深刻理解容器网络的工作原理非常的重要.只有这样将 ...

  3. docker容器网络及其配置

    docker容器网络及其配置 文章目录 docker容器网络及其配置 虚拟化网络 单主机与多主机的Docker网络 单节点容器间通信 不同节点容器间通信 Docker网络驱动 选择Docker网络驱动 ...

  4. Docker容器网络实例管理

    Docker容器网络实例管理 Linux内核实现名称空间的创建 ip netns命令 可以借助ip netns命令来完成对 Network Namespace 的各种操作.ip netns命令来自于i ...

  5. 解锁TrueNAS SCALE 三方docker容器网络访问限制,默认是关闭的;并添加Portainer-CE容器webGUI管理器

    解锁TrueNAS SCALE 三方docker容器网络访问限制,默认是关闭的:并添加Portainer-CE容器webGUI管理器 本教程环境版本:TrueNAS-SCALE-21.06-BETA. ...

  6. docker容器网络 - 同一个host下的容器间通信

    2019独角兽企业重金招聘Python工程师标准>>> 对于复杂的应用,不可避免需要多个服务部署在多个容器中,并且服务间存在相互间通信的情况.比如服务A需要连接mysql的容器.本文 ...

  7. Docker 容器网络访问原理,SNAT和DNAT

    docker使用教程相关系列 目录 目录 容器网络访问原理图 网络访问的过程图 DOCKER SNAT与DNAT 容器访问外部实现 外部访问容器实现 容器网络访问原理图 首先我们会有这个 宿主机命名空 ...

  8. Kubernetes Docker 容器网络终极之战(十四)

    与 Docker 默认的网络模型不同,Kubernetes 形成了一套自己的网络模型,该网络模型更加适应传统的网络模式,应用能够平滑的从非容器环境迁移到 Kubernetes 环境中. 自从 Dock ...

  9. 初学Docker容器网络不得不看的学习笔记

    [技术沙龙002期]数据中台:宜信敏捷数据中台建设实践|宜信技术沙龙 将于5月23日晚8点线上直播,点击报名 一.关于Docker Docker 是一个开源的应用容器引擎,基于 Go 语言 并遵从Ap ...

最新文章

  1. http在链接中加入用户名_爬虫基础——HTTP基本原理
  2. Zerodium以100万美元求Tor浏览器0day漏洞以转售给政府
  3. 史上最强翻译器,没有之一,不接受反驳
  4. 技术干货 | 为高音质保驾护航 - 通信中的回声消除
  5. activity中指定一页面动态设置软键盘弹出属性
  6. mvc模式缺点 php,mvc模式有哪些优缺点
  7. java 英文分词器使用代码_java文本英文分词
  8. 堪称奇迹!8 天诞生一个产品,这家创业公司做到了
  9. 快速开发框架工作笔记001---项目开发中整理_整合好的_springcloud快速开发框架_springcloud框架_springcloud架构
  10. 表格列数太多 页面怎么设计_B端产品设计规范分享
  11. 服务器图纸被自动删除,JavaWeb项目图片消失的原因之一————服务器上图片目录被误删...
  12. RMAN Crosscheck 和 Delete 命令的2个实例
  13. 程序化(量化)交易怎样选择服务器
  14. jk背带是什么意思_JK 制服和 LO 装 (科普向)
  15. latex 定理环境,引理,定义,自定义 proof 环境
  16. fullpage初使用
  17. IPv6地址基础理论讲解
  18. 量化人才之战如何取胜
  19. ASEMI快恢复二极管SFP3006和瞬态二极管一样吗?SFP3006和TVS能否代换
  20. Windows10系统安装软件后软件界面异常(变得很小或很大),界面显示不完全的解决方案

热门文章

  1. python终端指令大全_使用python模拟命令行终端的示例
  2. 修改服务器时间报错,修改服务器时间linux
  3. 福建学业水平测试计算机考点大纲,福建高中信息技术学业水平考试说明大纲
  4. 计算机对环境的影响的案例,计算机环境下的呈现方式对学生样例学习的影响
  5. lammps计算聚合物例子_LAMMPS模拟聚合物结构,非晶态聚合物变形行为的模拟,纳米线变形模拟,单轴张力模拟,晶格参数计算...
  6. php是走什么协议,TCP是什么协议
  7. java监控rabbitMq服务状态,spring cloud 的监控turbine-rabbitmq的示例
  8. 毕业就业推荐表计算机,毕业生就业推荐表
  9. 公钥 私钥_区块链中私钥、公钥和钱包地址三者关系
  10. cmd c语言 图形,CMD-C彩图隐写方案