
Kubernetes正在成为运行无状态应用程序的事实上的标准。 主要是因为它可以大大减少新功能交付的上市时间。 启动有状态的应用程序-数据库,有状态的微服务-仍然是一项艰巨的任务,但是承受竞争和保持高交付率的需求促使公司在这一领域进行试验,并产生了对此类解决方案的需求。
我们向您介绍用于启动有状态集群的解决方案
Tarantool Cartridge :
Tarantool Kubernetes Operator ,有关详细信息,我请教。
目录:
- 而不是一千个字
- 操作员完全做什么
- 关于细微差别
- 操作员如何工作
- 运营商的扩展
- 总结
Tarantool是一个软件包中的开源数据库和应用程序服务器。 作为数据库,它具有许多独特的特性:铁的高效利用,灵活的数据方案,对内存和磁盘存储的支持以及通过使用Lua语言进行扩展的可能性。 作为应用程序服务器,它使您可以将应用程序代码移到尽可能靠近数据的位置,同时实现最短的响应时间和最大的吞吐量。 此外,Tarantool拥有
广泛的生态系统 ,提供用于解决应用程序问题的现成模块:
分片 ,
队列 ,促进开发的模块(
盒式磁带 ,
luatest ),操作解决方案(
度量标准 ,
ansible )-这些仅是示例。
尽管具有所有优点,但单个Tarantool实例的功能并非无限:存储数TB的数据并处理数百万个请求,您需要引发数十个和数百个实例,这是一个分布式系统,存在所有固有的问题。 为了解决这些问题,我们有一个
Tarantool Cartridge框架,其主要任务是隐藏编写分布式应用程序时遇到的各种困难,并使开发人员可以专注于应用程序的业务价值。 Cartridge提供了一组功能强大的组件,用于自动集群流程,自动数据分发,用于操作的webui和开发人员工具。
Tarantool不仅是技术,还是一支工程师团队,致力于开发整套企业系统,盒式解决方案以及对开源组件的支持。
在全球范围内,我们的任务可以分为两个领域:新系统的开发和现有解决方案的扩充。 例如,有一个著名供应商的庞大基地。 为了扩展它以进行读取,他们在Tarantool上放置了一个最终一致的缓存。 反之亦然:为了扩展记录,他们将Tarantool置于热/冷配置中,在此过程中,当他们“冷却”数据时,会将其转储到冷存储中并与分析队列并行。 或者,为了备份现有系统,编写了该系统的精简版(功能储备),该储备保留了主“热”数据并从主系统复制数据。 更多信息可以在
T + 2019的
报告中找到。
所有这些系统都有一个共同点:它们很难操作。 快速将包含100多个实例的集群冗余部署到3个数据中心,更新应用程序以存储数据,而不会造成停机和维护损失,在发生灾难或人为错误的情况下进行备份还原,确保组件进行谨慎的故障转移,组织配置管理...总体而言,很多有趣。
如果所有这些都仍像开发中一样简单地操作,那就太好了。 Kubernetes可以实现期望的结果,但是使用专业的操作员可以使生活更加轻松。
而不是一千个字
我们准备了一个基于Tarantool弹药筒的小例子,我们将使用它。 一个简单的应用程序,例如“具有HTTP接口的分布式键值存储”。 启动后,我们得到以下图片:

其中:
- 路由器-群集中负责接受和处理传入HTTP请求的部分;
- 存储是集群中负责存储和处理数据的部分,每个主副本和副本中都有3个分片。
为了平衡路由器上的传入HTTP流量,使用了Kubernetian入口。 使用
vshard组件,可以
在Tarantool本身的级别将数据分发到存储中。
我们需要kubernetes 1.14 +,minikube可以
工作 。 同样,
kubectl的可用性也不会受到损害。 要启动操作员,您需要为其创建ServiceAccount,Role和RoleBinding:
$ kubectl create -f https://raw.githubusercontent.com/tarantool/tarantool-operator/0.0.1/deploy/service_account.yaml $ kubectl create -f https://raw.githubusercontent.com/tarantool/tarantool-operator/0.0.1/deploy/role.yaml $ kubectl create -f https://raw.githubusercontent.com/tarantool/tarantool-operator/0.0.1/deploy/role_binding.yaml
Tarantool Operator通过其资源定义扩展了Kubernetes API,我们将创建它们:
$ kubectl create -f https://raw.githubusercontent.com/tarantool/tarantool-operator/0.0.1/deploy/crds/tarantool_v1alpha1_cluster_crd.yaml $ kubectl create -f https://raw.githubusercontent.com/tarantool/tarantool-operator/0.0.1/deploy/crds/tarantool_v1alpha1_role_crd.yaml $ kubectl create -f https://raw.githubusercontent.com/tarantool/tarantool-operator/0.0.1/deploy/crds/tarantool_v1alpha1_replicasettemplate_crd.yaml
一切准备就绪,可以启动操作员,让我们开始:
$ kubectl create -f https://raw.githubusercontent.com/tarantool/tarantool-operator/0.0.1/deploy/operator.yaml
我们正在等待操作员启动,我们可以继续启动该应用程序:
$ kubectl create -f https://raw.githubusercontent.com/tarantool/tarantool-operator/0.0.1/examples/kv/deployment.yaml
在带有示例的yaml文件中,Ingress在Web UI上声明; 在
cluster_ip/admin/cluster
上可用。 当至少有一个来自Ingress的Pod启动时,您可以去那里,观察如何将新实例添加到集群中以及其拓扑结构如何变化。
我们正在等待集群的使用:
$ kubectl describe clusters.tarantool.io examples-kv-cluster
我们希望在“群集状态”中包含以下内容:
… Status: State: Ready …
一切就绪,该应用程序即可使用!
需要更多的存储空间? 添加分片:
$ kubectl scale roles.tarantool.io storage --replicas=3
分片不能应付负荷吗? 通过编辑副本集模板,增加分片中的实例数:
$ kubectl edit replicasettemplates.tarantool.io storage-template
设置
.spec.replicas
(例如2),以将每个副本集中的实例数增加到两个。
不再需要集群吗? 我们将其与所有资源一起删除:
$ kubectl delete clusters.tarantool.io examples-kv-cluster
出问题了吗?
拿到票后 ,我们会迅速拆卸。 :)
操作员完全做什么
Tarantool Cartridge集群的启动和操作就是在特定时刻以特定顺序执行特定动作的故事。
群集本身主要通过admin API:基于HTTP的GraphQL进行管理。 当然,您可以降低级别并将命令直接驱动到控制台,但这很少发生。 例如,这是集群启动的方式:
- 例如,在systemd下,我们提高了所需的Tarantool实例数量。
- 将实例合并为成员:
mutation { probe_instance: probe_server(uri: "storage:3301") }
- 将角色分配给实例,规定实例和副本集标识符。 GraphQL API也用于此目的:
mutation { join_server( uri:"storage:3301", instance_uuid: "cccccccc-cccc-4000-b000-000000000001", replicaset_uuid: "cccccccc-0000-4000-b000-000000000000", roles: ["storage"], timeout: 5 ) }
- 我们执行负责分片的组件的引导。 同样通过API:
mutation { bootstrap_vshard cluster { failover(enabled:true) } }
容易吧?
扩展集群时,一切都会变得更加有趣。 该示例中路由器的角色可以简单地扩展:增加更多实例,将它们挂接到现有群集上-您完成了! 存储的角色有些棘手。 存储是分片的,因此在添加/删除实例时,有必要重新平衡数据以移至新实例/从已删除实例移出。 如果不这样做,则在一种情况下,我们将获得实例不足的负载;在第二种情况下,我们将丢失数据。 如果不是只有一个,而是十二个具有不同拓扑结构的集群正在运行?
通常,Tarantool操作员会忙于所有这一切。 用户描述了Tarantool Cartridge集群的期望状态,并且操作员将其转换为对k8s资源的一组操作,并在特定时刻将其转换为以特定顺序对Tarantool集群admin API的某些调用,并且通常尝试向用户隐藏所有细微差别。
关于细微差别
使用Tarantool Cartridge管理集群API时,调用的顺序及其到来的位置都很重要。 为什么这样
Tarantool Cartridge带有其拓扑库,其服务发现组件和配置组件。 群集的每个实例在yaml文件中存储拓扑和配置的副本。
servers: d8a9ce19-a880-5757-9ae0-6a0959525842: uri: storage-2-0.examples-kv-cluster:3301 replicaset_uuid: 8cf044f2-cae0-519b-8d08-00a2f1173fcb 497762e2-02a1-583e-8f51-5610375ebae9: uri: storage-0-0.examples-kv-cluster:3301 replicaset_uuid: 05e42b64-fa81-59e6-beb2-95d84c22a435 … vshard: bucket_count: 30000 ...
更新使用
两阶段提交机制一致进行。 成功的升级需要100%的仲裁:每个实例必须响应,否则将回滚。 就操作而言,这意味着什么? 向admin API修改集群状态的所有请求最可靠地发送给一个实例,再发送给领导者,否则我们冒着在不同实例上获得不同配置的风险。 Tarantool弹药筒不知道如何进行领导者选举(但还不知道如何进行),而Tarantool操作员可以-并且您只能将其视为有趣的事实,因为操作员会破坏一切。
而且,每个实例必须具有固定的标识,即一组
instance_uuid
和
replicaset_uuid
以及
advertise_uri
。 如果存储突然重新启动,并且其中一个参数发生更改,则可能会导致仲裁中断-操作员也会这样做。
操作员如何工作
操作员的任务是使系统进入用户设置的状态,并将系统保持在此状态,直到收到新的指示为止。 为了使操作员能够执行其工作,他需要:
- 系统状态的描述。
- 使系统进入此状态的代码。
- 一种将此代码集成到k8中的机制(例如,接收状态变化的通知)。
通过
自定义资源定义(CRD)以k8来描述Tarantool弹药群。 操作员需要3个这样的自定义资源,这些资源合并在tarantool.io/v1alpha组中:
- 群集是与一个Tarantool Cartridge群集相对应的顶级资源。
- 角色-就Tarantool弹药筒而言,这是一个用户角色 。
- ReplicasetTemplate-用于创建StatefulSets的模板(为什么有状态-我稍后再告诉您;不要与k8s ReplicaSet混淆)。
所有这些资源都直接反映了Tarantool Cartridge集群描述模型。 有了通用的字典,操作员可以更轻松地与开发人员进行交流并了解他们希望在产品中看到的内容。
使系统进入给定状态的代码-就k8而言,它是控制器。 对于Tarantool Operator,有几个控制器:
- ClusterController-负责与Tarantool Cartridge集群进行交互,将实例连接到集群,并断开实例与集群的连接。
- RoleController是一个用户角色控制器,它负责从模板部署StatefulSet并将其编号保持在给定的数量。
控制器是什么样的? 一组代码,可以逐步使您周围的世界井井有条。 可以这样示意性地描绘ClusterController:

入口点是检查事件发生的群集资源是否存在的检查。 不存在吗? 我们要走了 有吗 我们继续进行下一个步骤:获取用户角色的所有权。 捕获了一个-左,在第二个圆上我们捕获了第二个。 依此类推,直到我们捕获所有内容。 是否捕获了所有角色? 因此,转到下一个操作块。 这样,直到我们走到最后。 那么我们可以假设受控系统处于给定状态。
总的来说,一切都很简单。 确定通过每个阶段的成功标准很重要。 例如,我们认为成功加入集群的操作不是在返回条件成功= true时发生,而是在返回诸如“已加入”之类的错误时进行。
该机制的最后一部分是将控制器与k8s集成在一起。 在鸟瞰图中,整个k8包含一组控制器,这些控制器生成事件并对其进行响应。 事件经过我们可以订阅的队列。 可以用以下方式表示:

用户调用
kubectl create -f tarantool_cluster.yaml
,将
kubectl create -f tarantool_cluster.yaml
相应的群集资源。 通知ClusterController创建集群资源。 他要做的第一件事是找到所有应该属于此集群的角色资源。 如果找到,则将“群集”分配为“角色”的“所有者”并更新“角色”资源。 RoleController接收到一个Role更新通知,看到该资源具有Owner,并开始创建StatefulSets。 依此类推,依次为:第二个的第一个触发器,第三个的第二个触发器,依此类推,直到有人停下来。 您还可以按时触发,例如每5秒触发一次,这有时很有用。
这就是整个操作员:创建自定义资源并编写代码以响应资源上的事件。
运营商的扩展
操作员的行动最终导致k8创建Pod和容器。 在部署在k8上的Tarantool Cartridge集群中,所有Pod都合并到StatefulSets中。
为什么选择StatefulSet? 如我之前所写,每个Tarantool集群实例都保留一个集群拓扑和配置的副本,并且在应用服务器上通常不行,不行,并且它们使用某种空间,例如依次或引用数据,这已经是完整状态。 而且StatefulSet还保证了身份Pod的保存,这在群集实例中群集时很重要:实例的身份必须固定,否则重新启动时有失去仲裁的风险。
创建所有群集资源并将其置于所需状态后,它们将形成以下层次结构:

箭头指示资源之间的所有者依赖关系。 例如,在删除群集的情况下,有必要在我们之后清理
垃圾收集器 。
除了StatefulSets,Tarantool运算符还创建了Headless选举所需的Headless服务,并通过该实例相互通信。
Tarantool Operator的背后是
Operator Framework ,该操作员代码本身在golang中,在这里没什么特别的。
总结
一般而言,仅此而已! 我们正在等待您的反馈和门票-如果没有它们,alpha版本将完全相同。 接下来是什么? 然后有很多工作要牢记在心:
- 单元,端到端测试;
- 混沌猴子测试
- 压力测试;
- 备份/还原;
- 外部拓扑提供程序。
这些主题中的每个主题本身都是广泛的,应该使用单独的材料,请等待更新!