在产品的开发过程中,对数据量要求较高的网站进行架构设计时如何部署是个很复杂的问题,涉及到多个层面的不同要求,七牛首席架构师李道兵在部署工具和测试以及持续集成方面给出了自己的思考。在七牛的开发者实践日中与大家分享《从开发到上线 实战持续交付》。
李道兵首先从不同的层面来分析目前的网站的架构的设计,从数据库、缓冲层的方面引出部署的概念。然后开始介绍部署工具的进化史,从最初的安装文档、FTP/SFTP、War包、系统安装包到一键部署工具,一直到最近兴起的docker,逐个分析了每种部署工具的优缺点。针对capistrano+puppet,进行了重点的讲解和说明。最后结合七牛的实例分析了如何从代码到上线的整个部署、测试、持续集成过程和其中可能会遇到的问题。下面是完整的现场记录:
这次想和大家分享的是从开发到上线,特别是我们七牛在这方面关于一些持续交付的事情。在演讲之前我推荐两本书,一本是《精益创业》,更多讲的是从想法变成实现的。其实从想法变成一个代码,再到运营的阶段,通过运营阶段反馈修整你的想法,形成这样一个循环,达到用户的快速增长或者快速试错调整你的产品。我今天讲的是从想法到代码,从代码再到运营阶段。第二本书是《持续交付》发布可靠软件的系统方法,讲的是你需要做哪些事情,包括持续集成和一键部署,大家有兴趣的可以看一下。
我们回到整体,简单的想法不管是做什么,现在大部分的形式要不然是APP,要不然是网站。我们从网站开始讲,从前往后最前面是提供静态文件或者是分发动态请求。再后来是业务逻辑层,我们尽量做到无状态设计,它的好处是一天的量从一百万上升到一千万,你从十台变成一百台就可以了,这种伸缩不用改代码,只需要后面有多少个后端而已。一个用户来访问,肯定要有相关业务逻辑,一般是用什么做呢?数据库,里面要注意两个事情,一个是容灾,一个是容错。容灾更多的是高可靠的架构处理你的需求,容错是做一些近期备份的工作。剩下的就是一个需求了,现在我们对媒体的应用非常多,用户有音频、视频等等的需求。用户上传的时候,用一些小的软件或者更直接一点上公有云存储都有很好的解决方案。剩下一些不是可选的,但是建议尽早上的就是缓冲层,目的是让数据无压力。就是你怎么保证你的数据库的数据一致性,缓存也有使用的问题。比如说机器宕了,怎么解决一下子就把数据库压跨的事情。作为一个开发人员,已经把你的想法变成了产品,接下来的问题是在一个反馈循环之后需要改代码,相当于这些东西重新上线。考虑到这些事情的时候,我们又应该做哪些事情呢?
这个过程我们称之为部署,这么多年我们一直是这么做的。第一个是安装文档,比如wordpress,按照安装文档做这个事情,最大的问题是如果你要对它里面的东西做一些改动怎么做呢?要不然在线上修改,如果是大的改动的话,就需要重新安装一遍。相当于对你整个时间的消耗会很大,成本会高很多。第二个常见的是FTP/SFTP,比如PHP这种,其实上传了之后你的整个服务就直接可用了。这里面比较麻烦一点是适用面很窄,本地要做版本管理。如果不做版本管理的话,很可能原文件都找不到了,非常尴尬。第三个常见的是Java里面的一些war文件,是你把服务端有一个Java的文件,需要手动拷贝,多机情况下仍然要搭配一些合适部署系统做这个事情。第四个是系统安装包,比较累一点,但是对大规模部署压力很小。比如说你有几百台、上千台的机器,以前yahoo是这样部署的,不知道现在是不是。最大的麻烦是系统安装这一部分比较费时间。
最后一个是可以做到一键部署,配置文件比较简单案件,比如说现场有一些小错误,你可以五分钟之内把这个错误修复掉,不用等到漫长的部署流程。puppet和salt可以用来解决配置问题。在这样的情况下,基本上就把这个东西解决了。现在有一些比较新兴的部署方面的技术方向是docker,现在有一定程度上可以取代一些虚拟机的东西,也可能成为一种新的软件分发方式。但是现在来讲,在我们看来有一些地方不太成熟,有一些严重的错误绕不过去,我们正在做研究,但是还没有完成采用。
我们多介绍一点capistrano,右边是整个部署的目录图,下面分三个子目录,一个是软件机,再一个是目录的部署结构。再下来是一个共享的东西,比如说配置是共享的,每次部署的时候用的是同一份配置。比如说你的一些文件都是共享的,在它部署的过程中,一键部署具体怎么做呢?第一个步骤很简单,下载新的代码到releases目录,里面就有五个了,在当前的时间形成新的目录名,再把对应的一些代码拷贝过去。第二步是把所有的配置文件、日志目录全部链接过去。第三步是链接到最新的目录里,第四步是重启程序,相当于程序里面的定义目录都以这个为主的,你整个的程序就一下子到了新版本。你这边其实是一键做了四件事情,非常简单。第一个切换capistrano链接到上一个版本,测试以后马上可以回滚了,非常有用。
这种方法有一个小问题,你的机器在线上的代码修改能很好的解决,如果你的系统盘毁掉了就有一个问题,你需要重装系统。重装系统需要多少时间呢?有可能记得之前你安装完系统的时候,你打了哪些补丁、调了那些配置?配置了哪些目录和日志滚动?但是你有没有做文档化,如果做了的话,顺着文档重装一遍需要多久?如果说这段时间内做的不好,服务就下线了,做的好也许就是在一个阶段里面。你需要多久能够恢复服务,这是面临很麻烦的问题。第二个问题是业务的大量增长,你需要把你得有些服务器扩容,比如说一台扩展到十台,十台扩展到一百台。扩展带来两个问题,第一个问题是怎么把这个事情做的非常快,不用浪费很多人力。第二件事情是怎么把事情做的很对,其实扩容的机器当中有很微妙的差异,比如说原来的机器上面已经调过了,但是这个没有。怎么通过一些方法把事故消除掉?如果有这些方法大家会很高兴。新来的机器是不是监控配齐了?原来的机器一步一步积攒过了监控,但是新机器有没有呢?是我们关心的问题。
puppet/salt这两套系统是来帮助你解决这个问题的,首先第一个所有的配置入库,你的所有机器上不管是要预装哪些软件或者怎么样,都可以在版本库里看到信息。第二个是胸模板简化配置,十台或者一百台不需要把所有文件拷贝,只需要指定一下就可以了,对应的哪些功能有哪些自然就知道了。而且也不用一台一台的应用这个配置,而且会定期做一些安全更新,或者其他的一些配置调整的东西,哪些需要重启,它可以引入一些依赖或者消息,通过这些指令使你不用想配置哪些配置了,类似的也会有一些其他的需求,这些需求都可以通过它来满足。
对于我们来讲cap+puppet的问题是我们要考虑的,我们回滚的时候只能回滚程序,如果程序和配置偶合的很紧的时候,新版本程序需要一个新配置,你可以用编程来解决,这样程序员就有很大负担了。第三个有中心架构,但是优化的不够好。当数量达到一千台往上的水平,它有一点支撑不住了,这是我们另外重新开发的理由。我们做云存储对数据安全非常关心,我们去线上裁判数据。开发人有很强的需求了解你的日志,你线上的一些情况。比如说网络流量,一些连接数等等都是我们开发人员想去了解的。这样的情况下,你就需要很多的脚本帮助你的开发人员了解这些情况,这些也是之前不够用的,需要我们另外开发一套新的部署系统的理由。这个新的系统我们现在已经逐步在采用了,也许将来有机会可以对外发布一下。
前面讲了从代码到上线的整个流程,尽管回滚很方便,但是你肯定不希望把错误的版本放上去。更何况有时候回滚并不能解决问题,因为有的错误已经发生了。比如说转账转错了,钱损失了,也挽回不了。你要保证代码的质量就是要测试,我们推崇的是自动测试和持续集成。首先来讲你的开发人员要写你的单元测试和一些集成测试。第二个是你每次做一些合并请求的时候,所有单元测试和集成测试都会跑一遍,保证你所有的提交不会干扰你原有的业务逻辑。负责这个代码的人员会特别注意,针对你写的新功能或者错误修复是否补充了测试?如果没有补充,直接就被拒绝了。最后发布的时候我们会有一个更加好的测试,我们这个软件可以决定跑一个大规模的测试,来决定我们这个是不是最好的版本。但是一些代码质量的可视化工作是它做不了的,比如说单元测试的总数量是否随着时间缓慢上升,你的测试覆盖率是否稳定在可接受的水平,这些都是很方便量化的。量化的可视化能够帮助主管或者其他人也好,很方便的知道这个团队或者说代码质量是否有裂化的问题,也方便大家介入。
从头串一下工具链,我们怎么做一个小事情的呢?第一个是提交issue,所有的工作跟它关联起来。第二个是修改代码提交你的PR(Pull Request),如果持续集成通过了,这个PR就合并掉了。持续集成通过,通过之后进行部署,部署上线之后再次检验issue,然后就可以关闭了。我们把这个流程做的个很短,十几二十分钟就会完成,使得我们的试错周期更短一些。这里面有一些我们的经验教训,第一个是所谓的正规化。
正规化是几个点,第一个是所有的原码需要入库,特别是第三方软件,你迟早有一天对它修改。你尽早把它入库,针对入库的代码做一份发布系统的话,即使是第三方软件也纳入到部署流程里面来,这对你以后是一个好事。第二个是你线上的配置永远不要入代码库,因为涉及一些隐私,特别是安全问题。如果说开发人员拿到线上数据库密码的话,我们认为是一个风险。第三个是你线上的配置要入库,不是入代码,是入一个部署库。最关键的密码和私钥的部分不要做到库里面,比如说你把模板配置做到里面,让我们的整个过程很安全。整个程序也不需要上级领导盖章和批准,全部可以自动化,又比较安全
刚才提到从测试到上线流程,有一个我们提的是测试环境。集成测试可以避免一些问题,但是仍然不放心。或者说我们真的是对它不放心,我们就搭一个测试环境。比如说你可以有测试环境和线上环境乃至更多的环境,首先部署到测试环境,通过手动检验完成之后再决定是否在线上部署。测试环境做到,两点一个是跟你的线上系统不要有任何关联,不管是数据库还是怎么样的。你们之间不要共享一些共同的密钥之类的东西,这样会导致权限问题,如果别人拿到你测试系统的账号,有可能在线上系统可以用的话,这种情况下是一个安全流动,这个就是需要注意的事情。
我们另外做的是小入口,测试环境和线上环境不一样。于是特别小型的东西,现在小做一个部署,然后才大规模的放量做一些部署,挺好的。第二个是你在小入口上面,我的整个请求量和访问量非常小。非常小的时候,很多线上测试工具就可以用了。如果你有一个小入口,不管是通过什么操作做的请求,都可以达到类似的效果。在这两个情况下,都是很好的,不仅是测试手段,也是一个部署的手段。看上去也很简单,像一个一个的日志。
我们有一个遗留问题,第一个是Go语言的问题,是一个编译性语言,我们不希望它到服务器去做,因为对我们服务器扰动很大,我们通过可执行程序做分发。第二个问题是可执行程序分发的时候,内网达到一定程度的话,会使你的服务器扛不住。我们自己是做存储、分发的,最简单的方法是文件加一步推送到内网的分发节点,这样就可以支撑你内网上千台服务器。第三个是很多依赖外网的东西,我们很多机器是完全跟外网不能连接的。不仅外网访问不了它,它也访问不了外网。比如说像安全中心,或者说安装部署的配置脚本,就需要它去外网做一些东西,我们可以做一些图片包安装的,剩下的问题我们可以通过它绕过来。
谢谢!
该分享来自七牛首席架构师李道兵在此次的沙龙活动中带来了题为《从开发到上线 实战持续交付》的分享。
顺便通知一下大家:开发者最佳实践日·第7期-互联网产品从设计到上线 上海站将于12月13日在上海创业者公共实训基地5号楼1层,EFG一楼举行,欢迎Segmentfault社区的各路猿们来相聚!
报名地址:
http://qiniu-7.eventdove.com
「开发者最佳实践日」是由七牛云存储发起并联合各方小伙伴为开发者举办的系列技术沙龙,关注开发者在实际应用中可能遇到的技术问题。致力于为勇于创新的开发者们提供行业内最前沿最热门的技术干货,以技术驱动应用创新,让更多的开发者享受技术带来的生活乐趣。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。