美图公司成立于 2008 年 10 月,怀揣着“成为环球懂美的科技公司”的愿景,创造了一系列软硬件产品,如美图秀秀、美颜相机、短视频社区美拍以及美图拍照手机, 美图产品的多样化也催生了繁芜多样的做事端技能,亿级 MAU 对做事真个技能哀求也越加严苛。

2016 年我们开始调研容器化干系技能,2017 年我们开始拥抱 Kubernetes,2018 年容器平台基本落成并推进业务的整体容器化。
我们期望通过容器化可以提升公司研发职员的线上支撑能力,提升持续开拓和集成的能力,提升整体资源利用率和做事的可用性。

二、容器化培植

2.1 容器化之前

美图秀秀php历时三年美图周全容器化踩过的坑 Ruby

在业务容器化之前,我们业务因此物理机的办法支配到北京、宁波等多个 IDC,部分做事支配到公有云。
个中大部分业务是单 IDC 支配,部分业务存在跨 IDC 间的调用,然后 IDC 之间通过专线打通。
当时存在的几个主要的问题:

做事支配没有进行隔离,业务混部须要掌握得非常小心,资源的利用率非常低;业务类型较多,缺少完备统一和完善的自动化运维手段,业务的增长会伴随着掩护人力的增加;测试环境与生产环境存在较大差异,这也导致一些生产环境问题不能在测试期间创造;开拓职员线上意识较薄弱,线上故障率持续较高;面对机房级故障时业务迁移非常困难,出问题时只能尴尬地等机房规复。

对此,我们希望通过积极的调度来办理掉存在的各类问题,而容器化是一个非常好的机会,可行性也比较高,同时我们希望借着这个机会对我们的技能架构以及干系职员做一次从意识到技能的全面提升,为未来的技能演进铺平部分道路。

2.2 选择 kubernetes

2017 年容器编排的“战役”打完,Kubernetes 取得领先并趋于成熟。
我们也彻底投入到 Kubernetes 当中,容器系统的大规模落地离不开成熟的容器编排系统。
kubernetes 对容器的编排、资源的调度以及强大的扩展能力极大地方便了我们平台的构建。

单体容器如集装箱,它统一的标准方便了调度运输。
Kubernetes 供应了对集装进行集中调度的码头和轮渡,让统统有条不紊并且易于履行。
容器根本平台则好比基于容器和 kubernetes 之上的完全的运输系统,它须要集装箱,码头轮渡,高速公路等整套体系,实际做事容器化的过程不是一个大略的打包装箱和调度方案过程。
大部分的做事与外界是有割不开联系的,须要保持与外界的联通性,做事进驻容器更像是用户住进集装箱屋子,她须要相应的根本配套举动步伐,像水,电,燃气等等。
以是我们首先是供应各种根本举动步伐,让做事能进驻容器而不会水土不服。
比如须要做好资源隔离,打通底层网络,做好负载均衡,处理好运用日志等等。

2.3 容器平台培植

首先我们对多地机房及云端资源进行梳理,成为我们打算及存储的资源池。
同时我们构建起根本的容器网络,日志系统,存储做事等底层根本,然后依托 Kubernetes 完成了基于多租户的容器管理平台培植,供应完善的项目管理,做事编排,资源调度,负载均衡等能力。

我们供应同集群多租户的模式,以是对集群内的业务隔离,资源调度,弹性伸缩等都会有很高的哀求。
同时我们也存在多集群稠浊云的运用处景,因此存在着对跨集群跨机房的容器网络互通,多集群的负载均衡等的特定需求。

2.3.1 根本培植之网络

先来看根本培植中网络这一层。
网络属于底层培植,办理的问题非常关键,容器网络办理的问题紧张包括:

Pod 内部容器间的通信;Pod 与 Pod 的通信;Pod 与 Service 的通信;Service 与集群外部的通信;跨集群跨网段通信。

容器网络除了要办理上述的 5 个问题的同时也须要考虑如何将网络层的性能丢失最小化。
接下来先来看看在网络方案选择上我们的一些考虑。

Kubernetes 通过 CNI 供应了非常强的扩展能力,同时社区的生动也让网络插件有了更多选择的空间。
CNI 是 CNCF 旗下的一个项目,作为容器网络标准,由一组用于配置网络接口的规范和库组成,同时还包含了一些插件。
CNI 仅关心容器创建时的网络分配和当容器被删除时开释网络资源。
CNI 具有广泛的支持,而且规范易于实现,社区支持非常丰富,网络可选方案也比较多。

网络方案的选型方面,我们会比较看重性能、稳定性、可掩护性干系。
在对 Flannel、Opencontrail、Contiv、Weave、Calico、Romana 等开源方案进行详细的比拟和剖析之后,终极选择了 Calico 方案。
经由我们实际的压测验证,Calico 的性能比较靠近 Host 的性能。
从社区生动度、方案成熟程度和稳定性方面考虑 Calico 也是较为良好,同时其基于 BGP 的方案也为我们后继的网络扩展供应了可能性。

那么在 Calico 方案里面,Kubernetes 创建 Pod 时的过程是若何的呢?Kubernetes 在创建 Pod 时,会先创建 Sandbox 的虚拟网络,然后 Pod 中的容器直接继续 Sandbox 网络。
在创建网络时,Kubelet 做事会通过标准的 CNI 接口调用 Calico CNI 插件,为容器创建一个 veth-pair 类型的网卡,并写入路由表信息。
节点上的路由表通过 Calico Bird 组件以 BGP 的形式广播到其他邻居上,其他节点在收到路由条款后在进一步聚合路由写入到自身节点上。

Calico 在同子网内直策应用 BGP、跨子网时利用 IPIP。
而 IPIP 由于其单行列步队的设计存在着性能瓶颈,这严重限定了节点的吞吐能力,特殊是作为 LB 时影响更为严重,以是我们须要规避 IPIP 的问题。
其余由于我们多机房培植须要打通不同机房、不同集群、不同网段的网络,以是我们须要进一步推进网络的优化。

图一

进一步的网络培植紧张是三方面内容:

多集群的容器网络与物理网络打通;去掉 IPIP,关闭 NAT 优化性能;增加限速,对节点网络进行保护;

图一中是简化后的一个网络拓扑图,集群的 Calico-RR(反射器) 与物理网关通过 BGP 进行打通,实现机房物理网络与容器网络拉平,办理了多集群网络互通的问题,同时由于网络已拉到同一平面,也直接规避 IPIP 的性能问题。
从上图可以看出,每个机房作为一个 AS 支配一个 Kubernetes 集群,机房内部冗余有多个 RR(反射器),RR 分别与机房内的网关建立 iBGP 连接,机房间的路由器通过 OSPF 同步彼此之间的路由。

除了私有云我们也须要办理稠浊云的场景,实现集群网络跨云打通。
受限于协议的支持,我们并没有采取与私有云一样的打通办法。
由于云端网段相对固定,在方案完成后变动较少,因此我们采取静态路由的办法,机房网关上配置相应网段的静态路由规则,同时在云端路由上也配置上相应路由规则,终极打通路由路径。

我们在履行的过程中碰着了不少细节上的问题,比如旧集群单个集群跨了三机房,在打通网络时存在环路的情形须要通过静态路由来规避问题。
在做网络限速时,插件存在 Bug 并且社区没有办理(目前最新版本已办理了)须要手动修复。
不过问题逐一办理后,网络根本也完成了生产落地和打通。

2.3.2 根本培植之 LB

Kubernetes 在设计上实在是充分考虑了负载均衡和做事创造的,它供应了 Service 资源,并通过 kube-proxy 合营 Cloud Provider 来适应不同的运用处景。
此外还有一些其它负载均衡机制,包括 Service,Ingress Controller,Service Load Balancer,Custom Load Balancer。
不过 Kuernetes 的设计有它实际适用的场景和局限性,并不能完备知足我们繁芜场景落地,同时也考虑到社区方案成熟度问题,终极我们利用了自定义开拓的 Custom Load Balancer。

七层负载的选型上,我们是利用了较为成熟的 Nginx Custom Controller 的方案。
我们也对 Envoy 等方案进行了仔细比拟,但是考虑到我们公司对付 Nginx 有非常成熟的运维履历,以及我们很多业务依赖于 Nginx 的一些第三方扩展的功能,以是从推动业务容器快速落地角度、掩护稳定性角度,我们终极选择了 Nginx 作为早期的落地方案。
不过在与 kubernetes 结合方面,Nginx 还是存在着不少的细节问题,我们也一贯在推动办理,同时我们也在考虑 Envoy 等后续的优化方案。

Custom Load Balancer 由 Nginx、 Kubenernet Controller 以及管理组件组成。
Kubenernet Controller 卖力监听 Kubernetes 资源,并根据资源情形动态更新 Nginx 配置。
Nginx Upstream 直接配置对应的 Service Endpoints,并增加相应的存活检测机制。
由于在网络层面上物理网与容器网络已拉平,以是 Nginx 与各集群的 Service 的 Endpoint 是完备链路可达的,因此也可直接支撑多集群的负载均衡。

LB 供应了友好的 UI 界面,提高了发布的效率、减少人为故障。
同时,LB 也具备灰度升级,流量掌握,故障降级等干系根本功能,并且供应了丰富的指标,让运维监控可视化。

2.3.3 根本培植之日志

我们再来看一下其余一个比较主要的根本举动步伐——日志。
日志实在是较为关键的根本举动步伐,它是审计,排障,监控报警等所必需的。
日志标准化一贯是比较难推进的一件事情,特殊是存在大量旧系统的情形下。
一方面面临业务代码的改造,另一方面面临着开拓及运维习气的改造。
而容器化恰好是推进日志标准化很好的一个机会。

图二

日志架构上我们选用的是 Cluster-Level 的办法,利用 Fluentd 作为节点采集 Agent。
Docker 日志驱动利用 Json log-driver,业务容器日志输出到标准输出,终极落盘到容器所属的目录中。
Fluentd 采集 Docker 输出日志,并写入 Kafka 行列步队,Logstash 卖力消费行列步队数据并入 Elasticsearch, 同时由 Kibana 供应统一的日志查询界面。

在业务容器化落地的过程中,日志也暴露了很多的问题。
如:兼容问题,标准输出的日志可能经由多层封装导致日志被截断或者添加了额外内容,如 PHP-FPM ,Nginx 等。
又比如日志格式分歧一,不同类型业务日志格式各不相同,比较难完备统一。
再比如业务日志可靠性哀求,有些许可极度情形下丢失,有些不许可丢失。

为了让业务能更快速将旧业务迁移至容器平台,我们为每种特性的业务类型做了定制化方案,赞助业务快速接入。
比如对 PHP,业务将日志输出至 pipe 再由 tail 容器读取 pipe 数据输至标准输出。
再如大数据业务,由于统计日志与事宜日志是分割开的,一起输到标准输出会须要较大改造量,改造韶光较长,以是对采集办法进行适配调度,业务直接输出日志到 rootfs,并由宿主机 agent 直接采集 rootfs 约定目录的日志数据。

总之日志由于与其它系统以及职员习气耦合度太高,以是要完成标准化,完成系统解耦和职员依赖改变是比较花费韶光精力的一件事情。

2.3.4 根本培植之弹性调度

再来看关于调度的一些培植。
容器调度,实在是为理解决资源利用率最大化的问题,实质上是一个整数方案问题。
Kubernetes 的调度策略源自 Borg, 但是为了更好的适应新一代的容器运用,以及各种规模的支配,Kubernetes 的调度策略相应做的更加灵巧,也更加随意马虎理解和利用。
Kubernetes 对 Pod 的调度通过两个阶段来实现。
Predicates 阶段用于过滤出符合基本哀求的节点,Priorities 阶段用于得到最优节点。
不过由于调度是根据分配量来进行而不是实际利用率,以是业务须要准确评估出自己的资源利用量,如果评估不准有可能会造成资源的摧残浪费蹂躏或者影响业务质量。

图三

比如我们看图三的实例,左边为空闲做事器,中间每个 Pod 都申请了内存的 Request 及 Limit, 调度器根据 Request 打算,做事器能放得下这个几个 Pod,于是调度至了做事器上面。

可以看到实际 Limit 是机器可用资源的两倍。
那么如果这时 Pod1 内存利用超过了 Request,但是远没有达到 Limit,这时做事器有可能会涌现 Swap,而更进一步,机器资源不敷后,有可能会涌现 OOM,内存最多且 Request/Limit 比值最小的那个 Pod 中的进程将会被 OOM Kill。
而这种 Kill 将会造成业务的抖动。
同时如果涌现大量 Swap 也会使硬盘涌现 IO 瓶颈,影响同机器上的实在业务。

在这样的场景下,当前 kubernetes 的调度器实现会面临一些问题,由于它是根据配额来调度的,而业务用户不合理的配额需求导致了很多预期之外的场景,以是它无法大略办理。

针对这种场景,我们总结出了以下几个优化点:

优化业务 Request 值,根据业务历史数据调节 Request;增加运行时指标,将节点当前利用率考虑进内;对付分外质量保障的业务设置 Guaranteed 级别;规避 Pod 内存进行 swap;完善 IO 及网络等资源的隔离机制。

实际上我们在业务的开拓测试集群中就碰着了资源紧张同时调度不屈衡导致大量 OOM 的场景,并且一度影响到了业务的接入。
实质这还是资源利用率过高时调度的不合理造成。
后面经由优化改进才从这种困境中逃离。

再看其余一个场景,我们在将集群分配率挤压到高于 50% 以上时,很随意马虎涌现资源碎片。
这时一些资源需求较大的 Pod 会涌现无法调度的情形。
在这种场景下则须要通过一些人工干预来进行调度调度。
针对这种场景,我们实在是须要通过一些策略调优来优化我们的调度,包括:Reschedule、 MostRequestedPriority、以及优先级来优化集群的调度。

图四

图四是简化后一个单资源的示例。
实际运用中我们更希望集群有足够的冗余度来做资源调度,同时在稠浊云场景,我们更希望是只管即便利用完部分节点再扩容新的节点。
比如图四所示,希望存在一些大块的空缺节点,只管即便减少碎片空间。

不过提高器利用率,则会碰着上一场景提到的利用率过高时 Pod 资源不敷的问题。
以是必需首先办理好资源利用的预估及调度优化。
而且也须要在利用率及冗余度上做平衡,设定对相应的策略权重,通过一定水位限定确保节点仍有一定冗余度,如 MostRequestedPriority 的水位限定。
水位掌握与我们希望的集群利用率直接干系。

图五

前面考虑很多时候是先从单个维度出发来考虑问题,实际场景中我们是多维度的。
多维度下会繁芜得多,并且碎片的情形更随意马虎涌现,在这种情形下很多时候得到的只是一个局部最优调度而不是全局最优。

有时候为了更靠近全局最优分配须要进行一定重调度。
这须要我们自定义掌握器做特定的调度策略,同时考虑到大量调动 Pod 可能会带来的业务抖动,特殊是对付部分优雅关闭没有做很好的业务,以是须要较严谨的保护规则和机遇掌握。
如只在机器资源较为紧张时对优先级较低的做事进行调度。

在实际业务容器化过程中,业务对付资源的依赖繁芜多样,根据业务的实际需求,我们进一步引入了 IO,网络,内存带宽等一些资源需求的调度,我们在调度策略中也新增了对应的扩展资源。

2.3.5 根本培植之弹性伸缩

调度办理了业务资源合理分配,弹性伸缩组(HPA)则是在提升资源利用率的同时对业务进行资源保障的主要手段。
它担保在业务量级上来时能及时的进行扩容,在业务量级低落后又能及时回收资源,HPA 是根据特定的指标以及业务设定的目标值来增加或减少 Pod 的数量。
这部分须要考虑三方面:

伸缩指标的扩展;错峰调度的实现;伸缩时的业务抖动的优化。

图六

图六左侧是我们扩展指标的架构图,从图中我们可以看到,通过扩展采集模块及 CME 构建了扩展指标的采集面,通过预测器输出预测指标,通过 custom-metrics 供应 HPA 所须要的扩展指标。

伸缩指标我们紧张是扩展出了 4 个指标,包括 QPS,网络入带宽,网络出带宽,行列步队积压长度。
错峰调度则是通过定时策略实现。

我们这边有一个云处理的业务,会对付视频做 H.265 转码、编码优化等 CPU 密集型操作,常常会有突峰的转码需求导致该业务须要大量的 CPU 资源。
在这种情形下,HPA 的扩缩容有时会跟不上节奏造成业务处理延时变长,紧张是伸缩组算法在打算伸缩值时算法较为大略,随意马虎在业务量变小后立时过量缩量,这就会导致业务量颠簸时反复进行伸缩影响业务稳定,以是我们引入了慢缩容机制同时增加紧缩滑动窗口以达到消峰的浸染。

图七

图七是一个错峰调度的案例。
我们某个处理做事,白天须要大量资源占用,高峰时须要 2500 核心,而低峰期间所需求的资源则非常少,最小时仅须要 60 核。
而其余一个做事的一些离线打算任务没有太强韶光急迫性哀求,可以放在夜间处理,还有一些统计类定时任务也是可以放置在夜间,这样整体集群资源可以被充分利用。

2.3.6 根本培植之监控

监控是我们生产稳定的一个主要的保障手段。
在容器化之前,我们运维体系实在已经有一套成熟的监控机制,容器化之后相应的监控并不须要完备推倒重做,部分体系可以复用比如物理机监控,在这之上引入新的容器监控系统。
容器平台紧张监控是几方面的内容。

物理机根本监控,紧张是像硬盘,IO,内存,网络等;业务指标监控,包括非常监控以及性能监控;容器行为的监控,监控 Pod 资源,容器事情等;容器组件的监控。

实际上监控指标是持续在丰富及优化的过程,我们最初只监控了紧张的四方面的指标,而终极进行汇总剖析时则是从集群,做事,Pod,业务等等视角进行多维度的汇总剖析输出。

图八

监控报表对付我们问题剖析及排障会供应巨大的便利。
图八是一个 CPU 监控图,图中可以看出有两个高峰期 Pod 的 CPU 是跑满了。
实际对应到的正是线上的某一次业务非常,当时业务的 SLA 在流量高峰时部分处理延时较高并导致报警。

根本平台的培植涉及到的还有很多内容,比如多集群的管理、多租户管理、DNS 的优化,镜像做事的优化,稠浊云落地等等,限于篇幅不一一展开。

三、业务落地

前面讲了比较多是根本平台构建的一些内容,下面我们聊一下业务接入的一些事情。
我们知道容器化给业务带来的收益是非常多的,但是我们也是须要考虑可能会给业务带来的困难。

考虑各种迁移和改造的问题,我们须要将平台更进一步优化,让业务接入本钱只管即便低。
供应更大略方便的 CI/CD 流程,我们须要供应友好的统一的操作界面,供应完全的接入辅导,快速排障工具,定期的培训等。

图九

比如我们优化 CI/CD 流程,全体构建和发布过程对开拓只管即便透明,图九右边是新的流程,开拓提交完代码之后,Gitlab-CI 会自动匆匆发测试及构建过程,并将镜像推送到仓库。
开拓同学仅须要在统一门户网上面操为难刁难应的版本进行发布,平台会自动天生 Deployment 并发布至相应的集群。

再比如,我们供应一个友好的管理平台,它可以减少业务学习本钱以及出错的概率。
同时也供应了灵巧的软件阶段定制支持。
可以利用大略的办法定制多个阶段,包括:Dev、Test、Pre、 Beta、Canary、Realse … …

综上,实在我们仅根本平台培植是远不足,实际业务接入过程中须要考虑很多其它成分。
同时业务的需求也不断地优化我们的平台架构,终极实现整体的落地。
实际上我们做的也确实还远远不足,业务在接入过程还是须要面临着浩瀚的问题,以是我们须要在各方面都进一步完善,业务实在一贯在教导我们如何做好一个平台,我们也在不断地学习接管。
这也是我们不断提升的源动力之一。

四、展望未来

未来我们将长期运行多集群稠浊云的架构,会逐步引入多家公有云,优化调度系统,更进一步提高我们的资源利用率,同时也会保持着对 ServiceMesh、Serverless、边缘打算等方向的关注,会结合着业务的需求,更进一步优化容器根本平台。

本文作者:章敏鹏,目前就职于美图技能保障部架构平台,紧张从事容器根本平台培植,流媒系统统干系培植。
卖力容器平台根本组件培植、调度系统研发、多机房及稠浊云培植、流媒体根本做事培植及用户体验优化等。
在容器化技能及流媒体方向有着多年的积累及丰富的实战履历。