本文作者: 柴克斌 (网易传媒技术团队)
网易传媒做为国内最早的内容资讯平台,随着业务体量的增加,业务迭代的速度加快,基础架构也面临着很多挑战,为了能提供快速、稳定、安全的基础架构及基础服务,传媒在2019年初进行了一次基础架构的容器化升级,本文就传媒基础架构在容器化升级方面所做的工作、面临的问题、提出的解决方案及未来的规划都做一些详细描述,希望能给也在容器化升级的读者有所借鉴
**1.**遇到的问题及升级目标
随着传媒业务的不断增加,基础架构也面临着很多挑战,主要包括以下几类问题
-
资源利用率:传媒基础架构在容器化之前,资源使用率在20%左右,存在着波峰波谷,波谷时期整体利用率得不到有效的利用
-
流程繁琐,人工操作较多:业务在申请资源的时候,需要经过流程审批,耗时比较长,在遇到流量高峰的时候,扩容资源比较耗时,流量高峰后,运维还需要手动将资源下线
-
频繁的数据爬取:传媒一些核心接口,一直存在被外网爬取的情况,导致了服务的不稳定及流量突增
-
手动扩容、缩容,耗费人力:业务比较难设置合理的资源使用量,当出现业务高峰时,就需要扩容,高峰过后,又需要手动缩容
-
服务依赖拓扑不清晰:当服务要做迁移或下线时,服务提供方需要通过各种人工方式找到所有的服务依赖方,并做下线通知,人工方式经常会找不全所有依赖方,导致服务提供方下线后,消费方出现了故障
为了应对上述的挑战,传媒决定对基础架构做一次整体的升级,希望达到如下目标
-
提升资源整体利用率:传媒希望整体资源利用率提升至50%~60%,并且将波谷资源充分利用起来,能够优化成本
-
提高效率:传媒计划取消一些繁琐的流程,业务方随时从资源池中获取资源,我们会监控用户对资源的使用情况,对于资源使用率低的业务进行资源优化
-
加固安全:传媒需要搭建网关服务,为用户提供统一的流量入口,在网关上增加熔断降级、安全、审计等方面的能力
-
保障稳定:资源出现故障后业务能快速迁移,流量突增后能快速扩容,流量恢复后也能自动缩容
-
快速定位业务依赖:传媒希望能够快速、准确的定位到业务的依赖,能够看到整体的业务依赖拓扑图,能够看到核心接口的URL调用拓扑图
**2.**基础架构演进之路
传媒的架构演进主要经历了四个阶段
-
物理机阶段:最早传媒业务都在物理机上进行部署
-
虚拟化阶段:为了节约成本,传媒整体业务都迁移到了专属云
-
容器化阶段:为了进一步节约成本、提高效率、保障稳定、加固安全,传媒整体业务都迁移到了容器云,资源做到了池化,业务随用随取
-
容器云升级阶段:迁移到容器云后,基础资源管理更为方便,为弹性、精细化资源管理提供了强大的基础
本文主要介绍传媒容器化阶段所做的工作
3**.划分资源池**
在对业务架构和需求了解后,我们规划了三个资源池
-
容器资源池:所有无状态业务都容器化,运行在容器资源池内
-
云主机资源池:有些使用了rsync、IP绑定、IP哈希、有状态的服务,暂时没有时间进行架构改造,就使用云主机资源池
-
物理机资源池:有些使用资源很大的应用(推荐、算法等)没有必要进行容器化改造和云主机迁移的应用,还是保留物理机
-
容器资源池和云主机资源池在一个大的VPC内,物理机在VPC外,之间的通信使用DGW网关和NLB进行
容器资源池,我们划分为以下7类
-
APP:这个资源池部署所有的业务应用
-
Redis:Redis属于敏感性业务,单独划分资源池,对这个资源池的使用做独立的配置
-
PUSH:PUSH业务白天的时候会占用70%以上的资源,但夜晚的资源使用率很低,独立一个资源池,防止PUSH在白天高峰的时候对其他业务产生影响
-
Rec:推荐资源池,推荐业务要求独立资源池,不想与其他业务共同使用资源池
-
Kafka:Kafka Operator资源池,部署所有Kafka服务
-
ES:ES Operator资源池,部署所有ES服务
-
GW:网关资源池,部署所有容器网关
4**.容器内外互相调用**
我们规划了容器资源池、虚拟机资源池和物理机资源池,但容器内外的服务如何互相调用,经过讨论和设计,我们采用如下的方式进行容器内外的调用
-
容器外调用容器外服务:我们使用传媒自己的服务框架NDSF做为服务调用的框架,使用Consul做为服务注册中心
-
容器内调用容器内服务:容器内我们直接使用ServiceName进行调用
-
容器内调用容器外服务:容器内调用容器外服务,我们也使用NDSF服务框架,使用Consul做为服务注册中心
-
容器外调用容器内服务:容器外服务调用统一经过入口网关
5**.Beta环境/生产环境**
为了完全隔离生产环境和测试环境,我们独立创建了Beta环境,Beta环境与生产环境网络设备,机柜和机器完全隔离,Beta环境有独立的VPC资源池,也有独立的容器云资源池和云主机资源池
默认情况下,Beta环境是不能访问生产环境的,但有些业务方有Beta环境访问生产环境的需求,为此,我们设置了白名单安全组,如果有需要访问生产环境的业务需求,将IP列表加入白名单即可访问生产环境
6**.IP资源规划**
我们创建了容器资源池和云主机资源池,容器资源池的网络使用了OVS,在整个VPC环境内和虚机都是网络平行的,我们划分了一个大的网段,并在这个大网段中划分了若干子网
-
容器公网网段:这个网段允许访问公网
-
容器内网网段:这个网段不允许访问公网,大部分容器实例都在这个网段
-
云机公网网段:这个网段允许访问公网
-
云机内网网段:这个网段不允许访问公网,大部分虚机实例都在这个网段
-
网关网段:容器网关所使用的子网
-
Redis网段:Redis所使用的子网
-
Kafka网段:Kafka所使用的子网
-
ES网段:ES所使用的子网
-
管控网段:Kubernetes管控组件所使用的子网
7**.技术选型**
专属云环境我们选择了网易杭研提供的云1.0,云1.0基于OpenStack扩展,提供了虚拟机、RDS、NCR、LB等基础资源和公共中间件,网络使用OVS,云1.0是我们一直使用的云环境,比较稳定,可靠,运维团队对这个环境也非常的熟悉
容器云环境我们使用了网易杭研提供的轻舟平台,轻舟平台是在社区Kubernetes的基础上推出的PAAS平台,轻舟本身基于社区,但在社区提供的能力基础上,稳定性和可用性方面都做了很大的提升,符合生产环境的使用要求,我们使用了轻舟的以下组件
-
NCS:容器服务,提供基础、可靠的容器实例服务
-
NSF:容器ServiceMesh,使用Envoy和Istio,并在社区开源的基础上支持了Dubbo、Thrift协议,支持了热升级、懒加载、服务治理能力等。我们在使用NSF时,只启动了调用端拦截
-
混部:离线/在线业务混部平台,用于整体提高CPU的使用率
-
容器网关:容器池外访问容器内服务的唯一入口,用于安全,审计,熔断/降级,监控等能力
-
Redis Operator:容器Redis PAAS服务
-
Kafka Operator:容器Kafka PAAS服务
-
HPA:Kubernetes默认支持的能力,使用HPA实现弹性扩缩容,支持了CPU负载,QPS,任务积压量等指标的弹性扩缩容
8.当前状态
经过1年左右的努力,传媒已经建立起来了容器资源池、云机资源池和物理机资源池,容器资源池运行了上万个POD,上千个Service,核心业务都已经迁移到了容器环境,80%的物理机资源都进入了容器资源池
9**.ServiceMesh**
服务调度和治理框架做为微服务的基础框架,起到了非常重要的作用,市面上有很多可用的开源框架,比如:Dubbo、Spring Cloud等,传媒容器化之前有自己的一套服务调用与治理框架,我们命名为NDSF。
NDSF的功能架构图如下所示:
NDSF框架主要包括三大块内容
-
SDK:提供了数据库、中间件的Client SDK,这些SDK封装了用户密码,访问地址等敏感信息,同时也提供了核心指标的调用统计能力
-
Framework:提供了微服务启动的基础能力,包括:服务上线/下线、服务状态查看、服务拓扑上报、服务健康状态监控等基础能力
-
服务调用:服务框架调用、服务注册、服务发现、负载均衡、服务治理能力的提供
NDSF做为传媒的基础框架,基本所有服务都在使用,在服务调用和服务治理层面,NDSF框架使用Consul做为服务注册中心,基于HTTP2.0C的服务调用框架,服务的调用实现了Jar包调用和Proxy调用两种能力
-
Jar包方式调用:适用于Java语言的微服务,通过在工程里引入NDSF Jar包实现服务的调用、注册和治理
-
Proxy方式调用:适用于非Java语言的微服务,通过在每台机器上部署一个Proxy实现服务的调用、注册和治理
NDSF目前已经覆盖了传媒80%以上的业务,在业务方接入完成,使用过程中,也出现了很多问题,主要的问题如下
-
框架的升级需要业务方的感知,业务方需要配合升级Jar包,重新发布应用,这种方式对升级造成了很大的阻碍
-
业务在接入或升级过程中,比较容易引起Jar包冲突,导致业务方进行修复,增加了业务方接入成本和时间
-
业务方接入NDSF时需要修改服务调用代码,对接入也造成了很多困难
为了做到让业务无感知,也不需要做任何改动,我们决定使用ServiceMesh做为整个服务治理的框架,通过ServiceMesh,我们想达到以下几个目的
-
业务无感知:对业务代码无需修改,业务代码无需引入Jar包
-
语言无关:业务方所使用的语言都能支持
-
多协议支持:支持常见的Dubbo、Thrift、HTTP、gRPC协议
-
动态生效:服务治理、服务发现、服务注册都能够动态生效,版本升级也对业务无感知
-
完善的服务治理能力:能够支持超时重试、熔断降级、黑/白名单、限流等服务治理能力
在ServiceMesh方案选型方面,我们使用了NSF做为ServiceMesh的底层支撑,NSF是基于Istio和Envoy做了增强,不仅支持了Dubbo和Thrift,也在动态升级、动态拦截、服务兜底策略方面做了比较大的升级
由于传媒有部分服务使用的Dubbo,ServiceMesh需要对Dubbo协议做一些支持,NSF在Istio和Envoy的基础上,增加了对Dubbo的支持,主要的架构如下:
-
业务方使用Dubbo直接调用目标IP的方式
-
通过 iptable ⽅式对指定端⼝(⽬前10000端⼝)的流量进⾏拦截,重定向到 envoy
-
保留 zk 注册中⼼,扩展 galley 组件从 zk 拉取 dubbo 服务的注册信息及服务依赖
-
galley 组件通过 mcp 上报 service entry 资源给 pilot,扩展字段⾥包含了 dubbo 服务的依赖关系
-
pilot 在同步 xds 配置的时候根据 service entry 上的依赖关系,按需下发所需的配置
VirtualService协议上面扩展了Dubbo
ServiceMesh目前支持的能力如下表所示
10**.混部**
传媒容器化的主要一个目标之一就是能够提高整体CPU的利用率,节省资源。目前传媒整体资源利用率在20%左右,波谷的资源得不到有效的利用,我们希望整体资源利用率能够达到50%~60%,并且不能影响整体服务的稳定性,经过分析后,将资源的使用情况分为以下三类
-
资源利用率很低,在3%~5%左右,这类应用业务比较敏感,不能有延迟,比较典型的就是Redis集群
-
存在明显的波峰波谷,在线资源在波谷的情况下,会空闲出大量的计算资源
-
在线资源的使用率不是很低,一般在15%~20%左右,这些业务不是很敏感,比较典型的就是无状态服务
我们对目前的资源使用情况和业务进行了分析,将整体业务区分为在线业务和离线业务两种类型,这两种业务的主要特点如下:
我们对离线业务/在线业务混部做了如下限制:
-
离线业务不能影响在线业务
-
优先在线服务可用的资源保证
-
在线服务资源使用率低的情况下,离线服务使用资源,保证整体资源使用率在一定水位
容器资源池,按照在线/离线维度,划分为三类资源池
-
在线资源池:只能调度在线业务上来
-
离线资源池:只能调度离线业务上来
-
混部资源池:可以混合调度在线、离线业务上来业务类型
容器内部署的业务类型,划分为四类业务类型
-
Job:离线任务,会调度到离线资源池
-
Serivce:在线任务,会调度到在线资源池
-
Colocation-job:允许混部的Job,会调度到混部资源池
-
Colocation-service:允许混部的service,会调度到混部资源池
我们使用了轻舟团队提供的Zeus混部系统做为传媒混部的基础平台,Zeus能保证在线业务稳定的前提下,最大化利用CPU资源,并且支持灵活的混部策略和弹性扩缩容
Zeus主要架构如下图所示:
混部系统主要包括六个组件,管控面有四个组件:
-
zeus-webhook-server:基于kubernetes dynamic admission control开发的准入控制插件。拦截用户请求,进行默认值的设置和字段的合法性检查
-
zeus-manager:基于kubernetes的 operator 模式开发的控制器。实现CRD资源的生命周期管理、离线任务重调度等
-
zeus-scheduler-extender:基于kubernetes的 scheduler http extender机制 开发的用来实现离线业务动态调度的扩展调度器
-
zeus-exporter: 类似于kube-state-metrics组件,统计汇总各种资源数据,推送到prometheus
数据面有两个组件:
-
zeus-monitor-agent:采集节点监控数据的agent,运行在混部资源池的每个节点上。monitor组件将节点的负载情况定期更新到NodeProfile(custom resource)资源中,并且提供本地restful api,提供给zeus-isolation-agent查询
-
zeus-isolation-agent:是一个负责实现在/离线业务资源隔离的组件,运行在混部资源池的每个节点上。访问 zeus-monitor-agent 获取监控数据,定期执行隔离规则。
我们依据业务使用资源的特性,将整个容器混部资源池划分为三类
-
APP:在线应用资源使用率较高,有可能存在突增现象
-
NCR:在线应用资源使用率很低,但比较敏感,离线不能占用太多资源,防止抖动
-
PUSH:在线应用有明显的波峰波谷,波谷资源利用率需要提高
在合理估算离线任务可使用的CPU的方面,如果在线业务负载较高,节点资源利用率高,那么分配给离线业务的资源就会少或者该节点就完全不需要混部离线业务;如果在线业务负载较低,节点资源利用率低,那么分配给离线业务的资源就可以变多,基于以上的分析,我们定义了如下公式
Capacity = MAX(节点可分配资源总量 * 节点目标利用率 - 在线业务使用量, 最低资源保证核数)
节点目标利用率是我们根据每个资源池的敏感情况动态设置的
比如一个混部节点的cpu capacity为56核,allocatable为55核,该节点上的在线业务实际使用了10核,并且该节点的CPU目标利用率配置为50%,最低资源保证为2核,则该混部节点的可用CPU核数为:
Capacity = Max( 55*50% - 10 , 2 ) = 12.5 核
经过在线/离线混部,目前资源整体利用率得到了很大提升,节省了30%左右的资源
我们对预定义的混部资源池进行监控,整体资源使用趋势如下:
11**.遇到的坑**
-
必须对业务进行容量评估
需要对进入容器的业务提前进行容量的评估,以防止容器集群整体压力增大而导致的故障,评估的主要内容包括:网络带宽、PPS、QPS、CPU算力、内存大小、磁盘IO、业务特性、LB带宽、网关带宽等关键项和指标,以确保新业务进入容器集群后,容器集群也能够支持这个流量
-
节点标签不宜打太多
K8S可用通过在节点上打不同的Tag,业务在部署的时候可以选择不同的资源池进行部署,如果资源池的Tag打的过多,则会带来很大的管理难度,对于资源的合理应用也会造成很大的阻碍,建议业务部署选择的资源池按业务特性进行选择,传媒目前主要的资源池包括:APP、网关、Redis、Kafka、PUSH、推荐、ES,这些资源池都有独立的特性,不能互相影响
-
合理创建Service
如果服务需要用到Service资源,再去创建。Service资源创建后,当业务发布、扩容、缩容、更新后,都会导致Endpoint发生变化,IPTables会全量刷新,如果Service下的POD很多,全量刷新会很慢,对整个网络也会造成不必要的影响
-
容器IP不宜粒度太细
用户在访问Mysql数据库的时候,为了数据安全,都会对要访问的库增加IP白名单限制,在使用云机和物理的时候,IP都能固定住,但在容器的场景下,IP地址是在一个大网段随机分配的,如果要细粒度管理IP资源,就需要用到IPPool,为每一个集群创建自己的IPPool,这样对于管理复杂度有很大的增加,而且对于IP资源也有很大的浪费,因为对每一个集群,都需要预留一定数量的IP做为备份,建议在容器集群下,就不要限制Mysql数据库白名单,或者限定容器集群的一个大段,在管理上会方便很多
12**.后续规划**
目前,传媒的核心业务都已经进入了容器,对基础资源的管理做到了下沉,后续,在K8S的基础上主要做以下几件事
-
资源精细化管理:我们分析目前容器集群的使用情况,发现业务申请资源和实际使用的资源差距很大,导致业务占用了很多资源,但其他业务无法从资源池中获取需要的资源,接下来,我们将会细化每个集群的资源申请量和实际使用量之间的差距,争取缩小两者的差距
-
资源使用自动推荐:依据集群历史实际资源的使用情况,能够给应用一个合理的资源量,让业务能有个参考
-
资源弹性:为了应对流量的徒增能快速扩容,需要增加容器的弹性,目前已经在使用HPA解决资源的弹性,也在尝试VPA方面解决资源的弹性问题
-
推广ServiceMesh:服务治理能力下沉,让业务方更关注于业务本身,目前我们已经支持了ServiceMesh的能力,接下来会给业务推广这样的能力
-
容量管理:容器集群内的整体容量监控,包括:网络、计算、内存、磁盘等资源的整体容量监控,为新业务进入容器做到准确的评估