2.数据库自动化
在单个Jepsen测试中,DB封装了用于设置和拆除我们所测试的数据库、队列或者其他分布式系统的代码。我们可以手动执行设置和拆除,但是让Jepsen处理它可以让我们在持续集成(CI)系统中运行测试、参数化数据库配置和连续地从头开始运行多个测试,等等。
在src/jepsen/etcdemo.clj中,我们需要使用jepsen.db、jepsen.control、jepsen.control.util和jepsen.os.debian命名空间,每个名称有别名作为简称。clojure.string将帮助我们为etcd建立配置字符串。我们还将从clojure.tools.logging中引入所有功能,为我们提供log功能,例如info,warn等。
(ns jepsen.etcdemo
(:require [clojure.tools.logging :refer :all]
[clojure.string :as str]
[jepsen [cli :as cli]
[control :as c]
[db :as db]
[tests :as tests]]
[jepsen.control.util :as cu]
[jepsen.os.debian :as debian]))然后,在给定特定版本字符串的情况下,我们将编写一个构造Jepsen DB的函数。
(defn db
"Etcd DB for a particular version."
[version]
(reify db/DB
(setup! [_ test node]
(info node "installing etcd" version))
(teardown! [_ test node]
(info node "tearing down etcd"))))如上代码块所示,(defn db ...之后的字符串是文档字符串 ,记录了函数的行为。 当获得version时,db函数使用reify构造一个满足Jepsen的DB协议的新对象(来自db命名空间)。该协议指定所有数据库必须支持的两个功能:(setup!db test node)和(teardown! db test node),分别代表设置和拆除数据这两大功能。 我们提供存根(stub)实现在这里,它仅仅是输出一条参考消息日志。
现在,我们将通过添加:os来扩展默认的noop-test,以告诉Jepsen如何处理操作系统设置,以及一个:db,我们可以使用刚编写的db函数来构造。我们将测试etcd版本v3.1.5。
跟所有Jepsen测试一样,noop-test是一个有诸如:os, :name和:db等键的映射表。有关测试结构的概述详见jepsen.core,有关测试的完整定义详见jepsen.core/run。
当前noop-test只有这些键的一些存根实现。但是我们可以用merge来构建一份赋予这些键新值的noop-test映射表的拷贝。
如果运行此测试,我们将看到Jepsen使用我们的代码来设置debian,假装拆除并安装etcd,然后启动其工作者。
看到了版本字符串"v3.1.5"是怎么从etcd-test传递到db,最终被reify表达式获取使用的吗?这就是我们参数化Jepsen测试的方式,因此相同的代码可以测试多个版本或选项。另请注意对象reify返回的结果在其词法范围内关闭,记住version的值。
安装数据库
有了已经准备好的DB函数框架,就该实际安装一些东西了。让我们快速看一下etcd的安装说明。 看来我们需要下载一个tarball,将其解压缩到目录中,为API版本设置一个环境变量,然后使用它运行etcd二进制文件。
想要安装这些包,必须先获取root权限。因此我们将使用jepsen.control/su来获取root特权。请注意,su(及其伴随的sudo、cd等等)确立的是动态而非词法范围,他们的范围不仅作用于包起来部分的代码,还包括所有函数的调用栈。然后,我们将使用jepsen.control.util/install-archive!来下载etcd安装文件,并将其安装到/opt/etcd目录下。
在我们移除etcd目录的时候,我们正在使用jepsen.control/su变成root用户(通过sudo)。jepsen.control提供了全面的领域特定语言(DSL)在远程节点上执行任意的shell命令。
现在,lein run test将自动安装etcd。请注意,Jepsen在所有节点上同时进行“设置”和“拆卸”。这可能需要一些时间,因为每个节点都必须下载tarball,但是在以后的运行中,Jepsen将重新使用磁盘上缓存的tarball。
启动数据库
根据etcd集群化命令,我们需要生成一串形如"ETCD_INITIAL_CLUSTER="infra0=http://10.0.1.10:2380,infra1=http://10.0.1.11:2380,infra2=http://10.0.1.12:2380"的字符串。这样我们的节点才知道哪些节点是集群的一部分。让我们写几个短函数来构造这些字符串:
->>是Clojure的一个宏,将一个形式插入到下一个形式作为最后一个参数(因为作用类似于缝衣服时候的穿线,因此在英文中命名这个宏为“threading”)。因此,(->> test :nodes)就变成了(:nodes test),而(->> test :nodes (map-indexed (fn ...)))就变成了(map-indexed (fn ...) (:nodes test)),以此类推。普通的函数调用看起来就是“由内而外”,但是->>这个宏让我们“按顺序”编写一系列操作,类似于一个面向对象语言的foo.bar().baz()表示形式。
在函数initial-cluster中,我们从test映射表中获取到了数个节点,并将每个节点通过Clojure内置的map映射为相应的字符串:节点名称、“=”和节点的peer的url。然后我们将这些字符串用(英文)逗号合并起来。
准备好之后,我们会告诉数据库怎么以守护进程的方式启动。我们可以使用初始化脚本或者服务来启动和关闭程序,不过既然我们正在使用的是一个单纯的二进制文件,我们将使用Debian的start-stop-daemon命令在后台运行etcd。
我们还需要一些常量:etcd二进制文件名、日志输出的地方和存储pidfile文件的地方。
现在我们将使用jepsen.control.util内用于启动和关闭守护进程的函数来启动etcd。根据documentation,我们将需要提供一个节点的名称、用于监听客户端和peer节点的数个URLs和集群初始状态。
我们将在启动集群之后调用sleep函数让程序暂停一会儿,这样集群才能有机会完全启动并执行初始的网络握手。
拆除
为了确保每次运行都是从零开始,即使先前的运行崩溃了,Jepsen也会在测试开始之前进行DB拆除,然后再进行设置。然后在测试结束时将数据库再次撕毁。要拆除,我们将使用stop-daemon!,然后删除etcd目录,以便将来的运行不会意外地从当前运行中读取数据。
我们使用jepsen.control/exec运行shell命令:rm -rf。Jepsen会自动指定使用exec,以便在db/setup!期间设置的node上运行一些操作,但是我们可以根据需要连接到任意节点。请注意,exec可以混合使用字符串、数字和关键字的任意组合,它将它们转换为字符串并执行适当的shell转义。如果需要,可以将jepsen.control/lit用于未转义的文本字符串。:>和:>>是Clojure的关键字,被exec接收后可以执行shell的重定向。对于需要配置的数据库,这是将配置文件写到磁盘的一个简单方法。
现在让我们试试看!
上面的运行结果看起来很棒。我们可以通过在测试后检查etcd目录是否为空来确认teardown是否已完成工作。
日志文件
等等——如果我们在每次运行后删除etcd的文件,我们如何确定数据库做了什么?如果我们可以在清理之前下载数据库日志的副本,那就太好了。为此,我们将使用db/LogFiles协议,并返回要下载的日志文件路径的列表。
现在,当我们运行测试时,我们将为每个节点找到一个日志副本,存储在本地目录store/latest/<node-name>/中。 如果我们在设置数据库时遇到问题,我们可以检查那些日志以查看出了什么问题。
寻找“选举产生的领导者”这一行,这表明我们的节点成功地形成了集群。如果您的etcd节点彼此看不到,请确保使用正确的端口名,并在node-url中使用http://而不是https://,并且该节点可以互相ping通。
准备好了数据库之后,接下来该编写客户端。
Last updated
Was this helpful?