6.6 考虑运用相关的问题
结合使用之前介绍过的部署自动化相关的各类工具就能够构建部署流水线。部署流水线建成并进入到维护的循环阶段后,就可以在理想的时间,安全地向正式环境部署新的代码,这样就可以频繁地对正式环境进行部署了。这时需要考虑新的问题,就是该如何处理向正式环境部署过程中发生的服务中断,以及由版本更新所引发的问题。
不解决这些问题就无法对正式环境进行频繁的部署,因此下面将介绍部署时无需中断服务的机制,以及部署后发生的问题的应对方法。
6.6.1 不中断服务的部署方法
多数 Web 应用程序在代码被更新后都需要重启 Web 服务。在请求处理过程中重启,不仅会造成该请求失败,万一启动失败的话还有可能造成服务中断。既然部署作业有可能会造成服务中断,那么在工作日白天进行部署的话就不会被允许吧。无论部署作业的自动化程度有多高,这样也无法实现频繁的部署。因此这里首先介绍不会因部署造成服务中断的机制,即零中断时间部署的相关内容。
6.6.2 蓝绿部署(blue-green deployment)
首先我们来看一下零中断时间部署方法之一的蓝绿部署(图 6.20)。
图 6.20 蓝绿部署
图 6.20 中设置有负载均衡器,后端有两台应用程序服务器分担负载,下面我们就以这样的构造为例来讲解零中断时间部署。应用程序服务器分别分成蓝环境(blue.example.com)和绿环境(green.example.com),负载均衡器采用 Apache 的 mod_proxy_balancer 模块和 mod_rewrite 模块的组合,配置示例如下。
#模块的加载以及mod_rewrite的有效化 LoadModule proxy_module modules/mod_proxy.so LoadModule proxy_balancer_module modules/mod_proxy_balancer.so LoadModule rewrite_module modules/mod_rewrite.so RewriteEngine On RewriteLogLevel 1 RewriteLog logs/rewrite.log #负载均衡器的管理画面用的URL /balancer-manager的配置 <Location /balancer-manager> SetHandler balancer-manager Order deny,allow Deny from all Allow from 192.168.0.0/16 </Location> #测试账户用的负载均衡组 <Proxy balancer://test.example.com> BalancerMember http://blue.example.com:8080 loadfactor=50 BalancerMember http://green.example.com:8080 loadfactor=50 </Proxy> #一般用户用的负载均衡组 <Proxy balancer://prod.example.com> BalancerMember http://blue.example.com:8080 loadfactor=50 BalancerMember http://green.example.com:8080 loadfactor=50 </Proxy> #测试账户的配置 #测试账户的IP地址 RewriteCond %{REMOTE_ADDR} ^10.8.15.1$ RewriteCond %{REQUEST_FILENAME} !^/balancer-manager RewriteRule ^/(.) balancer://test.example.com/$1 [P] #一般用户的配置 RewriteCond %{REQUEST_FILENAME} !^/balancer-manager RewriteRule ^/(.) balancer://prod.example.com/$1 [P]在浏览器中输入 http://${remote_host}/balancer-manager ,访问采用上述配置的负载均衡器,会显示负载均衡器的管理画面(图 6.21)。
图 6.21 负载均衡器的管理画面
如图 6.21 所示,可以对发送至测试账户的负载均衡组和一般用户的负载均衡组的蓝绿环境的请求进行配置。在上述环境下,实现蓝绿部署的步骤如下所示。
❶ 将 balancer://test.example.com 的 green.example.com 设置为 Disable
❷ 将 balancer://prod.example.com 的 blue.example.com 设置为 Disable
❸ 对 blue.example.com 实施自动部署,更新应用程序
❹ 从 IP 地址为 10.8.15.1 的机器执行测试(被划分到测试用的负载均衡组),到下一个步骤为止的时间段中,一般用户的请求被分配到 prod.example.com,服务持续提供
❺ 通过测试后,将 balancer://test.example.com 的 green.example.com 设置为 Enable
❻ 将 balancer://test.example.com 的 blue.example.com 设置为 Disable
❼ 将 balancer://prod.example.com 的 blue.example.com 设置为 Enable
❽ 将 balancer://prod.example.com 的 green.example.com 设置为 Disable。这时一般用户可以开始使用新版本的服务
❾ 对 green.example.com 实施自动部署,更新应用程序
❿ 从 IP 地址为 10.8.15.1 的机器上执行测试
⓫ 通过测试后所有的负载均衡组都设置为 Enable,部署作业结束
以这样的流程进行部署,应用程序更新过程中就无需停止服务。mod_proxy_balancer 的各个负载均衡组的 Disable 和 Enable 的切换可以通过 HTTP 来操作,因此可以使用 Mechanize 这样的程序来控制。蓝绿部署时需要注意的是,通常不进行发布时,为了不浪费资源配置,会将一般用户分别均分到蓝环境和绿环境,而发布作业时由于需要降级运行 25 ,因此要选择用户较少的时间段,或者平时保留一定接入能力的冗余。
25 降级运行是指在部分停止系统的功能或性能的状态下维持系统运行。——译者注
6.6.3 云(cloud)时代的蓝绿部署
AWS 等 IaaS(Infrastructure as a Service)能够瞬间添加服务器资源,这个特长同样能够运用到零中断时间发布上。以刚才的蓝绿部署的应用为例,可以采用这样的方法:平时只运行绿环境这一套系统,仅在发布时利用 IaaS 功能制作绿环境的镜像,再从镜像启动蓝环境进行发布作业。只要能实现上述模式的发布,就基本上可以克服一般的蓝绿部署的弱点了。
发布时无需降级运行,平时也不需要准备双份的服务器资源,就能够随时实施发布。缺点在于需要增加镜像的制作、服务器的启动,以及发布结束后删除蓝环境这一系列的工作。发布作业变得复杂,失败的风险也随之上升。
在服务器启动时采取下列对策能有效地降低风险,可以在对比这些对策的准备工作所需的开销以及所带来的收益的基础上再进行探讨。
服务器启动后用 serverspec 实施自动测试
(AWS 的情况下)服务器的启动操作用 awscli 等工具来实现自动化
平时在 staging 环境上对一系列部署的自动化进行测试
6.6.4 回滚(rollback)相关问题的考察
●…… 随时准备好退路
无论事前进行多么充分的测试,对正式环境实施发布总是隐藏着发生意外的危险。比如,用户使用了超出 QA(品质保证)部门预测的服务等,进而造成正式环境发生故障。
也就是说,要时常意识到发布时有可能将 bug 也一同发布出去。需要根据 bug 的严重程度,判断是进行回滚还是在下次发布时修正。因此在进行发布之前,要准备好随时回滚的机制。反过来说就是,不应该实施无法回滚的发布作业。
●…… 数据库模式的版本管理
代码的回滚只需要回退到升级前运行的版本,多数情况下这样就可以了。一般的 Web 应用程序将持久性数据保存在 RDBMS 中。如果需要回滚数据库的话,可以使用数据库迁移工具 26 。
26 这款工具在第 3 章中有详细介绍。
●…… 回滚的验证
假设有下面这样的作业流程,我们需要验证在哪个阶段失败,以及如何回滚。
❶ 显示维护画面
❷ 停止应用程序
❸ 更新源代码
❹ 更新配置文件
❺ 更新数据库模式
❻ 启动应用程序
❼ 实施冒烟测试
❽ 实施性能测试
❾ 解除维护画面
❿ 报告发布结束
最理想的情况是能够为所有阶段的失败提供自动化的回滚机制,但这样会耗费高昂的成本。不过至少也一定要准备好确实可行的回滚机制,即使是手动的也行。手动回滚的话,Rlogin 等辅助工具是非常有用的。
下面我们来考虑一下在两个比较主要的回滚点发生问题时的处理流程。
●…… 只更新代码的发布时的回滚
例如在步骤 ❸ 发布作业中断,无法继续进行发布的情况下,回滚的步骤如下。
❶ 将更新了的代码回退到以前的版本
❷ 启动应用程序
❸ 实施冒烟测试
❹ 解除维护画面
❺ 报告发布延期
这是最简单的流程,只需退回到之前的版本就可以了。但是回滚结束后必须用 serverspec 等对服务器进行测试,确认程序是否正常回退,并实施冒烟测试,确认应用程序是否正常运行。
●…… 数据库模式更新时的回滚
到冒烟测试为止都正常运行,在步骤 ❽ 时发现比起发布前,一部分功能的性能慢了 10 倍,这样的情况下该怎么做呢?当然,发布前的各种测试必须对这类问题进行覆盖,但用测试用例覆盖所有问题是不可能的。长期运营的系统会经常遇到这类问题。
例如,特定用户的特定项目的数据量极端大的情况下,或者是性质或倾向不确定的多用户系统中,随着各类使用模式的增加,性能有时会发生退化。发生性能相关问题的情况下必须立即进行回滚。
如果没有上述步骤 ❺ 的话,如上所述,只需回滚代码就能解决问题。即便是能够立即修复的问题也请先行回滚,经过通常的流程后再次进行发布。
由步骤 ❺ 造成性能相关的问题时,以下情况可以通过回退到原来的状态,也就是说通过回滚数据库模式来解决。
系统允许该数据丢失(为了收集针对新功能的日志而存储的数据等)
更新的内容是数据迁移或规格化、非规格化等不会造成数据丢失的作业
但是也存在下面这些无论如何都无法回滚的情况。
系统不允许该数据丢失
回滚时有可能产生数据一致性问题
这样的情况下无法回滚数据库模式。因此要预先确认更新的数据库模式属于上述哪种形式。
可以回滚的模式没有什么需要特别担心的,只要使用迁移工具实施预先测试过的回滚操作,修正 bug 后重新进行发布即可。
无法回滚的模式虽说并非完全没有规避的方法,但为此需要创建大规模的机制,非常麻烦。对于这样的高风险作业,建议设置停机维护时间,在对用户影响较小的日期、时间实施发布。具体做法这里就不再详述了。万一发生问题的情况下,将风险最小化,并尽早解决问题,这样的方案是比较现实的,并且成本方面也可以说是最佳的。
另外,有 1 种在数据库模式更新后即便产生 bug 也可以不回滚数据库模式的技术,那就是将现行的代码和下次发布的代码都运行在数据库更新后的状态下。
测试时请确认即使只更新数据库的模式,现在的代码也可以通过测试。
只要确保即使先只更新数据库,程序也能够正常运行,就不必回滚数据库模式了。因此在步骤 ❽ 发现比发布前性能慢 10 倍的情况下,只需要回退代码即可。
采用这样的机制必须能够分别实施代码的版本管理和基于迁移工具的数据库的版本管理,并且可以使用 Fabric 或 Capistrano 来实现各个部署、回滚的自动化。
