当前位置: 首页 > article >正文

k8s优雅重启

理论上处于terminating状态的pod,k8s 就会把它从service中移除了,只用配置一个优雅停机时长就行了。kubectl get endpoints 验证

因此,优雅重新的核心问题,是怎么让空闲长连接关闭,再等待处理中的请求执行完。
一些底层 HTTP 服务器(如 uvicorn),在收到SIGTERM 信号后会优雅地关闭进程,这包括清理所有的活动连接(包括空闲的 HTTP Keep-Alive 长连接),可以通过以下方法验证:

telnet <ip> <port>

# 输入以下内容按两次Enter
GET /health HTTP/1.1
Host: <ip>
Connection: keep-alive

你将看到正常的HTTP响应,且连接没有被关闭:

date: Fri, 24 Jan 2025 02:05:43 GMT
server: uvicorn
content-length: 4
content-type: application/json

"ok"

这个时候你去让这个pod处于terminating状态,你会发现这个连接被关闭了:Connection closed by foreign host.

简介

使用kubernetes启动容器时,一般都会配置一些探针来保证pod的健康,并通过terminationGracePeriodSeconds控制pod 在接收到终止信号后等待完成清理的最大时间。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
  labels:
    app: my-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      terminationGracePeriodSeconds: 60
      containers:
      - name: my-app-container
        image: my-app:latest
        ports:
        - containerPort: 8080
        readinessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 10
          timeoutSeconds: 2
          successThreshold: 1
          failureThreshold: 3
        livenessProbe:
          tcpSocket:
            port: 8080
          initialDelaySeconds: 10
          periodSeconds: 10
          timeoutSeconds: 2
          successThreshold: 1
          failureThreshold: 10

通过就绪探针存活探针,使得容器启动就绪后才会有流量转发进来,容器故障后也能自动重启。
但对于请求成功率要求较为严格的应用,这种方式存在一个较为严重问题:
pod滚动发布的过程中,虽然terminationGracePeriodSeconds让容器在一定时间后再退出,给了执行中的请求一些处理时间。但是terminating的过程中还是不断会有新请求进来,最终还是会有些请求受影响。

优雅重启原理

优雅重启最核心的问题就是pod在销毁过程中,不要再转发新请求进来。pod切换到terminating状态时,会发送一个SIG_TERM信号,应用端需要捕获到这个信号,将就绪探针的健康检查接口返回400+的状态码(503表示未准备好),这样失败failureThreshold次后,k8s就不会再转发新请求进来,在给一定时间让在途请求处理完成。

简介中给的yaml示例,pod在收到SIG_TERM信号后,将健康检查接口标记为不可用,就绪探针每10秒检查一次,连续3次失败就不再转发流量到该pod(30-40秒),terminationGracePeriodSeconds配置的是60秒,执行的请求此刻则还剩20-30秒时间处理。如果你觉得时间不够,可以考虑加大terminationGracePeriodSeconds的值。

优雅重启示例

python

python可以使用signal这个内置库来监听信号。

stop_event = threading.Event()

def _handler_termination_signal(signum, frame, app: FastAPI) -> None:
    match signum:
        case signal.SIGINT:
            logging.info("Received SIGINT signal, mark service to unhealthy.")
        case signal.SIGTERM:
            logging.info("Received SIGTERM signal, mark service to unhealthy.")
        case _:
            logging.warning(f"Received unexpected signal: {signum}")
            return


signal.signal(signal.SIGTERM, partial(_handler_termination_signal, app=app))
signal.signal(signal.SIGINT, partial(_handler_termination_signal, app=app))  # ctrl + c 停止

@app.get("/health")
async def health_check(request: Request):
    if stop_event.is_set():
        return PlainTextResponse("stopped", status_code=503)
    return "ok"

gunicorn

gunicorn会管理自己的主进程和worker进程,代码中使用signal无法捕获SIG_TERM信号,需要按照它的语法规范去捕获。

  1. 新建gunicorn_config.py文件
import logging
import signal


# 处理 SIGTERM 信号的函数
def handle_sigterm(signum, frame):
	from main import stop_event
    logging.info("Worker received SIGTERM, setting health to unhealthy...")
    stop_event.set()


# Worker 初始化时设置信号处理器
def post_worker_init(worker):
    signal.signal(signal.SIGTERM, handle_sigterm)
    logging.info("Signal handler for SIGTERM set in worker")

  1. gunicorn启动时设置config类
gunicorn -c gunicorn_config.py main:app
  1. main.py的健康检查接口使用stop_event
import threading
from flask import Response


stop_event = threading.Event()

@app.route("/health")
def health():
    if stop_event.is_set():
        return Response(
            json.dumps({"pid": os.getpid(), "status": "unhealthy"}),
            status=503,
            content_type="application/json",
        )
    else:
        return Response(
            json.dumps({"pid": os.getpid(), "status": "ok"}),
            status=200,
            content_type="application/json",
        )

http://www.kler.cn/a/516863.html

相关文章:

  • ubuntu调用图形化网络测试工具
  • STM32 FreeRTOS中断管理
  • HTML5使用favicon.ico图标
  • 阿里巴巴开发规范手册MySQL
  • 数据结构测试题2
  • ⽤vector数组实现树的存储(孩⼦表示法)c++
  • 【GORM】事务,嵌套事务,保存点事务的使用,简单电商平台go案例
  • 力扣动态规划-5【算法学习day.99】
  • 快速排序:一种高效的排序算法
  • Python 预训练:打通视觉与大语言模型应用壁垒——Python预训练视觉和大语言模型
  • WPS计算机二级•表格保护与打印
  • 深入解析:使用 Python 爬虫获取苏宁商品详情
  • 总结 uniapp 上不适配iphone的:new Date 时间、border线条、渐变
  • 深入探索 Nginx 的高级用法:解锁 Web 服务器的强大潜能
  • 2. Flink分区策略
  • vue3的组件v-model(defineModel()宏)
  • 第十五届蓝桥杯大赛软件赛省赛C/C++ 大学 B 组
  • 深度学习|表示学习|卷积神经网络|通道 channel 是什么?|05
  • 怎样使用树莓派自己搭建一套ADS-B信号接收系统
  • 栈和队列刷题篇
  • 新能源汽车充电桩选型以及安装应用
  • 2025.1.20——四、[强网杯 2019]Upload1 文件上传|反序列化
  • STM32——KEY按键
  • ETLCloud在iPaas中的是关键角色?
  • 若依 v-hasPermi 自定义指令失效场景
  • Java核心技术解析:泛型与类型安全全面指南