基础设施建设之路(一)

发布于: 2020 年 06 月 21 日

我所在的公司是一个国内Top地产企业所投资的互联网公司,致力于建设智慧社区操作系统,赋能整个智慧社区生态。或许是成立时间较短,技术积累不够深厚亦或其他原因,在技术体系建设上存在一些不足,近一年来,通过我所在团队所有成员的共同努力,基础设施建设初具雏形,本文将做为总结回顾这一历程。

很⻓一段时间以来,公司一直承受着基础设施薄弱、架构混乱所带来的烦恼,并且随着业务发展不断加剧。具体表现为:

  1. 应用部署、配置方式各异,上线成功率低且耗时较长,一次上线甚至经常持续数小时之久。

  2. IT资产管理、网络策略混乱,带来严重的管理风险及安全隐患。

  3. 应用、基础设施层面缺少监控,完全不清楚线上系统的运行状况。

  4. 基础设施没有容灾,一旦发生系统级故障恢复时间较长甚至难以恢复。

这些问题在不断的发⽣并且从来未彻底解决,如同梦魇般纠缠着每一个人。拨开重重迷雾,这⼀切的背后既有管理原因也有技术原因。如同⼤多数创业公司一样,系统设计早期以追求满足业务所需快速上线为主,很少进行完整的考虑才导致各种问题的频繁发生。

一、规范有用吗?默契大于规范

很多人会想,既然缺少全面的考虑,那么就制定一个全面的规范吧,技术团队按规范执行就可以解决这些问题了。很遗憾事实并非如此,制定完善的规范规避各种风险是制造业所经常采用的方法,2000年后,伴随着中国加入WTO,很多软件公司加入到软件外包的队伍中,由于语言、地域、文化不同,为了保证离岸开发质量,必须有一套行之有效的管理方法保证交付,于是国内各大公司纷纷执着追求CMMI(Capability Maturity Model Integration),通过管理水平的提升来获取海外项目订单。

有趣的是,无论是Google、Facebook这些世界级的互联网巨头,还是国内的BAT都很少提及CMMI,年轻一代的程序员甚至不知道CMMI为何物,问题出在哪了呢,自工业革命起几百年所积累的各种管理方法、规章制度为什么在互联网时代失效了呢?背后的原因恐怕是复杂度的指数级增长,汽车厂商做为制造业时代的典型代表,即使当今世界顶级的汽车厂商同时生产、售卖的也仅仅几款车型而已,而一个互联网产品却在每日持续增加功能,传统的管理方法很难适应快速的互联网开发节奏。

就一个国家而言,每个公民必须遵纪守法否则就会受到法律的制裁。但大多数人和我一样,从来不会主动去买一部法律文书来学习,更谈不上温故知新了。即便如此,社会还是处于有秩序的发展中,背后的原因就是每个人都会被时时潜移默化的教育,形成了共同的认知,这就是一种人与人之间默契的体现。传统的制造业不重视个体,而互联网却处处体现人本主义,面对高复杂性、不确定性,研发人员间共同的默契才是解决问题的根本,因此我们说默契大于规范。

二、敏捷开发有用吗?DevOps看起来不错

敏捷开发是一套软件工程管理方法,虽然不是新概念却也算一个高频词汇,读过《人月神话》的工程师都知道没有银弹这样一个公理,简而言之没有哪种软件工程方法可以很好解决开发过程中的各种复杂问题,这也是被业界广泛认可的,更何况也不能像做药品试验一样做大样本随机双盲实验验证有效性。实际应用时或许有效也或许无效,所以还是别太迷信了,以免一叶障目。

说了这么多悲观的论调,难道就没有办法了吗,管没有银弹,但DevOps却也是一个更为先进、有效的方法。不同于敏捷,DevOps面向交付、快速迭代、持续构建,更倾向于通过技术方法来降低软件开发过程中的复杂度。尽管我们说默契大于规范,但并不是不需要规范,DevOps另辟蹊径,围绕着软件开发中的各个环节,通过各种工具的使用,潜移默化的对研发人员进行教育,形成趋同性的默契。

三、上云

未上云之前公司的系统部署在虚拟机中,虚拟机又被部署于物理机中,完全使用人工方式进行整个服务器资源的管理,管理效能非常低下,是引发混乱的一个重要根源,具体表现为:

  1. 无配额管理,通常一台虚拟机中部署若干个应用,应用之间没有资源隔离机制,当有的程序占用内存高时就会影响运行在同一虚拟机上的其他程序。

  2. 无环境隔离,一台虚拟机上不能启动使用相同端口的程序,需要预先分配某个程序使用哪个端口,如果程序被迁移到其他虚拟机中就可能存在端口冲突,还需要再次修改端口。

  3. 无法动态部署,系统被预先分配到指定的虚拟机中,如果需要扩容非常麻烦,需要找到另一个资源空闲更高的虚拟机,将程序重新部署。

  4. 资源利用率低,服务器资源利用率不高,存在大量的资源碎片。

由于公司的业务特点,我们直接跳过了基于OpenStack的Iaas层,直接选择了Kubernetes做为云计算平台。

四、监控

程序的稳定运行离不开监控,监控云产品就是为实现这一目标所设立。一般来讲,监控主要从基础设施监控、应用监控、用户端监控几个方面进行。对于基础设施监控,我们使用业界比较流行的Prometheus+AlertManager进行监控。Prometheus内置时间序列数据库,查询性能非常好,且有Thanos这种集群解决方案,在业界应用非常广泛。针对目前公司所使用的服务器、中间件、数据库我们都基于Prometheus+AlertManager建立了完善的监控系统。

相比之下应用程序监控要复杂很多,主要涉及Log、Metrics、Trace几方面,Log、Metrics比较容易收集,但Trace收集要复杂很多,主要难点体现在是否接受代码侵入性改造。可接受代码侵入性改造常见的可选方案有Zipkin、Jager、Cat,不可接受代码侵入性改造的常见方案有PinPoint、SkyWalking、Elastic APM。为了尽可能的减少研发介入,我们选择了非侵入型方案,并进行了详细的评估。

由于PinPoint部署过于复杂,需要HBase环境,首先排除PinPoint。SkyWalking告警规则不能运行时修改也被排除在外。Elastic APM不支持Dubbo,但代码结构清晰,监控数据存储在ElasticSearch中,容易扩展开发,同时Elastic APM可以于Kibana结合,利用Kibana丰富的可视化功能,方便的展示应用运行时信息。结合我们的实际情况,最终我们选择了Elastic APM做为APM,整个APM架构如下:

为了实现发送告警通知,我们参考Prometheus的PromQL查询语言实现了一套新的查询表达式,方便研发进行告警规则设置。比如:avg(http{appname="qdp-polaris-server"} by url range 5m) > 2000,计算5分钟内qdp-polaris-server系统各个http接口的平均响应时间,如果超过2000ms则产生报警。 相当于SQL中的select avg(response_time) from table where timestamp between now() - 5m and now() group by url。值得注意的是,Skywalking 6.5.0后也实现了运行时动态配置告警规则的功能,当仍然不够灵活,Skywalking需要静态的配置告警指标,比如某个URL平均响应时间,然后才能配置告警规则,如某个URL平均响应时间超过5秒进行报警。Skywalking 6.5.0之后仅仅能动态的配置告警规则,而指标仍然需要静态配置,修改之后需要重启系统,不够灵活。用户端监控方面,目前我们使用监控宝进行监控,周期性查看来自用户端的性能报告来优化系统。通常APM都是被动式的根据程序执行情况发送告警通知,而非主动式的进行探测,为此我们基于Http Runner做了一个主动检查的系统,主动向系统发送请求,如果探测到异常,也会向相关负责人发送告警通知,起到双保险的作用。

毫无疑问,监控是必要的,但是否进行了监控后系统就会稳定了呢,显然不是这样,就如同经常做体检也不一定身体健康一样,好的身体状态需要保持健康的生活方式,当然也离不开基因遗传。通常来讲,通过长周期的系统监控,可以详细记录下来系统的运行时状态,进行针对性优化。2B业务不同于2C业务,系统出现问题后,用户有快速的反馈渠道,经常出现系统收到告警后用户的报障也随之而来,仅凭告警也解决不了太多问题。极端情况下甚至出现系统卡顿无法向外发出告警的情况。因此周期性检查系统运行时状态,针对性优化系统才是监控的真正意义所在。

上图是一次生产环境系统故障,一个程序突然启动3w+线程,操作系统出现严重卡顿,系统内部的告警程序无法向外发出告警消息。

五、CI/CD

几年前一本运维界的小说《凤凰项目—一个IT运维的传奇故事》将我带入DevOps世界。持续构建、持续集成、持续部署、按需创建环境、限制半成品、流程自动化、保证上游的质量、不断试错、持续改进、构建能够顺利变更的安全系统和组织是这本书的精髓。很长一段时间以来,整个研发团队之间基本没有默契,各个产品线差异非常大,并且都存在很多问题,日志配置各异、启动参数各不相同、甚至编译方法都不同,上线失败率极高。

这种现象的背后既有技术原因也有管理原因,从技术上讲,之前程序部署在虚拟机中,一个虚拟机部署多个程序,研发也拥有一些服务器的登录账号,并参与到部署环节中,由于团队之间缺少一些默契,总能出现点个性化配置方案,维护成本非常高。

DevOps给了我们很好的启发,围绕整个开发周期通过工具的应用来降低复杂度、提高质量。于是我们决定做研发云这样一个产品来实现DevOps在千丁的落地应用。毫无疑问CI/CD是首先要做的事情,CI/CD方面,我们之前使用Jenkins,但Jenkins仅仅适合编译,不适合发布,各个项目的Jenkins编译脚本也不相同,难以统一维护。研发云实现编译过程仍然使用Jenkins,通过调用Jenkins API进行编译。之前Jenkins编译脚本采用Shell,为了支持多语言环境,在研发云的Jenkins环境上,我们使用了Jenkins 2.0之后出现的Pipeline方式,使用Groovy开发编译脚本,每个Stage根据不同的编译器及版本,在相应的Docker容器中运行,避免了在一个Jenkins编译主机上安装不同编译环境所引起的环境冲突问题。同时,我们将编译脚本放到Git上,相同语言的项目对应同一个编译脚本,当需要修改编译过程时,只需要全局修改即可。程序编译通过后,将编译好的程序打包成Docker镜像,推送至Docker Hub中。运行时,选择相应镜像直接运行与Kubernetes环境中。

之前在使用Jenkins、Apollo这些独立的工具时,几乎每个人都有自己独特的用法,从个体角度上看没有问题,但从群体角度上看就会出现极大的差异化,难以达成默契。本着少即多这样一种设计哲学,甄选了其中核心且最为常用的功能集成到研发云中,避免了工具滥用所带来的潜在风险。

生产环境上运行的程序,不同于开发、测试环境,需要考虑的更多,如实现资源配额管理、运行时修改配置、系统回滚、蓝绿发布、灰度发布等等。研发云上,我们通过启动系统时设定资源配额的方式,将配额设定传递给Kubernetes,由Kubernetes来管理运行时程序所能使用的计算资源。在运行时修改配置方面,研发云集成了携程开源被业界广泛使用的Apollo配置中心,通过API方式修改程序配置。在蓝绿发布、灰度发布方面,我们正与华为云合作,将Service Mesh的最流行实现方案Istio引入研发云。

用户头像

星际行者

关注

赋时光以生命、文明以岁月 2019.03.28 加入

还未添加个人简介

评论

发布
暂无评论
基础设施建设之路(一)