网易伏羲成立于2017年,是国内专业从事游戏与泛娱乐AI研究和应用的顶尖机构。为了更好的为游戏提供中间件服务支持,伏羲云计算团队构建了伏羲云原生游戏中间件平台。本文主要介绍伏羲云原生游戏中间件平台的实践过程。
伏羲私有云为游戏以及AI研究应用提供完善、可靠的计算能力。游戏作为网易的核心业务,如何更好的为游戏服务是平台的重点。
游戏是网易的核心业务。在游戏项目中,从游戏的开发、测试、到上线各个阶段都具有各种中间件的需求。在此背景下,对我们如何为游戏提供高效、可靠的中间件服务提出巨大的挑战。
传统的方式是通过运维人员手动搭建维护,而这种方式主要存在下面问题:
-
环境依赖:服务可能会因为基础环境的变化导致异常情况。
-
缺少标准化:各个游戏项目对中间件的使用方式存在差异。
-
缺少平台统一动态管理:缺少统一平台进行创建、删除等自主运维能力。
-
服务可靠性低:
服务不可用等状态难以及时发现,以及人工修复效率比较低。
为了解决这些问题,伏羲云计算团队构建了云原生游戏中间件平台。伏羲云原生游戏中间件平台基于Kubernetes技术,提供了基于容器的数据库、CacheServer、SymbolServer等游戏项目中的中间件服务,为游戏在开发、测试、上线各个阶段需要各种中间件支持,帮助游戏更好的专注于游戏本身。
云原生游戏中间件平台作为服务于游戏业务的核心平台,面临着以下严峻的挑战:
-
灵活性:各个游戏业务对基础服务使用存在差异,需要满足各个业务多样化的需求。
-
稳定性:云环境中存在节点异常、网络IO抖动,磁盘IO抖动等各种情况,这些情况都有可能导致服务异常,基础服务直接影响上层业务的稳定性和项目的推进,因此如何保证服务的稳定性十分重要。
-
故障自愈:由于各种异常情况,服务可能存在主动退出、或者被动退出的情况。当出现服务的情况,服务需要具备自愈能力,服务能够被快速重新拉起,并且保证服务中数据的不丢失,从而避免人工介入修复。
-
易观测:
中间件运行的过程中,运行日志,指标参数需要能被完善收集便捷的展示,帮助用户了解中间件服务运行的状态。
-
中间件生命周期管理:中间件服务的生命周期管理就是对中间件的各个状态进行管理,各个状态具有对应可执行的操作。
在技术架构上,伏羲云原生游戏中间件平台核心基于Kubernetes进行构建,充分利用了Kubernetes提供的容器编排管理能力。利用Kubernetes Operator模式扩展Kubernetes API,将各个中间件服务抽象成Kubernetes中的资源进行管理。
云原生游戏中间件平台分为:用户层、业务服务层、核心服务层以及基础设施层。
核心服务层作为平台的大脑提供了Kubernetes扩展自定义中间件资源的生命周期管理能力,以及中间件资源的可观测性等能力。
自定义资源(Custom Resource) 是对 Kubernetes API 的扩展,通过CRD(Custom Resource Definitions)动态注册自定义资源类型,并可以与Kubernetes内置资源对象(如 Pod、Deployment等)相同的方式来管理它们。CRD可以充分利用Kubernetes已有的能力,核心能力包括:Schema定义校验、多版本、status/scale子资源等。
CRD仅仅提供了自定义资源结构化数据的存储能力。为了使自定义资源成为声明式API(声明资源的期望状态,并尝试让Kubernetes对象的当前状态同步到其期望状态),资源达到期望状态的逻辑就需要CRD Controller来实现。
Kubernetes Operator模式是将CRD与CRD Controller相结合,实现自定义资源以及资源自动化运维。
云原生游戏中间件平台采用的是Operator模式管理游戏中间件服务,每一个游戏中间件对应一个Operator实现,通过Operator实现游戏中间件服务的生命周期管理,自动化运维。
中间件的可观测性作为中间件平台的核心能力,目的是帮助检测定位中间件服务的运行状态(例如:服务异常、错误、响应缓慢等)。服务观测主要包含日志和指标监控两个层面。
中间件服务容器的标准输出、标准错误输出的日志会以文件的形式存储在Pod所在宿主机上。通过Kubernetes DaemonSet方式在各个节点上部署Fluentd,并采用hostPath方式将宿主机上容器日志挂载到Fluentd的Pod中。Fluentd将中间件容器的日志发送到Kafka,最终存储到ElasticSearch中。
在一些场景下中间件日志存储在容器中,不同中间件的日志文件命名也不相同。针对容器内日志收集架构如下:
容器内日志收集采用Sidecar模式中间件Pod中增加日志收集容器Filebeat,Filebeat容器与中间件容器的日志目录通过emptyDir挂载的方式实现目录共享。Filebeat根据ConfigMap定义的配置,将中间件日志发送到Kafka,最终存储到ElasticSearch中。
传统的以主机为中心的基础设施中,我们仅监控两个层次:中间件服务和运行它们的主机。而云原生平台监控包含四个维度:节点监控、Kubernetes资源状态监控、容器监控、游戏中间件服务监控。整体架构如下:
exporter通过RESTful API的方式暴露指标数据,Prometheus从各个exporter中拉取指标数据,并进行持久化存储。Alertmanager则是Prometheus内置的告警模块。这里主要介绍exporter实现四个维度监控。
-
节点监控,节点的指标收集通过node exporter实现。node exporter通过DaemonSet方式部署在Kubernetes的每个节点上,用于采集节点的运行指标,包括节点CPU,内存,文件系统等指标。
-
Kubernetes资源状态监控,kube-state-metrics采用Deployment方式部署,通过监听Kubernetes apiserver将Kubernetes资源的数据生成相应指标。在云原生游戏中间件平台场景下,我们需要对自定义游戏中间件资源状态进行监控。
-
容器监控,cAdvisor对节点容器进行实时监控和性能数据采集,包括CPU使用情况、内存使用情况、网络吞吐量及文件系统使用情况。cAdvisor以及集成在kubelet中,当kubelet启动时会自动启动cAdvisor。
-
游戏中间件服务监控,上述监控从Kubernetes资源状态、节点、容器层进行,而更细粒度的就需要游戏中间件服务本身进行监控,不同游戏中间件需要监控的服务指标也不相同。
游戏中间件平台层就需要针对各个中间件资源实现各自的exporter,从而达到服务级别的监控。
以上介绍了云原生游戏中间件平台落地的核心通用层面。然而各个游戏中间件具有不同的特点,为了更好的实现游戏中间件服务的灵活性、稳定性、以及故障自愈能力,就需要从Operator实现、CacheServer相关优化等深入考虑。
下面以CacheServer中间件为例,介绍如何实现云原生游戏CacheServer中间件。
CacheServer是Unity的缓存服务器。网易基于Unity CacheServer v1协议,自研了CacheServer服务,CacheServer由Nginx作为入口,资源数据都存储在ARDB中。ARDB是一个类似Redis的KV存储,但相比Redis的优点就是数据都由底层的RocketsDB持久化存储在磁盘上,在Redis服务中只存储所有资源数据内容的key,以及在key上设置TTL,再由Cleaner Server负责清理。架构如下:
下文中出现的CacheServer即为网易自研CacheServer。
上图是CacheServer是基于Kubernetes的云原生架构。CacheServer Pod包含了CacheServer容器和Cleaner容器。CacheServer容器提供了CacheServer核心服务能力包含了Nginx、CacheServer Server、ARDB和Redis服务。Cleaner容器以Sidecar的模式,提供缓存清理能力。ARDB数据以emptyDir方式进行挂载,随Pod生命周期。CacheServer Service提供Kubernetes的访问入口。
type Cacheserver struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec CacheserverSpec `json:"spec,omitempty"`
Status CacheserverStatus `json:"status,omitempty"`
}
type CacheserverSpec struct {
Storage int64 `json:"storage"`
Image string `json:"image"`
Resources corev1.ResourceRequirements `json:"resources"`
Cleaner Cleaner `json:"cleaner"`
Tolerations []corev1.Toleration `json:"tolerations"`
NodeSelector map[string]string `json:"nodeSelector"`
ImagePullSecrets []corev1.LocalObjectReference `json:"imagePullSecrets,omitempty"`
}
type CacheserverStatus struct {
Phase string `json:"phase"`
}
type Cleaner struct {
Webhook string `json:"webhook"`
Image string `json:"image"`
}
CacheServer Operator实现逻辑:
CacheServer Operator分为资源管理和状态管理两个部分: