来源:同程艺龙技术中心
同程旅行自2019年开始启动服务上云计划,到现在为止已成功将20多种组件迁移上云。但是随着业务的不断发展,平台在使用过程中出现了很多的痛点:
1.资源配额多样性难以管理
2.基于组件管理服务的方式粒度不够细致
3.配置文件管理过于依赖人工
4.系统架构存在组件循环依赖问题
5.智能化运维无法实施
选型与挑战
Golang在云原生领域存在不可撼动的地位,但是同程旅行的开发人员全部都是JAVA出身,因此能否适应一个新的领域也将面临着巨大的挑战。
1)CustomResourceDefinition版本
同程旅行生产环境中使用的Kubernetes版本是v1.14,只能支持v1beta1版本的CRD,然而随着Kuberntes v1.16的发布,CRD的版本已经被升级到正式版v1,并且在Kubernetes v1.22版本中已经彻底将v1beta1版本的CRD删除。
为了后续开发的兼容性,我们决定升级线上的Kubernetes,使用v1版本的CRD作为Operator支持的版本。
2)Operator开发框架
社区中存在了很多Operator开发框架,其中最为常用的还是kubebuilder与operator-sdk。
通过对两者的对比,kubebuilder提供了更高的灵活性、更充分的测试及资源扩展以及更加庞大的社区支持,因此kubebuilder成为了我们的不二选择。
应用实践
在进行架构设计时,我们着重考虑了组件配置版本化管理、容器调试方式及Pod手动启停方案,最后我们通过watch-broadcast的方式让用户可以第一时间获取到集群内容器的状态变更。下图是我们的整体架构:
apiserver:面向前端用户,提供API管理、CR资源路由、文件管理、权限管理等,为Kubernetes内部运行的应用提供操作入口。
k8s cluster:我们为Kubernetes提供了更多的控制器,其中包括Operators和Watchdogs。Operators是用于管理应用程序资源的子集群。Watchdogs则负责监控集群内资源变化,并将Kubernetes资源转换为应用层需要的资源存储在数据库中,同时也负责执行一些定时任务。
monitoring:监控层负责采集集群内所有应用的指标,同时提供指标可视化及报警等相关工作。
3.1、配置版本化
为了改进git项目管理配置文件的方式,我们引入了平台层面的文件版本管理。当用户在平台上更新配置文件时,文件管理器会维护文件的元数据信息并将文件内容上传到S3进行存储。
通过这种方式,运维同学不再需要重复变更推送的工作,同时应用管理员也可以实时查看配置文件变更的内容。
为了避免因为某些未知原因导致Pod重建时配置文件内容发生变化,平台在滚动部署服务时会主动将用户在平台上维护的配置文件压缩成tar包并推送到S3。即使应用Pod被意外删除,也可以按照原配置进行恢复,避免用户更新部分配置对正在运行的线上服务造成影响。
3.2、容器启动方案
容器启动时会首先使用configini完成配置文件初始化,随后根据运行模式判定后续的执行逻辑。
在当前的方案中,我们摒弃了在脚本中使用sed命令替换文本的方案,而是使用了自研初始化工具的方式简化配置管理。
为了更加方便的管理容器内的脚本,我们针对dockerfile制定了以下规范:
1. 容器内依赖的脚本必须存放在/script目录下
2. 容器的启动命令必须使用entrypoint.sh结尾
3. 健康检查命令必须使用readiness.sh结尾
4. 存活检查命令必须使用liveness.sh结尾
5. entrypoint.sh与liveness.sh必须支持调试模式
调试模式:启动脚本只初始化配置文件后阻塞等待,直到用户退出;存活检查脚本跳过检查,避免容器重启。
3.3、调谐逻辑设计
在Operator逻辑组织上,为了提高Operator的可控性,我们分别在Controller层级以及Reconcile层级设计了暂停逻辑,这种设计在一些紧急处理的场景下非常有效。
在调用具体的调谐函数之前,首先会检查一下当前是否处于暂停状态。最后需要调用对应的领域函数实现对应的调谐逻辑,其中最为复杂的就是Pod的调谐逻辑,在Pod的调谐逻辑中需要为Pod生成对应的元数据信息,同时也需要管理Pod的状态,我们的主要工作也应该放在这里面。
3.4、资源监听
同程旅行提供了很多的Operator用于管理Kubernetes内的资源,但是这些操作都是针对Kubernetes的,没办法第一时间让用户感知到。为了可以让用户立即看到容器状态变更,我们思考了几种方案:
1)直接访问Kubernetes API
这种方案必须排除,我们不应该让用户的查询请求直接访问到Kube-Apiserver,这会加重Kube-Apiserver的负担,对集群的稳定性存在很大的影响。
2)在WebServer内存中缓存Kubernetes资源
在WebServer内存中缓存Kubernetes资源,会导致WebServer启动缓慢,也会浪费大量的内存,同时对于list操作不够友好,请求仍然会透传到Kube-Apiserver。
3)watch-broadcast机制
这是我们最终选择的方案,通过自研的Watchdog时刻watch k8s资源变动,并将监听到的数据持久化到数据库中,同时向外部发送变更广播,由感兴趣的监听者主动查询资源。
3.5、平台
截止目前,同程旅行云平台已经支持7种服务进行Operator自动化管理,管理的服务也达到100多个。
同程旅行云平台面向的是大数据组件研发人员,为了更加方便组件研发人员针对不同应用类型的管理,我们在平台侧细分了组件管理方式。组件研发人员可以根据自己的需求灵活配置组件集群。
通过自定义Operator,我们为Pod提供了更多自定义的功能,例如负载变更、运行模式变更以及Pod启停功能;同时也可以增加一些组件专属的属性展示,对于组件管理人员的操作更加友好。
配置文件管理页面允许用户自定义任何配置,用户只需要根据平台要求的格式编写文件内容,在下次进行滚动部署时,所有的配置文件都将按照用户的配置存放在对应的目录内。这种方式不仅可以减少平台开发人员适配文件变更的问题,还可以增加组件管理人员更新配置的便捷性。
3.6、优势
在当前的架构设计下,用户通过浏览器发送的请求会全部访问云平台后端的apiserver上,对于查询请求会直接访问缓存或者数据库,而对于和k8s相关的创建或更新请求则会直接发送给Kube-Apiserver,由对应的operator监听执行对应的逻辑,operator触发的事件由Watchdog进行监听并同步到数据库中,同时对外发送广播,apiserver接收到广播后处理资源推送给前端用户。
当前的架构相较于老架构更加有优势:
1. apiserver服务只需要处理用户的请求,无需关心Kubernetes资源的调谐逻辑
2. apiserver完全无状态,对于横向扩容更加友好。
3. 资源配置固化,避免发生Pod重建资源不一致的情况。
4. operators按照组件打散,每个operator负责对应组件的业务场景,做到了各司其职。
5. watchdogs部署在Kubernetes集群内部,同时使用Kubernetes提供的leader election功能,移除了老版本中对于zk的依赖。
6. 状态同步更加及时,用户体验更好。
未来规划
1)丰富Operator规模,支持更多服务组件
当前支持Operator的服务相较于原平台还是很少,我们的目标是将公司内部所有的大数据组件全部迁移上云。
2)服务血缘
现在我们将所有的组件打散,使用更细粒度的方式进行管理,虽然可以简化组件管理员的运维成本,但是服务使用者没有办法第一时间看到各个组件的血缘关系。
3)资源动态混部
通过在Operator层面为对应的服务制定个性化动态扩缩容策略,不仅可以提升组件的吞吐量,还可以提升机器的整体使用率。
4)混合云
通过与外部云厂商合作,不断的增加大数据集群的计算能力。