Mesos/Marathon 折腾久了,咱们一直但愿有机会深刻到 Swarm 内部一探究竟。 另外, Mesos 这一套东西虽然是久经企业级考验的, 可是安装、部署和使用相对复杂,上手有门槛。同时,在今年的 DockerCon 上,内置了Swarm 功能的 Docker 1.12 发布。基于以上背景,数人云计划围绕 Docker 1.12 Swarm 开发一版轻量级的集群管理工具,也借此与 Mesos/Marathon 对比下。目前,咱们初版数人云容器管理面板 Crane 已经开发完毕,过程也是磕磕绊绊,这里趁机总结几篇技术分享。node
正文开始前先八卦一下,关注 Docker 技术的小伙伴们应该清楚 Docker 1.12 的 Swarm mode 颇受争议:首先是有人认为 Docker 公司 Market Drive Develop,违背了 Linux 信徒恪守的哲学——一个工具只干一件事情;其次, 有人认为 Swarm mode 的功能不及 Mesos 和 K8S,还不适合生产环境使用,这一点我倒认为稳定性而不是功能才是 Swarm 目前不适合生产环境的缘由;最后, Docker 的向后兼容性不足也引来口水无数,毕竟 Docker 还在 active develop。其它的像容器网络标准的争议, Runc 的争议这些都把 Docker 推到了风口浪尖。固然,不辩不明,相信 Docker 给咱们提供的不止眼前这些。mysql
咱们首先从应用编排( Application Stack)谈起,应用编排是 Docker 1.12 引入的概念,目前仍是 experimental 的功能,必须得安装 experimental 的包才能够尝试。除去编排 (stack), Docker 1.12 还引入了服务 (service) 和任务 (task) 的概念, Docker 借此从新阐述了应用与容器 (container) 之间的关系。上述几个概念的关系以下图所示:
(图片来自网络)git
即:一个应用编排表明一组有依赖关系的服务,服务之间能够相互发现(后面详细介绍),每一个服务由多个任务组成,任务的数量能够扩缩 (scale),而任务则物化为一个具体的 Docker 容器及其配置。github
Docker 经过扩展名为 dab( Distributed application bundles) 的文件来描述一个应用编排,下图是一个带有两个服务的 dab 文件例子:sql
这里有dab文件的详细介绍:
https://github.com/docker/doc...docker
其中 Image 这里推荐使用 image@digest 而不是 image:tag,缘由是为了不这种状况 : 在集群中部署服务时, image:tag 没法保证镜像是全局一致的,本地的 image:tag 可能与镜像仓库里面的 image:tag 数据不一致,这个问题在跨机环境中被放大。而 image@digest 这种方式经过中心化仓库设置全局惟一的 digest 值,避免了上述问题。安全
除此以外,还有下述几个关键特性值得分享:网络
滚动更新:服务的镜像更新是一个基本诉求。 Docker 能够经过关键词 update-parallelism 和 update-delay 来控制并行更新的频率。 Marathon 也提供了相似的功能。这个特性很关键,若是没法控制更新频率,成百上千的镜像拉取和任务调度会致使严重的资源波峰。 Docker 文档还声称支持更新失败回滚,尝试了下,目前没发现怎么玩,还没来得及看底层代码。架构
服务模式 (service mode): Docker 1.12 提供了两种方式控制任务数量—— replicated 和 global,在 replicated 方式下,咱们须要提供指望的任务数量, Swarm 将一直尝试维护这个任务数;而在 global 方式下, Swarm 尝试在每一个节点上启动一个任务,这种方式特别适合向每一个节点下发 agent 的场景。app
stop-grace-period 参数:在服务缩容时,咱们比较关心容器被强制 kill 而带来的事务 (transaction) 问题,配合该参数 stop-grace-period( 强制杀死容器前的等待时间 ) 和容器内部的退出信号监听,能够达到容忍程序友好退出和快速回收资源之间的平衡。 Mesos / Marathon 也采用了相似的策略来解决这个问题。
with-registry-auth 参数:在服务建立时,该参数声明将管理节点 (Swarm manager) 上的 registry authentication 信息带到工做节点 (worker node) 上,从而为工做节点提供从 registry 拉取镜像的认证信息。在 Docker 的非 Swarm 场景下, Docker-client 负责 registry 认证信息的管理,但在 Swarm 方式下,再也不是 Docker-client 触发镜像拉取动做,因此服务没法使用工做节点本地的 registry 认证信息了,必需要经过上述方式从管理节点分发认证信息。同时节点间的加密通讯也保证了认证信息传输的安全性。
任务的生命周期:在容器的生命周期之上, Docker 1.12 引入了任务 (task) 的生命周期。某任务下的容器异常退出时,带有一样任务编号 (slot) 的新容器将会被尝试启动。不一样于容器的生命周期只囿于一台固定主机上,任务的生命周期是与主机无关的,咱们能够依此对容器的日志进行聚合获得任务的日志。这一点正好是 Mesos / Marathon 所欠缺的。
重启策略:Docker 1.12 提供了三种重启条件 -any, none, on-failure,其中 none 指的是退出不重启,on-failure 指的是失败( exit code 不是零)时重启,而 any 指的是不管任务正常或是异常退出,都重启。any 方式配合参数重启间隔 (restart-delay) 能够知足定时任务的场景; none 方式则能够知足批处理任务场景。另外参数评估间隔 (restart-window) +参数尝试次数 (restart-max-attempts) 能够控制在一段时间内的任务重启次数,避免异常任务频繁重启带来的集群资源失控。
固然,Swarm mode 还有不少问题亟待解决:
dab 文件的表达能力有限:当前版本的 dab 文件只有寥寥数个关键词,服务 (service) 建立的诸多参数 dab 都没法支持。在咱们的实践中,为了解决这个问题, team 不得不二次开发引入服务的其它参数,以期对服务的参数全量支持。
容器回收问题:按照目前的设计,只要服务 (service) 还在,退出的容器是不会被自动回收掉的,在某些场景下,这会致使集群失控,各主机的文件描述符被耗尽。
容器的健康检查 (healthcheck): 在我看来,这是 Swarm mode 以外, Docker 1.12 引入的最关键功能,有了 healthcheck 这个 feature,咱们能够将业务内部真正的健康情况暴露出来了。这个功能落后于 Marathon 足足一年的时间。但惋惜的是,服务( service)建立目前还不支持这个关键词,这就限制了服务 (service) 异常重启的能力,从而间接下降了服务容错能力。这一点 Marathon 作的特别好。
资源控制: 1.12 目前支持单个任务 CPU / mem 的资源控制。还没法像 Mesos 那样,自由的配置管理磁盘, GPU,端口等资源。
没法使用主机网络:经过服务( service)启动的容器是没法使用主机网络的 (network host is not eligible for Docker services),但同时按照网络上 Percona 提供的压测结果, overlay 网络相较于主机网络有 60% 的性能损耗。这严重局限了 Swarm 的应用场景,咱们能够认为这是编排功能带来的架构反作用。而 Mesos 从资源维度管理集群很好的规避了这个问题。
接下来让咱们看看下一层: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/... 的网络压测结果,相较于 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...。
这里摘抄一个简短解释:
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 在底层作了什么来实现上述功能的呢?以下图所示:
在 Swarm 集群初始化时, Docker 会建立一个 overlay 网络 ingress,同时在每一个节点上建立与 ingress 关联的 sandbox 网络命名空间,这样集群中的每一个主机都变为了 ingress 网络的一部分;
当咱们建立 service 申请一个 publish port 时, Docker 会经过 Iptables rules 创建 主机 IP:Port 到 sandbox IP:Port 间的映射,即 : 将对应端口的包转发给 ingress 网络;
同时在 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...
https://sreeninet.wordpress.c...