K8S – 如何部署crontab

应用迁移K8S,不可忽略的就是crontab怎么迁移上去。

传统linux crontab可以满足下面的需求:

  • 避免任务并发执行(通过文件锁可以搞定)
  • 磁盘上可能写入有状态的文件

那么K8S呢?下面介绍2个方案。

cronjob方案

K8S支持cronjob资源类型,它可以满足:

  • 支持并发控制,前一个任务没跑完,这次会跳过

但是无法满足有状态的东西,因为POD执行完就退出了。

关于状态的问题,我们可以通过挂载volume解决,这个大家可以基于云平台或者自建的分布式存储来支持。

那么cronjob好不好用呢?首先我们明确,一个定时任务需要配置一个cronjob,如果定时任务数量特别多,那么就会遇到下面2个问题:

  • 发布应用的时候,需要循环推送大量的cronjob YAML到K8S,很容易出现异常。
  • 资源分配需要具体到每个cronjob(当然不限制POD资源也是可以的),这非常难于评估。
  • cronjob执行失败的具体原因很难得知,因为POD跑完就退了,留不下任何痕迹。

上述3个问题是我放弃cronjob的主要原因。

deployment方案

该方案就是基于deployment来执行定时任务。

我们传统linux crontab都是放在虚拟机或者物理机上跑,迁移到deployment的方式就相当于启动一个常驻的POD,里面还是基于crontab调度若干定时任务,区别并不大,但带来的好处就很明显了:

  • 多条crontab可以放到同一个POD内执行,发布的时候只需要推送一次deployment YAML。
  • 资源分配可以针对一批crontab进行整体评估,原先虚拟机/物理机用多少就可以分配多少。
  • 因为deployment拉起的POD是常驻的,所以crontab执行留下的输出可以登录到POD查看。

相比于cronjob方案,优势非常明显。

为了在发布平台里实现自动化的定时任务deployment发布,我们需要一点巧妙的设计。

将cron表达式放入POD内

为了执行定时任务,我们需要把cron表达式放到POD里面,才能交给linux crontab调度。

那么cron表达式如何放入POD呢?

  • 也许可以把cron表达式配置到POD的env中
  • 也许可以配置一个configmap保存cron表达式,再通过volume挂载到POD内。

我选择的是后一种。

在docker环境下的pid=1进程不是systemd,所以crond守护进程默认是没有拉起的。

我们需要在POD的启动命令中主动加载crontab文件,并拉起crond进程:

其中dot_cron文件中记录的是cron表达式,我在deployment YAML中配置了configmap volume挂载:

挂载的xxxe-service-cron-1是一个ConfigMap,它长这样:

复用web镜像

作为一个发布平台来说,原先已经构建了针对应用的web发布镜像,如何将其直接复用到crontab场景呢?

我们只需要编写一个用于crontab的deployment YAML,配置上述的cron表达式挂载,改变POD的启动命令即可实现,完全不必为了执行crontab而为应用专门订制镜像。

避免并发

首先为deployment配置replicas为1,这样就只会有1个POD执行linux crontab。

但仅仅这样不够,因为当deployment滚动升级的时候,一定会先创建新POD再杀死旧POD,所以我们需要配置deployment YAML中的滚动升级策略:

这样它会先杀死旧的POD,再启动新的POD。

避免发布应用导致任务异常中断

我们一般为了如下2个原因发布应用:

  • 镜像升级,通常也就是代码要升级
  • 调整POD数量

第一种情况一定要重启所有POD的,所以也会中断crontab depoyment中正在运行中的任务。

第二种情况不会影响到crontab deployment。

针对第一种情况,我们肯定希望能优雅一点,我目前想到的做法只能是自己实现一个cron调度器取代linxu crontab,这样可以在POD退出前自定义优雅退出逻辑,避免任务被POD退出而异常中断。

更新cron表达式触发deployment滚动

默认我们更新configmap后,会立即反馈变化到POD中。

但是我目前仅仅在容器启动的时候才会加载一次cron表达式文件,那么我后续更新configmap肯定是不会立即生效的。

修改完configmap后再发布一次deployment YAML也是没用的,因为deployment YAML没有任何改变。

因此,我在发布crontab deployment YAML的时候,将cron表达式文件做了一个hash值,作为deployment POD的一个env环境变量,这样每次点发布按钮的时候,就可以确保congimap变化后deployment也可以得到滚动升级。

参考资料

https://github.com/bambash/kube-cron-example

如果文章帮到了你,请您乐于扫码捐赠1元钱,以便支持服务器运转。

发表评论

电子邮件地址不会被公开。