Docker容器网络解析

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/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/535428.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Docker 数据管理介绍

默认容器的数据是保存在容器的可读写层&#xff0c;当容器被删除时其上的数据也会丢失&#xff0c;所以为了实现数据的持久性则需要选择一种数据持久技术来保存数据。官方提供了三种存储方式&#xff1a;Volumes、Bind mounts和tmpfs。前面还介绍了&#xff1a;Docker 服务终端…

Docker 数据持久化的三种方案

容器中的数据可以存储在容器层。但是将数据存放在容器层存在以下问题&#xff1a; 数据不是持久化。意思是如果容器删除了&#xff0c;这些数据也就没了 主机上的其它进程不方便访问这些数据 对这些数据的I/O会经过存储驱动&#xff0c;然后到达主机&#xff0c;引入了一层间…

Git 存储原理及相关实现

Git 是目前最流行的版本控制系统&#xff0c;从本地开发到生产部署&#xff0c;我们每天都在使用 Git 进行我们的版本控制&#xff0c;除了日常使用的命令之外&#xff0c;如果想要对 Git 有更深一步的了解&#xff0c;那么研究下 Git 的底层存储原理将会对理解 Git 及其使用非…

Git内部原理

Git有什么特点&#xff1f; fast&#xff0c;scalable&#xff0c;distributed revision control system&#xff08;快速&#xff0c;可扩展的分布式版本控制系统&#xff09; 几乎所有操作都是本地执行 每一个clone都是整个生命周期的完整副本 the stupid content tracker&a…

详解设计模式:中介者模式

中介者模式&#xff08;Mediator Pattern&#xff09;也被称为调停者模式&#xff0c;是在 GoF 23 种设计模式中定义了的行为型模式。 中介者模式 是用来降低多个对象和类之间的通信复杂性。这种模式提供了一个中介类&#xff0c;该类通常处理不同类之间的通信&#xff0c;并支…

RPC 服务 与 HTTP 服务的区别

1、什么是RPC RPC&#xff08;Remote Procedure Call&#xff09;—远程过程调用&#xff0c;它是一种通过网络从远程计算机程序上请求服务&#xff0c;而不需要了解底层网络技术的协议。RPC协议假定某些传输协议的存在&#xff0c;如TCP或UDP&#xff0c;为通信程序之间携带信…

缓存雪崩、击穿、穿透解决方案

用户的数据一般都是存储于数据库&#xff0c;数据库的数据是落在磁盘上的&#xff0c;磁盘的读写速度可以说是计算机里最慢的硬件了。 当用户的请求&#xff0c;都访问数据库的话&#xff0c;请求数量一上来&#xff0c;数据库很容易就奔溃的了&#xff0c;所以为了避免用户直…

【Docker】容器镜像有哪些特性

首先解释一下什么是Docker镜像&#xff1f; Docker镜像它其实是一个模板&#xff0c;拥有这个模板我们才能创建我们的Docker容器&#xff0c;镜像里含有启动 docker 容器所需的文件系统结构及其内容&#xff0c;因此是启动一个 docker 容器的基础。docker 镜像的文件内容以及一…

nginx中的location指令

1、location 介绍 location是Nginx中的块级指令(block directive)&#xff0c;location指令的功能是用来匹配不同的url请求&#xff0c;进而对请求做不同的处理和响应&#xff0c;这其中较难理解的是多个location的匹配顺序&#xff0c;本文会作为重点来解释和说明。 开始之前…

容器底层实现技术Namespace/Cgroup

Docker容器实现原理 Docker容器在实现上是通过namespace技术来进行进程隔离&#xff0c;通过cgroup技术实现容器进程可用资源的限制&#xff0c;当docker启动一个容器时&#xff0c;实际是创建了多了namespace参数的进程。 Namespace Namespace&#xff1a;命名空间 作用&#…

异方差与多重共线性对回归问题的影响

异方差的检验 1.异方差的画图观察 2.异方差的假设检验&#xff0c;假设检验有两种&#xff0c;一般用怀特检验使用方法在ppt中&#xff0c;课程中也有实验&#xff0c;是一段代码。 异方差的解决办法 多重共线性 多重共线性可能带来的影响&#xff1a; 多重共线性的检验 多重…

如何修改Docker的镜像源

改或新增/etc/docker/daemon.json 文件 vi/etc/docker/daemon.json 添加需要修改的国内镜像源镜像源 { "registry-mirrors":["http://hub-mirror.c.163.com"] } 重启Docker服务 Systemctl restart docker.service 方法二 修改或新增 /etc/sysconfig…

Oracle行转列语法总结大全

一、decode语法 SELECT deptno, nvl(SUM(decode(job, MANAGER, sal)), 0) s_MANAGER, nvl(SUM(decode(job, ANALYST, sal)), 0) s_ANALYST, nvl(SUM(decode(job, CLERK, sal)), 0) s_CLERK, nvl(SUM(decode(job, PRESIDENT, sal)), 0) s_PRESIDENT, …

Nginx的11个执行流程

1 Nginx简介 Web服务器市场份额 Nginx [engine x] 最初由 Lgor Sysoev 编写。根据 Netcraft 的数据&#xff0c;到2020年9月&#xff0c;Nginx 服务或代理了25.76&#xff05;站点&#xff0c;市场份额占到了约34.03&#xff05;。 Nginx 被广泛用作&#xff1a; HTTP服务器…

Nginx的执行阶段详解

在了解nginx的执行阶段前&#xff0c;先看一个例子 对echo不熟悉的&#xff0c;可以先看文章Nginx调试必备了解下echo扩展 回到上面这个例子&#xff0c;在server块中配置这样的location&#xff0c;你觉得输出是什么样子&#xff1f; 按照正常的逻辑&#xff0c;输出应该是32 …

Docker挂了,数据如何找回

docker在实际使用中&#xff0c;让运维人员诟病的&#xff0c;除了安全问题外&#xff0c;大概就是数据的问题了 很多人在初用docker的时候&#xff0c;很多时候都忘记或不知道docker中需要保留的数据需要挂载到宿主机文件夹到容器内部对应目录&#xff08;当然除了挂载宿主机目…

TCP总结

TCP这些东西&#xff0c;基本每个程序猿都或多或少是掌握的了。虽然感觉在实际开发中没有什么用武之处&#xff0c;但&#xff0c;面试他要问啊 而最近大家伙过完年&#xff0c;也都在准备春招&#xff0c;我也一样。阅读了一些okHttp源码之后&#xff0c;又屁颠屁颠地跑回来重…

LDAP组的概念以及命令

Oracle统一目录支持组&#xff0c;组是作为单个对象管理的条目集合。通常&#xff0c;目录管理员配置打印机组、软件应用程序组、员工组等。在为一组用户分配特殊访问权限时&#xff0c;组尤其有用。例如&#xff0c;您可以配置一组访问管理器&#xff0c;并分配权限&#xff0…

oracle中修改process

可以用如下命令查看数据库连接的消耗情况 select b.MACHINE, b.PROGRAM, b.USERNAME, count(*) from v$process a, v$session b where a.ADDR b.PADDR and b.USERNAME is not null group by b.MACHINE, b.PROGRAM, b.USERNAME order by count(*) desc 在 oracle中&…

安装python3.9

GCC版本 这个版本的编译器不适合编译Python3.9&#xff0c;在编译时会产生如下的错误。我们用这个老版本编译器编译一个新的GCC 9.2版。 Could not import runpy module Traceback (most recent call last):File "Python-3.8.1/Lib/runpy.py", line 15, in <mod…