Kubernetes如何改变美团的云基础设施?
Kubernetes已经成为美团云基础设施的管理引擎,它带来的不仅仅是高效的资源管理,同时也大幅降低了成本,而且为美团云原生架构的推进打下了坚实的基础,支持了Serverless、云原生分布式数据库等一些平台完成容器化和云原生化的建设。
本文根据美团基础架构部王国梁在KubeCon 2020云原生开源峰会Cloud Native + Open Source Virtual Summit China 2020上的演讲内容整理而成。
一、背景与现状
Kubernetes是让容器应用进入大规模工业生产环境的开源系统,也是集群调度领域的事实标准,目前已被业界广泛接受并得到了大规模的应用。
从2013年开始,美团就以虚拟化技术为核心构建了云基础设施平台;2016年,开始探索容器技术并在内部进行落地,在原有OpenStack的资源管理能力之上构建了Hulk1.0容器平台;2018年,美团开始打造以Kubernetes技术为基础的Hulk2.0平台;2019年年底,我们基本完成了美团云基础设施的容器化改造;2020年,我们坚信Kubernetes才是未来的云基础设施标准,又开始探索云原生架构落地和演进。
当前,我们构建了以Kubernetes、Docker等技术为代表的云基础设施,支持整个美团的服务和应用管理,容器化率达到98%以上,目前已有数十个大小Kubernetes集群,数万的管理节点以及几十万的Pod。不过出于容灾考虑,我们最大单集群设置为5K个节点。
下图是当前我们基于Kubrnetes引擎的调度系统架构,构建了以Kubernetes为核心的统一的资源管理系统,服务于各个PaaS平台和业务。除了直接支持Hulk容器化之外,也直接支持了Serverless、Blade等平台,实现了PaaS平台的容器化和云原生化。
二、OpenStack到Kubernetes转变的障碍和收益
对于一个技术栈比较成熟的公司而言,整个基础设施的转变并不是一帆风顺的,在OpenStack云平台时期,我们面临的主要问题包括以下几个方面:
- 架构复杂,运维和维护比较困难:OpenStack的整个架构中计算资源的管理模块是非常庞大和复杂,问题排查和可靠性一直是很大的问题。
- 环境不一致问题突出:环境不一致问题是容器镜像出现之前业界的通用问题,不利于业务的快速上线和稳定性。
- 虚拟化本身资源占用多:虚拟化本身大概占用10%的宿主机资源消耗,在集群规模足够大的时候,这是一块非常大的资源浪费。
- 资源交付和回收周期长,不易灵活调配:一方面是整个虚拟机创建流程冗长;另一方面各种初始化和配置资源准备耗时长且容易出错,所以就导致整个机器资源从申请到交付周期长,快速的资源调配是个难题。
- 高低峰明显,资源浪费严重:随着移动互联网的高速发展,公司业务出现高低峰的时间越来越多,为了保障服务稳定不得不按照最高的资源需求来准备资源,这就导致低峰时资源空闲严重,进而造成浪费。
2.1 容器化的过程和障碍
为了解决虚拟机存在的问题,美团开始探索更加轻量级的容器技术的落地,也就是Hulk1.0项目。不过基于当时的资源环境和架构,Hulk1.0是以原有的OpenStack为基础资源管理层实现的容器平台,OpenStack提供底层的宿主机的资源管理能力,解决了业务对弹性资源的需求,并且整个资源交付周期从分钟级别降低到了秒级。
但是,随着Hulk1.0的推广和落地,也暴露出一些新的问题:
- 稳定性差:因为复用了OpenStack的底层资源管理能力,整个扩容过程包括两层的资源调度,且数据同步流程复杂,机房的隔离性也比较差,经常出现一个机房出现问题,其他机房的扩缩容也受到影响。
- 能力欠缺:由于涉及的系统多,并且是跨部门协作,故障节点的迁移和恢复能力不易实现,资源类型也比较单一,整个故障排查和沟通效率低下。
- 扩展性差:Hulk1.0的控制层面对底层资源的管理能力受限,无法根据场景和需求快速扩展。
- 性能:业务对于扩缩容和弹性资源的交付速度需求进一步提高,且容器技术的弱隔离性导致业务的服务受到的干扰增多,负面反馈增加。
容器化的核心思路是让Kubernetes做好资源层面的管理,而通过上层的控制层解决对美团应用管理系统和运维系统的依赖问题,保持Kubernetes的原生兼容性,减少后续的维护成本,并完成了快速收敛资源管理的需求。同时,也减少了用户基于新平台的资源申请的学习成本,这点非常重要,也是后续我们能快速大规模迁移基础设施资源的“基础”。
2.2 容器化过程的挑战和应对策略
2.2.1 复杂灵活、动态和可配置的调度策略
美团产品众多,业务线和应用特点五花八门,所以相应的,我们对于资源类型和调度策略的需求也是非常多。例如,有些业务需要特定的资源类型(SSD、高内存、高IO等等),有些业务需要特定的打散策略(例如机房、服务依赖等),所以如何很好地应对这些多样化的需求,就是一个很大的问题。
为了解决这些问题,我们为扩容链路增加了策略引擎,业务可以对自己的应用APPKEY自定义录入策略需求,同时基于大数据分析的服务画像,也会根据业务特点和公司的应用管理策略为业务策略推荐,最终这些策略会保存到策略中心。在扩容过程中,我们会自动为应用的实例打上对应的需求标签,并最终在Kubenretes中生效,完成预期的资源交付。
2.2.2 精细化的资源调度和运营
精细化的资源调度和运营,之所以做精细化运营主要是出于两点考虑:业务的资源需求场景复杂,以及资源不足的情况较多。
我们依托私有云和公有云资源,部署多个Kubenretes集群,这些集群有些是承载通用业务,有些是为特定应用专有的集群,在集群维度对云端资源进行调配,包括机房的划分、机型的区分等。在集群之下,我们又根据不同的业务需要,建设不同业务类型的专区,以便做到资源池的隔离来应对业务的需要。更细的维度,我们针对应用层面的资源需求、容灾需求以及稳定性等做集群层的资源调度,最后基于底层不同硬件以及软件,实现CPU、MEM和磁盘等更细粒度的资源隔离和调度。
2.2.3 应用稳定性的提升和治理
不管是VM,还是最初的容器平台,在应用稳定性方面一直都存在问题。为此,我们需要在保障应用的SLA上做出更多的努力。
2.2.3.1 容器复用
在生产环境中,宿主机的发生重启是一种非常常见的场景,可能是主动重启也可能是被动,但用户角度来看,宿主机重启意味着用户的一些系统数据就可能丢失,代价还是比较高的。我们需要避免容器的迁移或重建,直接重启恢复。但我们都知道,在Kubernetes中,对于Pod中的容器的重启策略有以下几种:Always、OnFailure和Never,宿主机重启后容器会重新被创建。
为了解决这个问题,我们为容器的重启策略类型增加了Reuse策略。流程如下:
- kubelet在SyncPod时,重启策略如果是Reuse则会获取对应Pod已退出状态的App容器,如果存在则拉起最新的App容器(可能有多个),如果不存在则直接新建。
- 更新App容器映射的pauseID为新的pause容器ID,这样就建立了Pod下新的pause容器和原先App容器的映射。
- 重新拉起App容器即可完成Pod状态同步,最终即使宿主机重启或内核升级,容器数据也不会丢失。
2.2.3.2 Numa感知与绑定
用户的另一个痛点与容器性能和稳定性相关。我们不断收到业务反馈,同样配置的容器性能存在不小的差异,主要表现为部分容器请求延迟很高,经过我们测试和深入分析发现:这些容器存在跨Numa Node访问CPU,在我们将容器的CPU使用限制在同一个Numa Node后问题消失。所以,对于一些延迟敏感型的业务,我们要保证应用性能表现的一致性和稳定性,需要做到在调度侧感知Numa Node的使用情况。
为了解决这个问题,我们在Node层采集了Numa Node的分配情况,在调度器层增加了对Numa Node的感知和调度,并保证资源使用的均衡性。对于一些强制需要绑定Node的敏感型应用,如果找不到合适的Node则扩容失败;对于一些不需要绑定Numa Node的应用,则可以选择尽量满足的策略。
2.2.3.3 其他稳定性优化
- 在调度层面,我们为调度器增加了负载感知和基于服务画像应用特征的打散和优选策略。
- 在故障容器发现和处理上,基于特征库落地的告警自愈组件,能够秒级发现-分析-处理告警。
- 对于一些有特殊资源需求,例如高IO、高内存等应用采用专区隔离,避免对其他应用造成影响。
2.2.4 平台型业务容器化
相信做过ToB业务的同学应该都了解,任何产品都存在大客户方案,那么对于美团这样的公司,内部也会存在这种情况。平台型业务的容器化有个特点是:实例数多,以千或万计,所以资源成本就比较高;业务地位比较高,一般都是非常核心的业务,对性能和稳定性要求很高。所以,如果想要通过“一招鲜”的方式解决此类业务的问题,就有些不切实际。
这里,我们以MySQL平台为例,数据库业务对于稳定性、性能和可靠性要求非常高,业务自己又主要以物理机为主,所以成本压力非常大。针对数据库的容器化,我们主要是从宿主机端的资源分配定制和优化为切入口。
- 针对CPU资源分配,采用独占CPU集合的方式,避免Pod之间发生争抢。
- 通过允许自定义SWAP大小来应对短暂的高流量,并关闭Numa Node和PageCache来提升稳定性。
- 在磁盘分配中采用Pod独占磁盘进行IOPS的隔离,以及通过预分配和格式化磁盘来提升扩容的速度,提升资源交付效率。
- 调度支持独有的打散策略和缩容确认,规避缩容风险。
最终,我们将数据库的交付效率提升了60倍,并且在大多数情况下性能比之前的物理机器还要好。
2.2.5 业务资源优先级保障
对于一个企业而言,基于成本考虑,资源一直会处于不足的状态,那么如何保障资源的供给和分配就显得非常重要。
- 业务预算配额确定资源供给,通过专区来做专有资源专用。
- 建设弹性资源池和打通公有云来应对突发资源需求。
- 按照业务和应用类型的优先级保障资源使用,确保核心业务先拿到资源。
- 多个Kubenretes集群和多机房来做容灾,应对集群或机房的故障。
2.2.6 云原生架构的落地
在迁移到Kubernetes之后,我们进一步实现了云原生架构的落地。
为了解决云原生应用管理的障碍,我们设计实现了美团特色的云原生应用管理引擎——KubeNative,将应用的配置和信息管理对平台透明化,业务平台只需要创建原生的Pod资源即可,不需要关注应用的信息同步和管理细节,并支持各PaaS平台自己来扩展控制层面的能力,运行自己的Operator。
下图就是目前我们整个的云原生应用管理架构,已支持Hulk容器平台、Serverless以及TiDB等平台的落地。
2.3 基础设施迁移后的收益
- 完成全公司业务98%的容器化,显著提升了资源管理的效率和业务稳定性。
- Kubernetes稳定性99.99%以上。
- Kubernetes成为美团内部集群管理平台的标准。
三、运营大规模Kubernetes集群的挑战和应对策略
在整个基础设施迁移过程中,除了解决历史遗留问题和系统建设,随着Kubernetes集群规模和数量快速增长,我们遇到的新的挑战是:如何稳定、高效地运营大规模Kubernetes集群。我们在这几年的Kubernetes运营中,也逐渐摸索出了一套验证可行的运营经验。
3.1 核心组件优化与升级
我们最初使用的Kubernetes是1.6版本,性能和稳定性是比较差的,当我们达到1K节点的时候就逐渐出现问题,达到5K节点时基本集群不可用。例如,调度性能非常差,集群吞吐量也比较低,偶尔还发生“雪崩”的情况,扩缩容链路耗时也在变长。
针对核心组件的分析和优化,这里从kube-apiserver、kube-scheduler、etcd以及容器等四个方面来概括下。
- 针对kube-apiserver,为了减少重启过程长时间地发生429请求重试,我们实现了多级的流量控制,将不可用窗口从15min降低为1min,并通过减少和避免外部系统的List操作降低集群负载,通过内部的VIP来做节点的负载均衡,保障控制节点的稳定性。
- 在kube-scheduler层,我们增强了调度的感知策略,调度效果相比之前更稳定;对调度性能的优化提出的预选中断和局部最优策略也已合并到社区,并成为通用的策略。
- 针对etcd的运营,通过拆分出独立的Event集群降低主库的压力,并且基于高配的SSD物理机器部署可以达到日常5倍的高流量访问。
- 在容器层面,容器复用提升了容器的故障容忍能力,并通过精细化的CPU分配提升应用稳定性;通过容器的磁盘预挂载提升Node的故障恢复速度。
另外,社区版本的迭代是非常快的,高版本在稳定性和特性支持上更好,不可避免我们需要进行版本的升级,但如何确保升级成功是一个很大的挑战,尤其是我们在没有足够的Buffer资源进行资源腾挪情况下。
集群升级,业界通用的方案是直接基于原有集群升级,方案存在以下几点问题:
- 升级版本有限制,不能跨大版本升级:只能一点点从低版本升级到高版本,耗时费力,而且成功率低。
- 控制平面升级风险不可控:尤其是有API变更的时候,会覆盖之前的数据,甚至是不可回滚的。
- 用户有感知,容器需要新建,成本和影响较高:这个是比较痛的点,无可避免会发生容器新建。
为此,我们深入研究了Kubernetes对容器层面的控制方式,设计实现了一种能够平滑将容器数据从低版本集群迁移到高版本集群的方案,将集群升级细化为Node粒度的逐个宿主机上容器的原地热升级,随时可以暂停和回滚。新方案主要是通过外部工具将Node和Pod数据从低版本集群迁移到高版本集群,并解决Pod对象和容器的兼容性问题。核心思路是两点:通过低版本兼容高版本的API,通过刷新容器的Hash保障Pod下的容器不会被新;通过工具实现Pod和Node资源数据从低版本集群迁移到高版本集群。
该方案亮点主要包括以下4个方面:
- 大规模生产环境的集群升级不再是难题。
- 解决了现有技术方案风险不可控的问题,风险降到了宿主机级别,升级更为安全。
- 通用性强,可做到任意版本的升级,且方案生命周期长。
- 优雅地解决了升级过程中容器新建问题,真正做到了原地热升级。
3.2 平台化与运营效率
大规模的集群运营是非常有挑战的事情,满足业务的快速发展和用户需求也是对团队极大的考验,我们需要从不同纬度的考虑集群的运营和研发能力。
在Kubernetes与etcd集群的整个运营和运维能力建设上,我们关注的目标是安全运营、高效运维、标准化管理以及节约成本。所以针对Kubernetes与etcd集群,我们已经完成了平台化的管理运营,覆盖了特性扩展、性能与稳定性、日常运维、故障恢复、数据运营以及安全管控等6个方面。
对于一个非公有云业务的Kubernetes团队,人力还是非常有限的,除了集群的日常运营还有研发任务,所以我们对于运营效率的提升非常关注。我们将日常运维逐步的沉淀转换,构建了一套美团内部的Kubernetes集群管理平台。
- 将集群的管理标准化、可视化,避免了黑白屏的运维操作。
- 通过告警自愈和自动巡检将问题处理收敛掉,所以虽然我们有大几十个集群,但我们的运维效率还是比较高的,值班同学很少需要关注。
- 全部的运维操作流程化,不仅提升了运维效率,人为操作导致的故障的概率也减小了。
- 通过运营数据的分析进一步做了资源的精细化调度和故障预测,进一步提前发现风险,提升了运营的质量。
3.3 风险控制和可靠性保障
规模大、覆盖业务广,任何的集群故障都会直接影响到服务的稳定性甚至用户的体验,在经历了多次运维故障和安全压力下,我们形成了一套可复制的风险控制和可靠性保障策略。
在整个风险管控链路中,我们分为指标、告警、工具、机制&措施和人员5个层面:
- 指标数据采集,从节点、集群、组件以及资源层面采集核心指标作为数据源。
- 风险推送,覆盖核心指标的多级、多维度的告警机制。
- 在工具支持上,通过主动、被动以及流程化等减少误操作风险。
- 机制保障上,打通测试、灰度验证、发布确认以及演练等降低疏忽大意的情况。
- 人是风险的根本,这块我们一直也在努力建设和轮值,确保问题的响应。
在可靠性验证和运营方面,我们笃信需要把功夫用在评审,通过集群巡检来评估集群的健康情况,并推送报表;定期的宕机演练保障真实故障能够快速恢复,并将日常问题补全到全链路测试中,形成闭环。
四、总结与未来展望
4.1 经验心得
- Kubernetes的落地完全兼容社区的Kubernetes API;只会做插件化的扩展,并尽量不改控制层面的原有行为。
- 对社区的一些特性,取长补短,并且有预期的升级,不盲目升级和跟进社区版本,尽量保持每年度的一个核心稳定版本。
- 落地以用户痛点为突破口,业务是比较实际的,为什么需要进行迁移?业务会怕麻烦、不配合,所以推进要找到业务痛点,从帮助业务的角度出发,效果就会不一样。
- 内部的集群管理运营的价值展现也是很重要的一环,让用户看到价值,业务看到潜在的收益,他们会主动来找你。
在容器时代,不能只看Kubernetes本身,对于企业内的基础设施,“向上”和“向下”的融合和兼容问题也很关键。“向上”是面向业务场景为用户提供对接,因为容器并不能直接服务于业务,它还涉及到如何部署应用、服务治理、调度等诸多层面。“向下”,即容器与基础设施相结合的问题,这里更多的是兼容资源类型、更强大的隔离性、更高的资源使用效率等都是关键问题。
4.2 未来展望
- 统一调度:VM会少量长期存在一段时间,但如果同时维护两套基础设施产品成本是非常高的,所以我们也在落地Kubernetes来统一管理VM和容器。
- VPA:探索通过VPA来进一步提升整个资源的使用效率。
- 云原生应用管理:当前,我们已将云原生应用管理在生产环境落地,未来我们会进一步扩大云原生应用的覆盖面,不断提升研发效率。
- 云原生架构落地:推进各个中间件、存储系统、大数据以及搜索业务合作落地各个领域的云原生系统。
作者简介
国梁,美团技术专家,现负责美团Kubernetes集群的整体运营和维护以及云原生技术落地支持。
下一章:新一代垃圾回收器ZGC的探索与实践
很多低延迟高可用Java服务的系统可用性经常受GC停顿的困扰,作为新一代的低延迟垃圾回收器,ZGC在大内存低延迟服务的内存管理和回收方面,有着非常不错的表现。 本文从GC之痛、ZGC原理、ZGC调优实践 ...