Docker 测试开发之路 (工具篇)--docker 1.12 的 swarm mode

孙高飞 · 2016年12月27日 · 最后由 徐旻 回复于 2017年05月02日 · 2149 次阅读
本帖已被设为精华帖!

背景

我一直使用 docker 管理公司的开发和测试环境。为了提高团队的工作效率。至少人手一整套环境 (除了产品各模块外还包含了数据库,etcd 等持久化组件) 以减少环境冲突。 所以随着业务发展,团队人数增加, 越来越多的环境将服务器资源耗尽。到了一定程度后就会分部门使用不同的服务器以缓解资源的消耗。但到了这时候就有主要了以下两个痛点。

  1. 没有动态分配资源和负载均衡能力,资源利用极其不合理。会出现某个服务器很忙但是另一个服务器很空闲的情况。
  2. 部署环境的脚本和镜像在不同服务器上的处理问题。

所以最近工作有空闲的时候开始调研 swarm mode,mesos 和 kubernetes 这种分布式的资源调度框架。今天主要说明一下 swarm mode 的机制。

docker machine:

docker machine 是一个非常有用的学习工具,它能够帮我们迅速搭建起多套装有 docker 的宿主机。用以我们试验管理集群。建议大家学会它。下面简单介绍下使用方式。

MAC 上安装:
curl -L https://github.com/docker/machine/releases/download/v0.8.2/docker-machine-`uname -s`-`uname -m` >/usr/local/bin/docker-machine && \
chmod +x /usr/local/bin/docker-machine
Virtualbox 安装:

如果是在 mac 上,需要使用一个虚拟机来帮助创建 docker host。下载链接:https://www.virtualbox.org/wiki/Downloads

创建 docker host:
docker-machine create --driver virtualbox default (manager node 我暂时就命名为default了)
docker-machine create --driver virtualbox slave1
docker-machine create --driver virtualbox slave2

上面我们就很简单的创建了 3 个带有 docker 最新版本的宿主机供我们这次 demo 使用。下面介绍一下简单的命令使用。

  • help 查看帮助信息
  • active 查看活动的 Docker 主机
  • config 输出连接的配置信息
  • create 创建一个 Docker 主机
  • env 显示连接到某个主机需要的环境变量
  • inspect 输出主机更新信息
  • ip 获取 Docker 主机地址
  • kill 停止某个 Docker 主机
  • ls 列出所有管理的 Docker 主机
  • regenerate-certs 为某个主机重新成功 TLS 认证信息
  • restart 重启 Docker 主机
  • rm 删除 Docker 主机
  • scp 在 Docker 主机之间复制文件
  • ssh SSH 到主机上执行命令
  • start 启动一个主机
  • status 查看一个主机状态
  • stop 停止一个主机
  • upgrade 更新主机 Docker 版本为最新
  • url 获取主机的 URL
  • ssh 进入到宿主机中

例如我运行 docker-machine ssh default 这条命令。我们就会以 ssh 的方式进入到 manager 这台主机中。

swarm mode

swarm mode 是即 swarm 和 swarmkit 之后,内置在 docker1.12 极其以上版本的集群管理框架。特点是简单,易用,易理解。相较于 mesos 和 kubernetes,它的简单易用很适合在公司内搭建非生产环境集群,尤其是给运维经验不足的 QA 使用。普通的 docker 使用者可以很快的过渡到 swarm mode 的使用方式上。当然业界现在基本没人使用它搭建生产环境,尤其是其 overlay 的网络性能尤其让人诟病。

搭建一个集群

用上面 docker-machine 准备的三台机器我们来搭建一个集群

初始化 manager node:
docker swarm init --listen-addr 192.168.99.100:2377 --advertise-addr 192.168.99.100

如果机器有多个 ip 地址要指定--adverties-addr 选择一个 ip,否则会报下面的错:

Error response from daemon: could not choose an IP address to advertise since this system has multiple addresses on different interfaces (10.0.2.15 on eth0 and 192.168.99.100 on eth1) - specify one with --advertise-addr

命令成功后会返回一个全球唯一的 token。这是初始化命令到 docker hub 上申请的 token。用来唯一标识 swarm 的 manager。 之后 node 加入到集群的时候会用到。这样我们就将

将 worker node 加入到集群中
docker swarm join \
    --token SWMTKN-1-47e8gcgrv91taf01zdr252wp7no10m3nb9tgjcgyjgy9r1fszp-7y7h5c06f6qul2t6hwml0d1c7 \
    192.168.99.100:2377

在这里使用之前初始化 manager 的时候生成的全球唯一的 token。

发布一个服务

在这里需要注意的是在 swarm mode 中对外暴露的是服务(service)的概念,而不是容器。在 swarm mode 的设计中,为了保持高可用架构,它准许同时启动多个容器共同支撑一个服务,如果一个容器挂了,它会自动使用另一个容器。所以这里单机的 docker engine 不同。大家要适应这个概念。当然我们在测试和开发环境中基本上不用着什么高可用,我们知道有这么个东西就行了。

docker service create --replicas 3 --mount "type=bind,source=$PWD,target=/var/lib/registry" --publish 8080:80 --name helloworld alpine ping docker.com

在 manager 节点上运行上面这条命令就使用 alpine 这个镜像创建了名为 helloworld 的服务。--replicas 3 这个参数代表给这个服务启动 3 个容器 (刚才说的高可用) ,--mount 这个参数其实就是 docker engine 的-v,将数据卷挂载到宿主机。 --publish 就是 docker engine 的 port,将容器的 80 端口 map 到集群的 8080 端口中。(注意:不论你访问集群中哪一个 node 的 ip 地址。只要访问这个端口号。 swarm node 都会路由到正确的容器中)

下面我们运行 docker service ls 这条命令来查看一下我们所有的服务

可以看到我们刚才创建的 helloworld 上的 3 个实例都已经启动了。我们再运行一个 docker service ps helloworld 来查看一下部署的详细情况

可以看到 swarm 根据内置的资源分配策略,选择了在 slave1 上部署了两个容器,在 manager node 上部署了一个容器。

其他命令和参数

swarm mode 还可以让我们控制更多东西,例如跟 docker engine 的-e 一样可以控制环境变量的-env。可以限制容器资源使用的--reserve 系列参数等等。大家可以使用--help 或者去官方文档中查看。一切都跟 docker engine 很像,我就不多说了。

容器夸主机互联方案

我们都知道在宿主机中,docker 会创建一个默认的 docker0 网桥用以桥接宿主机和容器。 这个 docker0 网桥对于宿主机来说是一个虚拟的网络设备。但是对于 docker 容器来说就是一个三层交换机。所以我们的容器可以通过 docker0 访问外网 (三层交换机有路由功能)。但是外部无法访问容器,因为每台宿主机上的 docker 容器都分别在自己的一个私有的局域网络中,也就是说外部路由器没有这些容器的路由。所以我们在没有集群的时候一般选择 briage 方案将 docker 容器与宿主机部署再同一个网络下,或者直接以 map 容器端口到宿主机端口上,或者直接以 host 网络模式启动容器来达到外部访问容器服务的目的。 这几种方案我不详细讲了,之后转们开一个帖子说 docker 的网络方案。今天我们讲一下 swarm node 内置的 overlay 网络。

overlay

简单介绍下 overlay,我们学过计算机网络基础。都知道有一句常说的话是 2 层 (数据链路层) 转发,3 层 (网络层) 路由。 2 层设备 (网桥,2 层交换机) 并不记录逻辑地址 (ip 地址),而是记录物理地址 (mac) 通过广播的形式寻址。 而三层设备 (三层交换机,路由器) 通过路由规则解析逻辑地址并一跳跟着一跳的通往目的主机。 他们都属于底层的网络通信协议。同一台宿主机的同一个网桥下的所有容器可以互相访问,因为他们处于同一个物理网络,走 2 层协议用 mac 地址进行通信。 容器可以访问外网,因为默认的 docker0 相当于一个三层交换机,具有三层网络的路由功能。上面说的使用 briage 模式达到外部通过 ip 地址访问容器的方式利用的就是三层网络的通信协议。而 overlay 处于应用层,是上层的网络通信协议。通常需要借助一些 K,V 存储设备来保存信息,例如 etcd,ZK 等等,它的性能比较慢,在网上看的资料说 docker 的 overlay 网络方案有 30% 的性能折损。这也是 swarm mode 不适合生产环境的原因之一。具体的原理我也没深研究过,就不卖弄了,大家有兴趣的自己去网上查一下资料吧。

创建 overlay 的网络设备

swarm mode 内部已经支持了 overlay 的网络,所以不需要我们增加额外的 K,V 机制去配置 overlay 的网络。 你需要确保以下的端口已经被打开

  • Port 7946 TCP/UDP for container network discovery.
  • Port 4789 UDP for the container overlay network.

运行命令:

docker network create \
  --driver overlay \--subnet 10.0.9.0/24 \--opt encrypted \
  my-network

这时候我们就创建了一个 overlay 的网络。然后我们在创建服务的时候使用--network 参数指定使用这个 overlay 网络。那么不论容器处于集群中的哪个节点。他们都可以正常的互相通信。

service discovery

好了现在我们的容器可以互相访问了,但是我们怎么知道目的主机的 ip 地址呢? 答案是不知道,因为每次启动容器都是又 overlay 动态的分配一个 VIP(虚拟 IP 地址)。只有在容器创建之后你才知道 IP 地址是什么。那么在制作镜像的时候你怎么知道使用哪个 ip 地址才能访问到我们真正想要去的容器呢? 熟悉 docker engine 的人一定知道 link 这个参数。 swarm mode 的 service discovery 同样实现了类似的功能。swarm 会将容器的 VIP map 到 swarm 的内置的 dns 中,使用 service name 作为域名。所以集群的容器可以使用服务名称当做 IP 地址来互相访问。

高可用

接下来我们讲讲 swarm mode 的一致性策略,也是 swarm mode 的高可用机制,作为 QA 的话这个其实不用了解,因为测试环境一般没有那么大的规模需要做高可用,对此有兴趣的同学可以看一看。先讲讲高可用的概念,我们的例子里只有一个 manager node 控制所有的 worker node,这个唯一的节点维护着所有的服务,健康检查,service api。一旦 manager 节点挂掉我们的服务就会瘫痪。所以需要运行多个 manager 节点来保持我们的高可用。但是多个 manager 节点会碰到另一个问题就是信息的一致性问题,如果当前的 leader 的 manager 节点挂了,那么上面的信息怎么办?我们知道早期的 swarm 以及其他的一些分布式框架都是用 etcd 或者 ZK 这种 K,V 存储辅以一致性算法来保证 master 节点 (在 swarm mode 中叫 manager 节点) 的高可用的。swarm mode 虽然不使用 etcd 或者 ZK 这些东西 (可能是已内置了),但是它依然是使用 Raft 一致性算法的。这里简单介绍一下 Raft 一致性算法的概念:

Raft 一致性算法概念描述

在一个由 Raft 协议组织的集群中有三类角色:

  • Leader(领袖)
  • Follower(群众)
  • Candidate(候选人)

就像一个民主社会,领袖由民众投票选出。刚开始没有领袖,所有集群中的参与者都是群众,那么首先开启一轮大选,在大选期间所有群众都能参与竞选,这时所有群众的角色就变成了候选人,民主投票选出领袖后就开始了这届领袖的任期,然后选举结束,所有除领袖的候选人又变回群众角色服从领袖领导。这里提到一个概念「任期」,用术语 Term 表达。关于 Raft 协议的核心概念和术语就这么多而且和现实民主制度非常匹配,所以很容易理解。三类角色的变迁图如下,结合后面的选举过程来看很容易理解。

Leader 选举过程

在极简的思维下,一个最小的 Raft 民主集群需要三个参与者(如下图:A、B、C),这样才可能投出多数票。初始状态 ABC 都是 Follower,然后发起选举这时有三种可能情形发生。下图中前二种都能选出 Leader,第三种则表明本轮投票无效(Split Votes),每方都投给了自己,结果没有任何一方获得多数票。之后每个参与方随机休息一阵(Election Timeout)重新发起投票直到一方获得多数票。这里的关键就是随机 timeout,最先从 timeout 中恢复发起投票的一方向还在 timeout 中的另外两方请求投票,这时它们就只能投给对方了,很快达成一致。

选出 Leader 后,Leader 通过定期向所有 Follower 发送心跳信息维持其统治。若 Follower 一段时间未收到 Leader 的心跳则认为 Leader 可能已经挂了再次发起选主过程。

Leader 节点对一致性的影响

Raft 协议强依赖 Leader 节点的可用性来确保集群数据的一致性。数据的流向只能从 Leader 节点向 Follower 节点转移。当 Client 向集群 Leader 节点提交数据后,Leader 节点接收到的数据处于未提交状态(Uncommitted),接着 Leader 节点会并发向所有 Follower 节点复制数据并等待接收响应,确保至少集群中超过半数节点已接收到数据后再向 Client 确认数据已接收。一旦向 Client 发出数据接收 Ack 响应后,表明此时数据状态进入已提交(Committed),Leader 节点再向 Follower 节点发通知告知该数据状态已提交。

在这个过程中,主节点可能在任意阶段挂掉。我们通过在这些节点中复制信息的方式防止数据信息丢失,其中的细节在这不描述了。有兴趣的在家网上查一下资料吧。我的图也是网上剽窃下来的哈哈。

swarm mode 中的 manager node 和 worker node

下面是 swarm node 的架构图:

manager node:

在 swarm mode 中,所有节点分为 manager node 和 worker node。 manager node 作为整个集群的调度者,负责分配资源,分发 task,管理各种 service。 同时 manager node 使用一种 Raft 一致性算法来保持集群的高可用状态。 这种算法是在所有的 manager node 中采取选举方式,决定哪一个 ndoe 是 leader。 所以为了保持高可用和容错率,需要保持多个 manager node。通过上面的 Raft 一致性算法的描述我们知道,这种算法在决定 leader 的时候是通过选举的方式,所以为了保持这种机制的有效性,必须维持至少 3 个以上的容器才能保持有效的容错率和高可用。 下面是官网的信息:

  • A three-manager swarm tolerates a maximum loss of one manager.(3 个 manager node 可以有一个的容错率)
  • A five-manager swarm tolerates a maximum simultaneous loss of two manager nodes.(5 个 manager node 可以有 2 个的容错率)
  • An N manager cluster will tolerate the loss of at most (N-1)/2 managers.(N 个 manager node 可以有 (N-1)/2 个的容错率)

Docker recommends a maximum of seven manager nodes for a swarm.(推荐在一个集群中保持 7 个 manager node)

同时一个 manager ndoe 也是一个 worker node,也就是说 manager node 也可以执行 task,启动容器。 但是我们可以配置 manager 节点为 manager only 模式。上面说推荐在一个集群中保持 7 个 manager node。既然 manager node 也可以同时是 worker node,那为什么不让所有的节点都是 manager 呢? 因为 manager node 为了保持一致性会有很多额外的资源开销。

worker node:
worker node 就是实际执行任务的节点。 它不负责维护集群的整体信息,也不负责维护集群的 API。这个就没啥好说的了。

为集群加入 manager node

加入 manager node 的方式也很简单,我们上面 init 一个 swarm 集群的时候会返回一个 token,那个 token 是 worker 节点加入集群的 token,现在我们在 manager 节点上运行 docker swarm join-token manager 这个命令就会得到一个 manager node 的 token。当我们用这个 token 加入一个集群的时候,就是添加了一个 manager node。如下:

总结

接下来会去调研一下 mesos 和 kubernetes,然后继续发帖子比较一下他们的不同和优劣。

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 8 条回复 时间 点赞
恒温 将本帖设为了精华贴 12月28日 09:07

有几个错别字,另外能对应测试项目举个例子不?

swarm 虽然是官方的方案. 但是 docker 这家公司正在式微, 他推的方案跟 mesos, k8s 竞争, 太固步自封了.
这种最好是专门的运维团队去搞. 坑太多了. 需要联合研发运维确定一个统一的制度. 雪球之前对外讲过一个自己的持续交付实践. 用的是自己的一套简化的体系.

#3 楼 @seveniruby docker 在生产环境确实不尽人意,没人用。我也只是想在公司内部看看 swarm 是否可行,之后也会去调研一下 mesos 和 k8s。 我们的产品之后要涉及到多租户,资源隔离。所以大家都在调研各种集群部署方式。 多了解一些以后测试也方便,之前跟一些人沟通,发现涉及到这些的东西都是开发和运维自测,很少有 QA 介入,都觉得 QA hold 不住,我也是有野心想搞一搞。 我们公司的情况也比较特殊。暂时没有专职的运维,但是有一票做过运维的开发。所以运维的工作都是我们平分了。 公司内的所有产品环境都是用我的那套机制去维护。所以好多东西也就逼得我去了解了。另外对 devops 的东西非常感兴趣,有意思往这方面发展一下,所以最近也是积极的学习一些运维的知识。

#3 楼 @seveniruby 正在尝试 QA 涉足 devops 的路线是否可行,希望能以 QA 之身在公司内部打通开发 - 测试 - 运维的界限。做出更好的工程化实践。

#2 楼 @Lihuazhang 额,目前还在调研中,没有实际用在公司的项目中。 之后比较一下 mesos 和 k8s,最后跟同事商量一下到底用哪种方案。 没准真正落地是很久之后的事了

#5 楼 @ycwdaaaa 这种思路很好,多尝试,👍

#5 楼 @ycwdaaaa 希望能分享经验,目前也是在兼职测试开发和运维开发,反正就是正统开发以外的所有。。

@ycwdaaaa 很同意你的 QA 涉及 devops 的想法。其实自动化测试用例什么写写脚本相对来说还是挺简单的。后来接触到 CI,jenkins,又发现 docker,统一的测试环境,这些东西其实冲概念上来说都是运维的东西。但是真的要落实到自动化测试框架的时候这些东西还真的不可以缺少,渐渐的发现什么东西都要学。不求精通,至少要会用。这样才能真的构建成一个自动化测试框架。最近在看 docker,不知道能不能请教私下请教些问题呢?

ABEE ycwdaaaa (孙高飞) 在 TesterHome 的发帖整理 中提及了此贴 01月12日 13:47
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册