【AICon】AI 大模型超全落地场景&最佳实践 了解详情
写点什么

UCloud 基于 Kubernetes Operator 的服务化实践

  • 2019-11-08
  • 本文字数:7419 字

    阅读完需:约 24 分钟

UCloud基于Kubernetes Operator的服务化实践

8 月 16 - 19 日,与零一万物李开复、蔚来李斌、面壁智能李大海,及工商银行、交通银行、华夏银行等 100+ 行业专家相聚 FCon x AICon

KUN(Keep UCloud Nimble)是面向 UCloud 内部、基于 Kubernetes 打造的容器服务平台,旨在提升内部研发效率,帮助改善、规范研发流程。在 KUN 平台的建设过程中,内部用户对于一些基础通用的分布式软件如 Redis、Kafka 有强需求,但又不想操心其部署及运维。KUN 团队在分析这些痛点后,决定利用 Kubernetes Operator 的能力,并弥补了开源 Operator 的一些不足,将 Operator 产品化来帮助用户部署和管理这些分布式、带状态的应用。通过 Operator 服务化,KUN 平台扩充了 Kubernetes 交付 Pod、PVC、SVC 的能力,能够快速交付 Redis 等分布式、带状态的系统,提供了一个平台之上的平台。


在这篇文章里,我们主要来聊一下 Operator 对于 Kubernetes 的价值以及我们团队基于 Operator 所做的相关工作。

Operator 是什么,解决了什么问题

为什么需要 Operator

无状态和有状态

2014-2015 年容器和微服务的出现,为软件开发和基础架构带来了巨大的创新和挑战。容器提供了隔离和限制,同时容器的状态是易失的,它对自己外部的状态和数据不关心,专注于单一的服务,比如 Web 应用、日志服务、业务程序、缓存等。这些服务都能作为容器交付和运行,而一旦容器数量形成规模,管理的难度也越来越大。


Kubernetes 作为容器编排框架,可以减轻配置、部署、管理和监控大规模容器应用的负担。事实上早期的 Kubernetes 非常善于管理无状态的应用程序,比如 Kubernetes 提供的 Deployment 控制器。它认为所有的 Pod 都是完全一样的,Pod 间没有顺序和依赖,扩容的时候就根据模板创建一个一样的新的应用,也可以任意删除 Pod。但对于像数据库这样的有状态的应用程序,添加删除实例可能需要不同的节点做不同的配置,与已有的集群进行通信协商等,这些操作通常需要我们人工来干预,这就会增加运维的负担,并且增加出错的可能性,最重要的是它消除了 Kubernetes 的一个主要卖点:自动化。



这是一个大问题,那么如何在 Kubernetes 中管理有状态的应用程序呢?

StatefulSet 的价值和不足

Kubernetes 的 1.5 版本开始出现了 StatefulSet,StatefulSet 提供了一系列资源来处理有状态的容器,比如:volume,稳定的网络标识,从 0 到 N 的顺序索引等。通过为 Pod 编号,再使用 Kubernetes 里的两个标准功能:Headless Service 和 PV/PVC,实现了对 Pod 的拓扑状态和存储状态的维护,从而让用户可以在 Kubernetes 上运行有状态的应用。


然而 Statefullset 只能提供受限的管理,通过 StatefulSet 我们还是需要编写复杂的脚本通过判断节点编号来区别节点的关系和拓扑,需要关心具体的部署工作,并且一旦你的应用没办法通过上述方式进行状态的管理,那就代表了 StatefulSet 已经不能解决它的部署问题了。



既然 StatefulSet 不能完美的胜任管理有状态应用的工作,那还有什么优雅的解决方案呢?答案是 Operator。Operator 在 2016 年由 CoreOS 提出,用来扩充 Kubernetes 管理有状态应用的能力。

Operator 核心原理

解释 Operator 不得不提 Kubernetes 中两个最具价值的理念:“声明式 API” 和 “控制器模式”。“声明式 API”的核心原理就是当用户向 Kubernetes 提交了一个 API 对象的描述之后,Kubernetes 会负责为你保证整个集群里各项资源的状态,都与你的 API 对象描述的需求相一致。Kubernetes 通过启动一种叫做“控制器模式”的无限循环,WATCH 这些 API 对象的变化,不断检查,然后调谐,最后确保整个集群的状态与这个 API 对象的描述一致。


比如 Kubernetes 自带的控制器:Deployment,如果我们想在 Kubernetes 中部署双副本的 Nginx 服务,那么我们就定义一个 repicas 为 2 的 Deployment 对象,Deployment 控制器 WATCH 到我们的对象后,通过控制循环,最终会帮我们在 Kubernetes 启动两个 Pod。


Operator 是同样的道理,以我们的 Redis Operator 为例,为了实现 Operator,我们首先需要将自定义对象的说明注册到 Kubernetes 中,这个对象的说明就叫 CustomResourceDefinition(CRD),它用于描述我们 Operator 控制的应用:redis 集群,这一步是为了让 Kubernetes 能够认识我们应用。然后需要实现自定义控制器去 WATCH 用户提交的 redis 集群实例,这样当用户告诉 Kubernetes 我想要一个 redis 集群实例后,Redis Operator 就能够通过控制循环执行调谐逻辑达到用户定义状态。



所以 Operator 本质上是一个个特殊应用的控制器,其提供了一种在 Kubernetes API 之上构建应用程序并在 Kubernetes 上部署程序的方法,它允许开发者扩展 Kubernetes API,增加新功能,像管理 Kubernetes 原生组件一样管理自定义的资源。如果你想运行一个 Redis 哨兵模式的主从集群或者 TiDB 集群,那么你只需要提交一个声明就可以了,而不需要关心部署这些分布式的应用需要的相关领域的知识,Operator 本身可以做到创建应用、监控应用状态、扩缩容、升级、故障恢复,以及资源清理等,从而将分布式应用的使用门槛降到最低。

Operator 核心价值

在这里我们总结一下 Operator 的价值:


  • Operator 扩展了 Kubernetes 的能力;

  • Operator 将人类的运维知识系统化为代码;

  • Operator 以可扩展、可重复、标准化的方式实现目标;

  • Operator 减轻开发人员的负担。

Operator 服务化目标

聊完 Operator 的能力和价值我们把目光转向 KUN 上的 Operator 平台。前面说过,用户想在 Kubernetes 中快速的运行一些分布式带状态的应用,但是他们本身不想关心部署、运维,既然 Operator 可以灵活和优雅的管理有状态应用,我们的解决方案就是基于 Operator 将 Kubernetes 管理有状态应用的能力方便地暴露给用户。


核心的的目标主要有两方面:

1、针对 Operator 平台

  • 提供一个简单易用的控制台供用户使用,用户只需要点点鼠标就能快速拉起有状态应用。并且能在控制台上实时看到应用部署的进度和事件,查看资源,更新资源等。

  • 通过模板提交声明,参数可配置化,创建应用的参数通用化,将应用名称等通用配置和应用参数(如:redis 的 maxclients、timeout 等参数)解耦。这样带来的好处就是不同的 Operator 可以共用创建页面,而不需要为每种 Operator 定制创建页面,同时 Operator 暴露出更多的应用配置参数时,前端开发也不需关心,由后端通过 API 返回给前端参数,前端渲染参数,用户修改参数后,通过 API 传递到后端,后端将参数与模板渲染成最终的实例声明提交到 Kubernetes 中,节省了前端开发时间。



  • 可以管理通过公共的 Operator 和 Namespace 私有的 Operator 创建的实例。用户可以用我们提供的公用 Operator,也可以把 Operator 部署到自己的 NameSpaces,给自己的项目提供服务,但这两种 Operator 创建的应用实例都可以通过 Operator 控制台管理。

  • 可以无限添加 Operator。

2、针对 Operator 控制器

  • 拉起分布式集群,自动运配置、运维;

  • 可以动态更改所控制应用参数;

  • 控制器本身需要无状态,不能依赖外部数据库等;

  • 实时更新状态,维护状态,推送事件;

  • 可以运行在集群范围,也能运行在单 NameSpace,并且可以共存,不能冲突;


针对这些设计目标最终我们的 Operator 控制台如下:



同时我们为 Operator 控制台定制了第一个 Operator:Redis Operator,未来会推出更多的 Operator,接下来我们就来看下 Redis Operator 的实现。

Redis Operator

Redis 集群模式选型

我们知道 Redis 集群模式主要有主从模式、哨兵模式、Redis 官方 Cluster 模式及社区的代理分区模式。


分析以上几种模式,主从模式的 Redis 集群不具备自动容错和恢复功能,主节点和从节点的宕机都会导致读写请求失败,需要等待节点修复才能恢复正常;而 Redis 官方 Cluster 模式及社区的代理分区模式只有在数据量及并发数大的业务中才有使用需求。哨兵模式基于主从模式,但是因为增加了哨兵节点,使得 Redis 集群拥有了主从切换,故障转移的能力,系统可用性更好,而且客户端也只需要通过哨兵节点拿到 Master 和 Slave 地址就能直接使用。因此我们决定为 Kun Operator 平台提供一个快速创建哨兵模式的 Redis 集群的 Redis Operator。

开源 Operator 的不足

目前已经有一些开源的 Redis Operator,通过对这些 Operator 分析下来,我们发现都不能满足我们的需求,这些开源的 Operator:


  • 不能设置 Redis 密码。

  • 不能动态响应更改参数。

  • 没有维护状态,推送事件。

  • 不能在开启了 istio 自动注入的 Namespace 中启动实例。

  • 只能运行在集群或者单 Namespace 模式。

改进工作

当前我们定制开发的 Redis Operator 已经在 Github 上开源https://github.com/ucloud/redis-operator。提供:


  1. 动态响应更改 Redis 配置参数。

  2. 实时监控集群状态,并且推送事件,更新状态。

  3. 误删除节点故障恢复。

  4. 设置密码。

  5. 打开关闭持久化快捷配置。

  6. 暴露 Prometheus Metrics。


使用 Redis Operator 我们可以很方便的起一个哨兵模式的集群,集群只有一个 Master 节点,多个 Slave 节点,假如指定 Redis 集群的 size 为 3,那么 Redis Operator 就会帮我们启动一个 Master 节点,两个 Salve 节点,同时启动三个 Sentinel 节点来管理 Redis 集群:



Redis Operator 通过 Statefulset 管理 Redis 节点,通过 Deployment 来管理 Sentinel 节点,这比管理裸 Pod 要容易,节省实现成本。同时创建一个 Service 指向所有的哨兵节点,通过 Service 对客户端提供查询 Master、Slave 节点的服务。最终,Redis Operator 控制循环会调谐集群的状态,设置集群的拓扑,让所有的 Sentinel 监控同一个 Master 节点,监控相同的 Salve 节点,Redis Operator 除了会 WATCH 实例的创建、更新、删除事件,还会定时检测已有的集群的健康状态,实时把集群的状态记录到 spec.status.conditions 中:


status:  conditions:  - lastTransitionTime: "2019-09-06T11:10:15Z"    lastUpdateTime: "2019-09-09T10:50:36Z"    message: Cluster ok    reason: Cluster available    status: "True"    type: Healthy  - lastTransitionTime: "2019-09-06T11:12:15Z"    lastUpdateTime: "2019-09-06T11:12:15Z"    message: redis server or sentinel server be removed by user, restart    reason: Creating    status: "True"    type: Creating
复制代码


为了让用户通过 kubectl 快速查看 redis 集群的状态,我们在 CRD 中定义了如下的 additionalPrinterColumns:


additionalPrinterColumns:  - JSONPath: .spec.size    description: The number of Redis node in the ensemble    name: Size    type: integer  - JSONPath: .status.conditions[].type    description: The status of Redis Cluster    name: Status    type: string  - JSONPath: .metadata.creationTimestamp    name: Age    type: date
复制代码


由于 CRD 的 additionalPrinterColumns 对数组类型支持不完善,只能显示数组的第一个元数据,所以需要将 spec.status.conditions 中的状态按时间倒序,最新的状态显示在上方,方便用户查看最新的状态。同时用户也可以通过 kubectl 命令直接查看集群的健康状况:


$ kubectl get redisclusterNAME   SIZE   STATUS    AGEtest   3      Healthy   2d
复制代码

cluster-scoped 和 namespace-scoped

我们在 WATCH Redis 集群实例的新建、更新、删除事件时,添加了过滤规则,shoudManage 方法会检测实例是否含有 redis.kun/scope: cluster-scoped 这条 annotation,如果含有这条 annotation 并且 Redis Operator 工作在全局模式下(WATCH 了所有的 Namespace),那么这个实例的所有事件才会被 Operator 所接管。


Pred := predicate.Funcs{UpdateFunc: func(e event.UpdateEvent) bool {// returns false if redisCluster is ignored (not managed) by this operator.if !shoudManage(e.MetaNew) {return false}log.WithValues("namespace", e.MetaNew.GetNamespace(), "name", e.MetaNew.GetName()).Info("Call UpdateFunc")// Ignore updates to CR status in which case metadata.Generation does not changeif e.MetaOld.GetGeneration() != e.MetaNew.GetGeneration() {log.WithValues("namespace", e.MetaNew.GetNamespace(), "name", e.MetaNew.GetName()).Info("Generation change return true")return true}return false},DeleteFunc: func(e event.DeleteEvent) bool {// returns false if redisCluster is ignored (not managed) by this operator.if !shoudManage(e.Meta) {return false}log.WithValues("namespace", e.Meta.GetNamespace(), "name", e.Meta.GetName()).Info("Call DeleteFunc")metrics.ClusterMetrics.DeleteCluster(e.Meta.GetNamespace(), e.Meta.GetName())// Evaluates to false if the object has been confirmed deleted.return !e.DeleteStateUnknown},CreateFunc: func(e event.CreateEvent) bool {// returns false if redisCluster is ignored (not managed) by this operator.if !shoudManage(e.Meta) {return false}log.WithValues("namespace", e.Meta.GetNamespace(), "name", e.Meta.GetName()).Info("Call CreateFunc")return true},}// Watch for changes to primary resource RedisClustererr = c.Watch(&source.Kind{Type: &redisv1beta1.RedisCluster{}}, &handler.EnqueueRequestForObject{}, Pred)if err != nil {return err}
复制代码


通过识别 annotation,Redis Operator 可以运行在单个 Namespace 下,也可以运行在集群范围,并且单 Namespace 和集群范围的 Operator 不会互相干扰,各司其职。

快速持久化

我们还了解到用户使用 Redis 时,有一些使用场景是直接将 Redis 当做数据库来用,需要持久化配置,而有些只是当做缓存,允许数据丢失。为此我们特意在 Redis 集群的 CRD 中添加了快速持久化配置的开关,默认为启用,这会为用户自动开启和配置 RDB 和 AOF 持久化,同时结合 PVC 可以将用户的数据持久化起来。当节点故障,被误删除时数据也不会丢失,并且 PVC 默认不会跟随 Redis 集群的删除而删除,当用户在相同 Namespace 下启动同名的 Redis 集群时,又可以使用上次的 PVC,从而恢复数据。


 podAntiAffinity:          preferredDuringSchedulingIgnoredDuringExecution:          - podAffinityTerm:              labelSelector:                matchLabels:                  app.kubernetes.io/component: redis                  app.kubernetes.io/managed-by: redis-operator                  app.kubernetes.io/name: test                  app.kubernetes.io/part-of: redis-cluster                  redis.kun/v1beta1: prj-shu_test              topologyKey: kubernetes.io/hostname            weight: 100
复制代码


为了让 Redis 拥有更高的可用性,我们为 Redis 节点提供了设置 node affinity, pod anti affinity 的能力,可以灵活的控制 Reids 数据节点跑在不同 Node 或者不同的数据中心,做到跨机房容灾。如上所示,Redis Operator 缺省情况下会为每个 Pod 注入 podAntiAffinity,让每个 redis 服务尽量不会运行在同一个 node 节点。

监控

生产级别的应用离不开监控,Operator 中还内置了 Prometheus Exporter,不光会将 Operator 自身的一些 Metrics 暴露出来,还会将 Operator 创建的每一个 Reids 集群实例的状态通过 Metrics 暴露出来。


# HELP redis_operator_controller_cluster_healthy Status of redis clusters managed by the operator.# TYPE redis_operator_controller_cluster_healthy gaugeredis_operator_controller_cluster_healthy{name="config",namespace="xxxx"} 1redis_operator_controller_cluster_healthy{name="flows-redis",namespace="yyyy"} 1# HELP rest_client_requests_total Number of HTTP requests, partitioned by status code, method, and host.# TYPE rest_client_requests_total counterrest_client_requests_total{code="200",host="[2002:xxxx:xxxx:1::1]:443",method="GET"} 665310rest_client_requests_total{code="200",host="[2002:xxxx:xxxx:1::1]:443",method="PATCH"} 82415rest_client_requests_total{code="200",host="[2002:xxxx:xxxx:1::1]:443",method="PUT"} 4.302288e+06rest_client_requests_total{code="201",host="[2002:xxxx:xxxx:1::1]:443",method="POST"} 454rest_client_requests_total{code="404",host="[2002:xxxx:xxxx:1::1]:443",method="GET"} 1rest_client_requests_total{code="404",host="[2002:xxxx:xxxx:1::1]:443",method="PATCH"} 235rest_client_requests_total{code="409",host="[2002:xxxx:xxxx:1::1]:443",method="POST"} 2rest_client_requests_total{code="409",host="[2002:xxxx:xxxx:1::1]:443",method="PUT"} 184# HELP workqueue_adds_total Total number of adds handled by workqueue# TYPE workqueue_adds_total counterworkqueue_adds_total{name="rediscluster-controller"} 614738# HELP workqueue_depth Current depth of workqueue# TYPE workqueue_depth gaugeworkqueue_depth{name="rediscluster-controller"} 0# HELP workqueue_longest_running_processor_microseconds How many microseconds has the longest running processor for workqueue been running.# TYPE workqueue_longest_running_processor_microseconds gaugeworkqueue_longest_running_processor_microseconds{name="rediscluster-controller"} 0
复制代码


这还不够,我们还为每个 Redis 节点提供了单独暴露 Metrics 的能力,用户可以在启动 redis 集群的时候为每个 redis 节点注入单独的 Exporter,这样每个集群的每个 Redis 数据节点都能被我们单独监控起来,结合 Prometheus 和 Alter Manger 可以很方便将 Operator 以及 Operator 创建的实例监控起来。


结合 Operator 的运维、Statefulset 的能力加上 Sentinel 的能力,等于说为 Redis 集群加了三重保险,可以确保集群的高可用。



UCloud 自研的 Redis Operator 目前已正式开源

总结

通过 Operator 服务化,KUN 平台可以向用户交付更多复杂的分布式应用,真正做到开箱即用。开发人员可以专心业务实现,而不需要学习关系大量的运维部署调优知识,推进了 Dev、Ops、DevOps 的深度一体化。运维经验、方案和功能通过代码的方式进行固化和传承,减少人为故障的概率,降低了使用有状态应用的门槛,极大了提升了开发人员的效率。


本文转载自公众号 UCloud 技术(ID:ucloud_tech)。


原文链接:


https://mp.weixin.qq.com/s/PV2a4wrlADO2l8BK4fKM5A


2019-11-08 10:281045

评论

发布
暂无评论
发现更多内容

NFTScan:05.15~05.21 NFT 市场热点汇总

NFT Research

【安全运维】免费运维软件有哪些?哪款好用一点?

行云管家

运维 安全运维 免费 小微企业

最全iOS 上架指南

雪奈椰子

直击面试!阿里技术官手码12W字面试小册在Github上爆火

做梦都在改BUG

Java java面试 Java八股文 Java面试题 Java面试八股文

云从科技进入百模大战,行业大模型成为胜负手

ToB行业头条

全网独一份微服务架构深度解析,连京东师哥都熬夜也要看完

小小怪下士

Java 程序员 微服务架构

含“AI”量超高!那些正在改变潮水方向的人丨创业邦2023年新青年创投榜单重磅发布

创业邦

阿里开源SpringBoot全栈小册!Github已标星百万

做梦都在改BUG

Java spring Spring Boot 框架

2023年企业降低云支出的小方法汇总

行云管家

云计算 云资源 云成本 云支出

数据驱动运营增长

MobTech袤博科技

java异常体系

echoes

自动驾驶成为汽车产业未来趋势,连接器行业迎来进一步发展

华秋电子

系统梳理面试6大专题,阿里爆款Java面试速成笔记也太香了

做梦都在改BUG

Java java面试 Java八股文 Java面试题 Java面试八股文

优秀!阿里甩出GC面试小册,仅7天Github获赞96.9K

做梦都在改BUG

Java JVM 垃圾回收 GC

为什么 GPU 能够极大地提高仿真速度?

思茂信息

gpu 仿真软件 计算机硬件 仿真技术

Seata-go TCC 设计与实现

阿里巴巴云原生

阿里云 云原生 TCC Seata-go

深化企业数据智能应用 用友敢当“急先锋”

用友BIP

2023用友BIP技术大会

智能的支柱:算法

TiAmo

算法 动态规划 分治 回溯算法 分支限界

【全网首发】华秋CAM:免费Gerber查看器,离线版!

华秋电子

DAPP合约拆分公排模式项目系统开发技术讲解

I8O28578624

理论+实操,带你了解多沙箱容器运行时Kuasar

华为云开发者联盟

云原生 后端 华为云 华为云开发者联盟 企业号 5 月 PK 榜

虚拟显示器软件:BetterDisplay Pro 激活Mac版

真大的脸盆

Mac Mac 软件 显示器校准软件

系统梳理面试6大专题,阿里爆款Java面试速成笔记也太香了

Java你猿哥

Java MySQL redis MQ java面试

共享电单车生产厂家排名!怎么选?

共享电单车厂家

共享电动车厂家 共享电单车生产 本铯共享电动车 共享电单车厂家排名

区块链DAPP互助逻辑模式系统开发技术方案

I8O28578624

华为云加速器首期加速营圆满结营,40+位创业者携手出海

科技热闻

加速信创生态建设 焱融科技与优炫软件完成兼容性互认证

焱融科技

文件存储 高性能存储 #分布式存储

救命稻草!阿里P8耗时5月打造的架构师速成手册,千金难求

程序知音

Java 分布式 java架构 Java进阶 后端技术

Amazon CodeWhisperer代码提示——Golang测评

衝鋒壹号

阿里、字节等大佬神创,必须是全网最全的Netty核心原理手册

Java你猿哥

Java 源码 Netty ssm netty内存管理

在行 | 唱响钢铁冶金行业绿色发展进行曲

用友BIP

UCloud基于Kubernetes Operator的服务化实践_文化 & 方法_舒梦辉_InfoQ精选文章