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

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

  •  
  •   chesha1 · 11 天前 · 1325 次点击
    比如一个 pod 中有一个 30 分钟后开始执行的任务,这个时候更新 deploy ,这个任务就没了

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

    但是感觉都不太优雅,有没有什么方法,不需要第三方组件,能用 k8s 本身的机制,或者在服务内部解决这个问题?
    18 条回复    2024-05-13 12:22:51 +08:00
    Chad0000
        1
    Chad0000  
       11 天前   ❤️ 1
    任务同时保存在 DB 中,创建和执行都会记录,启动时从 DB 中读取。目前我的任务就是这样设计的。
    codehz
        2
    codehz  
       11 天前
    不是弄个事件队列解决的吗,处理好的才标记成功,不成功就按策略重试
    nicoaz
        3
    nicoaz  
       11 天前
    首先,pod 最好是无状态,在 k8s 里更新、扩容、重启都方便。
    第二,没有持久化的话,本身这个程序也不可靠,总会遇到异常崩溃吧。
    单纯想更新后不丢任务,可以让程序监听到 terminal 后主动找个地方存一下,启动时再去读。
    rrfeng
        4
    rrfeng  
       11 天前 via Android
    直接用 k8s 自带的 cronjob 类型部署
    mightybruce
        5
    mightybruce  
       11 天前
    业务代码角度
    任务状态和任务信息保存在 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  
       11 天前
    如果是一些计划任务,建议用 k8s 的 cronjob 来处理
    nicoaz
        7
    nicoaz  
       11 天前
    @nicoaz 更正下 terminate 😅
    guanzhangzhang
        8
    guanzhangzhang  
       10 天前
    看看能不能分段拆分下,就像更新 10w 条记录,不应该一次全部更新,而是分段更新和加记录 cursor ,这样被中断后后续可以继续执行
    standchan
        9
    standchan  
       10 天前
    你定时设定的不对。要么用 cronjob ,要么用 db 保存上一个任务执行时间。
    LoliconInside
        10
    LoliconInside  
       10 天前
    把这部分逻辑抽出来单独做一个模块使用 cronjob 运行,deployment 中的应用应当确保无状态
    ZeroAsh
        11
    ZeroAsh  
       10 天前
    任务如果能做成可以断点续跑的,pod 随时干掉也没问题。


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

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