2017年中,我参与了一个亚太地区互联网公司并购的项目,客户收购了亚太地区 7 个国家的同行业互联网企业和产品。我作为其中的 DevOps 咨询师和 DevOps 工程师,和客户一起完成并购后的产品迁移和技术能力提升的设计、实施和培训。
客户希望采用新的统一产品,并根据不同地区的业务特色进行一些定制,与此同时,需要进行数据迁移以保证业务可以继续运行。其中一个很关键的步骤是把原系统的 URL 通过重定向的方式到新的产品中,因为有很多的第三方链接和搜索引擎依然保留了原系统中的链接。
初步统计了一下,将近有3000多个 URL 需要重定向,光是规则和正则表达式就写了 400 多条(没有统一模式的 URL 害死人啊),这就引发了一个问题:我该如何验证这些规则和覆盖这些 URL ?此外,大量的重定向不光对用户来讲不是很好的体验,如果我要优化这些规则,我如何保证我当前的转发规则不被破坏?
解决方案 #
最早,我们写了一个 Shell 脚本,用 curl
命令来验证这些 URL,最初只需要验证 200 条就可以满足需求,时间也不到两分钟。后来,我们采用了一个 Excel 文件来跟踪这些 URL,产品经理只需要把新的重定向 URL 补充到上面,我们就依据这些 URL 来开发 nginx 的重定向规则。
这让我想到了 TDD:先写出一个自动化测试用例,然后修复这个自动化测试用例。更好的是,有了自动化的测试做保护,你可以放心和安全的对代码进行重构。
此外,随着更多的 URL 需要重定向,这个数字在不断的增加。原先的 Shell 脚本执行的时间也从最初的 2 分钟增长到了15分钟。
现有的工具满足不了要求,一怒之下,我决定开发一个自己的工具。它必须具备以下特点:
- 可以通过文件读取规则,进行大批量验证。
- 多线程并发执行,可以提升效率。
- 很容易和 CI 集成。
- 能帮我做一定程度的重定向优化分析。
于是,我在一个周末的时间用 Python 写下了 vivian
: 一个多线程的批量自动化重定向验证工具。
它把原先的 15 分钟的验证时间缩短到了 17 秒,效率提升了 5294 % !!
此外,我把测试用例集成到了代码库里。并把 vivian 提交到了 pipy,这样我就可以通过 pip 在初始化 CI 上安装了。也无需增加到代码库里变成一个需要维护的代码脚本。
选择 Python 的原因主要是因为相较于 Ruby, Go, Java, NodeJS 来说。Python 的语言环境比较稳定,几乎每种 Linux 都包含 Python 的运行环境,且容易安装和集成。
安装使用 Vivian #
安装
使用
test.csv 非常简单,第一列是源 URL,第二列是目标 URL。例如:
http://www.github.com, https://github.com/
http://www.facebook.com, https://facebook.com/
采用 csv 文件的目的主要是方便使用 Excel 和文本工具编辑。
之后会得出下列结果:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| vivian -f example.csv
load test case from example.csv
2 cases loaded running in 2 threads
verifying http://www.github.com to https://github.com/
verifying http://www.facebook.com to https://facebook.com/
Failed cases:
============================================================
line: 2
origin: http://www.facebook.com
dist: https://www.facebook.com/
expect: https://facebook.com/
status: 200
redirect_count:1
------------------------------------------------------------
1/2 PASS in 5.8494720458984375 seconds
|
第一行输出提示测试用例文件位置。
第二行输出提示测试用例数量和线程数量。你也可以通过增加 -n 来指定线程的数量,默认线程数量等于 CSV 文件记录行数。
第三行到第四行列出了需要验证的 URL。
第五行开始就是失败的测试用例信息:
失败用例的第一行就是测试用例所在的文件行号。
失败用例的第二行是测试用例测试的源 URL。
失败用例的第三行是访问测试的 URL 的实际目标 URL。
失败用例的第四行是期望得到的 URL。
失败用例的第五行是访问测试用例源 URL 最后得到的 HTTP 状态。
失败用例的第六行是访问测试用例源 URL 到最后结果之间的 重定向次数,有了这个数字我们可以优化 URL。
最后一行表明有多少个用例通过了测试,同时统计了完成这些测试的总时间。
最佳实践 #
以下是我总结的使用 vivian 的最佳实践场景,希望能对你的 web 服务器维护工作起到帮助。
采用 Excel 生成测试用例 #
Excel,包括 Google Sheet。都是很方便的进行团队交流用的数据文件。对于很多开发和运维工程师来说,如果手动编写测试用例肯定是很费工夫的事情。所以,你只需要创建一个两列的 Excel 文件,分别存储源 URL 和目标 URL。之后你就可以采用 Excel 便捷的复制功能和计算功能生成很多的测试用例。
不过,需要注意的是,你需要将 Excel 文件保存为 CSV 格式,才可以识别哦。
采用 TDD 开发 nginx 规则 #
当有了一个自动化的测试工具,我们就可以构建测试驱动开发实践。下面我举个例子演示一下:
首先,我希望我的 nginx 配置可以帮我把所有请求通过 301 永久重定向转发到我的博客 http://wizardbyron.github.io 上去。
首先,我需要创建一个虚拟域名,例如:local.testhost.com
为了方便起见,我修改了我本地的 hosts 文件。加入如下一行:
local.testhost.com 127.0.0.1
因此,我需要写一个测试用例文件 redirect_case.csv :
http://local.testhost.com, https://wizardbyron.github.io
当然,你也可以采用你的测试环境。
接下来,我可以采用 vivian 来进行测试:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| vivian -f redirect_case.csv
load test case from redirect_case.csv
1 cases loaded running in 1 processs
verifying http://local.testhost.com to https://wizardbyron.github.io
Failed cases:
============================================================
line: 1
origin: http://local.testhost.com
dist:
expect: https://wizardbyron.github.io
status:
redirect_count:0
------------------------------------------------------------
============================================================
|
我们发现,测试失败,因为我们并没有启动任何一个 http 服务器。接下来,我们可以通过 docker 来启动一个 nginx 服务器:
1
| docker run --name nginx -d -p 80:80 nginx
|
记得要把 80 端口映射出来。接下来我们再次进行测试:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| vivian -f redirect_case.csv
load test case from redirect_case.csv
1 cases loaded running in 1 processs
verifying http://local.testhost.com to https://wizardbyron.github.io
Failed cases:
============================================================
line: 1
origin: http://local.testhost.com
dist: http://local.testhost.com
expect: https://wizardbyron.github.io
status: 200
redirect_count:0
------------------------------------------------------------
============================================================
|
通过这次测试,我们发现虽然有了 nginx 服务器,但是我们的结果不正确。这时候,我们可以把 nginx 的配置文件从 nginx 容器里面复制出来:
1
| docker cp nginx:/etc/nginx/nginx.conf .
|
然后修改配置文件为如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
| user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
include /etc/nginx/conf.d/*.conf;
# 增加 server 的配置
server {
listen 80 default_server;
listen [::]:80 default_server;
location / {
return 301 https://wizardbyron.github.io;
}
}
}
|
然后,我们把这个配置文件挂载到 nginx 容器中,取代默认的 配置文件。然后重新运行 nginx 容器:
1
| docker run --name nginx -d -v $(pwd)/nginx.conf:/etc/nginx/nginx.conf -p 80:80 nginx
|
然后我们再执行测试:
1
2
3
4
5
6
7
8
9
| vivian -f redirect_case.csv
load test case from redirect_case.csv
1 cases loaded running in 1 processs
verifying http://local.testhost.com to https://wizardbyron.github.io
Failed cases:
============================================================
No failed case.
============================================================
1/1 PASS in 0.9458322525024414 seconds
|
测试通过!
用 TDD 的方式重构 nginx 转发规则 #
如果可以做 TDD ,那么我们还可以对 Nginx 规则重构,采用正则表达式来精简配置文件。例如,我们有两个 nginx 规则:
1
2
3
4
5
6
7
| location = /path_to_site_a {
return 301 https://wizardbyron.github.io;
}
location = /path_to_site_b {
return 301 https://wizardbyron.github.io;
}
|
然后我们用 docker 启动 nginx,加载我们最新的配置文件。
接下来,我们可以编写两个测试用例:
http://local.testhost.com/path_to_site_a, https://wizardbyron.github.io
http://local.testhost.com/path_to_site_b, https://wizardbyron.github.io
它默认应该是通过的:
1
2
3
4
5
6
7
8
9
10
| vivian -f redirect_case.csv
load test case from redirect_case.csv
2 cases loaded running in 2 processs
verifying http://local.testhost.com/path_to_site_a to https://wizardbyron.github.io
verifying http://local.testhost.com/path_to_site_b to https://wizardbyron.github.io
Failed cases:
============================================================
No failed case.
============================================================
2/2 PASS in 1.1543898582458496 seconds
|
然后,我们可以修改 nginx 配置文件为:
1
2
3
| location ~ /path_to_site_(a|b) {
return 301 https://wizardbyron.github.io;
}
|
它仍然应该是通过的:
1
2
3
4
5
6
7
8
9
10
| vivian -f redirect_case.csv
load test case from redirect_case.csv
2 cases loaded running in 2 processs
verifying http://local.testhost.com/path_to_site_a to https://wizardbyron.github.io
verifying http://local.testhost.com/path_to_site_b to https://wizardbyron.github.io
Failed cases:
============================================================
No failed case.
============================================================
2/2 PASS in 1.1543898582458496 seconds
|
如果失败了,就要重新修改你的 nginx 转发规则。比如,我们在测试用例中增加一行:
http://local.testhost.com/path_to_site_c, https://stackoverflow.com
以上的配置文件就会失败:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| vivian -f redirect_case.csv
load test case from redirect_case.csv
3 cases loaded running in 3 processs
verifying http://local.testhost.com/path_to_site_a to https://wizardbyron.github.io
verifying http://local.testhost.com/path_to_site_b to https://wizardbyron.github.io
verifying http://local.testhost.com/path_to_site_c to https://stackoverflow.com
Failed cases:
============================================================
line: 3
origin: http://local.testhost.com/path_to_site_c
dist: http://local.testhost.com/path_to_site_c
expect: https://stackoverflow.com
status: 404
redirect_count:0
------------------------------------------------------------
============================================================
2/3 PASS in 0.8479752540588379 seconds
|
在这种模式下,你需要先把需要重定向的测试案例写到文件里,这时候运行 vivian 肯定会成功。之后你就可以优化合并一些正则表达式。由于有了 vivian 结合 docker 这种自动化测试的快速反馈机制。你可以很方便的优化你的 nginx 配置文件,无需部署之后再登录到主机上修改。
作为冒烟/回归测试集成在持续部署流水线里 #
我们有了自动化的测试工具,我们就可以把 nginx 配置和 测试用例文件保存到代码库里。在自己的 CI 服务器上安装 vivian,Vivian 需要你安装了 python3 和 pip,这对很多 Linux 发行版都不是一件困难的事情。
安装完成后,你可以在部署完成后用 vivian 检测测试用例当做冒烟测试来检测 nginx 是否配置得当。如果存在失败的 URL,则会以错误退出,你的流水线就会告诉你这个版本还不适合发布。如果你需要不断的修改和优化 nginx 配置文件,这些测试用例就可以作为回归测试,来避免你的修改影响到现有生产环境的配置。
一起来改进 Vivian #
用 Dev 的方式处理 Ops 的工作,也算一种 DevOps 吧!?
如果你对该工具感兴趣或者发现问题,请不要犹豫直接 Issue 或者 PR:https://github.com/wizardbyron/vivian