最近在公司的数据同步我的项目(以下简称 ZDTP)中,须要应用到散布式调度数据同步执行单元,目前应用的计划是将数据同步执行单元打包成镜像,应用 K8s 进行调度。
在 ZDTP 中,数据同步的动作可形象成一个执行单元(以下称为 worker),相似于线程执行单元 Runnable,Runnable 放入一个队列中期待线程的调度执行,执行完 Runnable 即实现了它的使命。当用户在 ZDTP 控制台中创立同步工作并启动工作时,会依据同步工作的配置,产生若干个用于该工作的 worker,假如这些 worker 都在本地执行,能够将其包装成一个 Runnable,而后创立一个线程执行,如下图示意:
然而在单机模式下,就会遇到性能瓶颈,此时就须要散布式调度,将 worker 调度到其余机器执行:
问题是咱们如何将 worker 更好地调度到其它机器中执行呢?
Worker 部署形式调研
1、基于虚拟机部署 Worker
Worker 在提前创立好的虚拟机中运行,工作启动时须要依据以后 Worker 负载状况进行抉择闲暇的 Worker,相当于 Worker 是以 Agent 的模式运行,如下图示意:
随同而来的毛病次要有以下几点:
- Worker Agent 数量绝对固定,虚拟机创立老本高,扩 / 缩容麻烦;
- 工作运行状况依赖 zk 监听机制,如果某个工作在运行中挂掉了,须要自行实现故障转移与主动重启机制,减少开发周期;
- Worker Agent 负载获取逻辑须要我的项目实现,准确获取负载信息实现难度大,减少开发周期。
2、基于 K8s 部署 Worker
将 Worker 打包成 Docker 镜像,应用 K8s 对 worker 容器进行调度作业,并且一个 Worker 只运行一个工作,如下图示意:
应用 K8s 的长处如下:
- 应用 K8s 集群调度的 Worker 容器具备故障复原性能,只有将 Pod 的重启策略设置为 restartPolicy=Always,无论 Worker 容器在运行过程中产生什么异样,K8s 都会主动尝试重启 Worker 容器,大大减少了运维老本,进步了数据同步的高可用性;
- 主动实现负载,比方当某个节点负载高,就会将 Worker 容器调度到负载低的节点上,更重要的是,某个节点宕机,其上的工作负载会被 K8s 主动将其转移到其它节点下面;
- Worker 的生命周期齐全交由 K8s 集群治理,只需调用相干接口即可分明 Worker 运行状况,大大减少开发周期。
K8s 容器调度计划调研
K8s 集群的调度对象是 Pod,调度形式有多种,这里次要介绍以下几种形式:
1、Deployment(全自动调度)
在讲 Deployment 前,先来说下 Replica Set,它是 K8s 一个十分重要的概念,它是在 Pod 这个形象上更为下层的一个形象,个别大家用 Deployment 这个形象来做利用的真正的治理,而 Pod 是组成 Deployment 最小的单元。它能够定义某种 Pod(比方包装了 ZDTP Worker 容器的 Pod)在任意时刻都放弃合乎 Replica Set 设定的预期值,比方 Replica Set 可预期设定 Pod 正本数,当 k8s 集群定期巡检发现某种 Pod 的正本数少于 Replica Set 设定的预期值,它就会依照 Replica Set 设定的 Pod 模版创立 Pod 实例,使得 Pod 的数量维持在预期值,也是通过 Replica Set 的个性,实现了集群的高可用性,同时缩小了运维老本。
Deployment 外部应用了 Replica Set 来实现,他们之间高度类似,也能够将 Deployment 看作是 Replica Set 的降级版本。
2、Job(批处理调度)
咱们能够通过 k8s Job 资源对象定义并启动一个批处理工作,并行或者串行解决一批工作项(Work item),解决实现后工作就完结。
1)Job Template Expansion 模式
依据 ZDTP Worker 运行形式,咱们能够应用一个 Job 对像对应一个 Worker,有多少个 worker 就创立多少个 Job,除非 Pod 异样,才会重启该 Pod,失常执行完后 Job 就退出,如下图示意:
2)Queue with Pod Per Work Item 模式
这种模式将客户端生成的 worker 寄存在一个队列中,而后只会创立一个 job 去生产队列中的 worker item,通过设置 parallelism 参数能够同时启动多少个 worker Pod 同时解决 worker,值得一体的是,这种模式下的 Worker 处理程序逻辑只会从队列拉取 worker 解决,解决完就立刻退出,completions 参数用于管制失常退出的 Pod 数量,当退出的 Pod 数量达到了 completions 后,Job 完结,即 completions 参数能够管制 Job 的解决 Worker 的数量。如下图所示:
3)Queue with Variable Pod Count 模式
这种调度模式看起来跟 Queue with Pod Per Work Item 模式差不多,其实不然,Queue with Variable Pod Count 模式的 Job 只有有一个 Pod 失常退出,即阐明 Job 曾经解决完数据,处于终止状态了,因为它的每个 Pod 都有查问队列是否还有 worker 的逻辑,一旦发现队列中没有了 worker,Pod 失常退出,因而 Queue with Variable Pod Count 模式 completions 参数只能设置 1,parallelism 参数能够同时启动多少个 worker Pod 同时解决 worker。
这种模式也要求队列可能让 Pod 感知是否还存在 worker,像 RocketMQ/Kafka 之类的消息中间件并不能做到,只会让客户端始终期待,因而这种模式不能选用 RocketMQ/Kafka,能够抉择数据库或者 Redis 来实现。如下图所示:
当然如果前面还有定时执行 Worker 的需要,应用 K8s 的 cronjob(定时任务调度)是一个十分好的抉择。
3、Pod(默认调度)
间接通过 kind=pod 的形式启动容器,这种形式不能设置容器的运行实例数,即 replicas = 1,通常生产利用集群都不会通过这个形式启动容器,因为这种形式启动容器不具备 Pod 主动扩缩容的个性。
值得一提的是,即便你的 Pod 正本只有 1 个,官网也举荐应用 Replica Set 的形式进行部署。
Pod 重启策略剖析
Pod 的重启策略包含 Always、onFailure、Never:
- Always:当容器生效时,k8s 主动重启该容器;
- onFailure:当容器终止运行时并且退出码不为 0 时,k8s 主动重启该容器;
- Never:不管容器运行状态如何,k8s 都不会重启该容器
Deployment/Replica Set 必须设置为 Always(因为它们都须要放弃 Pod 期待的正本数),而 Job 只能设置为 onFailure 和 Never,以确保容器执行实现后不再重启,间接 Pod 启动容器以上三个重启策略都能够设置。
这里须要阐明一点,如果应用 Job,状况可能略微简单些:
1)Pod 重启策略 RestartPolicy=Never
假如 Job 调度过程中 Pod 产生非正常退出,只管此时容器不再重启,因为 Job 须要至多一个 Pod 执行实现(即 completions 起码等于 1),Job 才算实现。因而,尽管非正常退出的 Pod 不再重启,但 Job 会尝试重新启动一个 Pod 执行,直到 Pod 失常实现的数量为 completions。
$ kubectl get pod –namespace zdtp-namespace
NAME READY STATUS RESTARTS AGE
zdtp-worker-hc6ld 0/1 ContainerCannotRun 0 64s
zdtp-worker-hfblk 0/1 ContainerCannotRun 0 60s
zdtp-worker-t9f6v 0/1 ContainerCreating 0 11s
zdtp-worker-v2g7s 0/1 ContainerCannotRun 0 31s
$ kubectl get pod –namespace zdtp-namespace
NAME READY STATUS RESTARTS AGE
zdtp-worker-5tbxw 0/1 CrashLoopBackOff 5 67s
因而,Job 目前还还不适宜调度 Worker Pod,全量同步 Worker 现阶段只适宜间接应用 Pod 进行部署,且须要设置 Pod 重启策略 RestartPolicy=Never。
全量同步 Worker 在数据同步完就退出,看起来 Job 调度或者间接创立 Pod 部署都能够满足,但现阶段因为全量同步临时没有记录同步进度,因而要求中途产生任何谬误容器退出后都不能主动重启,目前的做法是当 Worker 执行过程中产生非正常退出时,须要用户自行删除已同步的资源,再手动启动 Worker 再次进行全量同步。
2、全量同步 Worker
增量同步 Worker 会始终同步上来,中途不进行,这意味着 Pod 的重启策略必须为 RestartPolicy=Always,那么这种形式只能抉择 Deployment 调度或者间接创立 Pod 部署,但倡议应用 Deployment,官网曾经阐明了即便 Pod 正本为 1,仍然倡议应用 Deployment 进行部署。
1、增量同步 Worker
以上内容把 K8s 的调度计划与 Pod 的重启策略都钻研了一番后,接下来就须要针对我的项目的调度需要抉择适合的调度形式。
如何抉择 K8s 调度策略?
当 RestartPolicy=onFailure,Pod 产生非正常退出时,Pod 会尝试重启,直到该 Pod 失常执行实现,此时 Job 就不会重新启动一个 Pod 执行了,如下:
2)Pod 重启策略 RestartPolicy=onFailure
## 作者简介
作者张乘辉,善于消息中间件技能,负责公司百万 TPS 级别 Kafka 集群的保护,作者保护的公号「后端进阶」不定期分享 Kafka、RocketMQ 系列不讲概念间接真刀真枪的实战总结以及细节上的源码剖析;同时作者也是阿里开源分布式事务框架 Seata Contributor,因而也会分享对于 Seata 的相干常识;当然公号也会分享 WEB 相干常识比方 Spring 全家桶等。内容不肯定八面玲珑,但肯定让你感触到作者对于技术的谋求是认真的!
公众号:后端进阶
技术博客:https://objcoding.com/
GitHub:https://github.com/objcoding/