紧接上篇 TFS在项目中Devops落地进程(上)
再接着说TFS相关之前先插入一个番外篇,虽然跟TFS关系不大但跟DevOps关系很大,觉得有必要在此乱入一下。
我们之前并没有任何监控类产品(我指的是应用程序级别的),发生任何异常都是往数据库的表里insert个错误日志,all系统共用同一张错误表。
这其实意味着我们当时的系统是:系统异常基本不关注(线上数据库开发肯定没法查对吧),关注的时候肯定都出事了,对自己负责的系统的运行状态基本不了解,什么性能之类东西纯粹靠猜。
然后我就希望我们能有办法获取到我们系统的各种状态,而此时在VS2015的时候整合在VS里的application insights引起了我的关注。
简要介绍下application insights,是微软基于azure所推出的一款SAAS性质的APM(Application Performance Management,应用程序性能管理)服务,本人不会详细介绍这个,详情可参阅官方文档。
然后也感谢我大学年代曾经当过MSP(微软校园菁英)并透过这个拿到过Visual Studio Enterprise的订阅,然后里面附赠的每个月150美元额度的Azure费用,让我有条件可以先试用Application Insights。
然后自己将自己负责的站点搭上Insights做了个小Demo展示了给老大看,然后老大也对此表示满意。
(Application Insights的概览图)
在后续的一次PMO会上我将此拿出来进行了演示,也获得了CTO的认可和支持,然后终于可以将Application Insights投入到线上使用(当然此时用的是公司正式的账号而不是我那个150刀每月的测试号了)。
但是有Insights投入初期其他小伙伴热情度并不高,大家还是觉得记数据库挺好的,直到后面发生了一件事…
事情是这样的,那是一个要常规发布的夜晚,因为并没有数据库相关的修改,所以DBA已经下班了(要加班到夜里发布,DBA只是正常下班),然后某站点发布上去的时候报错了,然后对应的开发小伙伴不知道为什么,就知道出异常了。
然后此时DBA不在,无法查线上数据库,然后就干着急,最后通过在代码里让异常本地写txt临时解决了发布上的问题,但这个问题完整暴露出记录数据库的不靠谱:万一DBA不在呢?毕竟如果没数据库内容的话总把DBA留下来的话其实也不合理,我们应该要从更长远和更科学的方案解决这个问题。
至此后续我们所有项目加入Insights的进程就加快了,因为Insights里我们可以自主的查看到性能/请求/异常等各种数据了。
另外有了Insights后我们开发自己第一次有了自己的程序运行数据,有时候我们也会针对Insights里的数据做一些对应的优化。
如上图,这是一个通过城市Id获取城市名称的接口,原先调用量巨大(当时设置了50%采样率,所以实际调用量要比图中翻一倍)但是其实这个接口返回的内容可变性不高,然后让前端加了缓存后从原来至少10K以上每小时调用量下降到现在100多的调用量,而这些都是因为我们有了Insights之后才能进行的。
所以说:优化不能靠猜,我们要用数据说话。
而Insights的Analysis功能/智能预警功能等特性也在实际中帮我们解决了不少问题。
然后Insights也是可以跟TFS进行一定程度的整合的,其中主要包括自动发布的时候打上注解和在TFS的面板里查看Insights的信息
版本注解,有了这个我们可以从监控系统里知道什么时候上了预发布环境
可添加在TFS上的widget(新版本的,详情可参看Application Insights: VSTS dashboard chart widget now available)
然后最近Insights里还有各种Preview的功能,比如新的Preview性能面板里能查看到95线/99线等,新的异常面板里还能给你分析出你异常的共同点之类的,Insights也一直在进步着。
在定型监控系统的时候我们曾经议论过听云和是否自建服务端(当时确定了监控的SDK都是用Insights的,但是纠结于是否基于ELK自建服务端)
听云:
老实说从默认的图表来说,听云比Insights的图表更全面,但听云缺乏一个Insights里有的一个很重要功能,查询分析Analysis:
在Insights的Analysis里,可以通过一个长的很像Sql的一种语句,可以快速查询你想要的任何原始数据(精确到每一条),此语句也能绘制图表,这个绝对是对程序员Friendly的一个功能
比方说出了异常,可能从运维层面更关注异常的趋势,而开发层面更关注的是具体的每一个异常,和每一个异常对应的每一个请求等,另外听云因为是服务器层面的监控无法在代码层面埋点,我们Insights里则埋了如果异常的话将错误请求的Body记录到Insights的Traces里,这样就算是Post/Put之类的请求报错我们也能拿到原始请求报文。
自建服务端:
关于这个我觉得在我们现在连Redis啊队列啊这些更紧急的东西都没有落实的情况下在分出人力去搞这个,就担心做出来后就是个永恒的1.0版本,觉得类似这种东西除非说有专职负责这个的人/团队不然不应该自建,再说那个时候我们组已经用Insights好一段时间了(有接近1年了吧)习惯了上面各种高级特性,而原始版的ELK则呵呵哒(一个自个买的原始毛胚房VS一个租来的豪华精装房),当然如果有专职人员专门投入到上面基于ELK做定制开发我相信也肯定能做的跟Insights那样或者比Insights更好,但是,我们有更多更紧急的事不是么?
不过倒是有一点是因为费用问题我们的Insights是使用了采样率的(就是说并不是收集全部而是部分收集),但是我们希望异常不要被采样,所以后面可能要基于ELK搞个Exceptionless来专门记录异常。
另外Insights跟VS的整合也是棒棒哒,比如下面这样,直接告诉我哪个方法发生了异常,直接反映在CodeLens里
原先整个TFS服务器的搭建和维护都在运维那边,那为什么要拿出这个来说呢?
首先我们现在正处于一个快速发展的年代,什么框架啊每月总能冒出几个,技术各种日新月异,虽然绝大多数企业本着稳定至上的原则不会说总是用最新,但这不是固步自封,不思进取的借口,在一个“最新”变为”稳定“之后总要试着跳出舒适区步入现代化进程吧?
然后我们遇到的第一件事就是.Net版本升级,我们计划升级到.Net 4.6.2(原来是4.0到4.5.1不等),然后因为自动打包的原因所以要求Build Agent也要升级,然后原先运维部署配置的TFS是服务器+Build Agent放在同一个机器上的,搭载的是VS2015.
然后除了4.6.2之外的话我们有个别类库项目使用上了.Net Core里全新的xproj格式来进行多target framework的开发,这也要求服务器上要有.Net Core的Sdk。
在另外此时在讨论到代码质量审核,然后我们初定使用Sonarqube,而这个也要求Build Agent服务器要有Java的功能。
基于上述原因外加后面我们技术部某神秘人物毛哥批了一批(2台)服务器(也就一般工作电脑)资源给我,后面还有CTO批了一台大服务器(真·服务器,当时可是嘻嘻哒的内心class="wlEmoticon wlEmoticon-openmouthedsmile" src="/Upload/Images/2017111712/1475FC99F79DF52D.png" alt="Open-mouthed smile" />),然后我开始自己搭建Build Agent。
当前我们组自行维护的4台Build Agent,承载了包括持续集成,自动打包,代码分析,自动测试等一系列任务
装上了一堆现在要用或将来希望想用的功能,满足当前及其可预见的一段时间内的需求
其实关于Build Agent主要觉得这个必须要能适应开发组的步伐,首先我们.Net 4.5->.Net 4.6.2的时候,要求Build Agent要支持.Net 4.6.2或以上,之后我们有计划要上.Net Core(此时已经有基于.Net Core项目的Dll包了),而且之后.Net Core还有2.0版本(主要是NetStandard 2.0),所以我觉得这个Build Agent在我们组内的话我们能更好适配我们的前进步伐。
现在我们的Build Agent里装的是Vs2017 17.3 + .Net 4.7 + .Net Core 2.0 + Docker 17.03.2-ee + CMake 3.9.1 + Python 3.6.2(x64) + JMeter 3.0 + F# 4.1 + Node.js 8.4(x64) + Java 1.8
估计小半年内基本都能满足需求。
接着上篇说,我们有了自己的Build Agent,然后自动发布之类的基础功能也有了,于是我们就有更高层次的自动化追求了。
首先就是自动化代码质量检查,在此先声明一点:任何自动化代码质量检查工具都不能代替人肉的Code Review,但能减少人肉Code Review的工作量。
然后在选型上我们选了Sonarqube,当然你们会问为什么选Sonarqube,说来惭愧其实我之前并不了解相关产品,然后在TFS商店里看到了这货然后才知道还能自动化代码检查…然后就去研究这货,发觉总体还可以(规则之类的啊,还有跨平台等),在跟我们技术部神秘人物毛哥上报下技术选型获得认证后就拍板开始干。
通过自己手头有的服务器资源,搭建了个Sonarqube服务器,然后通过TFS的任务加上去
一个带Sonarqube分析的编译过程,作为C#程序员看上去好像一个using的结构(开始->释放)
某项目分析结果
好,然后代码分析质量有了,大家就能照着上面去“优化”代码了。
慢着,我写了一段代码,因为水平问题我也不知道有没有咖喱,然后一提交,然后上面就留下了一个永恒的污点(有历史记录的),这样肯定很不爽,所以后面我们就纠结有没办法能够让代码在进入到Sonarqube系统之前先有个反馈。
然后此时微软就发布了TFS2017,具体参考 TFS2017RTM Release Note
其中它引入了这么一个功能
在拉取请求的时候显示Sonarqube的分析结果(直接定位到你代码上),且该分析结果不记录到Sonarqube服务器里
接着怂恿下老大出面让运维那边帮忙升个级。
然后为了配合Sonarqube,那么每个项目在Pull Request的时候都要进行编译的过程(编译了Sonarqube才能分析),因此也顺带加上了阻止合并不能编译的代码这个额外附加项。
同意一次Pull Request需要一个非本人外的其他人同意且要编译成功
然后审核人也能看到Sonarqube分析的结果就不用每行仔细看也能大概知道个所以然。
当然实践过程中,看别人的代码发现不少什么超过3重if嵌套啊,类超过1000行啊这些,他们总说由于某些原因实在不好改的,那就只能忍了(你能怎么办呢,特别是多重if的往往都是各种业务条件判断复杂,然后自己也没空深入去看他们的业务),但自己项目要求要严格点,要严格控制不能出现任何形式的咖喱。
另外在此吐槽一点:TFS不能直接在Dashboard里浏览Sonarqube的结果,要看Sonarqube结果还要跳到Soanrqube里去,这不符合All in one的TFS理念啊,就没人想过弄个Widget什么的来解决解决这个问题?
自动测试在这里想主要分2块来说,单元测试和集成测试。
首先我觉得对于这2个概念尽管有很官方的说明但实践中很多人都有自己的理解,我简单说下我自己对上述2个词的理解和定义
单元测试:在代码内部进行的不依赖外部环境(网络/数据库等)进行的对某个方法级的测试,特点是只能测试一小块逻辑,能模拟数据且运行较快(毫秒级),代码执行结果可预测。
集成测试:可能是代码也可能是脚本依赖外部环境(网络/数据库等)进行的针对某个流程上的测试,特点是要造数据且运行较慢(至少是秒级),代码执行结果绝大多数情况下可预测(受外部因素影响不能100%可控)。
单元测试:
要让代码可以被单元测试,首先代码先要是可测试的,要如何做到代码可测试呢?答案很简单,随便街上抓一个伪计算机专业的都能给出你正确答案:解耦。
但是你要真的落实“解耦”到你的代码里,又往往是一个异常艰巨的任务(Talk is cheat, show me the code)。
首先在我自己负责的项目里,完全使用依赖注入的形式重构了,另外抽象出了各种各样的接口,理论上要做到所有的”Service“都是可以”被替换”的。
严格限制对static和new这2个关键字的使用(绝大多数情况下static或者new都意味着不可测试,当然static一个数据无关的方法比如Math.Min之类的或者new一个纯数据类这些例外)
在自己一番折腾下,现在自己负责的项目单元测试覆盖率70%+,也算是一个自己比较满意的数字了(这个是Resharper收集的,它将单元测试项目本身也包含进去了的)
在单元测试的加持下,自己想重构代码什么的都能放开手去重构,有没有影响老逻辑?测试跑一下就知道了。
而加新逻辑的时候也能知道会对原有流程造成怎么样的影响。
总之有单元测试后,你会对你的代码更加倍有信心。关于单元测试看看日后是否专门开一篇文章来说说,里面也有大学问。
上面说了那么多单元测试,那么它跟TFS有啥关系呢?测试写完了是吧?你怎么能确保它一定是有在跑呢?这时就可以将单元测试的运行整合在自动编译的流程里
整合到编译流程里的单元测试,每次跑完后会报告结果,如果单元测试失败那么会阻止pull request
在TFS主页的Dashboard里展示单元测试结果
集成测试
我们的集成测试现在主要是QA那边负责,QA那边有基于Jmeter来进行的测试,具体细节因为不是我负责所以我不是特别清楚,但是在Build Agent里装好Jmeter后(QA配置好了各种他们要的插件的版本)然后在自动发布的时候运行下就好了
运行QA他们指定的Jmeter,先从一个位置拷贝配置文件过来然后运行命令行,没错,从TFS角度去理解的话它只是去执行个命令行而已
不过当前QA他们弄的Jmeter是运行后发一个结果邮件,如果能够将Jmeter的测试结果转变为TFS可接收的某种测试结果格式(什么JUnit或者Xunit之类的测试结果)展示到TFS上那就更好了(如果有会的人请赐教)
在集成测试方面我自己也有一套基于代码的(当然现在主流都是基于脚本了)
基于Specflow+xunit的方式我自己做了几个我旗下项目主流程的测试用例(就是那种一出问题QA就邮件出来:”测试环境挂拉“的那种)
基于Specflow的BDD形式的描述文档,尽可能弄的贴近业务层面
描述文档背后对应的是若干代码
目前我自己这套集成测试仅放在Demo环境(第一个测试环境)使用,主要是一旦发觉个风吹草动,立马还原代码…(逃…
关于集成测试按道理此处应有以Selenium为代表的UI自动化测试,不过我自己主要负责的是接口,然后让折腾UI的那位同事目前也只是处于演示级阶段还没正式投入使用,此处就不献丑了。
当然TFS上还有管理手工测试等各种功能,不过由于QA组是基于Jira来管理任务所以并没使用。
尽管我们当前在经过一番折腾后全组项目目前已经统一到.Net 4.6.2,但短时间内也不会用上NetCore,但背后一直为了NetCore的迁移在默默准备着。
主要是考虑到在NetCore 2.0后出了个NetStandard2.0,它跟NetCore 2.0以及.Net Framework 4.6.1是兼容的。
那么我们现在项目代码的目标都是兼容Netstandard2.0(底层类库),但之后真要迁移的时候就改个TargetFramework以及表现层稍微改下就好了(嗯,很丰满的理想)。
说这篇呢主要是分享一个,我们现在TFS是2017Update2,在这个版本下你用常规方法是无法引用任何Netstandard2.0的包的(不信你们试试)
其本质是因为TFS2017Update2自带的Nuget还原工具是4.0版,而4.0是不支持NetStandard2.0,要4.3才可以,那难道我们在现有的TFS下(2018又没Release)就拿NetStandard没辙了?
然后就找到了这篇文章Using the latest NuGet in your build
在这篇文章指引下,将它 那堆Powershell脚本 粘贴上去执行,下面参数写个4.3.0
然后使用自定义的nuget
然后就迎来了胜利的曙光
任务管理
虽然我司一直是基于Jira做任务管理的,不过因为Jira并不能直接跟TFS上的代码进行关联,所以我私自上是喜欢在TFS里建对应任务然后关联上去
还能跟代码里结合起来
在TFS里也能做可视化的关联查询
自动备份分支
这是最近折腾出来的小玩意
在Release里加上这个步骤(这是个插件提供的,插件详情请点此处)
然后在执行到对应发布步骤的时候就会自动将当前发布的代码拉一个分支出来,全自动的哟。
上面的大概就是我们现在折腾TFS的成果,但我们的道路远没结束,或者说,现在又处在某种程度上的开始。
首先,Docker这个大趋势肯定之后或多或少都要涉足进去的(不过觉得先将诸如服务发现之类的前置条件先搞定在做这个)
还有撇了下隔壁TFS2018的Release Note,这回引入了Wiki功能,回头可能可以直接在TFS上直接写上各种知识库文章了
还有想整个OWASP ZAP然后跟TFS流程整合下来个自动渗透测试分析什么的,让我们自动化更上一层楼(if有空弄的话)。
大概就是扯那么多了。
The End。