前言

  随着云计算技术的不断发展,容器和Kubernetes已经成为云原生应用的基石,容器的周边生态也日益成熟,微服务、服务网格、DevOps等技术相继涌现。
  容器的出现,推动了软件开发、测试、部署、运维和运营模式的创新。容器云平台的建立承载了企业的IT基础设施和基础技术服务,为企业应用的创新和发展提供了强有力的支撑,同时促进了与产业链生态环境中上下游系统的高效对接与协同创新。
  Kubernetes作为容器云的编排工具,目前已经成为行业的事实标准,完美地解决了调度,负载均衡,集群管理等功能。
  开发者中心是用友云推出的一款PaaS级产品,它包含容器云平台,DevOps平台,微服务治理平台,定位于打造企业开发运维一站式服务。
  目前,开发者中心已在上线运行,支撑了大量的用友云服务,如友云采、Diwork等。其中也不乏用友2019年的战略产品。在将这些产品云化过程中需要解决诸多问题,如产品覆盖多个领域,集成难度高,技术架构多样。有些领域已使用容器云方式部署,而有些领域仍然在使用原生方式部署。在总的战略目标指导下,需要我们对那些原生方式部署的领域进行容器化改造。
  那么,如何全力支撑用友云产品有序的开发,测试,集成,上云?如何利用开发者中心的技术优势,打造技术中台的标杆项目,真正体现技术中台的核心价值?我们运维服务人员在支持这些项目的时候,又需要做哪些保障工作?
  本文将分别从产品运行环境规划、应用的构建与优化、应用的监控与报警三个维度展开,对支撑用友云产品的过程进行讲解。

1、产品运行环境规划
  产品线规划
  云上项目有很多,每个项目又可以划分为多个领域,如营销云、财务云、协同人力云等众多的领域。我们如何在开发者中心更好的管理这些领域并加以区分呢?
  在实际运维中,我们使用了产品线和产品的概念。我们可以根据项目创建产品线,并根据在整个产品线下面建立多个产品,产品根据领域和业务划分。这样我们就可以把整个项目下的应用归类成不同的产品,后期我们就可以方便对产品里的应用进行管理和授权。
全力支撑用友云产品 打造技术中台标杆项目_第1张图片

  环境资源规划
  用友云产品在上云过程中,可能涉及到多套环境的使用,如开发环境、测试环境、日常环境、预发环境、联调环境、生产环境等,开发者中心对这些环境的搭建都做了相应的支持。
  同时,对于具体项目,不同的环境可以用来满足差异化的需求。
  比如面向开发人员,可以用测试环境进行新功能的开发和自测。一些项目在开发过程中,可能出现开发进度快,测试频繁的情况,这时仅有测试环境无法保障环境的稳定和统一。此时,我们就可以专门部署一套日常环境来供测试人员进行测试操作。这样,开发人员研发的新功能或者修复的bug在测试环境经过自测后,就可以提交到日常环境中。我们在支撑项目时,可以在日常环境开启审核环节,由各领域测试负责人来进行审核,严格控制代码发布频率,保证测试进度和环境稳定。
  域名规划
  一些用友云产品可能涉及多个领域,有些领域从横向来说可以认为是相对完整而独立的产品。我们对每个领域规划并分配相应的域名,使每个领域拥有自己独立的域名。相关领域融合在一起就是整个项目,通过工作台统一对外提供服务。
  1) 每个环境通过一个唯一域名对外提供访问
全力支撑用友云产品 打造技术中台标杆项目_第2张图片
  2) 制定域名规范
  接下来要针对一个产品下各领域的域名制定规范。一般推荐按照以下规则建立域名。
  项目名称-二级站点-环境。yyuap.com
  主机资源规划
  在用友云产品的各个领域中,应用数目众多,这需要大量的机器去承载运行这些应用。本着合理利用资源的初衷,建议充分利用已有资源。一种合理的资源池主机内存使用率是控制在50% 至70%之间。
全力支撑用友云产品 打造技术中台标杆项目_第3张图片
  中间件规划
  一般来说,一个用友云产品可能使用很多不同的技术栈,当中也涉会及到很多种中间件,如MySQL,Redis,ZooKeeper,Kafka,MongoDB,Memcache,RabbitMQ,PostgreSQL,Fastdfs等。
  对于中间件,我们可以在测试环境使用单点模式部署,在日常环境采用集群模式部署,最大限度的合理利用和使用资源。
  下面我们以Mysql,RabbitMQ,Redis的部署来举例,说明下在一个用友云项目中的规划和使用情况。
  1) Redis
  Redis是一款开源的内存型非关系数据库,在最初的集群方案中我们备选了哨兵模式,Cluster,主从等方案。在和各个领域沟通中,有些领域代码不支持Cluster方案,哨兵模式也需要开发更改相关的代码,因此经过权衡,我们选择了主从模式进行部署。在部署过程中,为避免各个领域在使用中产生相互干扰,我们采用各领域分配不同库的解决方案。
全力支撑用友云产品 打造技术中台标杆项目_第4张图片
  2) MySQL
  MySQL是一个关系型数据库,目前各个领域大多数用来作为数据持久化存储。数据的重要性不言而喻,如何保证数据库稳定和数据不丢失,是我们在选择数据库集群方案中需要优先考虑的。目前主流的MySQL集群方案中有MHA、PXC和主从模式等。
  MHA(Master High Availability)在MySQL高可用方面是一个相对不错的解决方案,它支持高可用性环境下故障切换和主从性能提升。但是,这个模式下的Master在切换过程有几十秒时间不可用,而且及作者不再维护该软件,同时对目前MySQL主流版本的适配存在一些问题。
  PXC (PerconaXtraDB Cluster) 提供了MySQL高可用的另一种实现方法。推荐配置至少3个节点。此方案实现了多主复制,可以在任意节点进行写操作;同时同步复制功能,实现数据库事务要么在所有节点提交,要么不提交,保证了强一致性。但此方案受网络性能和磁盘IO性能影响较大,锁冲突、死锁问题产生的可能行和频率相对更多,且不支持LOCK TABLE等显式锁操作。实际项目中业务逻辑较多,如产生锁冲突和死锁问题,容易导致服务的不可用。
  权衡各种方案的利弊,我们选择主从+VIP模式。基于主从方式可以保证数据的可靠性和数据完整性;VIP方式保证整个系统的高可用。采用此方案,不仅可靠性可以得到保障,同时又可以最大化利用资源。
  在MySQL日常使用当中,我们严格施行权限管理,密码复杂度等安全措施。为各个领域分配独立的账号密码满足数据库连接要求,同时为开发人员提供公共读账号进行数据的查询,保证数据库的稳定可靠安全。
全力支撑用友云产品 打造技术中台标杆项目_第5张图片
  3) RabbitMQ
  RabbitMQ是一个开源的AMQP实现,采用默认的集群模式并不能保证队列的高可用性。尽管exchange、绑定这些可以复制到集群里的任何一个节点,但是队列内容不会复制。该模式可解决一部分节点压力问题,但队列节点宕机将直接导致该队列无法使用,此时只能等待重启宕机的服务。所以要想要实现队列节点发生宕机或故障时也能正常使用,就要复制队列内容到集群里的每个节点。因此,我们这里采用RabbitMQ镜像模式。在该模式下,消息实体会主动在镜像节点间同步,而不是Consumer取数据时临时拉取。
  诚然,镜像模式也有一定的副作用,如降低系统性能,需要消耗大量网络带宽等。但是在内网模式下,这些副作用对我们来说影响微乎其微。我们针对每个领域应用设置不同的vhost加以区分,这样各个领域拥有自己的exchange、队列、绑定等,既能将同一个Rabbit的众多客户区分开来,又可以避免队列和exchange的命名冲突。
全力支撑用友云产品 打造技术中台标杆项目_第6张图片
  4) ZooKeeper
  ZooKeeper是一个开源的分布式协调服务,最常用的使用场景就是用于担任服务生产者和服务消费者的注册中心。服务生产者将自己提供的服务注册到ZooKeeper,服务的消费者在进行服务调用时,先到ZooKeeper中查×××,获取到服务生产者的详细信息之后,再去调用服务生产者的内容与数据。
  用友云产品中的一些应用是基于Dubbo框架开发的,并使用ZooKeeper作为注册中心。我们使用分布式集群部署方式搭建ZooKeeper服务,通过配置守护进程,保证服务在出现意外退出的情况下,自动拉起服务。同时部署dubbo-admin,zkui等工具供开发进行排查分析。
全力支撑用友云产品 打造技术中台标杆项目_第7张图片
  配置提取方式规划
  用友云产品中各个领域的代码,配置文件管理方式不尽相同。比如营销云的配置文件存储在git代码仓库中。多环境下,不同环境间代码变动较少,其间更多变化的只是配置文件。在部署过程中我们抽取出配置文件,使用配置中心满足配置文件管理需求。
  配置中心提供了配置文件的多版本管理功能,因此代码在测试环境经过测试后,改变配置文件就可以发布到日常环境。
  访问链路规划
  当我们访问某个应用具体的功能时,访问请求一般来说会经过以下几个步骤:
  入口域名→防火墙→多重转发→某个应用的pod
  请求在转发时,每经过一层转发,就会多一次链路开销。所以我们在不影响安全和稳定的情况下,应该尽可能的减少转发次数。因此我们在内网DNS添加解析记录,应用调用时尽可能的走内网调用,减少链路消耗。
全力支撑用友云产品 打造技术中台标杆项目_第8张图片

2、应用的构建与优化
  在对产品的运行环境规划完成后,就可以按计划构建应用了。在实际的持续交付过程当中,针对常用的应用进行优化,可以加速产品的交付过程,减少研发时间,提高应用访问效率等。
  应用的优化方向很多,本文主选取镜像构建优化和Nginx跨域配置两个方向进行讲解。
  镜像构建优化
  在使用Docker容器时候,我们应该尽量使用符合要求的较小体积的基础镜像。这是因为在部署应用时候,体积较小的镜像可以在拉取时消耗的更少的时间,同时也占用更小的磁盘空间。
  在构建镜像的时候,Dockerfile中的RUN语句总是会创建一个新层,然而应用在生成最终镜像之前,总是要使用很多中间文件。那么在这种情况下,该如何获得体积更小的镜像呢?
  在这里我们使用下面二个最佳实践:
  ·使用更小体积的基础镜像
  ·使用Docker的multi-stage build(多阶段构建)
  使用小体积的基础镜像
  当前,容器已成为现代数据中心的必要组成部分。容器可以在各类操作系统中构建。那么企业该如何选择最合适的操作系统来运行自己的容器呢?不同企业的情况与需求不同,选择自然也不尽相同。不同的操作系统,如何在特性和基本功能方面进行比较?这些差异如何影响它们支持应用程序的方式?这些都是我们必须考量的重要问题。本文中我们将比较二类具有代表性的操作系统:
  ·全功能操作系统
  ·精简操作系统
  1) 全功能操作系统
  这类操作系统主流的有 Ubuntu,Debian,CentOS等。他们的功能无疑是最齐全的。不过这种“齐全”也是有一定代价的:在存储、内存和CPU资源方面,这类操作系统对系统资源的要求很高。同时,这些功能还会增加操作系统的***面,为潜在的***者提供更多的角落和缝隙进行***,同时这些镜像也往往比较大。
  2) 精简操作系统
  精简的操作系统只包含最基础的系统组件,我们可以根据自己的需要去添加安装软件。主流的精简的操作系统有Alpine 和Busybox等。Alpine采用的是musl-libc,不是通常用的glibc.针对大多应用使用glibc类库,这里我们使用glibc 来替换musl-libc满足日常使用。
  以下是镜像操作系统的体积对比:
全力支撑用友云产品 打造技术中台标杆项目
  multi-stage build(多阶段构建)
  从 Docker 1.10 开始,COPY、ADD 和 RUN 语句会向镜像中添加新层。镜像的层就像 Git 的提交(commit)一样。Docker 的层用于保存镜像的上一版本和当前版本之间的差异。实际上,本地Docker在向镜像仓库请求拉取镜像时,只是拉取当前主机尚未拥有的层。这是一种非常高效地共享镜像的方式。但额外的层并不是没有代价的,层仍然会占用空间。层数越多,最终的镜像就越大。
  将多个 RUN 语句组合在一行命令中,或许是解决镜像过大问题的一种很好的做法。但在现在,我们有更好的实现方式:multi-stage build.
  当我们构建的时候,构建编译过程中会下载开发语言的工具和库,而我们运行时候仅仅只需要编译后的程序。这样,我们使用多段构建把编译好的程序放入一个新的镜像当中,运行新的镜像即可。
  如下是对营销云基础服务upc-server做的多段构建Dockerfile:
全力支撑用友云产品 打造技术中台标杆项目_第9张图片
  在样例中,第二个FROM指令开启了一个使用java:8-jdk-alpine镜像作为基础镜像开始新的构建阶段,COPY ——from=0 将刚才构建的结果从前一阶段复制到这个新阶段。这样,在第一阶段构建好的应用被复制到下一个阶段的镜像当中。
  Nginx跨域
  前文提到,一个产品可能涉及多个领域,我们可以为每个领域分配自己独立的域名。相关领域融合在一起,通过工作台统一对外提供服务。而浏览器为了安全问题一般都限制了跨域访问,也就是不允许跨域请求资源。因此,各个领域之间应用相互调用会牵扯到跨域的问题。
  通常解决跨域问题的方法有以下几种:document.domain,Cross-Origin Resource Sharing(CORS),Cross-document messaging,JSONP,WebSockets.
  CORS 定义了一种浏览器和服务器之间是否允许跨站请求的标准。这种方式相对其它的来说更加灵活简单,也是W3C推荐的方法,在实际的项目中我们也使用这种方法进行跨域配置。
  CORS 标准定义了一组新的HTTP header,这组header给浏览器和服务器提供了一种判断跨域请求是否何法的依据。 因此,要实现CORS,浏览器(client)和服务器(server)都应该遵守该约定。浏览器端需要在请求的时候增加一个 Origin 的 HTTP header,值为当前页面的域(domain)。
  常用的Nginx跨域配置如下:
全力支撑用友云产品 打造技术中台标杆项目_第10张图片
  经过优化完整的Nginx跨域配置如下:
全力支撑用友云产品 打造技术中台标杆项目_第11张图片

3
应用的监控与报警
  随着用友云项目在多个环境的部署,应用数量也在不断的壮大。当应用出现问题后,定位问题往往需要花费很长时间,甚至很多问题没有办法复现,如果这样将可能产生严重的后果。仅仅依靠人工去发现问题是远远不够的,必须丰富完善自动化的监控系统。没有监控服务,就没办法对系统“望闻问切”;找不到系统的问题,就没办法做针对性的改进,这样线上服务就会处于随时崩溃的边缘。这些问题往往会成为悬在运维与开发人员头上的“达摩克利斯之剑”。
  在开发者中心,我们使用Prometheus来进行系统和服务的监控告警。Prometheus是由SoundCloud基于Go语言开发的开源监控报警系统和时序列数据库(TSDB)。Prometheus的基本原理是通过HTTP协议周期性抓取被监控组件的状态,任意组件只要提供对应的HTTP接口就可以接入监控。下面我们通过监控和告警两个方面进行介绍。
  基础设施监控
  主要对各个应用实例所在的基础设施进行监控,具体包括对这些设施的运行状态、资源使用情况等。基础设置我们主要的监控指标如下:
  ·使用率:CPU使用率(wait,sys,user),1分钟负载值 ,内存使用率等;
  ·吞吐量: 磁盘的IOPS(每秒读写次数),PBS(每秒读写量),时延等待;网卡的IOPS(每秒包数),PBS(出入带宽);
  ·容量: 磁盘文件系统(/目录,/data数据盘)总量,已使用量。
全力支撑用友云产品 打造技术中台标杆项目_第12张图片
  微服务通用监控
  主要针对微服务通用指标进行监控,包括服务实例处理请求的情况及实例调用其它服务的情况。具体而言包括请求总数、请求处理时延、请求结果统计、调用其它服务的结果(成功、失败、熔断、限流、超时和拒绝)统计及时延。
全力支撑用友云产品 打造技术中台标杆项目_第13张图片
  全链路监控工具(APM)
  全链路性能监控从整体维度到局部维度展示各项指标,将跨应用的所有调用链性能信息集中展现,可方便度量整体和局部性能,并且方便找到故障产生的源头。这在生产环境中可极大缩短故障排除时间。有了全链路监控工具,我们能够达到:
  ·请求链路追踪,故障快速定位:可以通过调用链结合业务日志快速定位错误信息;
  ·可视化: 各个阶段耗时,进行性能分析;
  ·依赖优化:各个调用环节的可用性、梳理服务依赖关系以及优化;
  ·数据分析,优化链路:可以得到用户的行为路径,汇总分析应用在很多业务场景。
全力支撑用友云产品 打造技术中台标杆项目_第14张图片
  某一段时间的堆栈信息。
全力支撑用友云产品 打造技术中台标杆项目_第15张图片
  中间件监控
  在项目当中,不少性能问题是由不起眼的中间件造成的。中间件也会成为系统性能问题的缔造者,但人们往往忽视了中间件本身对性能的影响,这种影响包括吞吐量的制约、响应时间的影响、调用成功率的影响等等。得益于 Prometheus 完善的生态,我们对常用数据库、消息队列及缓存进行监控的能力,具体包括 MySQL,Redis,Memcached,RabbitMQ,Etcd,MongoDB,PG等。
全力支撑用友云产品 打造技术中台标杆项目_第16张图片
  特殊服务额外监控
  在项目里,有些核心应用的健康度和访问链路,需要我们额外的去关注。比如基础服务的ProductCenter,库存采购的UstockService,以及各个领域单独独立域名URL.
全力支撑用友云产品 打造技术中台标杆项目_第17张图片
  至此,我们能收集大量的指标数据,也能通过强大而美观的面板将数据展示出来。不过作为一个监控系统,最重要的功能应该是能及时发现问题,并及时通知给相关负责人,这就是 Alerting(告警)。Prometheus 的告警功能被分成两部分:一个是告警规则的配置和检测,并将告警发送给 Alertmanager,另一个是 Alertmanager,它负责管理这些告警,去除重复数据,分组,并路由到对应的接收方式,发出报警。
  常见的告警接收方式有Email、PagerDuty、HipChat、Slack、OpsGenie、WebHook 等。
  告警规则
  目前我们在使用当中选取了Email,WebHook两种方式。
  1) 邮件告警
全力支撑用友云产品 打造技术中台标杆项目_第18张图片
  2) WebHook告警
  通过报警程序去调用Prometheus 和友空间API发送到相应的告警群组
全力支撑用友云产品 打造技术中台标杆项目_第19张图片

4、总结与展望
  技术更新与变化日新月异,不变的是对服务稳定性的需求。在用友云产品里,开发者中心致力于最大限度保障环境的稳定,解决项目上的问题,满足开发的诉求。现在,我们正不遗余力的推进集团统一运维标准和DevOps理念。在和各个领域的合作当中,我们也吸取了一些领域在长期项目实践过程中的优秀最佳实践和特性,不断完善开发者中心的能力,使其满足一些领域在特定场景下的需求。同时,我们也引导各个领域在容器云环境下部署发布项目和应用,提升管理应用,排查问题的能力等。
  今后,我们将继续孜孜不倦的实现产品功能,尽最大努力满足用户的需求和保障产品持续稳定高可用的运行。
  让我们一起加油,共同努力,为早日实现集团战略目标而奋斗!