K8S容器应用优雅关闭-修复5003 Error

发布时间 - 2025-07-14 00:00:00    点击率:

大家好,我是stanley「史丹利」,今天来谈谈技术:容器优雅关闭方案。

1、遇到的问题 在公司某服务接入效能平台后,发布过程中,页面偶尔会出现5003报错。最初以为是Nacos没有及时将服务反注册,即POD在已经正常关闭的情况下,注册中心依然保留POD信息,导致请求依然发送到已关闭的POD中。

5003报错

5003-error-2

2、问题排查 2.1 首先,我们与开发团队合作,检查了反注册逻辑及相关日志,没有发现任何异常。

2.2 后来偶然发现POD中的主进程PID不为1,而PID为1的进程是shell进程。这会导致容器关闭时,业务进程无法接收到k8s发送的SIGTERM信号,只能在等待15秒后被强行杀死。

process-shell

2.3 我们修改了程序的启动参数,通过EXEC启动模式,使得应用主进程的PID变为1。

process-exec

2.4 重新发布验证后,5003报错问题得到了修复。

3、根因分析 3.1、SHELL 模式和 CMD 模式带来的差异 通常在Dockerfile中使用CMD和ENTRYPOINT来启动应用。启动应用有两种模式:shell模式和exec模式。在shell模式下,PID为1的进程是shell进程;在exec模式下,PID为1的进程是业务本身。

SHELL模式

FROM golang as builder
WORKDIR /go/
COPY app.go    .
RUN go build app.go
FROM ubuntu
WORKDIR /root/
COPY --from=builder /go/app .
CMD ./app

这种方式构建的镜像,应用启动后PID为1的进程是shell进程。

EXEC模式

FROM golang as builder
WORKDIR /go/
COPY app.go    .
RUN go build app.go
FROM ubuntu
WORKDIR /root/
COPY --from=builder /go/app .
CMD ["./app"]

这种方式构建的镜像,应用启动后PID为1的进程是应用进程。

3.2、直接启动应用和通过脚本启动的区别 在实际生产环境中,由于应用启动命令通常会包含许多启动参数,我们通常会使用一个启动脚本来启动应用,以便管理。相应地,在容器内,PID为1的进程会是shell进程,但shell程序不会转发信号,也不响应退出信号。因此,如果容器应用中启动了shell并占据了PID=1的位置,那么就无法接收到k8s发送的SIGTERM信号,只能在超时后被强行杀死。启动脚本 start.sh 的内容如下:

start.sh

$ cat > start.sh

Dockerfile

FROM golang as builder
WORKDIR /go/
COPY app.go    .
RUN go build app.go
FROM alpine
WORKDIR /root/
COPY --from=builder /go/app .
ADD start.sh /root/
CMD ["/bin/sh","/root/start.sh"]

3.2.1 解决方案 方案一:通过k8s的prestop参数调用容器内进程关闭脚本,实现优雅关闭。在前面脚本启动的Dockerfile基础上,定义一个优雅关闭的脚本,通过k8s-prestop在关闭POD前调用优雅关闭脚本,实现POD优雅关闭。

stop.sh

#!/bin/sh
ps -ef|grep app|grep -v grep|awk '{print $1}'|xargs kill -15

通过yaml部署到k8s中:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: app-prestop
  labels:
    app: prestop
spec:
  replicas: 1
  selector:
    matchLabels:
      app: prestop
  template:
    metadata:
      labels:
        app: prestop
    spec:
      containers:
      - name: prestop
        image: xx/app:v1.0-prestop
        lifecycle:
          preStop:
            exec:
              command:
              - sh
              - /root/stop.sh

方案二:将shell脚本修改为exec执行。修改start.sh脚本:

#!/bin/sh
exec ./app

在shell中添加一个exec命令即可让应用进程替代当前shell进程,这样可以将SIGTERM信号传递到业务层,让业务实现优雅关闭。

方案三:通过第三方init进程传递SIGTERM到进程中。使用dump-inittini作为容器的主进程,在收到退出信号时,会将退出信号转发给进程组中的所有进程。主要适用于应用本身无关闭信号处理的场景。docker –init本身也是集成的tini

FROM golang as builder
WORKDIR /go/
COPY app.go    .
RUN go build app.go
FROM alpine
WORKDIR /root/
COPY --from=builder /go/app .
ADD start.sh tini /root/
RUN chmod a+x start.sh && apk add --no-cache tini
ENTRYPOINT ["/sbin/tini", "--"]
CMD ["/root/tini", "--", /root/start.sh"]

4、总结 1、对于容器化应用启动命令,建议使用EXEC模式。

2、对于已经在代码层面实现了优雅关闭的业务,但有shell启动脚本的,容器化后部署到k8s上,建议使用方案一和方案二。

3、对于代码层面没有实现优雅关闭的业务,建议使用方案三。


# docker  # golang  # ai  # 区别  # 容器化应用  # shell脚本  # cos  # Error  # 报错  # 镜像  # 通常会  # 我是  # 容器内  # 也不  # 基础上  # 模式下  # 适用于  # 大家好 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: 昵图网官网入口 昵图网素材平台官方入口  javascript中对象的定义、使用以及对象和原型链操作小结  如何快速搭建FTP站点实现文件共享?  Laravel如何生成API文档?(Swagger/OpenAPI教程)  三星、SK海力士获美批准:可向中国出口芯片制造设备  Windows10如何删除恢复分区_Win10 Diskpart命令强制删除分区  公司门户网站制作流程,华为官网怎么做?  网站制作怎么样才能赚钱,用自己的电脑做服务器架设网站有什么利弊,能赚钱吗?  利用python获取某年中每个月的第一天和最后一天  laravel怎么通过契约(Contracts)编程_laravel契约(Contracts)编程方法  如何在 React 中条件性地遍历数组并渲染元素  Laravel如何实现RSS订阅源功能_Laravel动态生成网站XML格式订阅内容【教程】  香港服务器网站推广:SEO优化与外贸独立站搭建策略  Laravel如何监控和管理失败的队列任务_Laravel失败任务处理与监控  实现点击下箭头变上箭头来回切换的两种方法【推荐】  香港服务器选型指南:免备案配置与高效建站方案解析  laravel怎么使用数据库工厂(Factory)生成带有关联模型的数据_laravel Factory生成关联数据方法  如何在万网主机上快速搭建网站?  Laravel中的withCount方法怎么高效统计关联模型数量  Laravel怎么实现软删除SoftDeletes_Laravel模型回收站功能与数据恢复【步骤】  Laravel怎么处理异常_Laravel自定义异常处理与错误页面教程  Laravel如何实现事件和监听器?(Event & Listener实战)  如何用已有域名快速搭建网站?  Laravel如何处理JSON字段的查询和更新_Laravel JSON列操作与查询技巧  如何在 Telegram Web View(iOS)中防止键盘遮挡底部输入框  LinuxShell函数封装方法_脚本复用设计思路【教程】  如何在自有机房高效搭建专业网站?  Laravel DB事务怎么使用_Laravel数据库事务回滚操作  北京网站制作费用多少,建立一个公司网站的费用.有哪些部分,分别要多少钱?  深圳网站制作公司好吗,在深圳找工作哪个网站最好啊?  Laravel如何实现API版本控制_Laravel版本化API设计方案  Laravel如何实现URL美化Slug功能_Laravel使用eloquent-sluggable生成别名【方法】  Laravel怎么防止CSRF攻击_Laravel CSRF保护中间件原理与实践  Laravel如何实现一对一模型关联?(Eloquent示例)  javascript和jQuery中的AJAX技术详解【包含AJAX各种跨域技术】  Laravel全局作用域是什么_Laravel Eloquent Global Scopes应用指南  Win11怎么开启自动HDR画质_Windows11显示设置HDR选项  bing浏览器学术搜索入口_bing学术文献检索地址  Laravel如何记录自定义日志?(Log频道配置)  哪家制作企业网站好,开办像阿里巴巴那样的网络公司和网站要怎么做?  详解jQuery中基本的动画方法  Laravel怎么在Blade中安全地输出原始HTML内容  如何用狗爹虚拟主机快速搭建网站?  Microsoft Edge如何解决网页加载问题 Edge浏览器加载问题修复  今日头条AI怎样推荐抢票工具_今日头条AI抢票工具推荐算法与筛选【技巧】  如何在阿里云虚拟服务器快速搭建网站?  php json中文编码为null的解决办法  Laravel Eloquent性能优化技巧_Laravel N+1查询问题解决  Laravel Eloquent关联是什么_Laravel模型一对一与一对多关系精讲  php做exe能调用系统命令吗_执行cmd指令实现方式【详解】