DockOne微信分享(二七二):企业级应用Service Management Automation X(SMAX)的微服务之路


【编者的话】相比于瞬息万变的互联网应用,企业级应用如何做到快速的兑现客户承诺,及时的响应用户和市场需求?技术转型是必然趋势,不破不立,微服务理念的崛起恰好给了企业技术转型的绝好机会。但微服务的改造之路并不是一帆风顺的,这里不仅涉及到技术层面的改造,还有管理模式,工作方式的改变。本次分享浅谈一些个人看法和Mico Focus公司的实践经验。

正文开始之前,让我先简单的介绍一下我们的产品,Service Management Automation X(SMAX)来自于全球领先的纯软件公司Micro Focus,基于ITIL(Information Technology Infrastructure Library)实现,ITIL是全球公认的一系列IT服务管理最佳实践。ITIL的基本架构可参见下图:
itil-service-lifecycle.jpeg

困局?

2017年的时候,我们整个项目组还在使用瀑布模型的方式开发、发布产品。每次产品的迭代发布周期是一年。相比于瞬息万变的互联网应用,可能每个月都有一次版本更新,我们的产品版本更新太慢。为何要如此频繁的更新产品呢?频繁的发布更新可以快速的兑现客户承诺,及时的响应用户和市场需求,最大化的实现商业价值,小步快跑的方式抢占市场。对于企业级应用来说,也是一样的,我们需要快速的兑现商业价值,让客户尽早的用上新功能。因此从公司的战略层面,决定将版本更新的频率提升4倍,发布周期从原来的1年,缩短为3个月。

要实现这个战略目标,对现有的组织架构、技术架构等都是非常大的挑战。我们需要一种新型战术来满足我们的业务需求。微服务正好可以解决产品快速迭代的问题,并且具有以下优点:
  1. 提升开发交流,每个服务足够内聚,足够小,代码容易理解;
  2. 服务独立测试、部署、升级、发布;
  3. 容易扩大开发团队,可以针对每个服务组建开发团队;
  4. 提高容错性,一个服务的内存泄露不会导致整个系统瘫痪。


最终我们选择了微服务,帮助我们快速的迭代产品,快速的实现商业价值。同时在开发流程上我们开始使用敏捷开发,并倡导DevOps文化,希望各个小团队可以达到团队自治,对市场做到快速响应。

破局?

当确定了战略目标后,就是怎么做了?可以从两个角度来讨论。
  1. 管理层面
  2. 技术层面


管理层面

微服务的转型之路,这个决定必然是由上而下的,从管理层到具体的每个开发人员,我们必须在观念上达成一致,不破不立,敢于尝试新技术,但也不可盲目采用新技术,为了技术而技术,前期适当的小规模验证过程是很有必要的。对于一些腐朽的旧思想也要能做到果断的抛弃。

我们整个开发团队的人数达到了数百人,对于如何管理一个数百人的敏捷团队,也缺乏业界的最佳实践,我们也只能摸着石头过河。对于如何做好敏捷开发,我们依然在不断探索中,找到最适合我们的方式。对于大型企业,现在业界有一套完整的敏捷开发解决方案SAFe,我们也在尝试中。

技术层面

回到技术层面,我将从以下几个方面来讲我们的转型之路:
  • 技术选型
  • 服务拆分

  • 安全
    • 容器安全
    • 敏感信息保护

  • 企业级可用(Enterprise Ready)
    • 健康检查
    • 零停机升级

  • DevOps
    • Pipeline
    • 性能测试
    • 监控
    • GitOps


技术选型

现在一提到微服务,很多人会想到容器技术,比如Docker。

那么微服务和容器之间到底有什么关系呢?我的回答是,微服务和容器没有任何关系。

微服务理念出现的时间要比容器技术早很多,其理念在70年代被提出。而容器技术在2013年才被提出,它最初由一个叫做dotCloud的项目发展而来,后来改名叫做Docker。基于微服务的思想开发应用程序是完全可以不用容器技术的,例如现在流行的Spring Cloud和Dubbo都是不使用容器技术来实现微服务思想的。

那为什么微服务和容器技术总是被同时提起?这主要是出于以下两个原因:
  1. 按照微服务的理念,如果使用容器作为基础设施,能够实现快速部署,快速迭代,独立运行;
  2. 在云计算中,容器作为替代虚拟机的基础设施受到大家的关注度更高。


如果能将容器技术应用于微服务中,简直能达到事半功倍的效果。既然是微服务,必然会产生许许多多小的服务,如何对其进行管理就成为了我们很现实的问题。2017年时,容器编排平台的百花齐放,我们犹豫了,到底该怎么选择?当时很多互联网公司开始自研自己的容器编排平台,虽然这能获得极大的独立自主性,但同时自研也需要大量的研发投入,这是我们不能承受的。然后我们把目光放到了开源社区,当时比较知名的容器编排平台有Kubernetes和Docker Swarm。
k8s_docker_swarm.png

可能出乎很多人的意料,我们一开始用的是Docker Swarm,选它的理由也很简单,Docker Swarm上手快,学习曲线平缓。但在使用或者尝试的过程中,我们低估了我们产品的复杂性,Docker Swarm远不能满足我们的需求。最终,我们又决定转移到Kubernetes平台,万幸的是,我们并没有在Docker Swarm上花太多时间,平台转移的决定也做的很坚定。这在微服务改造的过程中是很关键的,人总会有犯错的时候,勇于承认错误,并不断地做调整,不拖泥带水优柔寡断。一定的试错成本是不可避免地,不要害怕试错。

现在回过头来看,我们当时选择Kubernetes的决定,真的是“赌”对了。Kubernetes自身就是一个完整的分布式系统解决方案,支持服务发现,服务注册,扩缩容,跨主机部署,企业级就绪,自我修复,自动重启等功能,能帮助我们省下一大笔自研的投入。

Kubernetes起源于Google内部系统Borg,经过多年的实际运行,更成熟,更稳定。更是依托于活跃的社区,以及CNCF基金会的加持,云原生技术的不断发展,我们可以很容易的从社区得到帮助,以及很多非常棒的技术实现,帮助我们解决了很多问题。比如Hashicorp公司的Vault,一款出色的存储敏感信息的软件,还有监控软件Prometheus

服务拆分

有了技术选型,Kubernetes和Docker,我们就可以进一步进行微服务拆分。服务拆分,如何拆分?拆到什么程度?

谈到服务拆分,不得不提的就是著名的“康威定律”,“软件架构会反映出公司的组织架构,组织架构又反过来影响着软件架构。”

为获得更大的沟通效益,微服务架构的改造必然会导致组织架构的改变。我们需要在现有技术架构,组织架构间不断做调整,平衡两者的关系,最终将服务拆分为四大模块。
  1. 事件管理、问题管理、变更管理、服务资产/配置管理
  2. 租户管理
  3. 智能分析
  4. 通用服务


每个业务模块有一个团队负责开发维护,每个团队的人数大概是3-5人,并由团队自己做适当的继续拆分,落地为真正提供服务的Pod。

个人认为,对于大型企业级应用来说,因为本身架构、技术债务、组织架构等等原因,不急于将应用程序拆的非常细。一是拆的越细成本越高,花费的时间也会越长,短期内看不到业务价值,就很难说服业务方同意此方案。二是从未来来看,不能保证现在的拆分就是合理的,应遵循小步快走,快速试错的原则。

每个组件必须可以独立运行,这是业界对于微服务的公认标准。这里对于组件的定义,个人认为可以扩展到由多个Pod组成,共同提供功能,而不是要求每个Pod必须可以独立的提供功能。对于现有应用程序的微服务改造,不考虑现状,过高的标准是不合时宜的。我们可以以架构演进的方式,慢慢迭代开发,最终不断完善我们的应用程序。

总结来说,我们的拆分原则是,先按业务功能分成大的模块,在由各个团队,结合业务和技术实现继续拆分。同时针对微服务中的一些通用功能可以成立一个独立团队负责,常见的通用功能有路由模块、鉴权授权模块。
SMAX架构图.png

安全

容器安全

在以色列,我们有专门的安全团队负责对我们的产品进行安全扫描,可以从以下几个维度来检查及保证容器的安全性。
  1. 恶意及脆弱镜像,在Docker Hub市场中有成千上万的免费镜像,可随时在Docker容器中使用。然而一项研究表明,在测试的2500多个Docker镜像中发现了大量安全漏洞。选择官方或可信的镜像,避免引入脆弱组件,更甚者是恶意代码。Docker Hub也提供付费计划,可以对镜像进行“安全扫描”,检查镜像中的已知漏洞。
  2. 过量资源使用,一般情况下,Docker容器没有资源限制。因此,不受限制的Docker容器可能导致宿主机性能严重下降。要对内存、带宽和磁盘使用(I/O)设置资源限制,保证整体性能的稳定。
  3. 容器突破,在 Dockerfile 中,如果我们不显式指明用户,进行权限处理,那么Docker容器默认在运行时会以root身份运行,以root身份启动 Docker容器是一件很危险的事情。尽管,Docker容器内的root与宿主host本身的root并不一定具有一样的权限,但是在容器内部的root拥有和宿主机一样的UID(UID 0)。如果以priviledge的方式运行container,那么两者将会一样,从而产生巨大的安全隐患。应遵守最小权限原则,避免潜在的安全风险。
  4. 对Docker镜像进行签名及验证,保证客户能拿到真正由我们公司发布的Docker镜像,防止黑客对Docker镜像的恶意篡改。
  5. 不要在Docker镜像中存放敏感信息,现在市面上检查容器安全的工具有很多,我们目前使用的工具有AnchoreAujas


敏感信息保护

微服务间各个Pod之间的通信需要进行安全认证,需要用到证书以及用户名密码。证书该如何被安全的分发到各个Pod内部呢?Pod应该如何获取密码?基于Kubernetes强大的生态系统,Hashicorp公司的Vault正好可以满足我们的技术需求。有关Vault的更多使用可以访问官网:https://www.hashicorp.com/products/vault

企业级可用(Enterprise Ready)

健康检查

作为企业级软件,我们的很多客户都是跨国公司,在各个国家都有员工,跨时区,则要求我们的应用程序必须24小时稳定提供服务。那该如何保证高可用?

在软件行业,你可能经常听见这样一句话,“要不你重启试试?”的确,很多问题都可以通过重启解决,借助于Kubernetes提供的自愈功能,我们为所有的Pod设置了合理的Liveness探针和Readiness探针来提高整体应用的可用性。

Kubernetes使用Liveness Probe(存活探针)来确定何时重启容器。例如,Java程序内存泄漏了,程序无法正常工作,但是JVM进程却是一直运行的,对于这种应用本身业务出现了问题的情况,通过检测容器响应是否正常来决定是否重启,这是一种很好的健康检查机制。

Kubernetes使用Readiness Probe(就绪探针)来确定容器是否已经就绪可以接受流量。只有当Pod中的容器都处于就绪状态时Kubernetes才会认定该Pod处于就绪状态。该信号的作用是控制哪些Pod应该作为Service的后端。如果Pod处于非就绪状态,那么它们将会从Service的Endpoint中移除。

配置有效的Liveness Probe

Liveness Probe应该检查什么?

一个好的Liveness Probe应该检查应用内部所有关键部分是否健康,并使用一个专有的URL访问,例如/health,当访问/health时执行这个功能,然后返回对应结果。这里要注意不能做鉴权,不然Probe就会一直失败导致陷入重启的死循环。

另外检查只能限制在应用内部,不能检查依赖外部的部分,例如当前web server不能连接数据库时,这个就不能看成web server不健康。

Liveness Probe必须轻量。

Liveness Probe不能占用过多的资源,且不能占用过长的时间,否则所有资源都在做健康检查,这就没有意义了。例如Java应用,就最好用HTTP GET方式,如果用Exec方式,JVM启动就占用了非常多的资源。

零停机升级

产品总是在不断迭代中的,SMAX也不会只有一个版本。在Kubernetes集群中,如何升级应用程序,并且最小的宕机时间?

这里不得不佩服Kubernetes的强大,已经为我们提供了现成的解决方案,Rolling Update(滚动升级)。Kubernetes的Rolling Update提供了两个参数maxSurgemaxUnavailable来控制滚动升级的速度,不同的速度会导致不同的现象。

创建一个额外的Pod(新),然后删除一个旧的Pod(maxUnavailable = 0,maxSurge = 1)

假设我们有3个Pod,此配置允许在原有Pod的数量之上添加一个额外的Pod(maxSurge = 1),而可用Pod的数量不能低于3(maxUnavailable = 0)。此种配置保证了在新的Pod还没有正常提供服务时,依然会有旧的Pod存在,旧的Pod依然可用对外提供服务。但这种配置,在保证宕机时间的同时,也对应用程序提出了新的挑战,新旧Pod必须保证前后兼容。还有一个缺点就是更新Pod慢。
maxsurge1.png

删除一个Pod,然后添加一个新的Pod(maxUnavailable = 1,maxSurge = 0)

此配置不允许创建额外的Pod(maxSurge = 0),但允许有一个Pod不可用(maxUnavailable = 1)。在这种配置下,Kubernetes将首先停止一个Pod,然后再创建一个新的Pod。这种配置的主要好处是,不需要额外的计算资源,毕竟多创建一个Pod就是多一份计算资源消耗。
maxunavailable1.png

尽可能快地更新Pods(maxUnavailable = 1,maxSurge = 1)

此配置允许在额外创建一个Pod(maxSurge = 1)的同时,允许有一个Pod不可用(maxUnavailable = 1)。第三种配置结合了以上两种配置的优缺点,极大地减少了更新Pod的时间。
maxsurge1-maxunavailable1.png

这三种配置方式在我们SMAX产品中都存在,关键是结合自身选择最适合自己的配置方式。

以上就是我们产品SMAX的微服务迁移之路,想必每家公司的迁移之路都是不同的,全盘照搬是行不通的,要结合自身的行业属性,公司组织架构,人员配置,现有技术架构,不断地做权衡,设置优先级。完美的技术架构不可能是一步到位,需要不断地演进迭代。
伴随着微服务的改造,部署集群、测试等变得异常复杂,因此需要一种新的技术理念,提高工作效率,DevOps应运而生。DevOps是一种方法,它使开发人员和运维人员能够更紧密地协作,从而更快地交付高质量的软件。

DevOps

Pipeline

对于构建一个微服务集群来说,这是一个庞大而复杂的集成与交付步骤,如果这些步骤依然停留在手工操作的阶段,将严重拖慢“快速交付”的理念。通过Jenkins pipeline实现持续集成和持续交付(CI/CD)管道,完成自动化构建、测试和部署应用程序。

在开发和运维人员之间架起一座桥梁,方便开发人员快速验证新代码功能,保证质量。

在Pipeline方面,我们自研了很多的工具包,比如在2017年的时候,还没有kubeadm,我们自己开发了一套程序,可以做到快速的创建Kubernetes集群,并将整个SMAX集群部署其上,一切只需一条指令,大大节约了开发测试人员部署环境的时间。

性能测试

目前我们使用LoadRunner对整个集群进行压力测试,当前我们大约有50个关键事务(Transactions)用于压力测试,这50个关键事务基本涵盖了SMAX的核心功能。并且我们还有150个非关键事务处于开发调试阶段,力求对SMAX进行更完整全面的压力测试。未来我们依然会不断扩充我们的事务数量用于压力测试。

我们将上一次发布版本的测试结果作为基准线,计算此次压力测试的结果,性能是否有提升,还是衰退。通常如果有10%左右的性能衰退,我们就会引起警觉,花时间花人力进一步分析原因。参考LoadRunner的测试报告和其他的监控报告,帮助我们更快的定位问题。
LoadRunner.PNG

监控

当前我们从虚拟机和Kubernetes的两个角度来监控整个集群。虚拟机监控可以给到我们一个整体的性能报告。Kubernetes集群监控可以更具体到各个Pod的性能报告。
  • 虚拟机监控,我们使用了Zabbix监控整个集群的硬件使用率,比如CPU,Memory,磁盘IO等。这些参数相对来说还是比较粗略的查看集群的整体状态,但可以给到我们一个比较直观的整体性能趋势报告。当某些性能指标出现异常(超平均的5% - 10%)时,我们能快速的发现问题,然后采取措施,进一步分析问题。是否是新代码的引入导致了性能问题?
    zabbix.png
  • Kubernetes集群监控,利用Prometheus+Grafana搭建监控系统,我们可以很直观的看到Pod的运行状况,何时Pod的CPU和Memory处于高负载阶段,高负载的持续时间是否和LoadRunner压力测试的时间片吻合,如果不吻合,我们需要进一步分析导致高负载的原因。如果高负载的百分比大于平均值,也同样需要引起警觉,可能是一个性能下降的信号。
    prometheus_grafana.png


GitOps

GitOps要求在版本控制软件中(如GitHub,GitLab和Bitbucket),使用声明性规范(比如yaml文件)存储系统所需的状态,这样对整个系统的改动,都是可审计跟踪的,每一个改动都包含了提交时间、提交者,这意味着基础设施可以和应用程序一样做到版本化。

在Kubernetes中,所有的Kubernetes资源都是以声明性规范(yaml文件)的方式来创建的。对声明性规范的修改,Kubernetes会负责将集群的最终状态保持和声明性规范一致。

GitOps正是利用了Git的版本化和Kubernetes的声明性规范来描述、创建和观察整个系统,开发和交付基于Kubernetes的基础设施和应用程序的模型规范。

如果从CI/CD的角度来理解GitOps,那么每次的Pull Request被合并到主分支就是CI,CD意味着Kubernetes用Pull Request的文件改动来更新自己的Kubernetes集群。

我们运用GitOps的理念,为DevOps团队提供基础设施,如Jenkins instance,PostgreSQL数据库、Prometheus和Grafana等。

在DevOps方面,我们还有很多需要做的,比如,自动伸缩,流量监控,日志链路追踪。我们已经在开始尝试一些POC(Proof of Concept,验证性测试),相信不久的将来,我们的所有尝试都可以给客户带来真正的商业价值。

总结

架构总是处在不断演化的过程中,我们不会停止对新技术的探索,用更好的技术服务未来更复杂的业务需求,快速实现商业价值。

Q&A

Q:团队敏捷,最重要的是什么?团队最成功是那一part?
A:我认为团队敏捷最重要的应该是团队文化,要真正的是一个团队,大家万众一心,不要搞小集体,大家目标统一了就更容易办成一件事情。举个例子,在我们团队中是如何培养团队文化的?每个人团队中总会有1-2个乐于分享,专研技术的人,我们可以定期搞一些内部技术分享,小型的,时间也不用太长。慢慢的培养大家的极客精神,由1-2个人带动整个团队。以上是对某个具体团队,我认为最重要的事情。如果上升到整个产品部门或整个公司,我认为管理层的百分比支持也没关键,要有一条路走到黑的魄力。我们团队最成功的部分,我认为也就是我们团队的文化,乐于分享,我也希望这个文化可以一直保持下去。

Q:业务升级时,Kubernetes通过滚动升级策略,保证了最大在线Pod数。那请问,单个Pod如何保证流量的平滑,比如在postStart阶段会做哪些检查?
A:这个要看你Pod的具体业务是否是有状态,比如session信息等。新老Pod是否可以共存,同时提供服务。Pod提供的是在线服务还是离线服务。然后在决定在Pod生命周期的哪些阶段做哪些事情。如果我们按最简单的说:
  1. 新老Pod可以共存,并且Pod提供的是离线服务。那么在preStop阶段可以先把老Pod的readiness置
    为false,这样新的请求就到新的Pod上去了,然后等老Pod做完已有的离线服务就Terminate。
  2. 如果说新老Pod可以共存,但不能同时提供服务,可能需要考虑蓝绿部署,通过Service的label selector来切换流量。
  3. 如果在复杂的情况,我需要更多的信息,具体分析。相对应的,代码需要做的事情也会更多。我们可以群内详聊。


Q:服务拆分过程中需要注意的点有哪些?微服务架构的配置问题?旧服务集群化需要注意的点有哪些?新旧服务之间的网络是否可以互通,如果不互通如何解决?
A:个人认为服务拆分的注意点可以从“管理”和“技术”来说:
  • 管理层面,服务拆分后和现有组织架构的冲突问题如何解决,这里管理层必须要有打破旧有格局的决心,但适时的妥协不可避免。
  • 技术层面,还是根据业务来拆分,同一业务或相似业务尽量拆分在同一团队,这样业务知识至少不用跨团队。先做一个大的拆分,不是那么细的拆分,尝试一下,然后在根据业务,由团队决定继续拆分,可能会有一个大的团队变成两个小的团队。


在Kubernetes中可以使用ConfigMap和Secrets做配置,但如果是复杂应用,也可以使用开源的一些配置中心。旧服务集群化的注意点,这个问题有点大,基本今天分享的所有内容都是注意点,到时会整理成文章发布,可以关注一下。Kubernetes是可以和外部系统互通的,比如使用externalService。如果不互通,是否可以尝试使用Proxy等技术使其互通。

Q:使用Kubernetes来管理微服务会带来一些好处当然也会带来一些复杂度,比如容器平台本身维护的复杂度,你们是从哪些方面来权衡最终使用Kubernetes来管理微服务?
A:一个是自身业务的复杂度,如果业务简单,完全没必要使用Kubernetes。如果自身业务复杂,必然需要一个功能全面的容器编排平台,比如Kubernetes。甚至一些更复杂的业务,都会基于Kubernetes做二次开发。二是CNCF,云原生技术的推广,这些技术都是围绕Kubernetes的,社区的帮助也很重要,减少了自研的投入。

Q:麻烦问下,你们的基础服务器都是线下机房吗?
A:我们有线下机房,同时也支持部署在云端,比如Amzon EKS,Auzre AKS,GCP,阿里云。最近在尝试部署到OpenShift。

Q:你们的基础设施是自己公司的私有云还是用的公有云?在基础设施选择上你们会用公有云去简化基础设施的投入,而把注意力都放在软件研发上吗?
A:我们有线下机房,同时也支持部署在云端,比如Amzon EKS,Auzre AKS,GCP,阿里云。最近在尝试部署到OpenShift。其实我们是企业级应用,属于卖软件产品,最终部署在哪里?on-premise,私有云还是公有云是由客户自己决定的。所以每个平台我们都需要支持。但我们也有自己的SaaS服务是部署在AWS上,简化了基础设施的投入和维护成本。

以上内容根据2020年10月27日晚微信群分享内容整理。 分享人钟涛,Micro Focus高级软件工程师,SMAX产品微服务改造见证者与实践者。DockOne每周都会组织定向的技术分享,欢迎感兴趣的同学加微信:liyingjiesf,进群参与,您有想听的话题或者想分享的话题都可以给我们留言。

0 个评论

要回复文章请先登录注册