对于kubernetes老玩家而言,StatefulSet这种资源类型并不陌生。对于不少有状态服务而言,均可以使用 StatefulSet 这种资源类型来部署。那么问题来了:挖掘机技术哪家强?额,不对。算法
如何在 Rainbond 使用 StatefulSet 资源类型来部署服务呢?sql
组件部署类型
经过在服务组件的其余设置中,更改 组件部署类型 便可选择使用 StatefulSet 资源类型部署服务,操做以前要注意如下几点:数据库
- 组件须要处于关闭的状态;
- 对于有持久化存储的服务组件,切换组件部署类型会致使存储挂载的变动,必定要作好数据备份;
Rainbond 默认提供四种组件部署类型:后端
- 有状态单实例:使用 StatefulSet 部署服务,不能够进行实例的横向伸缩,实例数量始终为1;
- 有状态多实例:使用 StatefulSet 部署服务,实例数量能够进行横向伸缩;
- 无状态单实例:使用 Deployment 部署服务,不能够进行实例的横向伸缩,实例数量始终为1;
- 无状态多实例:使用 Deployment 部署服务,实例数量能够进行横向伸缩;
当你在 Rainbond 中将组件部署类型指定为有状态 (StatefulSet) 以后,服务组件将体现如下特性:架构
- 多实例状态下,全部实例将具有顺序性,实例的命名将相似于
gr6ec114-0
gr6ec114-1
,这一顺序性将体现为全生命周期的层面,顺序的启动、更新、重启、关闭。 - 上述的主机名在集群中将能够被解析,同团队下,尝试在任意 POD 中执行
nslookup gr6ec114-0
。不一样团队下,须要指定命名空间,可解析地址的彻底地址为:gr6ec114-0.gr6ec114.3be96e95700a480c9b37c6ef5daf3566.svc.cluster.local
其中3be96e95700a480c9b37c6ef5daf3566
为命名空间。 - 多实例状态下,每一个实例的持久化存储将被单独挂载,这意味着持久化数据在实例之间再也不共享。
- 单实例状态下,执行更新操做时,实例将会在彻底关闭以后,启动新的实例,这意味着服务会出现中断。
- 出于对持久化数据一致性的保护,运行了有状态服务的 k8s 节点一旦失去和管理节点的联络,处于
notready
状态时,其有状态服务的实例不会自动迁移。
总体来看,利用 StatefulSet
资源类型来部署服务,带来了新的特性的同时,会显得呆板了一些,但接下来的探讨,会发现这些限制是有意义的。负载均衡
细心如你必定会发现,咱们将 StatefulSet
这种资源类型和 “有状态” 绑定在了一块儿。那么,一个新的问题冒了出来:什么是服务的 “状态”。框架
服务的“状态”
有状态(Stateful)服务 = 无状态(Stateless)的应用程序 + 有状态的数据less
从有状态服务的名字就能够看出, 它和 StatefulSet 这种资源类型是有关联的。分布式
单纯说概念,可能很难理解什么是有状态服务。让我来举几个例子:微服务
- 最多见的有状态服务,就是DB类的数据库中间件。
对于常见数据库 Mysql 而言,同一份数据,在同一时刻只能够被一个 Mysql 程序使用。Mysql 在启动后,会在本身的数据目录下生成惟一的锁文件,并把这个文件“锁死”。这样一来,其余想要使用这份数据的 Mysql 程序,会由于发现这个锁文件被“锁死”,而中断启动的过程。这样作的好处,是保证了数据的强一致性,由于同一份数据在同一时刻,绝对只会被同一个 Mysql 应用程序所读写。
请回忆下 StatefulSet
资源类型带来的特性之一就是每一个实例都会挂载独立的持久化存储,这样能够确保 Mysql 服务能够被扩展成多个实例运行起来,不会由于锁文件的缘由被终止启动,可是由于彼此之间数据不共享,因此本质上实例之间没有什么关系。使用有状态单实例的方式运行 Mysql 看起来是最正确的选择。
状况相似的常见数据库中间件还有 Mongo、Postgresql、Redis、Etcd等。
- 另外一种常见的有状态服务场景,是 Web 类的服务提供的粘性
Session
。
这种粘性 Session
在某些状况下会保存在内存中,用来提供会话保持,自己也是一种数据。一旦将这种服务扩展多个实例,一旦访问到不正确的实例,那么就会由于找不到 Session
而丢失登录态。在负载均衡中使用 IP Hash 算法进行流量的分发能够在某种程度上解决这个问题,来自同个 IP 的流量会被分发到指定的实例。可是咱们更但愿流量的分发是轮询的,这样能够确保每一个实例的负载都是相近的,不会出现某个实例负载太高,而其余实例无所事事的状况。
这两种有状态服务场景,都向咱们指出,对有状态服务而言,不一样实例的数据是相互独立的。数据即“状态”。
相比较而言,无状态的服务就灵活不少。它们没有持久化数据,或者持久化数据支持共享。对于客户端而言,请求哪个实例得到的返回都是一致的。这样的特性意味着能够随意扩展无状态服务的实例数量,灵活的应对流量。
使用云服务最大的好处之一,就是它提供的弹性和灵活性,在业务遭遇流量高峰时,能够快速扩展实例进行应对。从这个角度出发,咱们但愿服务都是 “无状态” 的。那么,一个新的问题冒了出来:咱们能够去掉服务的 “状态”,使之变成无状态服务么?
处理服务的 “状态”
利用粘性 Session 保持登录态的这类 Web 服务,其状态是能够被去掉的。
原理比较简单,把 Session 和 Web 应用程序剥离,存储到其余中间件中去便可,好比保存到Mysql、 Redis、Memcached等数据库中间件中去。市面上常见的 Web 框架都会支持这种功能,甚至把这种处理方式做为默认选项,由于这实在太棒了!
处理完的 Web 服务,就变成了无状态服务,能够任意扩展实例数量了。来自客户端的请求不管被分配到哪个实例,其登录态都到后端数据库中调取,返回正确的登录态。在部署时,能够选择无状态多实例进行部署,即便用 Deployment
这种资源类型。
可是对于DB类的数据库中间件而言,其状态是不能够被随意去除的。
缘由在于这类数据库中间件使用本身的机制来确保数据强一致性,就好比 Mysql 的锁文件机制,指定的实例只能去读写对应本身的那一份数据。对这一类有状态服务而言,每一个实例独享一份持久化数据能够算做是必须的条件。而且随意扩展实例数量,会遭遇不少致命的问题:好比数据不一致,或者程序运行失败等等。这一类的有状态服务只能单点部署吗?
这些数据库中间件的出品厂商或者社区,也都很关注如何实现高可用方案,来解决上述的问题。甚至近些年推出的数据库中间件,在设计阶段就会被设计成分布式架构。好比 Etcd 对本身的定义就是:可靠的强一致性分布式键值数据库。其内部使用 Raft 协议进行实例间选举来明确统一的leader。而对于 Mysql 这样比较老牌的数据库中间件,也具有基于 Binlog 复制实现的主从集群方案。
因此针对这一类没法去除状态的服务而言,咱们的思路与宗旨,就是遵循其自身支持的集群方案,来实现高可用以及实例数量扩展。
实际部署这些集群方案时,能够总结出,大多数集群方案须要知足如下条件:
- 每一个实例挂载单独的持久化数据;
- 实例间须要获取彼此的通讯地址,来进行选举或者数据同步等动做,好比可解析的主机名或域名。获取地址时必定要使用主机名或域名而非实例 IP,由于随着实例的重启,主机名或域名不会改变,可是IP可能会改变,这很重要;
- 实例数量是有要求的,通常状况下选择 三、五、7··· 等奇数,来保证集群不会出现脑裂;
回想一下 StatefulSet
资源类型的特性,它能够知足上述的全部条件,就是为了有状态服务而生的。因此这一类有状态服务,其组件部署类型不管如何要使用有状态单/多实例。
Rainbond 云原生应用管理平台,实现微服务架构不用改代码,管理 Kubernetes 不用学容器,帮企业实现应用上云,一站式将任何企业应用持续交付到 Kubernetes 集群、混合云、多云等基础设施。是 Rainstore 云原生应用商店的支撑平台。
本文做者:郭逊