博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
关于docker容器网络的一些理解
阅读量:6411 次
发布时间:2019-06-23

本文共 10679 字,大约阅读时间需要 35 分钟。

这里写图片描述
打开微信扫一扫,关注微信公众号【数据与算法联盟】
转载请注明出处:
博主微博:
Github:


参考资料

1:

2:
3:
4:
5:


写在前边的话

      突然发现好久没有更新博客了,像我这种频繁发表博客的人竟然也会放慢了更新的速度,其实不是说自己不去写,不去更新,只是不愿意去将就,去发表一些让别人看了没有多大帮助的文章,作为2017年的开篇博客,我想和你们一起学习下docker容器网络的知识,首先声明,以下内容大部分都是来源网络,按照我对docker网络的理解,整理的一篇文章,一起学习。


docker容器网络概述

1:默认网络

      在默认情况下会看到三个网络,它们是Docker Deamon进程创建的。它们实际上分别对应了Docker过去的三种『网络模式』,可以使用docker network ls来查看

master@ubuntu:~$ sudo docker network lsNETWORK ID          NAME                DRIVER              SCOPE18d934794c74        bridge              bridge              localf7a7b763f013        host                host                local697354257ae3        none                null                local

      这 3 个网络包含在 Docker 实现中。运行一个容器时,可以使用 the –net标志指定您希望在哪个网络上运行该容器。您仍然可以使用这 3 个网络。

  • bridge 网络表示所有 Docker 安装中都存在的 docker0 网络。除非使用 docker run –net=选项另行指定,否则 Docker 守护进程默认情况下会将容器连接到此网络。在主机上使用 ifconfig命令,可以看到此网桥是主机的网络堆栈的一部分。
  • none 网络在一个特定于容器的网络堆栈上添加了一个容器。该容器缺少网络接口。
  • host 网络在主机网络堆栈上添加一个容器。您可以发现,容器中的网络配置与主机相同。

2:自定义网络

      当然你也可以自定义网络来更好的隔离容器,Docker 提供了一些默认网络驱动程序来创建这些网络。您可以创建一个新 bridge 网络或覆盖一个网络。也可以创建一个网络插件或远程网络并写入您自己的规范中。您可以创建多个网络。可以将容器添加到多个网络。容器仅能在网络内通信,不能跨网络进行通信。一个连接到两个网络的容器可与每个网络中的成员容器进行通信。当一个容器连接到多个网络时,外部连接通过第一个(按词典顺序)非内部网络提供。

(1):docker network 命令

执行 sudo docker network –help

master@ubuntu:~$ sudo docker network --helpUsage:  docker network COMMANDManage Docker networksOptions:      --help   Print usageCommands:  connect     Connect a container to a network  create      Create a network  disconnect  Disconnect a container from a network  inspect     Display detailed information on one or more networks  ls          List networks  rm          Remove one or more networksRun 'docker network COMMAND --help' for more information on a command.

(2):创建test-network网络

执行命令: sudo docker network create test-network

查看: sudo docker network ls

master@ubuntu:~$ sudo docker network lsNETWORK ID          NAME                DRIVER              SCOPE18d934794c74        bridge              bridge              localf7a7b763f013        host                host                local697354257ae3        none                null                localc4f6d347c8b4        test-network        bridge              local

查看自己创建的网络的信息

master@ubuntu:~$ sudo docker network inspect test-network[    {        "Name": "test-network",        "Id": "c4f6d347c8b47471b97e1b5621dd2e90aff303bb7db632db86b0bbec6ffb91d4",        "Scope": "local",        "Driver": "bridge",        "EnableIPv6": false,        "IPAM": {            "Driver": "default",            "Options": {},            "Config": [                {                    "Subnet": "172.18.0.0/16",                    "Gateway": "172.18.0.1/16"                }            ]        },        "Internal": false,        "Containers": {},        "Options": {},        "Labels": {}    }]

另外,还可以采用其他一些选项,比如 –subnet、–gateway和 –ip-range。

(3):启动容器连接到test-network

sudo docker run -itd –name=test –net=test-network lt:1.0 /bin/bash

master@ubuntu:~$ sudo docker run -itd --name=test  --net=test-network lt:1.0 /bin/bash09a9d7a9c37d691e0fc0f7cfdf3c9470b77f410592f9bf624fe90bff2b17e315

再次查看信息,可以看到挂载的容器

master@ubuntu:~$ sudo docker network inspect test-network[    {        "Name": "test-network",        "Id": "c4f6d347c8b47471b97e1b5621dd2e90aff303bb7db632db86b0bbec6ffb91d4",        "Scope": "local",        "Driver": "bridge",        "EnableIPv6": false,        "IPAM": {            "Driver": "default",            "Options": {},            "Config": [                {                    "Subnet": "172.18.0.0/16",                    "Gateway": "172.18.0.1/16"                }            ]        },        "Internal": false,        "Containers": {            "09a9d7a9c37d691e0fc0f7cfdf3c9470b77f410592f9bf624fe90bff2b17e315": {                "Name": "test",                "EndpointID": "7d1d57418a8ebde2d5404f37e27de68be41a917979fd74f394bc5fccc2601e08",                "MacAddress": "02:42:ac:12:00:02",                "IPv4Address": "172.18.0.2/16",                "IPv6Address": ""            }        },        "Options": {},        "Labels": {}    }]

当然也可以动态的将容器挂载到某个网络上

master@ubuntu:~$ sudo docker run -itd --name=test1 lt:1.0 /bin/bashb2aba703c5180819542d26e7bda784774ca87b896e8df612dd1b727218dde334master@ubuntu:~$ sudo docker network connect test-network test1master@ubuntu:~$ sudo docker network inspect test-network[    {        "Name": "test-network",        "Id": "c4f6d347c8b47471b97e1b5621dd2e90aff303bb7db632db86b0bbec6ffb91d4",        "Scope": "local",        "Driver": "bridge",        "EnableIPv6": false,        "IPAM": {            "Driver": "default",            "Options": {},            "Config": [                {                    "Subnet": "172.18.0.0/16",                    "Gateway": "172.18.0.1/16"                }            ]        },        "Internal": false,        "Containers": {            "09a9d7a9c37d691e0fc0f7cfdf3c9470b77f410592f9bf624fe90bff2b17e315": {                "Name": "test",                "EndpointID": "7d1d57418a8ebde2d5404f37e27de68be41a917979fd74f394bc5fccc2601e08",                "MacAddress": "02:42:ac:12:00:02",                "IPv4Address": "172.18.0.2/16",                "IPv6Address": ""            },            "b2aba703c5180819542d26e7bda784774ca87b896e8df612dd1b727218dde334": {                "Name": "test1",                "EndpointID": "5d18b876b62312698d4ce253e33b52552c9a3b1237bed90ec565d4cbd9f5710d",                "MacAddress": "02:42:ac:12:00:03",                "IPv4Address": "172.18.0.3/16",                "IPv6Address": ""            }        },        "Options": {},        "Labels": {}    }]

“石器时代”的容器网络模型

      目前对于刚起步接触docker容器的筒子们(当然也包括我),大部分使用网络的方式应该是这样的,把需要暴漏的端口做端口映射(docker run -itd -p 81:80 …),例如一个主机内有很多Apache容器,每一个Apache要往外抛80的端口,那我怎么办?我需要针对第一个容器和主机80端口做映射,第二个和主机81端口做映射,依此类推,到最后发现非常混乱,没办法管理。这样的容器网络模型对于企业来说是基本没办法被采用。

这里写图片描述

      这是石器时代网络模型,它是Docker1.9之前的容器网络,实现方式是只针对单台主机进行IPAM管理,所有主机上的容器都会连接到主机内部的一个Linux Bridge,叫Docker0,主机的IP它默认会分配172.17网段中的一个IP,因为有Docker0,所以在一个主机上的容器可以实现互联互通。但是因为IP分配的范围是基于单主机的,所以你会发现在其他主机上,也会出现完全相同的IP地址。很明显,这两个地址肯定没办法直接通信。为了解决这个问题,在石器时代我们会用端口映射,实际上就是NAT的方法。比如说我有一个应用,它有Web和Mysql,分别在不同的主机上,Web需要去访问Mysql,我们会把这个Mysql的3306端口映射到主机上的3306这个端口,然后这个服务实际上是去访问主机IP 10.10.10.3 的3306端口,这是过去的石器时代的一个做法。

      总结一下它的典型技术特征:基于单主机的IPAM;主机之内容器通讯实际上通过一个docker0的Linux Bridge;如果服务想要暴露到外部的话需要做NAT,会导致端口争抢非常严重;当然它有一个好处,对大网IP消耗比较少。


docker容器的单主机通信

      docker容器的单主机通信使用的是容器互联技术,即 –link,映射网络端口不是吧container彼此连接起来的唯一方法。Docker的linking系统允许你吧多个 container连接起来, 让他们彼此交互信息。Docker的linking会创建一种父子级别的关系。 父container可以看到他的子container提供的信息。

创建一个数据库容器:

sudo docker run -d –name db training/postgres

然后创建一个新的 web 容器,并将它连接到 db 容器

sudo docker run -d -P –name web –link db:db training/webapp python app.py

此时,db 容器和 web 容器建立互联关系。

–link 参数的格式为 –link name:alias,其中 name 是要链接的容器的名称,alias 是这个连接的别名。


docker容器的跨主机通信

      早期大家的跨主机通信方案主要有以下几种:

  • 容器使用host模式:容器直接使用宿主机的网络,这样天生就可以支持跨主机通信。虽然可以解决跨主机通信问题,但这种方式应用场景很有限,容易出现端口冲突,也无法做到隔离网络环境,一个容器崩溃很可能引起整个宿主机的崩溃。
  • 端口绑定:通过绑定容器端口到宿主机端口,跨主机通信时,使用主机IP+端口的方式访问容器中的服务。显而易见,这种方式仅能支持网络栈的四层及以上的应用,并且容器与宿主机紧耦合,很难灵活的处理,可扩展性不佳。
  • docker外定制容器网络:在容器通过docker创建完成后,然后再通过修改容器的网络命名空间来定义容器网络。典型的就是很久以前的pipework,容器以none模式创建,pipework通过进入容器的网络命名空间为容器重新配置网络,这样容器网络可以是静态IP、vxlan网络等各种方式,非常灵活,容器启动的一段时间内会没有IP,明显无法在大规模场景下使用,只能在实验室中测试使用。
  • 第三方SDN定义容器网络:使用Open vSwitch或Flannel等第三方SDN工具,为容器构建可以跨主机通信的网络环境。这些方案一般要求各个主机上的docker0网桥的cidr不同,以避免出现IP冲突的问题,限制了容器在宿主机上的可获取IP范围。并且在容器需要对集群外提供服务时,需要比较复杂的配置,对部署实施人员的网络技能要求比较高。

      上面这些方案有各种各样的缺陷,同时也因为跨主机通信的迫切需求,docker 1.9版本时,官方提出了基于vxlan的overlay网络实现,原生支持容器的跨主机通信。同时,还支持通过libnetwork的plugin机制扩展各种第三方实现,从而以不同的方式实现跨主机通信。就目前社区比较流行的方案来说,跨主机通信的基本实现方案有以下几种:

  • 基于隧道的overlay网络:按隧道类型来说,不同的公司或者组织有不同的实现方案。docker原生的overlay网络就是基于vxlan隧道实现的。ovn则需要通过geneve或者stt隧道来实现的。flannel最新版本也开始默认基于vxlan实现overlay网络。
  • 基于包封装的overlay网络:基于UDP封装等数据包包装方式,在docker集群上实现跨主机网络。典型实现方案有weave、flannel的早期版本。
  • 基于三层实现SDN网络:基于三层协议和路由,直接在三层上实现跨主机网络,并且通过iptables实现网络的安全隔离。典型的方案为Project Calico。同时对不支持三层路由的环境,Project Calico还提供了基于IPIP封装的跨主机网络实现。

docker容器的CNI模型和CNM模型

      目前围绕着docker的网络,目前有两种比较主流的声音,docker主导的Container network model(CNM)和社区主导的Container network interface(CNI)。

1:CNI

(1) 概述

      Container Networking Interface(CNI)提供了一种linux的应用容器的插件化网络解决方案。最初是由rkt Networking Proposal发展而来。也就是说,CNI本身并不完全针对docker的容器,而是提供一种普适的容器网络解决方案。因此他的模型只涉及两个概念:

      容器(container) : 容器是拥有独立linux网络命名空间的独立单元。比如rkt/docker创建出来的容器。

      这里很关键的是容器需要拥有自己的linux网络命名空间。这也是加入网络的必要条件。

      网络(network): 网络指代了可以相互联系的一组实体。这些实体拥有各自独立唯一的ip。这些实体可以是容器,是物理机,或者其他网络设备(比如路由器)等。

(2) 接口及实现

      CNI的接口设计的非常简洁,只有两个接口ADD/DELETE。

      以 ADD接口为例

      Add container to network

      参数主要包括:

  • Version. CNI版本号
  • Container ID. 这是一个可选的参数,提供容器的id
  • Network namespace path. 容器的命名空间的路径,比如 /proc/[pid]/ns/net。
  • Network configuration. 这是一个json的文档,具体可以参看network-configuration
  • Extra arguments. 其他参数
  • Name of the interface inside the container. 容器内的网卡名

      返回值:

  • IPs assigned to the interface. ipv4或者ipv6地址
  • DNS information. DNS相关信息

2:CNM

      相较于CNI,CNM是docker公司力推的网络模型。其主要模型如下图:

这里写图片描述

Sandbox

      Sandbox包含了一个容器的网络栈。包括了管理容器的网卡,路由表以及DNS设置。一种Sandbox的实现是通过linux的网络命名空间,一个FreeBSD Jail 或者其他类似的概念。一个Sandbox可以包含多个endpoints。

Endpoint

      一个endpoint将Sandbox连接到network上。一个endpoint的实现可以通过veth pair,Open vSwitch internal port 或者其他的方式。一个endpoint只能属于一个network,也只能属于一个sandbox。

Network

      一个network是一组可以相互通信的endpoints组成。一个network的实现可以是linux bridge,vlan或者其他方式。一个网络中可以包含很多个endpoints。

接口

      CNM的接口相较于CNI模型,较为复杂。其提供了remote plugin的方式,进行插件化开发。remote plugin相较与CNI的命令行,更加友好一些,是通过http请求进行的。remote plugin监听一个指定的端口,docker daemon直接通过这个端口与remote plugin进行交互。

鉴于CNM的接口较多,这里就不一一展开解释了。这里主要介绍下在进行docker的操作中,docker daemon是如何同CNM插件繁盛交互。

调用过程

  • Create Network

      这一系列调用发生在使用docker network create的过程中。

      /IpamDriver.RequestPool: 创建subnetpool用于分配IP

      /IpamDriver.RequestAddress: 为gateway获取IP
      /NetworkDriver.CreateNetwork: 创建neutron network和subnet

  • Create Container

      这一系列调用发生在使用docker run,创建一个contain的过程中。当然,也可以通过docker network connect触发。

      /IpamDriver.RequestAddress: 为容器获取IP

      /NetworkDriver.CreateEndpoint: 创建neutron port
      /NetworkDriver.Join: 为容器和port绑定
      /NetworkDriver.ProgramExternalConnectivity:
      /NetworkDriver.EndpointOperInfo

  • Delete Container

      这一系列调用发生在使用docker delete,删除一个contain的过程中。当然,也可以通过docker network disconnect触发。

      /NetworkDriver.RevokeExternalConnectivity

      /NetworkDriver.Leave: 容器和port解绑
      /NetworkDriver.DeleteEndpoint
      /IpamDriver.ReleaseAddress: 删除port并释放IP

  • Delete Network

      这一系列调用发生在使用docker network delete的过程中。

      /NetworkDriver.DeleteNetwork: 删除network

      /IpamDriver.ReleaseAddress: 释放gateway的IP
      /IpamDriver.ReleasePool: 删除subnetpool

3:CNI与CNM的转化

      CNI和CNM并非是完全不可调和的两个模型。二者可以进行转化。比如calico项目就是直接支持两种接口模型。

      从模型中来看,CNI中的container应与CNM的sandbox概念一致,CNI中的network与CNM中的network一致。在CNI中,CNM中的endpoint被隐含在了ADD/DELETE的操作中。CNI接口更加简洁,把更多的工作托管给了容器的管理者和网络的管理者。从这个角度来说,CNI的ADD/DELETE接口其实只是实现了docker network connect和docker network disconnect两个命令。

      kubernetes/contrib项目提供了一种从CNI向CNM转化的过程。其中原理很简单,就是直接通过shell脚本执行了docker network connect和docker network disconnect命令,来实现从CNI到CNM的转化。

你可能感兴趣的文章
cad填充图案乱理石_太快了吧!原来大神是这样用CAD图案填充的
查看>>
activator.createinstance 需要垃圾回收么_在垃圾回收器中有哪几种判断是否需要被回收的方法...
查看>>
rocketmq 消息指定_RocketMQ入坑系列(一)角色介绍及基本使用
查看>>
redis zset转set 反序列化失败_掌握好Redis的数据类型,面试心里有底了
查看>>
p图软件pⅰc_娱乐圈最塑料的夫妻,P图永远只P自己,太精彩了吧!
查看>>
jenkins 手动执行_Jenkins 入门
查看>>
怎么判断冠词用a还是an_葡语干货 | 葡萄牙语冠词用法整理大全
查看>>
js传参不是数字_JS的Reflect学习和应用
查看>>
三个不等_数学一轮复习05,从函数观点看方程与不等式,记住口诀与联系
查看>>
卡尺测量的最小范围_汽车维修工具-测量用具
查看>>
网优5g前景_5G网络优化师前景怎么样?
查看>>
竞态条件的赋值_[译] part25: golang Mutex互斥锁
查看>>
delmatch oracle_完美完全卸载(清除)oracle数据库的方式(方法)
查看>>
pyqt 滚动条 美化_Pyqt5 关于流式布局和滚动条的综合使用示例代码
查看>>
51单机片 编译hex_单片机爬坑记-05-编译环境(完)
查看>>
java 正则表达式 img_Java正则表达式获得html字符串里的<img src=""/> 中的url列表
查看>>
java 文件crc校验_一个获取文件crc32校验码的简洁的java类 | 学步园
查看>>
java flatmapfunction_Java8 Stream flatmap中间操作用法解析
查看>>
java rmi spring 4.0_Java Spring RMI一些尝试
查看>>
JAVA怎么连接华为的HDFS系统_JAVA-API操作HDFS文件系统(HDFS核心类FileSystem的使用)...
查看>>