在网站运维实践中,除了网络、服务器等硬件故障导致的系统可用性风险外,还有来自软件系统本身的风险。
网站需要保证 7 × 24 高可用运行,同时网站又需要不断地发布新功能吸引用户以保证在激烈的市场竞争中获得成功。许多大型网站每周都需要发布一到两次,而中小型网站则更加频繁,一些处于快速发展期的网站甚至每天发布十几次。
网站的发布过程事实上和服务器宕机效果相当,其对系统可用性的影响也和服务器宕机相似。所以设计一个网站的高可用架构时,需要考虑的服务器宕机概率不是物理上的每年一两次,而是事实上的每周一两次。
但是网站发布毕竟是一次提前预知的服务器宕机,所以过程可以更柔和,对用户影响更小。
发布过程中,每次关闭的服务器都是集群中的一小部分,并在发布完成后立即可以访问,因此整个发布过程不影响用户使用。
代码在发布到线上服务器之前需要进行严格的测试。
即使每次发布的新功能都是在原有系统功能上的小幅增加,但为了保证系统没有引入未预料的 Bug,网站测试还是需要对整个网站功能进行全面的回归测试。
此外还需要测试各种浏览器的兼容性。
在发布频繁的网站应用中,如果使用人工测试,成本、时间及测试覆盖率都难以接受。
目前大部分网站都采用 Web 自动化测试技术,使用自动测试工具或脚本完成测试。比较流行的 Web 自动化测试工具是 ThoughtWorks 开发的 Selenium。Selenium 运行在浏览器中,模拟用户操作进行测试,因此 Selenium 可以同时完成 Web 功能测试和浏览器兼容测试。
即使是经过严格的测试,软件部署到线上服务器之后还是会经常会出现各种问题,甚至根本无法启动服务器。主要原因是测试环境和线上环境并不相同,特别是应用需要依赖的其他服务,如数据库,缓存、公用业务服务等,以及一些第三方服务,如电信短信网关、银行网银接口等。
因此在网站发布时,并不是把测试通过的代码包直接发布到线上服务器,而是先发布到预发布机器上,开发工程师和测试工程师在预发布服务器上进行预发布验证,执行一些典型的业务流程,确认系统没有问题后才正式发布。
预发布服务器是一种特殊用途的服务器,它和线上的正式服务器唯一的不同就是没有配置在负载均衡服务器上,外部用户无法访问。
网站代码控制的核心问题是如何进行代码管理,既能保证代码发布版本的稳定正确,同时又能保证不同团队的开发互不影响。
SVN 代码控制和版本发布方式一般有以下两种:
主干开发、分支发布 代码修改都在主干(trunk)上进行,需要发布的时候,从主干上拉一个分支(branch)发布,该分支即成为一个发布版本,如果该版本发现 Bug,继续在该分支上修改发布,并将修改合并(merge)回主干,直到下次主干发布。
分支开发,主干发布 任何修改都不得在主干上直接进行,需要开发一个新功能或者修复一个 Bug 时,从主干拉一个分支进行开发,开发完成且测试通过后,合并回主干,然后从主干进行发布,主干上的代码永远是最新发布的版本。
这两种方式各有优缺点。
主干开发、分支发布方式,主干代码反应目前整个应用的状态,一目了然,便于管理和控制,也利于持续集成。
分支开发,主干发布方式,各个分支独立进行,互不干扰,可以使不同发布周期的开发在同一应用中进行。
火车发布模型:将每个应用的发布过程看作一次火车旅程,火车定点运行,期间有若干站点,每一站都进行例行检查,不通过的项目下车,剩下的项目继续坐着火车旅行,直到火车到达终点(应用发布成功)。
由于火车发布模型是基于规则驱动的流程,所以这个流程可以自动化。采用火车发布模型的网站会开发一个自动化发布的工具实现发布过程的自动化。根据响应驱动流程,自动构造代码分支,进行代码合并,执行发布脚本等。正常流程下,可以做到发布过程无人值守,无需 SCM(网站配置管理员)参与,每个项目相关人员基于流程执行相应的操作,即可完成应用自动发布。
人的干预越少,自动化程度越高,引入故障的可能性就越小,火车准点到达,大家按时下班的可能性就越大。
应用发布成功后,仍然可能发现因为软件问题而引入的故障,这时候就需要做发布回滚,即卸载刚刚发布的软件,将上一个版本的软件包重新发布,使系统复原,消除故障。大型网站的主要业务服务器集群规模非常庞大,即使想要发布回滚也需要很长时间才能完成,只能眼睁睁看着故障时间不断增加却干着急。
为了应付这种局面,大型网站会使用灰度发布模式,将集群服务器分成若干部分,每天只发布一部分服务器,观察运行稳定没有故障,第二天继续发布一部分服务器,持续几天才把整个集群全部发布完毕,期间如果发现问题,只需要回滚已发布的一部分服务器即可。
灰度发布也常用于用户测试,即在部分服务器上发布新版本,其余服务器保持老版本(或者发布另一个版本),然后监控用户操作行为,收集用户体验报告,比较用户对两个版本的满意度,以确定最终的发布版本。这种手段也被称作 AB 测试。