Categories
程式開發

小鹏汽车技术中台实践:云平台篇


今天我们不讨论该不该做技术中台,只说说中台给我们带来了什么。

小鹏汽车的智能离不开复杂系统的支撑,其特有的互联网基因要求业务能够应对市场的迅速变化:快速响应、快速试错、快速创新。同时,为了给客户提供优质服务,系统需要更高的可靠性,降本增效也是公司快速发展过程中特别关注的。技术中台正好契合了公司上述所有需求。

小鹏汽车的技术中台分为微服务中台和云平台两大部分,其中云平台为技术中台提供底层的基础支撑,基于主流的容器技术 Docker 和 Kubernetes 构建;微服务中台基于 SpringCloud 打造,提供微服务的管理、治理、监控、统一标准化配置。我们在上一篇文章《小鹏汽车技术中台实践 :微服务篇》中介绍了微服务平台的实践,本文将重点分享云平台的实践经验。

Havok云简介

小鹏汽车的云平台(内部代号:Havok云)基于OpenShift 3.11 / Kubernetes 1.11,涵盖了容器化部署平台、日志中心、监控中心等基础设施。Havok云的愿景是让开发、测试、运维对Kubernetes认知零门槛。本着降本增效的原则,Havok云平台通过封装标准化业务部署单元,以效率和可用性为目标,提供生产级应用最佳实践,简化应用在Kubernetes的生命周期管理。最新版本的Havok云平台于2019年6月完成研发上线。目前接入到Havok云平台的几百个微服务业务应用100%使用Havok云的标准部署方式,所有应用均能享受微服务中台以及云平台提供的能力。

2019年10月17日阿里云联合微软全球发布开放应用模型(OAM),该架构设计模型与目前Havok云核心的Logan-App-Operator的架构设计模型不谋而合,在应用层面屏蔽了Kubernetes的相关概念,采用Operator + CRD模式定义了应用以及运维能力。通过这样的架构模型,能用高效、标准的方式连接应用开发人员、运维人员、测试人员和应用的最终用户,分离不同用户的关注点,简单高效。

小鹏汽车技术中台实践:云平台篇 1

容器化部署平台

目前几乎所有云端应用都运行在Havok云上,大多数业务应用是Java应用,还有部分Python、PHP、NodeJS应用 。容器化部署平台提供用户友好的 UI、日志聚合、跨集群部署、历史修订版等能力,封装了标准的Boot概念,统一了开发、测试、运维术语。开发人员只需专注业务,通过 CICD 标准化构建就能将业务应用部署到云平台。

小鹏汽车技术中台实践:云平台篇 2

Logan-App-Operator

在较早期,为了让业务快速实现容器化部署,我们在每一个业务应用的项目源码中都加入了一个openshift.yaml的配置文件,该文件约300行,其实就是一些Deployment、Service之类的配置,冗长而且重复。底层架构的配置分散在业务应用中,每一次架构的升级都需要让业务配合着改配置然后发布版本。而一些充满好奇心的开发人员还会去试着改动配置参数,这就很尴尬了。前期项目数量少问题不明显,但是随着公司业务的高速发展,这个做法明显不能满足业务的需求。因此我们决定将这个文件去掉,将底层架构的升级都收敛在云平台上,从而有了Logan-App-Operator这个子项目。

Logan-App-Operator通过封装标准化业务部署单元,以效率和可用性为目标,提供生产级应用最佳实践,简化应用在Kubernetes的生命周期管理。

  • 基于operator-sdk,通过CRD和Controller扩展Kubernetes API;
  • 支持多种语言项目:Java、Python、PHP、Web/NodeJS;
  • 将部署架构经验封装到产品,屏蔽Kubernetes技术细节,以业务应用为粒度管理;
  • 封装标准Boot概念(应用视角):统一架构提升,标准化开发、测试、运维术语;
  • 动态注入内容:配置在configmap,可全局调整,下一次部署生效(例如:sidecar、环境变量、资源配置、harbor地址等)。

小鹏汽车技术中台实践:云平台篇 3

上图为Logan-App-Operator的架构流程,很标准的Operator做法:定义CRD,结合统一配置并复用Kubernetes原生的能力,将应用资源最终映射到Kubernetes原生的资源上,从而实现应用的容器化部署。因为有Operator的统一应用配置管理,对于可用性、反亲和性的统一提升以及管理也就得以实现,不再需要让业务应用配合发布版本。原来冗长的openshift.yaml配置文件只剩下寥寥几行,将该应用部署的配置整合进CI/CD中,最终就实现了对整个openshift.yaml的回收。

apiVersion: app.logancloud.com/v1
kind: JavaBoot
metadata:
  name: demo-javaboot
spec:
  image: "harbor/javaboot-sample"
  version: "1.0"

除了应用部署之外,我们针对前文微服务中台的技术栈做了一些适配工作,让开发效率更高效。

  1. 为支持跨语言的微服务调用,微服务中台采用了spring-cloud-netflix-sidecar的sidecar模式,Logan-App-Operator通过统一配置模板的方式让非Java应用可以动态注入initcontainer、container来实现sidecar模式。
  2. 微服务之间存在大量的服务依赖调用,而开发环境都在Kubernetes上,在网络方面对本地开发调用来说并不友好,因此在开发环境部署的服务都自动开启了NodePort以便本地开发调试服务。
  3. 针对AI类应用服务,提供共享GPU能力的支持,提高GPU的利用率。
  4. 提供Deployment和Statefulset负载类型支持,避免重复造轮子。
  5. Boot粒度支持历史修订版回滚能力,友好的UI更方便查看Boot应用生命周期演进过程。
  6. Boot粒度自动弹性伸缩HPA特性支持,可以更优雅地使用HPA。
  7. Boot存储PVC支持,支持应用挂载NAS存储。
  8. 跨集群Boot应用支持:多集群浏览Boot信息、应用配置多集群同步、应用故障自动部署迁移。
  9. 对Boot的重启支持。

Knative Serverless的演进

据Gartner预测,到2023年,70%的AI任务会通过容器、Serverless等计算模型构建。Serverless非常适合使用在按需付费、CICD、ML、区块链智能合约等场景。Knative就是基于Kubernetes平台,用于构建、部署和管理现代Serverless工作负载的项目,也是我们一直关注的项目。通过比较发现,基于Logan-App-Operator可以非常容易地演进到Knative Serverless。

# Havok云平台中标准的JavaBoot应用定义
apiVersion: app.logancloud.com/v1
kind: JavaBoot
metadata:
  name: demo-javaboot
spec:
  image: "harbor/javaboot-sample"
  version: "1.0"
  
---
# 标准的knative Service 定义
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
  name: demo-javaboot
spec:
  template:
    spec:
      containers:
        - image: harbor/javaboot-sample:1.0

两者的CRD定义非常相似,开发人员只需要指定应用名称、镜像和版本就可以完成应用的定义,通过kubectl apply 即可完成应用的部署。应用同样支持Revision历史管理,又一个不谋而合。基于不要重复造轮子的原则,我们正在尝试将标准Boot定义映射到Knative的资源定义上,从而让业务应用无缝地迁移到Serverless计算模型上。在节省资源的同时,AI和数据业务对灰度发布、AB Testing的强烈需求将由Knative来满足。

GPU调度的演进

目前社区里面的GPU应用大多数都是使用nvidia-dockerk8s-device-plugin 来实现在Kubernetes上使用GPU资源,这也是Nvidia官方支持的方案。但是该方案目前不支持Containers共享同一块GPU卡。如果用取巧的环境变量方案来实现GPU共享,又会出现资源争抢以及不均衡的问题。对于AI类的在线推理场景,应用独享GPU卡将会造成极大的资源浪费。在我们面对的场景中,对 GPU 算力的需求和对显存的需求是成正比的,因此我们在阿里云的gpushare-device-plugingpushare-scheduler-extender基础上实现了多Containers共享多块GPU卡,即显存级别的多GPU资源调度,从而既满足了业务需求也提升了资源利用率。

当前我们实现的方案主要还是软隔离,需要君子协定来约束,在开发、测试、部署等过程中难免出现人为操作失误,因此还需要一个针对应用规范的GPU资源使用监控方案。GPU的监控,除GPU设备自身的监控外,针对应用自身的使用情况,Nvidia官方也有相关的开源解决方案gpu-monitoring-tools。但是该方案是完全基于k8s-device-plugin ,即GPU为Containers独享情况下的监控,并不适合显存粒度的方案监控。因此我们将该项目作为code bases,进一步细化到应用级别,实现了将应用申请的资源以及实际使用的资源作为监控指标上报到监控系统。

小鹏汽车技术中台实践:云平台篇 4

基于Nvidia的MPS技术可以实现GPU资源的超卖,进而做到高度共享GPU,实现硬隔离,进一步提升GPU资源利用率。但是MPS技术需要采用Nvidia Volta架构Tesla V100类型的卡,硬件成本较高,不太适合当前的发展阶段,但这仍然是我们在关注的方向。

Kubeflow的演进

Kubeflow是Kubernetes的机器学习工具包,是运行在Kubernetes之上的一套技术栈。但以目前Kubeflow发展所处的阶段还很难直接将其打造为一个多租户平台化体系化的机器学习平台。 幸好Kubeflow这套技术栈包含了很多组件,组件之间的关系比较松散,我们可以配合起来用,也可以单独用其中的一部分。

小鹏汽车技术中台实践:云平台篇 5

我们基于Kubeflow和已有的Havok云打造了一套多租户的机器学习流程平台,更多细节分享敬请期待后续的文章。

实时跨机房日志聚合

为帮助开发人员快速排查应用问题,日志查看是必不可少的手段。基于EFK的日志中心更适合在事后排查问题,而对于开发阶段或线上问题现场排查,类似tail方式的日志查看更为合适。Kubernetes原生的日志查看只能针对单个pod的单个container,无法满足实时排查问题时,单个应用多个pod多个container的情况。甚至有部分应用已经实现了跨机房的部署,就更加无法满足实时查看多个机房应用日志的需求了。

小鹏汽车技术中台实践:云平台篇 6

基于降本增效的考虑,为解决该需求,我们借力于开源社区,组合了多个开源组件实现该方案。

  • https://github.com/boz/kail 可以聚合单个Kubernetes集群的所有container实时日志,支持多种方式筛选pod。但是该项目本身没有抽象成一个可以扩展的类库,对于扩展的支持较差。目前我们将该类库作为code base,进行项目所需要的二次开发,另外还增加了metrics信息暴露。
  • https://github.com/boz/kcache 类似K8s.io/client-go/tools/cache 的Kubernetes对象缓存库,提供事件驱动机制,可以通过订阅方式捕获对象的增删改事件。 Kail利用该库来实现对pod增删改事件的捕获,毕竟在启动日志查看后pod增删改也是常见的事情,无需重新启动也是极好的体验。
  • https://github.com/gorilla/websocket Golang社区比较著名的WebSocket库,大量的项目都采用该库来实现WebSocket,OpenShift WebConsole也是采用该库实现的WebSocket Proxy,虽然性能不是最好的,但是应用最广泛。

有了以上的配件,让汽车跑起来就变得很容易了。给Kail加上WebSocket,再加上多机房连接多租户服务,实时跨机房日志聚合就完成了。同时,为了让日志查看更加方便,我们还增加了关键字过滤、下载等功能。

统一用户体系集成

整个技术中台存在大量的第三方组件,某些组件没有用户体系例如Zipkin、Prometheus,某些组件拥有独立的用户体系例如Grafana、Jenkins等,某些组件通过OauthProxy集成在Openshift的账号体系里面例如Kibana、Havok WebConsole。研发人员每天都在频繁使用这些系统提高研发效率。怎样基于自有的用户体系将各个开源系统的用户体系以及权限整合在一起给研发人员带来一站式的开发体验就成了一个问题。

小鹏汽车技术中台实践:云平台篇 7

因此我们专门开发了一个Admin-Svr组件,用于和各个开源或自研的系统集成。

  • 无用户体系:无用户体系的系统一加上HTTP Basic authentication,由Admin-Svr完成自动向后端提交Authorization Header。
  • 自有用户体系:建立系统之间用户、角色、资源的映射关系,由Admin-Svr完成对开源系统的模拟登录获取到对应的Token,建立用户联系。
  • Openshift Oauth Proxy:构建在Openshift Oauth Proxy之下的系统相当于自有用户体系中的特例,均对接到了Openshift的用户体系,即Kubernetes的RBAC。依赖Openshift Oauth Client来完成模拟登录,建立用户联系。

除了完成与多方系统的对接之外,其还具备如下特性:

  • 集成方案对第三方系统零入侵,可跟随第三方系统做版本升级、修复漏洞等系统演进。
  • 配合OpenResty,零入侵魔改第三方系统UI呈现方式,带给用户高级定制体验。
  • 与自服务体系整合,新建用户,新建项目,自动创建第三方系统相关资源。
  • Cookies超时同步、关联系统自动登出等等。

小结

Havok云容器化部署平台目前仍然在演进当中。以Boot为粒度,按节奏丰富业务应用在Kubernetes上的最佳实践。

  • Boot粒度支持混沌测试,基于Chaos Mesh已经在测试环境中落地,敬请期待我们后续在混沌工程方向的分享;
  • Boot粒度自动弹性伸缩:VPA、CA;
  • Boot粒度调试工具注入;
  • 更多Operator:Namespace自服务、中间件(Redis等)。

日志中心

OpenShift本身带了一套很基础的EFK架构的日志中心方案,为快速实现满足业务需求,我们的日志中心也以此为基础开展。日志中心通过Fluentd收集应用程序日志到Elasticsearch,提供Kibana的可视化UI方便相关人员快速通过日志排查问题;同时也作为日志数据分发的汇聚点,通过Kafka、Logstash等组件将日志分发到阿里云的ODPS和实时计算平台。

系统架构

小鹏汽车技术中台实践:云平台篇 8

日志中心主要包含Fluentd、Elasticsearch、Kibana、Kafka、Logstash。和网络上能找到的基于Kubernetes的EFK架构差不多,毕竟搭建Demo跑通流程容易,但是怎么能和原有体系无缝整合并且能随着架构一起演进会更加重要,这里主要说一些不同点。

  1. 接入微服务中台的应用统一遵循中台日志规范,按统一格式输出到标准输出 / 错误。通过 Docker 的 json-file driver 统一落盘到 Node(Kubernetes 的工作节点)上。注意Docker日志的保留时间一定要长一些,避免故障或维护等情况下的日志数据丢失。
  2. Fluentd以DaemonSet方式运行在Node节点上,挂载日志目录对数据进行采集,通过fluent-plugin-viaq-data-model解析出container对应的namespace以及Elasticsearch索引时间前缀等元数据信息,将整体数据同时分发给Elasticsearch和Kafka。Fluentd缓冲区的buffer也需要长一些,避免后端存储故障或维护的情况下的日志数据丢失。
  3. Elasticsearch按namespace和天的粒度存储日志到索引中。基于性能考虑我们利用NodeSelector来使用Node节点本地的SSD数据盘,经过系统性的优化大多数的查询响应都能达到毫秒级别。另外基于成本考虑目前仅存储7天的数据,毕竟好些业务应用一天增量就几百GB,成本是不得不考虑的因素。
  4. 最重要的还是ACL权限的能力,毕竟Elasticsearch企业版版本才提供收费的第三方的ACL权限整合功能。但是开源才是王道,借力开源社区,Elasticsearch整合SearchGuard提供ACL基础能力,利用SearchGuard开启HTTPS证书验证,Fluentd以及Prometheus Exporter均基于证书验证与Elasticsearch交互,确保系统安全。系统之间的解决了,用户的呢?因为我们整个用户体系都是基于Kubernetes的RBAC,为提供顺滑的用户体验,用户访问Kibana查询日志也需要在这个权限体系下完成。因此我们利用OpenShift-Elasticsearch-Plugin将Kubernetes RBAC的身份认证和权限等信息同步到SearchGuard的ACL体系中,再由Kibana利用Oauth-Proxy串通Elasticsearch身份认证。同时为了让用户体验更加流畅,在Kibana中自动生成项目的Index Pattern,不同项目组仅能查看项目组内的数据。

跨机房搜索

小鹏汽车技术中台实践:云平台篇 9

为了让开发人员在使用日志中心时能更方便地查询到多个集群的日志数据,我们调研了Elasticsearch跨机房搜索的多种方案。

1)Cross-cluster Search

基于Elasticsearch官方提供的Cross-cluster Search特性即可实现跨机房搜索,而且该方案还能和我们的ACL方案无缝整合。但是我们目前的Elasticsearch版本为v5.6,还不支持该特性,该方案只能放弃。随着系统的演进,我们希望后续能将Elasticsearch版本升上去,从而采用该特性来实现跨机房的搜索。借力开源社区,保持对开源技术的敏感度是技术团队不变的品质。

2) 多机房分发

从采集的源头上由Fluentd将数据分发一份到另外一个机房,或者是由Kafka利用镜像模式同步一份数据到另外一个机房。这绝对是理论上可行的方案,但是需要双倍的跨机房流量、双倍的存储和计算资源,如若再增加第三个机房则资源又要再翻一翻。难道要搞多个TB级别的而且有备份功能的日志中心?每天TB级别增量,从成本角度基本放弃该方案,仅作为兜底方案考虑。

3)Tribe Node

经过一翻调研后,我们发现Elasticsearch v5.6版本仍然保留了Tribe Node特性,只需要两边集群都增加一个TribeNode,就能够以较低的成本实现在单个集群的Kibana中查询多个Elasticsearch集群,并且该方案与我们的ACL方案仍然能无缝整合。为了进一步节省跨机房流量,我们对 OpenShift-Elasticsearch-plugin做了二次开发,默认将Kibana的Index Pattern设置为本机房的Pattern。

优化工作

1) Fluentd优化

前期Fluentd数据分发到大数据平台是采用Http Plugin的方式发送给Logstash,但是我们发现数据发送经常延时。我们对Logstash进行压测发现,Logstash的Http Input表现不错,能抗下每天TB级别的流量压力。后来又分析了Fluentd的Http Plugin,发现该Plugin既不提供数据批量发送的配置,也没有连接池特性,每发送一条数据就会发起一次HTTP请求。随着数据量增长,会出现明显的瓶颈,最终造成数据发送延迟。

在这个问题上,我们曾经考虑过是否能利用Fluentd的多进程机制提高这个吞吐量,但是实验后发现效果并不理想。因此将该流程修改为发送到Kafka作为数据的缓冲区,以Kafka的吞吐量和扩展性足以支撑数据量增长带来的挑战。

后期我们还计划进一步优化链路,将Fluentd到Elasticsearch的链路修改为Fluentd->Kafka->Logstash->Elasticsearch这样的链路,避免在Kubernetes集群Node数量进一步扩大后Elasticsearch成为瓶颈导致出现丢数据的风险。这也是Elasticsearch社区比较推荐的一种优化链路。

2)OpenShift-elasticsearch-plugin插件优化

OpenShift-elasticsearch-plugin插件出于数据一致性的考虑,在社区升级到5.x版本的Elasticsearch时,社区将ACL信息缓存的功能去掉了,修改为每一个查询请求都通过Kubernetes API获取用户的ACL信息同步到SearchGuard做身份和权限认证。这无形中会对Kubernetes的API-Server造成不必要的压力,同时也会拖慢整个搜索查询流程。

在我们的实践当中,去掉缓存会让搜索耗时上升到5-10S,大量的时间都消耗在和Kubernetes的交互以及SearchGuard的ACL数据写入上。从我们目前的阶段来看,ACL信息在短时间内的不一致其实都是可以接受的。首先这是一个对内的日志中心,并非TO C的产品,ACL信息的变更本身就不会很频繁,并不要求严格的ACL数据一致性,能达到最终一致性即可。再次因为整个技术中台的体系庞大,自服务体系也已经成熟,用户权限的变更早已整合到自服务体系中,也就能更快地通知到日志中心。

因此我们在社区最新版本的基础上做了优化,将ACL信息定期从Kubernetes API-Server同步到SearchGuard的ACL。这么一来,每一次查询都只是在Elasticsearch内部完成相关的校验逻辑,避免了冗长的系统交互流程,Elasticsearch又愉快地进入了毫秒级响应的时代。

具体和社区的交流可查看:

监控中心

系统架构

小鹏汽车技术中台实践:云平台篇 10

如上图所示,监控中心主要包括集群监控和业务监控两部分:

  • 集群监控:基于cluster-monitor-operator,统一管理监控组件和资源。主要监控apiserver、kube-controllers、etcd、kube-state-metrics、node-exporter等
  • 业务监控:基于prometheus-operator,使用简单的声明式配置创建和管理Prometheus实例、告警规则等。主要监控各类业务应用及中间件,并通过联邦的方式聚合集群监控的指标数据,便于多维度业务监控及可视化管理。

随着业务的快速增长及集群规模的不断扩大,我们正考虑结合Thanos开源项目,实现Prometheus实例的分布式动态扩展,跟踪日益增长的时间序列数据及更复杂的环境。

告警自服务

基于prometheus-operator的动态资源对象更新机制,我们开发了监控告警的自服务,该系统解决如下问题:

  • 应用开发者(以下称为Developer)可直接通过Portal自助完成Alert规则的生命周期管理,不再依赖于管理员统一配置,大幅提高业务应用上线监控效率;
  • Developer可在所管辖的应用中快速复用管理员提供的公共规则,并根据实际需求调整告警阈值;
  • 通过增加权限隔离,Developer配置的规则只对所管理的应用生效,不会干预到其他应用或规则;
  • 提供表达式的合法性校验及Dry-run机制,可在线体验告警样式。

小鹏汽车技术中台实践:云平台篇 11

平台系统

子系统 自服务中的功能内容
Prometheus 由于Prometheus Operator允许通过CRD对象(PrometheusRule)来管理告警规则,且规则变化能被动态加载进Prometheus。Alert产生后发送到Alertmanager,然后由Alertmanager调用运维告警系统的WebHook来完成告警发送。
Logan Admin Server 作为Portal的后台服务,保存Developer编辑的规则数据,并同步至Prometheus。同时实现将项目的告警方式和接收人同步到运维告警系统。
Logan Portal 在现有自服务项目列表中,Developer选择某个项目可以进行告警规则管理,与logan-admin-svr进行交互来实现相关功能。

运维告警系统

运维告警系统记录自服务的项目告警人配置,给每个bootName和namespace根据level(优先级)记录了对应的告警方式、接收人。一旦告警生成,该系统根据告警信息中的bootName、namespace、level等标签来确定告警方式和接收人,从而发送告警。

小鹏汽车技术中台实践:云平台篇 12

优化工作

1)Prometheus配置更新不生效

在prometheus-operator中,通常使用service-monitoradditionalScrapeConfigs配置监控targets等,针对一些个性化的配置,我们会在Kubernetes中直接修改secret来更新prometheus.yaml配置,但出现修改后不生效的现象。

问题原因:由于直接修改secret后,导致配置文件的inode发生了变化,watcher无法再接收到更新事件,因此不会进行reload响应。

解决方案:inode变化时,watcher将接收到一个’op=CHMOD’事件,在该事件中判断inode是否变化,如已变更,则将新配置文件增加到watcher中,使之生效。

2)Jenkins监控数据项不够丰富且采集不准确

只有构建总次数,缺乏成功、失败次数的指标,无法跟踪业务项目的代码质量情况。同时统计各业务CICD次数时发现应用有编译但数据却未变化。

问题原因:由于应用数非常多,每天大量编译,每次编译都会生成一条job记录,为避免job记录历史数据过多,我们设定了一个月的保存期限,而jenkins-prometheus-plugin在收集metrics时,根据job记录进行统计,这就导致了历史数据的metrics不准确。同时,此方式也存在性能问题。

解决方案:修改jenkinsci/prometheus-plugin,增加成功、失败次数统计指标项,参见pr#75。针对统计值不准确的问题,在内存中增加map保留各应用的jobCount(当前统计值)及latestJobNum(最近一次的job记录编号),每次收集时,从各应用的最新job开始,若jobNum(当前job记录编号)比latestJobNum大,则jobCount值加1,并更新latestJobNum,然后取下一个job,直到没有新job时退出。

3)集群监控资源及告警规则修改不生效

由于集群监控组件自带的部分告警规则有错误,例如:alert:PrometheusTSDBWALCorruptions中指标tsdb_wal_corruptions_total应该为prometheus_tsdb_wal_corruptions_totalalert:PrometheusTSDBReloadsFailing的告警描述与配置不一致等。另外,我们针对Grafana做过定制开发,需修改Grafana的镜像版本。而直接修改这些资源对象后,会自动被恢复。

问题原因:集群监控的资源及配置由cluster-monitor-operator生成并控制,资源对象硬编码在cluster-monitor-operator中(例如Grafana的镜像版本等),无法通过修改资源对象以更新其配置。

解决方案:关停cluster-monitor-operator,使不再控制及恢复相关资源的修改,prometheus-server不受影响,依然由prometheus-operator进行控制。

4)跨集群指标怎么办

在业务监控中,部分alertrule依赖于集群监控的指标,而这些指标数据是跨集群跨数据中心的,可以通过prometheus-federation将需要使用到的监控数据聚合到业务监控,从而进行快速计算。

Kubernetes版本升级

基于OpenShift的Havok云,底层使用Kubernetes作为容器编排组件。Kubernetes社区火热,每3个月会发布一个Release版本,当前已更新到1.18;同时OpenShift4开始支持Service Mesh、Serverless,这也是我们一直关注和持续跟进的项目,当前最新版为v4.4。由于线上业务繁多,环境复杂,同时对可用性要求非常高,频繁的集群版本升级不是我们所期望的演进方式。我们选择紧跟新版本特性,根据不同阶段的业务发展需求,适时对Kubernetes版本进行升级,以下是我们的一次升级实践。

背景

Havok云早期基于OpenShift 3.9,对应Kubernetes的版本为1.9.1,随着小鹏汽车业务快速发展,Havok云容器化部署平台无法复用新特性及基于新特性的开发,例如:基于CRD subresource等特性开发Boot应用的HPA,当时OpenShift最新Release版为3.11,能满足业务迭代需求。同时,为进一步提升平台支撑能力,提高系统的可靠性、安全性、易用性,保持与社区同步,同时借力开源社区,避免重复造轮子,我们启动了Kubernetes版本升级工作。

升级方案

云平台支撑了小鹏汽车技术中台的整个微服务体系,包括注册中心、API网关、日志中心、监控中心、配置中心、调度平台等,覆盖了互联网及信息化90%以上的后端项目,因此升级方案必须保证服务的可用性,同时兼顾成本。

1)重建集群

第一个可选的升级方案是基于新版本重新初始化一个集群,然后再逐步迁移各业务应用。这是最保守但相对有保障的方式,相当于把之前做过的工作再做一遍;但缺点也很明显,一方面需要大量采购新机器进行集群初始化,同时重新部署并配置技术中台的几十个组件,再逐个与各业务研发侧对接,进行服务迁移,整个过程成本高、周期长。

2)蓝绿部署

另一个升级方案是蓝绿部署。蓝绿部署方式须先升级Master和Etcd服务器,然后通过加入新节点创建并行环境,待验证完新的部署后,将流量从旧的节点(蓝色部署)切换到新的节点(绿色部署),然后下线旧节点。此过程中,如果检测到问题,可快速回退。缺点是需采购新机器作为buffer,另外流程复杂性高,涉及的过程包括:1)初始化加入新节点;2)将旧节点上的应用驱逐到新节点;3)下线旧节点;4)将旧节点重新初始化并重复1~3步。

3)就地升级

第三个可选升级方案是就地升级。集群升级将在单个集群中的所有主机上执行,和蓝绿部署一样,须先升级控制平面,然后是数据平面,在节点升级开始之前,把pods从节点中驱逐出去,并在其他正在运行的节点上重新创建。这种方式直接基于原环境进行,无需添加冗余节点,不涉及应用迁移,因此成本低、速度快,但伴随的是风险高,相当于给高速运行中的汽车换引擎;另外由于无法跨版本进行,只能逐个版本升级,如果出现问题,回退很困难。

综合上述方案,由于成本太高,首先排除集群重建方式,针对蓝绿部署和就地升级方式,两者的差异主要在node节点,而node节点升级不涉及全局影响,风险可控,为降低采购新节点的成本,避免复杂的升级流程,最终选择采取就地升级的方式。

升级过程

1)可用性

可用性对于车企来讲尤为重要。线上环境的升级,首先必须确保业务的可用性。我们使用官方推荐的先驱逐pod再升级kubelete的方式,此过程会使得每个应用都重启一遍,为避免期间应用中断,我们要求每个业务应用必须scale到2个以上的pod,同时反亲和性生效,即每个业务的多个pod在不同的节点上。另外,提前定义升级队列,从每个业务节点组抽取1个节点到待升级队列,避免同组业务节点挨近升级而导致可能出现的同一个业务的2个pod都未就绪的问题,同时该方式有效扩大并发,一次性升级整个队列,提高升级效率。增加自动验证过程,在队列升级完后,检查所有业务应用的可用实例数与升级前一致,然后再进行下一个队列的升级,该过程同样是为保证每个业务至少有一个pod是可用状态。

2)效率提升

本次升级跨越2个版本,由于架构变化很大,不支持跨版本升级,意味着同样的过程需要做两次,时间也至少是双倍,为提升效率,我们对官方升级方案中的Task进行拆解,将在升级过程中针对每个待升级节点的前置准备或检查等独立抽出来,统一在升级前做好,例如:更新package原数据,刷新缓存等,避免节点升级时串行阻塞。将耗时长且受环境因素影响存在未知风险的步骤提前验证并处理,例如:升级Master时的迁移API对象。适当加大个别步骤的重试次数,例如:节点重启后Ready状态检查,控制平面镜像拉取等,避免因网络或机器性能原因导致的重试超限而使得整个任务失败重新再跑一次的错误。针对技术中台的一些能够版本兼容的组件,例如日志收集、监控告警等,在主流程升级完后,再单独进行升级,避免整个过程拉的很长。

3)快速回退及恢复

开发回退脚本和一键备份及还原程序,保证每一个向前的步骤都是可以回退的,即使整个集群升级失败,也是可以快速还原的。

Kubernetes在快速迭代中,这次的经验也为我们下一次的升级演进打下了坚实的基础。

总结

云平台作为整个基础设施的底座,能有效地促进各种资源整合,实现资源共享,提高资源的利用率。随着云平台的演进,我们将跟随并借力开源社区促进Service Mesh、Serverless、Edge Computing、Chaos Engineering等技术在小鹏汽车的落地与实践。敬请期待我们后续其他方面的分享。

“生命不止,奋斗不息”,小鹏汽车的技术中台还将持续演进。

作者介绍:

谭恒亮:YAML程序员,曾在欢聚时代等公司任职。目前就职小鹏汽车,在基础架构团队从事技术中台的研发。

蒋学军:曾在亚信科技等公司任职。目前就职小鹏汽车,在基础架构团队从事技术中台的研发。

延伸阅读:《小鹏汽车技术中台实践 :微服务篇》