V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
chesha1
V2EX  ›  Kubernetes

如何优雅地处理 k8s 中滚动更新带来的延时任务丢失的问题

  •  
  •   chesha1 · 2024-05-09 11:29:26 +08:00 · 2277 次点击
    这是一个创建于 405 天前的主题,其中的信息可能已经有所发展或是发生改变。
    比如一个 pod 中有一个 30 分钟后开始执行的任务,这个时候更新 deploy ,这个任务就没了

    比较简单粗暴的做法:
    1. 让 pod 等 30 分钟再关闭,但是如果 30 分钟内又有 deploy 更新,会有问题
    2. 把这个延时任务放到第三方组件里持久化,后面再取出来执行

    但是感觉都不太优雅,有没有什么方法,不需要第三方组件,能用 k8s 本身的机制,或者在服务内部解决这个问题?
    18 条回复    2024-05-13 12:22:51 +08:00
    Chad0000
        1
    Chad0000  
       2024-05-09 11:31:17 +08:00   ❤️ 1
    任务同时保存在 DB 中,创建和执行都会记录,启动时从 DB 中读取。目前我的任务就是这样设计的。
    codehz
        2
    codehz  
       2024-05-09 11:36:44 +08:00
    不是弄个事件队列解决的吗,处理好的才标记成功,不成功就按策略重试
    nicoaz
        3
    nicoaz  
       2024-05-09 11:42:09 +08:00   ❤️ 1
    首先,pod 最好是无状态,在 k8s 里更新、扩容、重启都方便。
    第二,没有持久化的话,本身这个程序也不可靠,总会遇到异常崩溃吧。
    单纯想更新后不丢任务,可以让程序监听到 terminal 后主动找个地方存一下,启动时再去读。
    rrfeng
        4
    rrfeng  
       2024-05-09 11:54:26 +08:00 via Android
    直接用 k8s 自带的 cronjob 类型部署
    mightybruce
        5
    mightybruce  
       2024-05-09 12:00:36 +08:00
    业务代码角度
    任务状态和任务信息保存在 DB 中, 修改代码逻辑,在服务里面加一些请求处理,服务增加优雅关闭处理,能够处理 sigterm 的信号。

    从 k8s 角度
    使用自定义钩子
    Deployment 资源支持以下几种钩子:

    PostStart 钩子:在容器启动后立即执行。这通常用于执行容器启动后的初始化任务,例如等待其他服务启动、注册服务到服务发现系统等。
    PreStop 钩子:在容器关闭之前执行。这通常用于执行容器关闭前的清理任务,例如保存数据、关闭连接、发送信号给其他进程等。

    在关闭之前执行一些持久化操作
    比如 可以执行一个 HTTP GET 请求 或者 exec 命令,并且它们执行是阻塞的,可以利用这个特性来做优雅停止。

    调用 HTTP GET

    spec:
    contaienrs:
    - name: my-container
    lifecycle:
    preStop:
    httpGet:
    path: "/stop"
    port: 8080
    scheme: "HTTP"

    修改 terminationGracePeriodSeconds , 将它调整到合适的值,不要过大。
    terminationGracePeriodSeconds 等于 服务优雅退出超时时间和 preStopHook 之和
    mightybruce
        6
    mightybruce  
       2024-05-09 12:01:40 +08:00
    如果是一些计划任务,建议用 k8s 的 cronjob 来处理
    nicoaz
        7
    nicoaz  
       2024-05-09 12:03:43 +08:00
    @nicoaz 更正下 terminate 😅
    guanzhangzhang
        8
    guanzhangzhang  
       2024-05-09 14:11:34 +08:00
    看看能不能分段拆分下,就像更新 10w 条记录,不应该一次全部更新,而是分段更新和加记录 cursor ,这样被中断后后续可以继续执行
    standchan
        9
    standchan  
       2024-05-09 14:21:17 +08:00
    你定时设定的不对。要么用 cronjob ,要么用 db 保存上一个任务执行时间。
    LoliconInside
        10
    LoliconInside  
       2024-05-09 14:25:21 +08:00
    把这部分逻辑抽出来单独做一个模块使用 cronjob 运行,deployment 中的应用应当确保无状态
    ZeroAsh
        11
    ZeroAsh  
       2024-05-09 14:41:53 +08:00
    任务如果能做成可以断点续跑的,pod 随时干掉也没问题。


    如果不能做成断点续跑的,就设置一个 30 分钟的 gracefully exit 时间,rollout 的时候印象中应该会等 pod 自己退出的。

    但这个方案应该会一个特殊场景会有问题,某次 rollout 的时候 pod 还没 ready ,就开始执行任务了,然后紧接着又 rollout 了一个,这个时候我印象不会等 gracefully exit 直接会干掉 pod 。
    justsomac
        12
    justsomac  
       2024-05-09 15:00:30 +08:00
    所以是 pod 重启会影响业务? k8s 中 pod 随时可能被重启的
    mooyo
        13
    mooyo  
       2024-05-09 15:03:40 +08:00
    你看下 pod 的 lifecycle 啊,pod 退出的时候会给你的程序发个信号,你收到信号的时候把任务取出来塞回队列里,新的 pod 再去队列拿回来就行了吧。
    tinyzilan123
        14
    tinyzilan123  
       2024-05-09 15:08:16 +08:00
    @mightybruce #5 这样就为服务增加状态了,而且很复杂
    ZXiangQAQ
        15
    ZXiangQAQ  
       2024-05-09 16:03:25 +08:00
    任务保持在 DB 中,存储任务状态和创建时间,然后服务内部开个线程轮询 30 分钟以前的未执行的任务,更新状态为执行中,更新成功的就本地执行,更新不成功的跳过,有可能被其他 Pod 抢到了,这块的处理是上锁最好,redis 锁或者 db 锁都行
    AceDogs
        16
    AceDogs  
       2024-05-10 10:35:49 +08:00
    这个问题不应该和 k8s 甚至任何环境有什么关系。 应该是系统设计问题,重新设计这个功能的实现吧。
    yyttrr
        17
    yyttrr  
       2024-05-13 11:04:29 +08:00
    我这里部署系统的设计是一个代码仓库打出来一个镜像,这个镜像有不同的运行方式,可以是无状态 rpc/http 使用 deployment ,也可以是有状态 binlog 使用 statefulset ,还可以是 Cronjob ,每次部署最小单元是一种部署方式
    julyclyde
        18
    julyclyde  
       2024-05-13 12:22:51 +08:00
    显然选 2
    如果用 cronjob ,则你的应用程序和 k8s 有耦合。将来万一 k8s 没落了呢
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5364 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 07:12 · PVG 15:12 · LAX 00:12 · JFK 03:12
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.