数人云工程师手记 | 基于 Docker 1.12 Swarm 的集群管理开发实践

2016-09-09 18:19:18 +08:00
 dataman

Mesos/Marathon 折腾久了,我们一直希望有机会深入到 Swarm 内部一探究竟。 另外, Mesos 这一套东西虽然是久经企业级考验的, 但是安装、部署和使用相对复杂,上手有门槛。同时,在今年的 DockerCon 上,内置了 Swarm 功能的 Docker 1.12 发布。基于以上背景,数人云计划围绕 Docker 1.12 Swarm 开发一版轻量级的集群管理工具,也借此与 Mesos/Marathon 对比下。目前,我们第一版数人云容器管理面板 Crane 已经开发完毕,过程也是磕磕绊绊,这里趁机总结几篇技术分享。

正文开始前先八卦一下,关注 Docker 技术的小伙伴们应该清楚 Docker 1.12 的 Swarm mode 颇受争议:首先是有人认为 Docker 公司 Market Drive Develop ,违背了 Linux 信徒恪守的哲学——一个工具只干一件事情;其次, 有人认为 Swarm mode 的功能不及 Mesos 和 K8S ,还不适合生产环境使用,这一点我倒认为稳定性而不是功能才是 Swarm 目前不适合生产环境的原因;最后, Docker 的向后兼容性不足也引来口水无数,毕竟 Docker 还在 active develop 。其它的像容器网络标准的争议, Runc 的争议这些都把 Docker 推到了风口浪尖。当然,不辩不明,相信 Docker 给我们提供的不止眼前这些。

我们首先从应用编排( Application Stack )谈起,应用编排是 Docker 1.12 引入的概念,目前还是 experimental 的功能,必须得安装 experimental 的包才可以尝试。除去编排 (stack), Docker 1.12 还引入了服务 (service) 和任务 (task) 的概念, Docker 借此重新阐述了应用与容器 (container) 之间的关系。上述几个概念的关系如下图所示:

(图片来自网络)

即:一个应用编排代表一组有依赖关系的服务,服务之间可以相互发现(后面详细介绍),每个服务由多个任务组成,任务的数量可以扩缩 (scale),而任务则物化为一个具体的 Docker 容器及其配置。

Docker 通过扩展名为 dab ( Distributed application bundles) 的文件来描述一个应用编排,下图是一个带有两个服务的 dab 文件例子:

这里有 dab 文件的详细介绍: https://github.com/docker/docker/blob/master/experimental/docker-stacks-and-bundles.md

其中 Image 这里推荐使用 image@digest 而不是 image:tag ,原因是为了避免这种情况 : 在集群中部署服务时, image:tag 无法保证镜像是全局一致的,本地的 image:tag 可能与镜像仓库里面的 image:tag 数据不一致,这个问题在跨机环境中被放大。而 image@digest 这种方式通过中心化仓库设置全局唯一的 digest 值,避免了上述问题。

除此之外,还有下述几个关键特性值得分享:

当然, Swarm mode 还有很多问题亟待解决:

接下来让我们看看下一层: Docker 是怎样在 stack, service, task container 之间建立联系的?同一个 stack 内的 service 又是如何相互发现的?

第一个问题很好回答, service label 和 container name , Docker 通过在 service 上添加 label: com.Docker.stack.namespace=XXX 来标示这个 service 属于哪一个 stack 。我们可以 inspect 一个 service 看下:

Docker 通过特定格式的 container name 标示这个 container 隶属于哪一个 service 下面,如下图所示:

容器名称 merryfox_mysql.1.by842qrj7xhsne93yzfpjp367 代表该容器是服务 merryfox_mysql 的任务 1 的容器。 Docker 在很多地方使用了这种技巧来处理数据。而第二个问题就引出了我们下面的——服务发现。

服务发现

在谈服务发现之前,我们简单讨论下 Docker overlay 网络的性能问题,根据 https://www.percona.com/blog/2016/08/03/testing-Docker-multi-host-network-performance/ 的网络压测结果,相较于 host 网络, overlay 有 60% 的网络性能损耗,问题主要出在多 CPU 下网络负载不均。同时容器无法在 Swarm 编排模式下使用 host 网络,这带来的问题就是:在 Docker 1.12 Swarm mode 下网络性能损耗无法避免。

与 Marathon / Mesos 的 Mesos-DNS 、 bamboo 类似, Swarm 的服务发现也分为内部服务发现 (internal service discovery) 与外部服务发现 (ingress service discovery),这两种服务发现使用了不同的技术。

如果想让一个服务暴露到集群之外,我们需要借助 service create 的参数 publish ,该参数显式的声明将集群特定端口 PORT_N (集群端口 PORT_N 代表集群中所有主机的端口 PORT_N )分配给这个服务。这样无论该服务的容器运行在哪台主机上,我们都可以通过集群中任何主机的 PORT_N 端口访问这个服务了。下图可以形象的描述这个场景:

接下来,我们就可以把集群中部分或所有主机的 IP 加上述端口配置到我们的前置负载均衡器上对公网提供服务了。一般称这种分布式的负载均衡策略为 routing mesh ,在 Calico 网络方案中也提到了类似的概念。由于没有了中心化的负载均衡器,集群不会因某台机器异常而导致整个服务对外不可用,很好的避免了单点问题,同时也带了可扩展性。关于 routing mesh 这个概念的详细介绍可以参考该链接( https://en.wikipedia.org/wiki/Mesh_networking)。

这里摘抄一个简短解释:

A mesh network is a network topology in which each node relays data for the network. All mesh nodes cooperate in the distribution of data in the network.

上述就是 Swarm 的外部负载均衡(也可以称为 routing mesh ),那么 Docker 在底层做了什么来实现上述功能的呢?如下图所示:

  1. 在 Swarm 集群初始化时, Docker 会创建一个 overlay 网络 ingress ,同时在每个节点上创建与 ingress 关联的 sandbox 网络命名空间,这样集群中的每个主机都变为了 ingress 网络的一部分;

  2. 当我们创建 service 申请一个 publish port 时, Docker 会通过 Iptables rules 建立 主机 IP:Port 到 sandbox IP:Port 间的映射,即 : 将对应端口的包转发给 ingress 网络;

  3. 同时在 sandbox 网络命名空间内, Docker 通过 Iptables rules + IPVS 控制对应 端口 / Port 的包负载均衡到不同的容器。这里 IPVS(IP virtual server) 的功能与 HAproxy 类似,承担着 Swarm 集群内部的负载均衡工作。

对于只供集群内部访问的服务,无需使用上述 routing mesh , Swarm 还提供了一套内部服务发现 + 负载均衡。如下图所示(这里我们只讨论基于 VIP 的负载均衡方法):

manager 会为该 service 分配一个 VIP(Virtual IP),并在内部 DNS 上建立一条 VIP 与 service name 的映射记录。注意这里的 DNS server 也是分布式的,每一个主机上都有一个 DNS server ; Iptables 配合 IPVS 将 VIP 请求负载到 service 下面的一个具体容器上。

另外,主机间 routing mesh , load balancing rule 等信息是利用 gossip 协议进行数据同步的,限于篇幅,这里不再深究这个问题。

最后友情提示几个雷区:

Q1 :为什么我的机器无法加入 (join) 到 Swarm 集群中?终端报错:加入集群超时。 A1 :这个问题极有可能是主机间时钟不同步导致的,建议开启 ntp 服务,同步主机时间。

Q2 :为什么我在 manager A 上通过命令 Docker network create 的 overlay 网络无法在集群另外的机器 B 上通过 Docker network ls 发现? A2: 这有可能是正常的。按 Swarm 当前的设计,只有使用相应网络的容器被调度到了机器 B 上, overlay 网络信息才会更新到机器 B 上去。

Q3: 为什么我的服务的 publish port 在有些机器上不生效?我使用 netstat – lnp 方式看不到端口监听。 A3: 与问题 1 一样,这也可能是时钟不同步导致的问题。

参考链接: http://collabnix.com/archives/1391 https://sreeninet.wordpress.com/2016/07/29/service-discovery-and-load-balancing-internals-in-Docker-1-12/

3177 次点击
所在节点    Docker
0 条回复

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/305142

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX