《Docker网络和服务发现》(上)

13 篇文章 0 订阅
13 篇文章 0 订阅


【编者的话】 本文是《Docker网络和服务发现》一书的全文,作者是Michael Hausenblas。本文介绍了Docker世界中的网络和服务发现的工作原理,并提供了一系列解决方案。前言当你开始使用Docker构建应用的时候,对于Docker的能力和它带来的机会,你会感到很兴奋。它可以同时在开发环境和生产环境中运行,只需要将一切打包进一个Docker镜像中,然后通过Docker Hub分发镜像,这是很直接了当的。你会发现以下过程很令人满意:你可以快速地将现有的应用(如Python应用)移植到Docker中,将该容器与另一个数据库容器(如PostgreSQL)连接。但是,你不会想手动启动一个Docker容器,也不会想自己去实现一个系统来监控运行中的容器和重启未运行的容器。

此时,你会意识到两个相互关联的挑战:网络和服务发现。不幸的是,说得好听一点,这两个领域都是新兴的课题;说得难听一点,在这两个领域里,还有很多不确定的内容,也缺乏最佳实践。幸运的是,在海量的博客和文章中,分散着各种各样的“秘方”。
本书因此,我对自己说:如果有人写本书,可以介绍这些主题,并提供一些基本的指导,对于每项技术给读者指引正确的方向,那该多好啊。

那个“人”注定是我, 在本书里,我将介绍Docker容器网络和服务发现领域中的挑战和现有的解决方案。 我想让大家了解以下三点:
  • 服务发现和容器编排就像一枚硬币的两面。
  • 对于Docker网络,如果没有正确的理解和完善的策略,那就糟糕了。
  • 网络和服务发现领域还很年轻。你会发现,刚开始你还在学习一套技术,过一段时间,就会“换挡”,尝试另一套技术。不要紧张,这是很正常的。在我看来,在形成标准和市场稳定之前,还会有两三年时间。

编排和调度
严格来讲,编排是比调度更广泛的一个概念:编排包含了调度,同时也包含其他内容。例如,容器的故障重启(可能是由于容器本身不健康,也可能是宿主机出了故障)。而调度仅仅是指,决定哪个容器运行在哪个宿主机上的过程。在本书中,我会无差别地使用这两个术语。

我这么做的原因是:第一,在IETF RFC和NIST标准中并没有对这两个概念的官方定义;第二,在不同公司的市场宣传中,往往会故意混淆它们,因此我希望你能对此有所准备。然而,Joe Beda(前Google和Kubernetes的策划者)对于该问题发表了一篇相当不错的文章: What Makes a Container Cluster?,你可以更深入地了解一下。


你我希望,本书的读者是:
  • 开发者们,正在使用Docker;
  • 网络Ops,正在为热情的开发者所带来的冲击做准备;
  • (企业)软件架构师,正在将现有负载迁移到Docker,或者使用Docker开始一项新项目;
  • 最后但同样重要的是,我相信,分布式应用的开发者、SRE和后端工程师们也能从其中获取一些价值。

需要注意的是,本书不是一本动手实践(hands-on)的书籍(除了第二章的Docker网络部分),更像是一本指导。当你计划实施一个基于Docker的部署时,你可以参考本书来做出一个明智合理的决定。阅读本书的另一种方式是,添加大量注释和书签(a heavily annotated bookmark collection)。


我在一个很酷的创业公司Mesosphere, Inc.(Apache Mesos背后的商业公司)工作,主要的工作内容是帮助DevOps充分利用Mesos。虽然我有偏见地认为Mesos是目前进行大规模集群调度的最佳选择,但是我仍然会尽我最大的努力来确保这种偏爱不会影响对于各项技术的介绍。


致谢向Mesosphere同事们James DeFelice和Stefan Schimanski(来自Kubernetes组)致谢,他们很耐心地回答了我关于Kubernetes网络的问题。向我的Mesosphere同事们Sebastien Pahl和Tim Fall(原Docker同事)致谢,我非常感谢你们关于Docker网络的建议。也很感谢另一个Mesos同事Mohit Soni,在百忙之中他提供了很多反馈。

进一步地, 我要感谢Medallia的Thorvald Natvig,他的讲话Velocity NYC 2015促使我深入地思考了网络这一领域,他也很友善地和我讨论Medallia生产环境中使用Docker/Mesos/Aurora的经验和教训。

很感谢Adrian Mouat(容器解决方案)和Diogo Mónica(Docker, Inc.)通过Twitter回答问题,尤其是在普通人睡觉的时候,花了数小时回答了我的问题。

我也很感谢来自Chris Swan的反馈,他提供了明确可操作的评论。通过解决他的问题,我相信本书变得更有针对性了。

在本书的写作过程中,来自Google的Mandy Waite提供了很多有用的反馈,特别是Kubernetes,我很感谢这一点。我也很感激Tim Hockin(Google)的支持,他帮助我澄清了很多关于Docker网络特性和Kubernetes的疑惑。

感谢Matthias Bauer对于本书的草稿提出的宝贵意见。

非常感谢我的O’Reilly编辑Brian Anderson。从我们讨论草稿开始,你一直都非常支持我,很高效,也很有趣。非常荣幸和您一起工作。

最后重要的一点是,对于我的家庭要致以最深刻的谢意:我的“阳光”Saphira,我的“运动女孩”Ranya,我的儿子和“Minecraft大师”Iannis,以及一直支持我的妻子Anneliese。如果没有你们的支持,我是不可能完成本书的。


动机


2012年2月,Randy Bias发表了一篇谈话,名为开放和可扩展云架构,提出了pets和cattle两种文化基因的碰撞。


  • 基础设施的pets方法(pets approach to infrastructure),每台机器或虚拟机被当做一个个体,会分配一个名字,各个应用被静态地部署在各个机器上。例如: db-prod-2是生产环境中的一台数据库服务器。应用是手动部署的,当一台机器出故障时,你需要修复它,并把应用手动部署到另一台机器上。在非云原生应用时代(non-cloud-native era),这种方法通常占据着主导地位。

  • 基础设施的cattle方法(cattle approach to infrastructure),所有的机器都是无名的、完全类似的(modulo hardware upgrades),它们只有编号,没有名字。应用是自动部署到机器群中的任意一台。当其中一台机器发生故障时,你不需要立刻处理它,只要在方便的时候更换故障机器(或者它的一部分,如一块损坏的HDD)。

尽管这些文化基因(meme)最初是针对虚拟机的,但是我们这里会将cattle方法套用在容器的基础架构上。

Go Cattle!


在基础设施中,使用cattle方法的一大好处就是,你可以在商用硬件上进行水平扩展。


这样一来,你就可以有很大的弹性来实施混合云。只要你愿意,一部分可以部署在premise环境中,一部分部署在公有云上,也可以部署在不同的云提供商的IaaS架构上。

更重要的是,从一名运维者的角度上来看,cattle方法可以保证一个相当好的睡眠质量。因为你再也不会像pets方法中那样,凌晨3点被叫起来去更换一块坏了的HDD或者将应用重新部署到另一台服务器上。

然而,cattle方法也带来了一些挑战,主要包括以下两个方面:

社交挑战(Social challenges)我敢说,大部分挑战会来自于社交方面:我应该怎么说服我的老板呢?我怎么让CTO买我的账呢?我的同事们会反对这种新的做法吗?这是不是意味着我们只需要更少的人来维护基础架构?现在,对于这个问题,我还不能提供一个完整有效的解决方案。你可以买一本《凤凰项目》,从中你可能可以找到答案。

技术挑战(Technical challenges)这个方面主要包括:机器的准备机制(provisioning mechanism)如何选择(例如,使用Ansible来部署Mesos Agents);在容器之间、在容器和外界之间如何通信;如何保证容器的自动部署并能被寻址(consequently findable)。

Docker网络和服务发现栈我们在图1-1中描述了整个技术栈,包括以下内容:

 底层的网络层(The low-level networking layer)这一层包括一系列网络工具:  iptables、路由、IPVLAN和Linux命名空间。你通常不需要知道其中的细节,除非你是在网络组,但是你需要意识到这一点。关于这一课题的更多信息,可以参考Docker网络基础。

 Docker网络层这一层封装了底层的网络层,提供各种抽象模型,例如单主机桥网络模式、多主机的每个容器一个IP地址的解决方案。在第2章和第3章中有相关介绍。

 服务发现层/容器编排层这里,我们将容器调度定义为通过底层提供的基本原语(primitives)决定将容器放在哪里。第4章提到了服务发现的必要背景,第5章从容器编排的角度讲解了网络和服务发现。

软件定义网络(SDN)

SDN真的是一个市场术语(umbrella term或者marketing term),它可以提供VM带来的网络优势,就像裸机服务器(bare-metal servers)一样。网络管理团队会变得更加敏捷,对商业需求的变化反应更加迅速。另一种看法是:SDN是一种使用软件来定义网络的配置方式,可能是通过API构建、通过NFV构建,或者由Docker网络来提供SDN。

如果你是一名开发者或架构师,我建议你看一下Cisco对于该课题的观点和SDxCentral的文章What’s Software-Defined Networking (SDN)?。

图 1-1
如果你在网络运维组的话,你可能已经准备好进入下一章了。然而,如果你是架构师或者开发者的话,对于网络知识可能有点生疏了,我建议你读一下Linux Network Administrators Guide来复习一下相关知识。

我需要All-In吗?


在各种会议和用户组中,我经常遇到一些人,他们对于容器领域的机会非常兴奋,同时他们也担心在从容器中受益之前需要投入多少。以下的表格是对于我了解的各种部署的非正式的总结(按照阶段不同排序)。



需要注意的是,不是所有的例子都使用Docker容器(显而易见的是,Google并不使用Docker容器)。其中,某些正在着手使用Docker,例如,在ad-hoc阶段;某些正在转向full-down阶段,例如Uber,请看ContainerSched 2015 London中的演讲。最后重要的一点是,这些阶段与部署的大小是没有必然关系的。例如,Gutefrage.de只有六台裸机服务器,仍然使用Apache Mesos来管理它们。 

在继续之前,还有最后一点:到目前为止,你可能已经注意到了我们处理的是分布式系统。由于我们总是希望将容器部署到一个集群中,我强烈建议你读一下分布式计算的谬论,以防你不熟悉这一主题。

现在言归正传,让我们进入Docker网络这一主题。

Docker网络基础

在我们进入网络细节之前,我们先来看一看在单主机上发生了什么。Docker容器需要运行在一台宿主机上,可以是一台物理机(on-premise数据中心中的裸机服务器),也可以是on-prem或云上的一台虚拟机。就像图2-1中描述的那样,宿主机上运行了Docker的daemon进程和客户端,一方面可以与Docker registry交互,另一方面可以启动、关闭和审查容器。


图 2-1
宿主机和容器的关系是1:N,这以为这一台宿主机上可以运行多个容器。例如,从Facebook的报告来看,取决于机器的能力,每台宿主机上平均可以运行10到40个容器。另一个数据是:在Mesosphere,我们发现,在裸机服务器上的各种负载测试中,每台宿主机上不超过250个容器是可能的。

无论你是在单主机上进行部署,还是在集群上部署,你总得和网络打交道:

  • 对于大多数单主机部署来说,问题归结于是使用共享卷进行数据交换,还是使用网络(基于HTTP或者其他的)进行数据交换。尽管Docker数据卷很容易使用,它也引入了紧耦合,这意味着很难将单主机部署转化为多主机部署。自然地,共享卷的优势是速度。

  • 在多主机部署中,你需要考虑两个方面:单主机上的容器之间如何通信和多主机之间的通信路径是怎样的。性能考量和安全方面都有可能影响你的设计决定。多主机部署通常是很有必要的,原因是单主机的能力有限(请看前面关于宿主机上容器的平均数量和最大数量的讨论),也可能是因为需要部署分布式系统,例如Apache Spark、HDFS和Cassandra。

分布式系统和数据本地化(Distributed Systems and Data Locality)

使用分布式系统(计算或存储)的基本想法是想从并行处理中获利,通常伴随着数据本地化。数据本地化,我指的是将代码转移到数据所在地的原则,而不是传统的、其他的方式。考虑下以下的场景:如果你的数据集是TB级的,而代码是MB级的,那么在集群中移动代码比传输TB级数据更高效。除了可以并行处理数据之外,分布式系统还可以提供容错性,因为系统中的一部分可以相对独立地工作。

本章主要讨论单主机中的网络,在第3章中,我们将介绍多主机场景。

简单地说,Docker网络是原生的容器SDN解决方案。总而言之,Docker网络有四种模式:桥模式,主机模式,容器模式和无网络模式。我们会详细地讨论单主机上的各种网络模式,在本章的最后,我们还会讨论一些常规主题,比如安全。

bridge模式网络


在该模式(见图2-2)中,Docker守护进程创建了一个虚拟以太网桥docker0,附加在其上的任何网卡之间都能自动转发数据包。默认情况下,守护进程会创建一对对等接口,将其中一个接口设置为容器的eth0接口,另一个接口放置在宿主机的命名空间中,从而将宿主机上的所有容器都连接到这个内部网络上。同时,守护进程还会从网桥的私有地址空间中分配一个IP地址和子网给该容器。


例2-1
$ docker run -d -P --net=bridge nginx:1.9.1
$ docker ps
CONTAINER ID   IMAGE                  COMMAND    CREATED
STATUS         PORTS                  NAMES
17d447b7425d   nginx:1.9.1            nginx -g   19 seconds ago
Up 18 seconds  0.0.0.0:49153->443/tcp,
           0.0.0.0:49154->80/tcp  trusting_feynman
注意:
因为bridge模式是Docker的默认设置,所以你也可以使用 docker run -d -P nginx:1.9.1。如果你没有使用-P(发布该容器暴露的所有端口)或者 -p host_port:container_port(发布某个特定端口),IP数据包就不能从宿主机之外路由到容器中。
图 2-2
小技巧:
在生产环境中,我建议使用Docker的host模式(将会在下一小节Host模式网路中讨论),并辅以第3章中的某个SDN解决方案。进一步地,为了控制容器间的网络通信,你可以使用flags参数:--iptables和-icc。
Host模式网络


该模式将禁用Docker容器的网络隔离。因为容器共享了宿主机的网络命名空间,直接暴露在公共网络中。因此,你需要通过端口映射(port mapping)来进行协调。


例2-2
$ docker run -d --net=host ubuntu:14.04 tail -f /dev/null
$ ip addr | grep -A 2 eth0:
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc mq state UP group default qlen 1000
link/ether 06:58:2b:07:d5:f3 brd ff:ff:ff:ff:ff:ff
inet **10.0.7.197**/22 brd 10.0.7.255 scope global dynamic eth0

$ docker ps
CONTAINER ID  IMAGE         COMMAND  CREATED
STATUS        PORTS         NAMES
b44d7d5d3903  ubuntu:14.04  tail -f  2 seconds ago
Up 2 seconds                jovial_blackwell
$ docker exec -it b44d7d5d3903 ip addr
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc mq state UP group default qlen 1000
link/ether 06:58:2b:07:d5:f3 brd ff:ff:ff:ff:ff:ff
inet **10.0.7.197**/22 brd 10.0.7.255 scope global dynamic eth0
我们可以从例2-2中看到:容器和宿主机具有相同的IP地址 10.0.7.197

在图2-3中,我们可以看到:当使用host模式网络时,容器实际上继承了宿主机的IP地址。该模式比bridge模式更快(因为没有路由开销),但是它将容器直接暴露在公共网络中,是有安全隐患的。
图 2-3

Container模式网络


该模式会重用另一个容器的网络命名空间。通常来说,当你想要自定义网络栈时,该模式是很有用的。实际上,该模式也是Kubernetes使用的网络模式,你可以在这里了解更多内容。


例2-3
$ docker run -d -P --net=bridge nginx:1.9.1
$ docker ps
CONTAINER ID  IMAGE        COMMAND   CREATED         STATUS
PORTS                      NAMES
eb19088be8a0  nginx:1.9.1  nginx -g  3 minutes ago   Up 3 minutes
0.0.0.0:32769->80/tcp,
0.0.0.0:32768->443/tcp     admiring_engelbart
$ docker exec -it admiring_engelbart ip addr
8: eth0@if9: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc noqueue state UP group default
link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff
inet **172.17.0.3**/16 scope global eth0

$ docker run -it --net=container:admiring_engelbart ubuntu:14.04 ip addr
...
8: eth0@if9: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc noqueue state UP group default
link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff
inet **172.17.0.3**/16 scope global eth0
结果(例2-3)显示:第二个容器使用了 --net=container参数,因此和第一个容器 admiring_engelbart具有相同的IP地址 172.17.0.3

None模式网络


该模式将容器放置在它自己的网络栈中,但是并不进行任何配置。实际上,该模式关闭了容器的网络功能,在以下两种情况下是有用的:容器并不需要网络(例如只需要写磁盘卷的批处理任务);你希望自定义网络,在第3章中有很多选项使用了该模式。


例2-4
$ docker run -d -P --net=none nginx:1.9.1
$ docker ps
CONTAINER ID  IMAGE          COMMAND   CREATED
STATUS        PORTS          NAMES
d8c26d68037c  nginx:1.9.1    nginx -g  2 minutes ago
Up 2 minutes                 grave_perlman
$  docker inspect d8c26d68037c | grep IPAddress
"IPAddress": "",
"SecondaryIPAddresses": null,
在例2-4中可以看到,恰如我们所料,网络没有任何配置。

你可以在Docker官方文档中读到更多关于Docker网络的配置。

 注意:
本书中的所有Docker命令都是在CoreOS环境中执行的,Docker客户端和服务端的版本都是1.7.1。
更多话题


在本章中,我们了解了Docker单主机网络的四种基本模式。现在我们讨论下你应该了解的其他主题(这与多主机部署也是相关的):


 分配IP地址频繁大量的创建和销毁容器时,手动分配IP地址是不能接受的。bridge模式可以在一定程度上解决这个问题。为了防止本地网络上的ARP冲突,Docker Daemon会根据分配的IP地址生成一个随机地MAC地址。在下一章中,我们会再次讨论分配地址的挑战。

 分配端口你会发现有两大阵营:固定端口分配(fixed-port-allocation)和动态端口分配(dynamically-port-allocation)。每个服务或者应用可以有各自的分配方法,也可以是作为全局的策略,但是你必须做出自己的判断和决定。请记住,bridge模式中,Docker会自动分配UDP或TCP端口,并使其可路由。

 网络安全Docker可以开启容器间通信(意味着默认配置 --icc=true),也就是说,宿主机上的所有容器可以不受任何限制地相互通信,这可能导致拒绝服务攻击。进一步地,Docker可以通过 --ip_forward--iptables两个选项控制容器间、容器和外部世界的通信。你应该了解这些选项的默认值,并让网络组根据公司策略设置Docker进程。可以读一下StackEngine的Boyd Hemphill写的文章Docker security analysis。

另一个网络安全方面是线上加密(on-the-wire encryption),通常是指RFC 5246中定义的TLS/SSL。注意,在写本书时,这一方面还很少被讨论,实际上,只有两个系统(下一章会详细讨论)提供了这个特性:Weave使用NACl,OpenVPN是基于TLS的。根据我从Docker的安全负责人Diogo Mónica那里了解的情况,v1.9之后可能加入线上加密功能。

最后,可以读一下Adrian Mouat的Using Docker,其中详细地介绍了网络安全方面。

 小技巧: 自动Docker安全检查 为了对部署在生产环境中的Docker容器进行安全检查,我强烈建议使用The Docker Bench for Security。现在,我们对单主机场景有了一个基本的了解,让我们继续看一下更有效的案例:多主机网络环境。
Docker多主机网络只要是在单主机上使用Docker的话,那么上一章中介绍的技术就足够了。然而,如果一台宿主机的能力不足以应付工作负载,要么买一个更高配置的宿主机(垂直扩展),要么你添加更多同类型的机器(水平扩展)。

对于后者,你会搭建一个集群。那么,就出现了一系列问题:不同宿主机上的容器之间如何相互通信?如何控制容器间、容器和外部世界之间的通信?怎么保存状态,如IP地址分配、集群内保持一致性等?如何与现有的网络基础架构结合?安全策略呢?

为了解决这些问题,在本章中,我们会讨论多主机网络的各种技术。

注意:
对于本章中介绍的各种选项,请记住Docker信奉的是“batteries included but replaceable”,也就是说,总会有一个默认功能(如网络、服务发现),但是你可以使用其他方案来替代。
Overlay2015年3月,Docker, Inc.收购了软件定义网络(SDN)的创业公司SocketPlane,并更名为Docker Overlay驱动,作为多主机网络的默认配置(在Docker 1.9以后)。Overlay驱动通过点对点(peer-to-peer)通信扩展了通常的bridge模式,并使用一个可插拔的key-value数据库后端(如Consul、etcd和ZooKeeper)来分发集群状态。

FlannelCoreOS Flannel是一个虚拟网络,分配给每个宿主机一个子网。集群中的每个容器(或者说是Kubernetes中的pod)都有一个唯一的、可路由的IP地址,并且支持以下一系列后端:VXLAN、AWS VPC和默认的2层UDP overlay网络。flannel的优势是它降低了端口映射的复杂性。例如,Red Hat的Atomic项目使用的就是flannel。

WeaveWeaveworks Weave创建了一个虚拟网络,用来连接部署在多主机上的Docker容器。应用使用网络的方式就像是容器直接插入到同一个网络交换机上,不需要配置端口映射和连接。Weave网络上的应用容器提供的服务可以直接在公共网络上访问,无论这些容器在哪里运行。同样的,无论位置在哪,现有的内部系统都是暴露给应用容器的。Weave可以穿越防火墙,在部分连接的网络中运行。流量可以加密,允许主机跨越非授信网络进行连接。你可以从Alvaro Saurin的文章Automating Weave Deployment on Docker Hosts with Weave Discovery中学习到Weave的更多特性。

Project CalicoMetaswitch的Calico项目使用标准IP路由,严格的说是RFC 1105中定义的边界网关协议(Border Gateway Protocol,简称BGP),并能使用网络工具提供3层解决方案。相反,大多数其他的网络解决方案(包括Weave)是通过封装2层流量到更高层来构建一个overlay网络。主操作模式不需要任何封装,是为可以控制物理网络结构的组织的数据中心设计的。

Open vSwitchOpen vSwitch是一个多层虚拟交换机,通过可编程扩展来实现网络自动化,支持标准管理接口和协议,如NetFlow、IPFIX、LACP和802.1ag。除此之外,它还支持在多个物理服务器上分发,和VMware的vNetwork distributed vSwitch、Cisco的Nexus 1000V类似。

PipeworkPipework由著名的Docker工程师Jérôme Petazzoni创建,称为Linux容器的软件定义网络。它允许你使用cgroups和namespace在任意复杂的场景中连接容器,并与LXC容器或者Docker兼容。由于Docker, Inc.收购了SocketPlane并引入了Overlay Driver,我们必须看一下这些技术如何融合。

OpenVPNOpenVPN,另一个有商业版本的开源项目,运行你创建使用TLS的虚拟私有网络(virtual private networks,简称VPN)。这些VPN也可以安全地跨越公共网络连接容器。如果你想尝试一下基于Docker的配置,我建议看一下DigitalOcean很赞的教程How To Run OpenVPN in a Docker Container on Ubuntu 14.04。

未来的Docker网络在最近发布的Docker v1.9中,引入了一个新的命令 docker network。容器可以动态地连接到其他网络上,每个网络都可以由不同的网络驱动来支持。默认的多主机网络驱动是Overlay。

为了了解更多实践经验,我建议看一下以下博客:
  • Aleksandr Tarasov的Splendors and Miseries of Docker Network
  • Calico项目的Docker libnetwork Is Almost Here, and Calico Is Ready!
  • Weave Works的Life and Docker Networking – One Year On

更多话题在本章中,我们讨论了多主机网络中的各种解决方案。这一小节会简要介绍一下其他需要了解的话题:

 IPVLANLinux内核v3.19引入了每个容器一个IP地址的特性,它分配给主机上的每个容器一个唯一的、可路由的IP地址。实际上,IPVLAN使用一个网卡接口,创建了多个虚拟的网卡接口,并分配了不同的MAC地址。这个相对较新的特性是由Google的Mahesh Bandewar贡献的,与macvlan驱动类似,但是更加灵活,因为它可以同时使用在L2和L3上。如果你的Linux发行版已经使用了高于3.19的内核,那么你很幸运;否则,你就无法享受这个新功能。

 IP地址管理(IPAM)多主机网络中,最大的挑战之一就是集群中容器的IP地址分配。

 编排工具兼容性本章中讨论的大多数多主机网络解决方案都是封装了Docker API,并配置网络。也就是说,在你选择其中一个之前,你需要检查与容器编排工具的可兼容性。更多主题,请看第5章。

 IPv4 vs. IPv6到目前为止,大多数Docker部署使用的是标准IPv4,但是IPv6正在迎头赶上。Docker从v1.5(2015年2月发布)开始支持IPv6。IPv4的持续减少将会导致越来越多的IPv6部署,也可以摆脱NATs。然而,什么时候转变完成还不清楚。

现在,你已经对底层网络和Docker网络的解决方案有了充分理解,让我们进入下一个内容:服务发现。

容器和服务发现管理基础架构的cattle方法的最大挑战就是服务发现。服务发现和容器调度是一枚硬币的两面。如果你使用cattle方法的话,那么你会把所有机器看做相同的,你不会手动分配某台机器给某个应用。相反,你会使用调度软件来管理容器的生命周期。

那么,问题就来了:如何决定容器被调度到哪台宿主机上?答对了,这就是服务发现。我们会在第5章详细讨论硬币的另一面:容器编排。

挑战服务发现已经出现了一段时间了,被认为是zeroconf的一部分。

zeroconf

zeroconf的想法是自动化创建和管理计算机网络,自动分配网络地址,自动分发和解析主机名,自动管理网络服务。

对于Docker容器来说,这个挑战归结于稳定地维护运行中容器和位置(location)的映射关系。位置这里指的是IP地址(启动容器的宿主机地址)和可被访问的端口。这个映射必须及时完成,并在集群中准确地重启容器。容器的服务发现解决方案必须支持以下两个操作:

 注册(Register)建立container->location的映射。因为只有容器调度器才知道容器运行在哪,我们可以把该映射当做容器位置的“绝对真理”(the absolute source of truth)。

 查询(Lookup)其他服务或应用可以查询我们存储的映射关系,属性包括信息的实时性和查询延迟。

让我们看一下在选择过程中相对独立的几点考虑:


  • 除了简单地向一个特定方向发送请求之外,怎么从搜索路径中排除不健康的宿主机和挂掉的容器?你已经猜到了,这是和负载均衡高度相关的主题,因为这是很重要的,所以本章的最后一小节将讨论这一主题。

  • 有人认为这是实现细节,也有人认为需要考虑CAP三要素。在选择服务发现工具时,需要考虑选择强一致性(strong consistency)还是高可用性(high availability)。至少你要意识到这一点。

  • 可扩展性也会影响你的选择。当然,如果你只有少量的节点需要管理,那么上面讨论的解决方案就够用了。然而,如果你的集群有100多个节点,甚至1000个节点,那么在选择某一项特定技术之前,你必须做一些负载测试。

CAP 理论
1998年,Eric Brewer提出了分布式系统的CAP理论。CAP代表了一致性(consistency)、可用性(availability)和分区容错性(partition tolerance): 

一致性
分布式系统的所有节点同时看到相同的数据。 
可用性
保证每个请求都能得到响应,无论该响应是成功还是失败。
分区容错性 
无论哪块分区由于网络故障而失效,分布式系统都可以继续运行。
CAP理论在分布式系统的实践中已经讨论多次了。你会听到人们主要讨论强一致性 vs 最终一致性,我建议读一下Martin Kleppmann的论文A Critique of the CAP Theorem。该论文提出了一种不同的、更实际的思考CAP的方法,特别是一致性。如果你想了解该领域更多的需求和根本的挑战,可以读一下Jeff Lindsay的Understanding Modern Service Discovery with Docker和Shopify的Simon Eskildsen在DockerCon分享的内容。

【编者的话】 本文是《Docker网络和服务发现》一书的全文,作者是Michael Hausenblas。本文介绍了Docker世界中的网络和服务发现的工作原理,并提供了一系列解决方案。前言当你开始使用Docker构建应用的时候,对于Docker的能力和它带来的机会,你会感到很兴奋。它可以同时在开发环境和生产环境中运行,只需要将一切打包进一个Docker镜像中,然后通过Docker Hub分发镜像,这是很直接了当的。你会发现以下过程很令人满意:你可以快速地将现有的应用(如Python应用)移植到Docker中,将该容器与另一个数据库容器(如PostgreSQL)连接。但是,你不会想手动启动一个Docker容器,也不会想自己去实现一个系统来监控运行中的容器和重启未运行的容器。

此时,你会意识到两个相互关联的挑战:网络和服务发现。不幸的是,说得好听一点,这两个领域都是新兴的课题;说得难听一点,在这两个领域里,还有很多不确定的内容,也缺乏最佳实践。幸运的是,在海量的博客和文章中,分散着各种各样的“秘方”。
本书因此,我对自己说:如果有人写本书,可以介绍这些主题,并提供一些基本的指导,对于每项技术给读者指引正确的方向,那该多好啊。

那个“人”注定是我, 在本书里,我将介绍Docker容器网络和服务发现领域中的挑战和现有的解决方案。 我想让大家了解以下三点:
  • 服务发现和容器编排就像一枚硬币的两面。
  • 对于Docker网络,如果没有正确的理解和完善的策略,那就糟糕了。
  • 网络和服务发现领域还很年轻。你会发现,刚开始你还在学习一套技术,过一段时间,就会“换挡”,尝试另一套技术。不要紧张,这是很正常的。在我看来,在形成标准和市场稳定之前,还会有两三年时间。

编排和调度
严格来讲,编排是比调度更广泛的一个概念:编排包含了调度,同时也包含其他内容。例如,容器的故障重启(可能是由于容器本身不健康,也可能是宿主机出了故障)。而调度仅仅是指,决定哪个容器运行在哪个宿主机上的过程。在本书中,我会无差别地使用这两个术语。

我这么做的原因是:第一,在IETF RFC和NIST标准中并没有对这两个概念的官方定义;第二,在不同公司的市场宣传中,往往会故意混淆它们,因此我希望你能对此有所准备。然而,Joe Beda(前Google和Kubernetes的策划者)对于该问题发表了一篇相当不错的文章: What Makes a Container Cluster?,你可以更深入地了解一下。


你我希望,本书的读者是:
  • 开发者们,正在使用Docker;
  • 网络Ops,正在为热情的开发者所带来的冲击做准备;
  • (企业)软件架构师,正在将现有负载迁移到Docker,或者使用Docker开始一项新项目;
  • 最后但同样重要的是,我相信,分布式应用的开发者、SRE和后端工程师们也能从其中获取一些价值。

需要注意的是,本书不是一本动手实践(hands-on)的书籍(除了第二章的Docker网络部分),更像是一本指导。当你计划实施一个基于Docker的部署时,你可以参考本书来做出一个明智合理的决定。阅读本书的另一种方式是,添加大量注释和书签(a heavily annotated bookmark collection)。


我在一个很酷的创业公司Mesosphere, Inc.(Apache Mesos背后的商业公司)工作,主要的工作内容是帮助DevOps充分利用Mesos。虽然我有偏见地认为Mesos是目前进行大规模集群调度的最佳选择,但是我仍然会尽我最大的努力来确保这种偏爱不会影响对于各项技术的介绍。


致谢向Mesosphere同事们James DeFelice和Stefan Schimanski(来自Kubernetes组)致谢,他们很耐心地回答了我关于Kubernetes网络的问题。向我的Mesosphere同事们Sebastien Pahl和Tim Fall(原Docker同事)致谢,我非常感谢你们关于Docker网络的建议。也很感谢另一个Mesos同事Mohit Soni,在百忙之中他提供了很多反馈。

进一步地, 我要感谢Medallia的Thorvald Natvig,他的讲话Velocity NYC 2015促使我深入地思考了网络这一领域,他也很友善地和我讨论Medallia生产环境中使用Docker/Mesos/Aurora的经验和教训。

很感谢Adrian Mouat(容器解决方案)和Diogo Mónica(Docker, Inc.)通过Twitter回答问题,尤其是在普通人睡觉的时候,花了数小时回答了我的问题。

我也很感谢来自Chris Swan的反馈,他提供了明确可操作的评论。通过解决他的问题,我相信本书变得更有针对性了。

在本书的写作过程中,来自Google的Mandy Waite提供了很多有用的反馈,特别是Kubernetes,我很感谢这一点。我也很感激Tim Hockin(Google)的支持,他帮助我澄清了很多关于Docker网络特性和Kubernetes的疑惑。

感谢Matthias Bauer对于本书的草稿提出的宝贵意见。

非常感谢我的O’Reilly编辑Brian Anderson。从我们讨论草稿开始,你一直都非常支持我,很高效,也很有趣。非常荣幸和您一起工作。

最后重要的一点是,对于我的家庭要致以最深刻的谢意:我的“阳光”Saphira,我的“运动女孩”Ranya,我的儿子和“Minecraft大师”Iannis,以及一直支持我的妻子Anneliese。如果没有你们的支持,我是不可能完成本书的。


动机


2012年2月,Randy Bias发表了一篇谈话,名为开放和可扩展云架构,提出了pets和cattle两种文化基因的碰撞。


  • 基础设施的pets方法(pets approach to infrastructure),每台机器或虚拟机被当做一个个体,会分配一个名字,各个应用被静态地部署在各个机器上。例如: db-prod-2是生产环境中的一台数据库服务器。应用是手动部署的,当一台机器出故障时,你需要修复它,并把应用手动部署到另一台机器上。在非云原生应用时代(non-cloud-native era),这种方法通常占据着主导地位。

  • 基础设施的cattle方法(cattle approach to infrastructure),所有的机器都是无名的、完全类似的(modulo hardware upgrades),它们只有编号,没有名字。应用是自动部署到机器群中的任意一台。当其中一台机器发生故障时,你不需要立刻处理它,只要在方便的时候更换故障机器(或者它的一部分,如一块损坏的HDD)。

尽管这些文化基因(meme)最初是针对虚拟机的,但是我们这里会将cattle方法套用在容器的基础架构上。

Go Cattle!


在基础设施中,使用cattle方法的一大好处就是,你可以在商用硬件上进行水平扩展。


这样一来,你就可以有很大的弹性来实施混合云。只要你愿意,一部分可以部署在premise环境中,一部分部署在公有云上,也可以部署在不同的云提供商的IaaS架构上。

更重要的是,从一名运维者的角度上来看,cattle方法可以保证一个相当好的睡眠质量。因为你再也不会像pets方法中那样,凌晨3点被叫起来去更换一块坏了的HDD或者将应用重新部署到另一台服务器上。

然而,cattle方法也带来了一些挑战,主要包括以下两个方面:

社交挑战(Social challenges)我敢说,大部分挑战会来自于社交方面:我应该怎么说服我的老板呢?我怎么让CTO买我的账呢?我的同事们会反对这种新的做法吗?这是不是意味着我们只需要更少的人来维护基础架构?现在,对于这个问题,我还不能提供一个完整有效的解决方案。你可以买一本《凤凰项目》,从中你可能可以找到答案。

技术挑战(Technical challenges)这个方面主要包括:机器的准备机制(provisioning mechanism)如何选择(例如,使用Ansible来部署Mesos Agents);在容器之间、在容器和外界之间如何通信;如何保证容器的自动部署并能被寻址(consequently findable)。

Docker网络和服务发现栈我们在图1-1中描述了整个技术栈,包括以下内容:

 底层的网络层(The low-level networking layer)这一层包括一系列网络工具:  iptables、路由、IPVLAN和Linux命名空间。你通常不需要知道其中的细节,除非你是在网络组,但是你需要意识到这一点。关于这一课题的更多信息,可以参考Docker网络基础。

 Docker网络层这一层封装了底层的网络层,提供各种抽象模型,例如单主机桥网络模式、多主机的每个容器一个IP地址的解决方案。在第2章和第3章中有相关介绍。

 服务发现层/容器编排层这里,我们将容器调度定义为通过底层提供的基本原语(primitives)决定将容器放在哪里。第4章提到了服务发现的必要背景,第5章从容器编排的角度讲解了网络和服务发现。

软件定义网络(SDN)

SDN真的是一个市场术语(umbrella term或者marketing term),它可以提供VM带来的网络优势,就像裸机服务器(bare-metal servers)一样。网络管理团队会变得更加敏捷,对商业需求的变化反应更加迅速。另一种看法是:SDN是一种使用软件来定义网络的配置方式,可能是通过API构建、通过NFV构建,或者由Docker网络来提供SDN。

如果你是一名开发者或架构师,我建议你看一下Cisco对于该课题的观点和SDxCentral的文章What’s Software-Defined Networking (SDN)?。

图 1-1
如果你在网络运维组的话,你可能已经准备好进入下一章了。然而,如果你是架构师或者开发者的话,对于网络知识可能有点生疏了,我建议你读一下Linux Network Administrators Guide来复习一下相关知识。

我需要All-In吗?


在各种会议和用户组中,我经常遇到一些人,他们对于容器领域的机会非常兴奋,同时他们也担心在从容器中受益之前需要投入多少。以下的表格是对于我了解的各种部署的非正式的总结(按照阶段不同排序)。



需要注意的是,不是所有的例子都使用Docker容器(显而易见的是,Google并不使用Docker容器)。其中,某些正在着手使用Docker,例如,在ad-hoc阶段;某些正在转向full-down阶段,例如Uber,请看ContainerSched 2015 London中的演讲。最后重要的一点是,这些阶段与部署的大小是没有必然关系的。例如,Gutefrage.de只有六台裸机服务器,仍然使用Apache Mesos来管理它们。 

在继续之前,还有最后一点:到目前为止,你可能已经注意到了我们处理的是分布式系统。由于我们总是希望将容器部署到一个集群中,我强烈建议你读一下分布式计算的谬论,以防你不熟悉这一主题。

现在言归正传,让我们进入Docker网络这一主题。

Docker网络基础

在我们进入网络细节之前,我们先来看一看在单主机上发生了什么。Docker容器需要运行在一台宿主机上,可以是一台物理机(on-premise数据中心中的裸机服务器),也可以是on-prem或云上的一台虚拟机。就像图2-1中描述的那样,宿主机上运行了Docker的daemon进程和客户端,一方面可以与Docker registry交互,另一方面可以启动、关闭和审查容器。


图 2-1
宿主机和容器的关系是1:N,这以为这一台宿主机上可以运行多个容器。例如,从Facebook的报告来看,取决于机器的能力,每台宿主机上平均可以运行10到40个容器。另一个数据是:在Mesosphere,我们发现,在裸机服务器上的各种负载测试中,每台宿主机上不超过250个容器是可能的。

无论你是在单主机上进行部署,还是在集群上部署,你总得和网络打交道:

  • 对于大多数单主机部署来说,问题归结于是使用共享卷进行数据交换,还是使用网络(基于HTTP或者其他的)进行数据交换。尽管Docker数据卷很容易使用,它也引入了紧耦合,这意味着很难将单主机部署转化为多主机部署。自然地,共享卷的优势是速度。

  • 在多主机部署中,你需要考虑两个方面:单主机上的容器之间如何通信和多主机之间的通信路径是怎样的。性能考量和安全方面都有可能影响你的设计决定。多主机部署通常是很有必要的,原因是单主机的能力有限(请看前面关于宿主机上容器的平均数量和最大数量的讨论),也可能是因为需要部署分布式系统,例如Apache Spark、HDFS和Cassandra。

分布式系统和数据本地化(Distributed Systems and Data Locality)

使用分布式系统(计算或存储)的基本想法是想从并行处理中获利,通常伴随着数据本地化。数据本地化,我指的是将代码转移到数据所在地的原则,而不是传统的、其他的方式。考虑下以下的场景:如果你的数据集是TB级的,而代码是MB级的,那么在集群中移动代码比传输TB级数据更高效。除了可以并行处理数据之外,分布式系统还可以提供容错性,因为系统中的一部分可以相对独立地工作。

本章主要讨论单主机中的网络,在第3章中,我们将介绍多主机场景。

简单地说,Docker网络是原生的容器SDN解决方案。总而言之,Docker网络有四种模式:桥模式,主机模式,容器模式和无网络模式。我们会详细地讨论单主机上的各种网络模式,在本章的最后,我们还会讨论一些常规主题,比如安全。

bridge模式网络


在该模式(见图2-2)中,Docker守护进程创建了一个虚拟以太网桥docker0,附加在其上的任何网卡之间都能自动转发数据包。默认情况下,守护进程会创建一对对等接口,将其中一个接口设置为容器的eth0接口,另一个接口放置在宿主机的命名空间中,从而将宿主机上的所有容器都连接到这个内部网络上。同时,守护进程还会从网桥的私有地址空间中分配一个IP地址和子网给该容器。


例2-1
$ docker run -d -P --net=bridge nginx:1.9.1
$ docker ps
CONTAINER ID   IMAGE                  COMMAND    CREATED
STATUS         PORTS                  NAMES
17d447b7425d   nginx:1.9.1            nginx -g   19 seconds ago
Up 18 seconds  0.0.0.0:49153->443/tcp,
           0.0.0.0:49154->80/tcp  trusting_feynman
注意:
因为bridge模式是Docker的默认设置,所以你也可以使用 docker run -d -P nginx:1.9.1。如果你没有使用-P(发布该容器暴露的所有端口)或者 -p host_port:container_port(发布某个特定端口),IP数据包就不能从宿主机之外路由到容器中。
图 2-2
小技巧:
在生产环境中,我建议使用Docker的host模式(将会在下一小节Host模式网路中讨论),并辅以第3章中的某个SDN解决方案。进一步地,为了控制容器间的网络通信,你可以使用flags参数:--iptables和-icc。
Host模式网络


该模式将禁用Docker容器的网络隔离。因为容器共享了宿主机的网络命名空间,直接暴露在公共网络中。因此,你需要通过端口映射(port mapping)来进行协调。


例2-2
$ docker run -d --net=host ubuntu:14.04 tail -f /dev/null
$ ip addr | grep -A 2 eth0:
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc mq state UP group default qlen 1000
link/ether 06:58:2b:07:d5:f3 brd ff:ff:ff:ff:ff:ff
inet **10.0.7.197**/22 brd 10.0.7.255 scope global dynamic eth0

$ docker ps
CONTAINER ID  IMAGE         COMMAND  CREATED
STATUS        PORTS         NAMES
b44d7d5d3903  ubuntu:14.04  tail -f  2 seconds ago
Up 2 seconds                jovial_blackwell
$ docker exec -it b44d7d5d3903 ip addr
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc mq state UP group default qlen 1000
link/ether 06:58:2b:07:d5:f3 brd ff:ff:ff:ff:ff:ff
inet **10.0.7.197**/22 brd 10.0.7.255 scope global dynamic eth0
我们可以从例2-2中看到:容器和宿主机具有相同的IP地址 10.0.7.197

在图2-3中,我们可以看到:当使用host模式网络时,容器实际上继承了宿主机的IP地址。该模式比bridge模式更快(因为没有路由开销),但是它将容器直接暴露在公共网络中,是有安全隐患的。
图 2-3

Container模式网络


该模式会重用另一个容器的网络命名空间。通常来说,当你想要自定义网络栈时,该模式是很有用的。实际上,该模式也是Kubernetes使用的网络模式,你可以在这里了解更多内容。


例2-3
$ docker run -d -P --net=bridge nginx:1.9.1
$ docker ps
CONTAINER ID  IMAGE        COMMAND   CREATED         STATUS
PORTS                      NAMES
eb19088be8a0  nginx:1.9.1  nginx -g  3 minutes ago   Up 3 minutes
0.0.0.0:32769->80/tcp,
0.0.0.0:32768->443/tcp     admiring_engelbart
$ docker exec -it admiring_engelbart ip addr
8: eth0@if9: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc noqueue state UP group default
link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff
inet **172.17.0.3**/16 scope global eth0

$ docker run -it --net=container:admiring_engelbart ubuntu:14.04 ip addr
...
8: eth0@if9: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc noqueue state UP group default
link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff
inet **172.17.0.3**/16 scope global eth0
结果(例2-3)显示:第二个容器使用了 --net=container参数,因此和第一个容器 admiring_engelbart具有相同的IP地址 172.17.0.3

None模式网络


该模式将容器放置在它自己的网络栈中,但是并不进行任何配置。实际上,该模式关闭了容器的网络功能,在以下两种情况下是有用的:容器并不需要网络(例如只需要写磁盘卷的批处理任务);你希望自定义网络,在第3章中有很多选项使用了该模式。


例2-4
$ docker run -d -P --net=none nginx:1.9.1
$ docker ps
CONTAINER ID  IMAGE          COMMAND   CREATED
STATUS        PORTS          NAMES
d8c26d68037c  nginx:1.9.1    nginx -g  2 minutes ago
Up 2 minutes                 grave_perlman
$  docker inspect d8c26d68037c | grep IPAddress
"IPAddress": "",
"SecondaryIPAddresses": null,
在例2-4中可以看到,恰如我们所料,网络没有任何配置。

你可以在Docker官方文档中读到更多关于Docker网络的配置。

 注意:
本书中的所有Docker命令都是在CoreOS环境中执行的,Docker客户端和服务端的版本都是1.7.1。
更多话题


在本章中,我们了解了Docker单主机网络的四种基本模式。现在我们讨论下你应该了解的其他主题(这与多主机部署也是相关的):


 分配IP地址频繁大量的创建和销毁容器时,手动分配IP地址是不能接受的。bridge模式可以在一定程度上解决这个问题。为了防止本地网络上的ARP冲突,Docker Daemon会根据分配的IP地址生成一个随机地MAC地址。在下一章中,我们会再次讨论分配地址的挑战。

 分配端口你会发现有两大阵营:固定端口分配(fixed-port-allocation)和动态端口分配(dynamically-port-allocation)。每个服务或者应用可以有各自的分配方法,也可以是作为全局的策略,但是你必须做出自己的判断和决定。请记住,bridge模式中,Docker会自动分配UDP或TCP端口,并使其可路由。

 网络安全Docker可以开启容器间通信(意味着默认配置 --icc=true),也就是说,宿主机上的所有容器可以不受任何限制地相互通信,这可能导致拒绝服务攻击。进一步地,Docker可以通过 --ip_forward--iptables两个选项控制容器间、容器和外部世界的通信。你应该了解这些选项的默认值,并让网络组根据公司策略设置Docker进程。可以读一下StackEngine的Boyd Hemphill写的文章Docker security analysis。

另一个网络安全方面是线上加密(on-the-wire encryption),通常是指RFC 5246中定义的TLS/SSL。注意,在写本书时,这一方面还很少被讨论,实际上,只有两个系统(下一章会详细讨论)提供了这个特性:Weave使用NACl,OpenVPN是基于TLS的。根据我从Docker的安全负责人Diogo Mónica那里了解的情况,v1.9之后可能加入线上加密功能。

最后,可以读一下Adrian Mouat的Using Docker,其中详细地介绍了网络安全方面。

 小技巧: 自动Docker安全检查 为了对部署在生产环境中的Docker容器进行安全检查,我强烈建议使用The Docker Bench for Security。现在,我们对单主机场景有了一个基本的了解,让我们继续看一下更有效的案例:多主机网络环境。
Docker多主机网络只要是在单主机上使用Docker的话,那么上一章中介绍的技术就足够了。然而,如果一台宿主机的能力不足以应付工作负载,要么买一个更高配置的宿主机(垂直扩展),要么你添加更多同类型的机器(水平扩展)。

对于后者,你会搭建一个集群。那么,就出现了一系列问题:不同宿主机上的容器之间如何相互通信?如何控制容器间、容器和外部世界之间的通信?怎么保存状态,如IP地址分配、集群内保持一致性等?如何与现有的网络基础架构结合?安全策略呢?

为了解决这些问题,在本章中,我们会讨论多主机网络的各种技术。

注意:
对于本章中介绍的各种选项,请记住Docker信奉的是“batteries included but replaceable”,也就是说,总会有一个默认功能(如网络、服务发现),但是你可以使用其他方案来替代。
Overlay2015年3月,Docker, Inc.收购了软件定义网络(SDN)的创业公司SocketPlane,并更名为Docker Overlay驱动,作为多主机网络的默认配置(在Docker 1.9以后)。Overlay驱动通过点对点(peer-to-peer)通信扩展了通常的bridge模式,并使用一个可插拔的key-value数据库后端(如Consul、etcd和ZooKeeper)来分发集群状态。

FlannelCoreOS Flannel是一个虚拟网络,分配给每个宿主机一个子网。集群中的每个容器(或者说是Kubernetes中的pod)都有一个唯一的、可路由的IP地址,并且支持以下一系列后端:VXLAN、AWS VPC和默认的2层UDP overlay网络。flannel的优势是它降低了端口映射的复杂性。例如,Red Hat的Atomic项目使用的就是flannel。

WeaveWeaveworks Weave创建了一个虚拟网络,用来连接部署在多主机上的Docker容器。应用使用网络的方式就像是容器直接插入到同一个网络交换机上,不需要配置端口映射和连接。Weave网络上的应用容器提供的服务可以直接在公共网络上访问,无论这些容器在哪里运行。同样的,无论位置在哪,现有的内部系统都是暴露给应用容器的。Weave可以穿越防火墙,在部分连接的网络中运行。流量可以加密,允许主机跨越非授信网络进行连接。你可以从Alvaro Saurin的文章Automating Weave Deployment on Docker Hosts with Weave Discovery中学习到Weave的更多特性。

Project CalicoMetaswitch的Calico项目使用标准IP路由,严格的说是RFC 1105中定义的边界网关协议(Border Gateway Protocol,简称BGP),并能使用网络工具提供3层解决方案。相反,大多数其他的网络解决方案(包括Weave)是通过封装2层流量到更高层来构建一个overlay网络。主操作模式不需要任何封装,是为可以控制物理网络结构的组织的数据中心设计的。

Open vSwitchOpen vSwitch是一个多层虚拟交换机,通过可编程扩展来实现网络自动化,支持标准管理接口和协议,如NetFlow、IPFIX、LACP和802.1ag。除此之外,它还支持在多个物理服务器上分发,和VMware的vNetwork distributed vSwitch、Cisco的Nexus 1000V类似。

PipeworkPipework由著名的Docker工程师Jérôme Petazzoni创建,称为Linux容器的软件定义网络。它允许你使用cgroups和namespace在任意复杂的场景中连接容器,并与LXC容器或者Docker兼容。由于Docker, Inc.收购了SocketPlane并引入了Overlay Driver,我们必须看一下这些技术如何融合。

OpenVPNOpenVPN,另一个有商业版本的开源项目,运行你创建使用TLS的虚拟私有网络(virtual private networks,简称VPN)。这些VPN也可以安全地跨越公共网络连接容器。如果你想尝试一下基于Docker的配置,我建议看一下DigitalOcean很赞的教程How To Run OpenVPN in a Docker Container on Ubuntu 14.04。

未来的Docker网络在最近发布的Docker v1.9中,引入了一个新的命令 docker network。容器可以动态地连接到其他网络上,每个网络都可以由不同的网络驱动来支持。默认的多主机网络驱动是Overlay。

为了了解更多实践经验,我建议看一下以下博客:
  • Aleksandr Tarasov的Splendors and Miseries of Docker Network
  • Calico项目的Docker libnetwork Is Almost Here, and Calico Is Ready!
  • Weave Works的Life and Docker Networking – One Year On

更多话题在本章中,我们讨论了多主机网络中的各种解决方案。这一小节会简要介绍一下其他需要了解的话题:

 IPVLANLinux内核v3.19引入了每个容器一个IP地址的特性,它分配给主机上的每个容器一个唯一的、可路由的IP地址。实际上,IPVLAN使用一个网卡接口,创建了多个虚拟的网卡接口,并分配了不同的MAC地址。这个相对较新的特性是由Google的Mahesh Bandewar贡献的,与macvlan驱动类似,但是更加灵活,因为它可以同时使用在L2和L3上。如果你的Linux发行版已经使用了高于3.19的内核,那么你很幸运;否则,你就无法享受这个新功能。

 IP地址管理(IPAM)多主机网络中,最大的挑战之一就是集群中容器的IP地址分配。

 编排工具兼容性本章中讨论的大多数多主机网络解决方案都是封装了Docker API,并配置网络。也就是说,在你选择其中一个之前,你需要检查与容器编排工具的可兼容性。更多主题,请看第5章。

 IPv4 vs. IPv6到目前为止,大多数Docker部署使用的是标准IPv4,但是IPv6正在迎头赶上。Docker从v1.5(2015年2月发布)开始支持IPv6。IPv4的持续减少将会导致越来越多的IPv6部署,也可以摆脱NATs。然而,什么时候转变完成还不清楚。

现在,你已经对底层网络和Docker网络的解决方案有了充分理解,让我们进入下一个内容:服务发现。

容器和服务发现管理基础架构的cattle方法的最大挑战就是服务发现。服务发现和容器调度是一枚硬币的两面。如果你使用cattle方法的话,那么你会把所有机器看做相同的,你不会手动分配某台机器给某个应用。相反,你会使用调度软件来管理容器的生命周期。

那么,问题就来了:如何决定容器被调度到哪台宿主机上?答对了,这就是服务发现。我们会在第5章详细讨论硬币的另一面:容器编排。

挑战服务发现已经出现了一段时间了,被认为是zeroconf的一部分。

zeroconf

zeroconf的想法是自动化创建和管理计算机网络,自动分配网络地址,自动分发和解析主机名,自动管理网络服务。

对于Docker容器来说,这个挑战归结于稳定地维护运行中容器和位置(location)的映射关系。位置这里指的是IP地址(启动容器的宿主机地址)和可被访问的端口。这个映射必须及时完成,并在集群中准确地重启容器。容器的服务发现解决方案必须支持以下两个操作:

 注册(Register)建立container->location的映射。因为只有容器调度器才知道容器运行在哪,我们可以把该映射当做容器位置的“绝对真理”(the absolute source of truth)。

 查询(Lookup)其他服务或应用可以查询我们存储的映射关系,属性包括信息的实时性和查询延迟。

让我们看一下在选择过程中相对独立的几点考虑:


  • 除了简单地向一个特定方向发送请求之外,怎么从搜索路径中排除不健康的宿主机和挂掉的容器?你已经猜到了,这是和负载均衡高度相关的主题,因为这是很重要的,所以本章的最后一小节将讨论这一主题。

  • 有人认为这是实现细节,也有人认为需要考虑CAP三要素。在选择服务发现工具时,需要考虑选择强一致性(strong consistency)还是高可用性(high availability)。至少你要意识到这一点。

  • 可扩展性也会影响你的选择。当然,如果你只有少量的节点需要管理,那么上面讨论的解决方案就够用了。然而,如果你的集群有100多个节点,甚至1000个节点,那么在选择某一项特定技术之前,你必须做一些负载测试。

CAP 理论
1998年,Eric Brewer提出了分布式系统的CAP理论。CAP代表了一致性(consistency)、可用性(availability)和分区容错性(partition tolerance): 

一致性
分布式系统的所有节点同时看到相同的数据。 
可用性
保证每个请求都能得到响应,无论该响应是成功还是失败。
分区容错性 
无论哪块分区由于网络故障而失效,分布式系统都可以继续运行。
CAP理论在分布式系统的实践中已经讨论多次了。你会听到人们主要讨论强一致性 vs 最终一致性,我建议读一下Martin Kleppmann的论文A Critique of the CAP Theorem。该论文提出了一种不同的、更实际的思考CAP的方法,特别是一致性。如果你想了解该领域更多的需求和根本的挑战,可以读一下Jeff Lindsay的Understanding Modern Service Discovery with Docker和Shopify的Simon Eskildsen在DockerCon分享的内容。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值