Docker1.12服务发现,负载均衡和Routing Mesh

本文源自9月6日数人云线上分享课,分享嘉宾数人云资深架构师春明。

Docker社区目前最热的话题莫过于Docker1.12的发布,大家都在讨论Docker1.12的新功能,各种关于1.12的使用手册,文章,深度教程层出不穷, 本文从Docker SwarmKit的引入讲起,为大家深入讲解了服务发现,负载均衡和Routing Mesh等功能。

个人理解Docker1.12最大的功能莫过于SwarmKit的引入,这应该算是Docker发展里程碑式的一个Release(上一个里程碑应该是runc和containerd的剥离,标志着docker公司自己孤独地玩到容器标准化概念引入),自此再也不要说Docker是主机容器管理了, Docker已经能够管理一群主机上的容器了。概念上Docker引入了node,service,task,甚至更高于service一层的stack, 自此大家会发现容器和网络已经不是Docker的first-class概念, 取而代之的是service, stack和跨主机网络。Docker SwarmKit的引入给容器编排投入了一颗重磅炸弹, 给我的感觉是原来容器编排还可以这么玩,终于不要某某sos和某8s的全家桶了, 在容器编排的远古时代(其实也不远,就是观念陈旧)不装个五六七八个组件都不好意思跟人说是编排, 真的有这个必要么?  当然了, Docker Swarm主要功能还是是借鉴了“远古时代”的几款产品, 吸收了前人的优秀经验。 让我列几个牛哄哄的功能给大家吧

  1.  内置Raft, 妈妈再也不用担心我还要依赖Zookeeper,Etcd, Consul了, 光部署这些东西就浪费我一整天外加三台机器。
  2. 不区分管理节点和Worker节点,想让谁上就让谁上, 猜想是参考了人家Nomad吧, 关键还能运行时Promote, Demote, 太方便了。
  3. Service,Stack外加DAB, 用过都说好, 方便。
  4. Rolling Update, Scale一个Service,灰度发布分分钟搞定(虽然有点鸡肋),这个不用说也是借鉴了K8S。

一张图展示一下Docker1.12的和老版本的功能对比。

20160907194607

当然当前Docker1.12的功能还很初级,还没有听说过有人大规模生产级的应用(据说能管理2000个节点,10w个容器),不过看的出来SwarmKit team 新功能推出的速度还是相当快的。 个人很看好这一趋势。

下面分享一下Docker的一个服务是如何被外部访问的。

在没有Docker的时代我们更喜欢的两种办法来部署一个服务 1, 单个机器上的单个进程,绑定到固定IP上的固定端口, 这种方式的优点是简单明了,程序员同学们需要做的事情最大化的优化单个进程的处理能力,  保障这个进程不会因为各种问题退出, 因为一旦退出就没得玩了。  2, 单个机器上的多个进程绑定到固定端口, 前面放一个代理工具。

容器化后大家发现以前的做法或多或少需要做些调整,  第一,由于主机概念被弱化,集群概念被突出, 应用程序不再依赖某个主机或者某个端口; 第二, 容器生命周期不固定, 容器可以随时被启动被停止, 容器的数量可多可少(这就是我们常说的弹性)。由于上面提到的两个容器化的特性,一些新的问题被引入,聪明的工程师们想到了一些解决办法来应对这些问题, 第一个解决办法就是我们要谈到的服务发现,针对的问题是服务对外地址不定和服务随时被启动停止。 服务被发现以后要解决是如何把任务按照一定规则负载到容器中, 这就是第二个我们要谈的负载均衡。 负载均衡解决了单一入口负载到多个容器上问题, 但是由于容器调度之后可能落到多个机器上, 假如某些主机上面没有工作的容器,而对外服务时候又希望服务可以被访问, Routing Mesh概念引入是解决多个入口点负载到单个容器的问题。

服务发现

单纯的服务发现很好实现, 无非就是监测Container的启动,停止, 根据这些变动记录下服务到底可以通过哪些IP+端口可以被访问, 理论上实现一个初级的可用的服务发现不会超过十行代码。成熟的服务发现还需要额外的工作比如健康检查, 注意到我们之前是假设了容器启动就可以对外服务, 实际中情况可能远非如此, 一个应用启动之后可能要准备环境, 加载数据之后才可以对外服务,健康检查是保证服务可用之后再对外服务。 另外一个成熟的服务发现必备条件是高可用, 明眼人可能已经发现之前的简单版本不具备高可用和状态一致性, 这也是为什么Docker引入了Raft协议。

负载均衡

传统理解的负载均衡根据协议不同可以分为7层的和4层的, 7层负载均衡特点是可以支持很复杂的用户规则,比如HTTP header,Cookie等应用逻辑, 而4层只能支持到IP地址负载不同服务。

Docker1.12的服务发现和负载均衡是结合到一起的, 实现办法有两种,DNS轮询和IPVS。 理解这两种策略之前需要知道的一个前提是Docker为每个服务都分配了一个虚拟IP, 这个IP对应的不是具体的一个Task或者一个具体的Container,而是专门为一个服务保留。

下面我们举例子分别说明一下两种负载均衡实现:

首先准备一下环境。

第一步如下图所示用Go写一段小代码, 编译成二进制放在一个Ubuntu基础镜像里, 同时我的Ubuntu里面安装了一些基础的网络工具, 比如dig, curl, ping等, 保存成镜像ubuntu-base。

20160907194619

20160907194628

第二步, 根据Docker官方安装文档 我们在虚拟机环境里启动两个虚拟机, 构造一个Docker Swarm集群。

20160907194635

第三步, 创建一个overlay网络, 取名overlay-test。

20160907194643

下面我们试一下DNS轮训模式的服务发现和负载均衡, 设置一个服务使用DNS轮询还是vip, 只要设置好endpoint-mode这个参数就好, 如下图所示:

20160907194651

使用下面命令启动我们的服务:

docker service create --network overlay-test  --name demo --replicas=4 --endpoint-mode=dnsrr ubuntu-base

—network overlay-test是指定使用刚刚创建的overlay网络, –replicas=4 指定task数量,  –endpoint-mode=dnsrr使用dnsrr方式做负载均衡。

启动成功后如下图所示 :

20160907194659

当前机器上运行 docker ps会发现有只有两个容器生成, 原因是另外两个task被调度到另外一台主机上。

20160907194706

下一步我们考虑进入容器中看一下虚拟出来的网络情况 。

20160907194715

我们之前明明指定了使用overlay-test网络, 按理应该只有两块网卡, 实际上Docker却为我们生成了4块, 多出来的两块是Docker为容器做的手脚, 让我来一一讲解为什么会有4块网卡的出现。

  1. lo网卡不用多说, 是本地网卡, 也叫回环网卡;
  2. eth2属于之前创建的overlay-test网络;
  3. eth1和docker_gwbridge网桥构成的网络可以使容器内的服务可以在主机上访问, 主机上telnet 172.18.0.4可以访问到我们的服务,如下图

20160907194723

eth0是ingress网络中的一块网卡, 是为了Routing Mesh而设,稍后我们会详细说明。

继续回到容器里面看我们查看一下服务情况, 执行命令:

20160907194730

20160907194739

大家会发现通过服务名或者容器ID都能找到ip地址, 区别是服务名发现了本机上所有提供服务的容器IP, 至此同时实现了服务发现和内部负载均衡。

以上就是通过DNS轮询实现的服务发现和负载均衡, 这样的负载均衡是有一些缺点的, 比如:

一些应用可能缓存DNS请求, 导致容器变化之后DNS不能实时更新;

DNS生效时间也导致不能实时反映服务变化情况;

由于以上原因导致的负载均衡不够准确。

下面我来演示一下VIP和IPVS模式的服务发现和负载均衡。 VIP和IPVS原理上比较容易理解, 就是Docker为每个服务分配了一个VIP, DNS解析服务名称或者自定义的别名到这个VIP上。

由于VIP本身没有容器提供服务,Docker把到VIP的请求通过IPVS技术负载到后面的容器上。

Docker发布一个服务时候默认会选择VIP模式, 下面我们用同样的镜像演示一下。

docker service create –network overlay-test  –name demo –replicas=4 –endpoint-mode=vip —publish 8080:8080 ubuntu-base

启动服务之后我们inspect一下, 会发现Virtual IPs有两个入口, 期中10.0.0.2就是overlay-test网络下面的一个Endpoint, 这个Endpoint是服务而非容器的。

20160907194747

我们进入容器中执行dig命令会发现:

20160907194755

此时DNS解析出来的是一个虚拟地址, 虚拟地址通过IPVS和一系列的iptables规则动态转发到容器中。 这样的负载均衡屏蔽了DNS轮询的时效性问题,同时可以支持UDP等协议, 性能也非常好。 缺点是不能做比如回话保持, 基于URL等应用层协议的规则的转发。

下面聊聊Routing Mesh, Routing Mesh的目的是每个主机上都为服务预留端口, 保证每台机器上都可以访问到服务。实现的办法就是Ingress网络, 之前我们提到容器中会多出一块网络,我们Inspect ingress网络,同时会发现网络对应的容器上多出一个容器 ingress-sbox。

20160907194807

一个请求到主机端口8080之后, 数据包的流向如下所示:

主机端口8080 => Ingress-sbox-VIP:8080 => 容器Ingress-sbox =>  IPVS分发到containers。

大家可以看到访问主机之后数据包流到了一个特殊的Sandbox容器里, 这个容器和我们的容器共享一个Ingress网络,通过Iptables和IPVS等重定向到了最终容器之上。 达到了服务在任何一台主机的8080端口都可达的目的。

以上就是我理解的Docker SwarmKit的几种功能, 一些新的网络技术大家也在讨论,比如Macvlan和Ipvlan等, 通过这些技术Docker的应用场景越来越丰富, 越来越成熟, 让我们拭目以待。

K8S中文社区微信公众号
分享到:更多 ()

评论 抢沙发

评论前必须登录!