K8s+Nacos实现应用的优雅上下线【生产实践】

news2024/12/27 1:37:01

`

文章目录

  • 前言
  • 一、环境描述
  • 二、模拟请求报错
  • 三、配置优雅上下线
    • 1.修改nacos配置
    • 2.修改depolyment配置
    • 3.重新apply deployment后测试
    • 4.整体(下单)测试流程验证是否生效
  • 四、期间遇到的问题


前言

我们在使用k8s部署应用的时候,虽然k8s是使用滚动升级的,先启动一个新Pod 等这个新Pod运行成功后,再干掉旧Pod;在这个过程中Pod会一直接收请求的,如果在Pod被干掉的那一刻正好有一部分请求打进来了,那么Pod被杀死了,就不会给这个请求返回结果,就会导致客户端出现请求500错误,这样就做不到平滑升级了,我们要做的就是在Pod升级的时候不能或者尽量避免这种情况;

我们公司使用的是java,中间件用的是nacos,应用在启动时会注册到nacos,然后走应用之间的内部调用,服务会不间断的向注册中心nacos发送自己的心跳(3s) 以及在每个 Pod 服务里本地也有一份缓存映射表(也有一个窗口时间更新30s),服务在停止的时候,自然也会在nacos中进行下线 但是如果有请求在应用下线的这个窗口期发起的话,就会出现K8s Pod 服务已下线,但是 Nacos 在窗口期之内注册列表未更新,导致请求达到一个根本不存在的旧服务里导致请求返回404或者旧请求已经打到旧服务里,但是高峰期时,程序处理较慢,还没来及返回响应体,服务就被关闭了返回500;

我们使用的解决方案是,在应用下线的第一时间(Pod被删除)先进行在nacos的下线操作不让其接受新的请求,然后等待Pod已接收的请求处理完成后 再进行删除Pod;
这里会使用到的知识以及需要自身考虑的点有:

  • k8s的prestop钩子(容器关闭前执行操作)
  • 需要判断自己应用处理的请求的时间(基本上30s内都能处理完成 如果不放心的话调整成50s 但是这样的话也会相应的增加上线时长,需要注意)
  • 需要在nacos(v2.x)中配置Nacos自动清理过期服务的过期时间(删除服务的元数据信息),防止请求过多/代码问题导致Pod的cpu打满 触发Pod的健康检查后 Pod重启以后依然是下线状态(不可用) 这样就出大问题了;
    在这里插入图片描述

一、环境描述

名称版本部署方式
kubernetesv1.20.11二进制
nacosv2.0.3集群模式

二、模拟请求报错

模拟请求报错就是不加任何配置直接使用测试脚本(这里用的是jmeter)不间断的去调用我们的应用,然后发布我们的新应用会有失败的请求

##现在的deployment文件为如下##
apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: data-center
  name: energy-order-api
  labels:
    app: energy-order-api
spec:
  replicas: 3
  selector:
    matchLabels:
      app: energy-order-api
  template:
    metadata:
      labels:
        app: energy-order-api
    spec:
      imagePullSecrets:
        - name: harbor-secret
      containers:
      - name: energy-order-api
        image: registry.xxxx/hqt-registry-pro/energy-order-api:P-1391-2023xxxx-15.47.45
        imagePullPolicy: IfNotPresent
        command: ["/bin/sh"]
        args: ["-c","java -jar 
              -Xmx2688m 
              -Xms2688m 
              -Xmn961m 
              -XX:MaxMetaspaceSize=512m 
              -XX:MetaspaceSize=512m 
              -Xloggc:/logs/gc-%t.log 
              -XX:+HeapDumpOnOutOfMemoryError  
              -XX:HeapDumpPath=/data/logs/heapdump_$MY_POD_NAME.hprof
              -XX:+PrintGCDetails 
              -XX:+UnlockExperimentalVMOptions 
              -XX:+UseCGroupMemoryLimitForHeap 
              -XX:NativeMemoryTracking=detail 
              -javaagent:/data/skywalking/skywalking-agent.jar=agent.service_name=energy-order-api,agent.instance_name=$MY_POD_NAME,collector.backend_service=internal-skywalking.xxxx.xxm:11800 
              -Dapollo.meta=http://apollo-configservice.infrastructure.svc.cluster.local:8080 
              -Denv=pro /data/app.jar;/sbin/tini -s"]
        env:
        #获取pod实例名称,因为一个pod可能会有多个副本,所以需要根据名称来进行区分;
        - name: MY_POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        resources:
          requests:
            memory: "4Gi"
            cpu: "2000m"
          limits:
            memory: "4Gi"
            cpu: "2000m"
        volumeMounts:
        - name: energy-order-api-logs
          mountPath: /data/logs
          subPathExpr: $(MY_POD_NAME)
        ports:
        - containerPort: 80
        livenessProbe:
          httpGet:
            path: /actuator/info
            port: 80
          initialDelaySeconds: 70 #pod启动多长时间后开始去探测;
          periodSeconds: 5 #每隔多长时间去探测一次;
          failureThreshold: 6
        readinessProbe:
          httpGet:
            path: /actuator/info
            port: 80
          initialDelaySeconds: 70
          periodSeconds: 5
          failureThreshold: 6
      affinity:
        #节点亲和性
        nodeAffinity:
          #硬策略
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              #标签键
              - key: ResourcePool
                operator: In
                #标签值
                values:
                - core
        #pod反亲和性配置
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - energy-order-api
            topologyKey: kubernetes.io/hostname        
 
      volumes:
      - name: energy-order-api-logs
        persistentVolumeClaim:
          claimName: energy-order-api-logs

---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: energy-order-api-logs
  namespace: data-center
spec:
  accessModes:
  - ReadWriteMany
  storageClassName: alicloud-nas-subpath
  resources:
    requests:
      storage: 1Gi

Pod启动成功如下:
在这里插入图片描述

在这里插入图片描述
设置30个线程开始去请求我们服务的接口
可以看到目前的请求都是成功的,此时我们修改镜像apply可以模拟下我们的服务发布,当新Pod启动后 删除旧Pod时注意观察请求是否有报错!

在这里插入图片描述

在这里插入图片描述
可以发现在删除pod时的这个动作会出现错误请求,随后就会正常
在这里插入图片描述
以上就是发版(新Pod替换旧Pod)的过程中会出现的问题;

三、配置优雅上下线

1.修改nacos配置

[root@iZbp1iz5ayf044rk5cqq26Z ~]# vim /hqtbj/hqtwww/nacos_workspace/conf/application.properties
#打开注释并修改
### The interval to clean expired metadata, unit: milliseconds.
nacos.naming.clean.expired-metadata.interval=5000
### The expired time to clean metadata, unit: milliseconds.
nacos.naming.clean.expired-metadata.expired-time=5000

保存后重启nacos

2.修改depolyment配置

需要添加k8s的prestop钩子,以及设置强制关闭pod的时间要比sleep的时间长
'curl -X PUT "http://my-nacos.xxx.com/nacos/v1/ns/instance?serviceName=服务名称&ip=服务IP&port=服务端口&weight=0" && sleep 30 && PID= && kill -SIGTERM $PID && while ps -p $PID > /dev/null; do sleep 1; done'
terminationGracePeriodSeconds: 40
如上命令的作用:

  1. 使用curl将注册到nacos的实例权重设置为0,设置为0后就不会再接受请求了,也可以调用nacos的下线接口,只需要将weight=0改为enabled=false即可;
  2. 不接受请求后sleep睡眠30秒 用于处理已经发送过来的请求;
  3. 然后再kill -SIGTERM进行优雅的关闭服务;
  4. 等待Pod中的服务完全停止,如果在 terminationGracePeriodSeconds 40s内 (默认 30s) 还未完全停止,就发送 SIGKILL 信号强制杀死进程(kill -9)。
#添加prestop钩子
lifecycle:
          preStop:
            exec:
              command: ["/bin/sh","-c",'curl -X PUT "http://my-nacos.xxx.com/nacos/v1/ns/instance?serviceName=energy-order-api&ip=${POD_IP}&port=80&weight=0" && sleep 30 && PID=`pidof java` && kill -SIGTERM $PID && while ps -p $PID > /dev/null; do sleep 1; done']  
#设置强制杀死Pod(kill -9)的时间,默认为30s   
terminationGracePeriodSeconds: 40

完整deployment内容如下

---
apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: data-center
  name: energy-order-api
  labels:
    app: energy-order-api
spec:
  replicas: 3
  selector:
    matchLabels:
      app: energy-order-api
  template:
    metadata:
      labels:
        app: energy-order-api
    spec:
      imagePullSecrets:
        - name: harbor-secret
      containers:
      - name: energy-order-api
        image: registry.xxxx/hqt-registry-pro/energy-order-api:P-1391-2023xxxx-15.47.45
        imagePullPolicy: IfNotPresent
        command: ["/bin/sh"]
        args: ["-c","java -jar 
              -Xmx2688m 
              -Xms2688m 
              -Xmn961m 
              -XX:MaxMetaspaceSize=512m 
              -XX:MetaspaceSize=512m 
              -Xloggc:/logs/gc-%t.log 
              -XX:+HeapDumpOnOutOfMemoryError  
              -XX:HeapDumpPath=/data/logs/heapdump_$MY_POD_NAME.hprof
              -XX:+PrintGCDetails 
              -XX:+UnlockExperimentalVMOptions 
              -XX:+UseCGroupMemoryLimitForHeap 
              -XX:NativeMemoryTracking=detail 
              -javaagent:/data/skywalking/skywalking-agent.jar=agent.service_name=energy-order-api,agent.instance_name=$MY_POD_NAME,collector.backend_service=internal-skywalking.xxxx.xxm:11800 
              -Dapollo.meta=http://apollo-configservice.infrastructure.svc.cluster.local:8080 
              -Denv=pro /data/app.jar;/sbin/tini -s"]
        env:
        #获取pod实例名称,因为一个pod可能会有多个副本,所以需要根据名称来进行区分;
        - name: MY_POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        resources:
          requests:
            memory: "4Gi"
            cpu: "2000m"
          limits:
            memory: "4Gi"
            cpu: "2000m"
        volumeMounts:
        - name: energy-order-api-logs
          mountPath: /data/logs
          subPathExpr: $(MY_POD_NAME)
        ports:
        - containerPort: 80
        lifecycle:
          preStop:
            exec:
              command: ["/bin/sh","-c",'curl -X PUT "http://nacos.wonxxxnk.cc/nacos/v1/ns/instance?serviceName=energy-order-api&ip=${POD_IP}&port=80&weight=0" && sleep 30 && PID=`pidof java` && kill -SIGTERM $PID && while ps -p $PID > /dev/null; do sleep 1; done']
        terminationGracePeriodSeconds: 40
        livenessProbe:
          httpGet:
            path: /actuator/info
            port: 80
          initialDelaySeconds: 70 #pod启动多长时间后开始去探测;
          periodSeconds: 5 #每隔多长时间去探测一次;
          failureThreshold: 6
        readinessProbe:
          httpGet:
            path: /actuator/info
            port: 80
          initialDelaySeconds: 70
          periodSeconds: 5
          failureThreshold: 6
      affinity:
        #节点亲和性
        nodeAffinity:
          #硬策略
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              #标签键
              - key: ResourcePool
                operator: In
                #标签值
                values:
                - core
        #pod反亲和性配置
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - energy-order-api
            topologyKey: kubernetes.io/hostname        
 
      volumes:
      - name: energy-order-api-logs
        persistentVolumeClaim:
          claimName: energy-order-api-logs

---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: energy-order-api-logs
  namespace: data-center
spec:
  accessModes:
  - ReadWriteMany
  storageClassName: alicloud-nas-subpath
  resources:
    requests:
      storage: 1Gi

3.重新apply deployment后测试

在这里插入图片描述
在这里插入图片描述
如上,现在Pod已经启动成功并且在nacos中也是可用状态

开始测试
在这里插入图片描述
如上当开始停止旧pod时, 会先调用我们配置的prestop钩子 如下 先把nacos中旧pod的权重改为0 不让其接受请求,然后再处理已接受的请求最后彻底关闭pod

在这里插入图片描述
可以看到整个流程下来是没有发现有请求失败的!
在这里插入图片描述

这是单对这一个order服务进行的测试,接下来需要走一遍整体下单的流程,调用多个服务进行测试的同时重新发布这个order服务看是否有失败的请求

4.整体(下单)测试流程验证是否生效

已启动的pod以及nacos状态如下:
在这里插入图片描述
在这里插入图片描述
开始测试
在这里插入图片描述
在这里插入图片描述
如上可以发现在停止Pod时,跟上面的结果是一样的,都是先把nacos中旧pod的权重改为0,然后等待处理请求再彻底关闭pod;

如下测试,整体一个下单流程再发布期间也是不回受到影响的,无报错
在这里插入图片描述

四、期间遇到的问题

如果不配置nacos清理元数据信息的话,会导致当cpu/内存使用超过限制而导致健康检查重启时(Pod实例自身重启而不是会起一个新pod),会出现即使pod重启完nacos里注册的服务权重是0/下线,导致服务直接不可用,只能手动再去启用!!所以下面在nacos的配置一定要进行使用!
在这里插入图片描述

如下:
服务因健康检查失败开始重启

在这里插入图片描述
在这里插入图片描述
此时的请求开始报错
在这里插入图片描述
服务的权重变为0,不接收请求
在这里插入图片描述
pod重启完成
在这里插入图片描述
发现nacos里注册的服务权重依然为0,并没进行接收请求
在这里插入图片描述
请求依旧报错
在这里插入图片描述


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1538389.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

畅谈AIGC,ISIG-AIGC技术与应用发展峰会成功举办

3月16日,第四届ISIG中国产业智能大会在上海中庚聚龙酒店如期开幕,此次大会由苏州市金融科技协会指导、企智未来科技(RPA中国、LowCode低码时代、AIGC开放社区)主办。大会聚集了来自不同领域的专家学者、行业领军人物及技术研发者&…

MySQL 更新执行的过程

优质博文:IT-BLOG-CN Select语句的执行过程会经过连接器、分析器、优化器、执行器、存储引擎,同样的 Update语句也会同样走一遍 Select语句的执行过程。 但是和 Select最大不同的是,Update语句会涉及到两个日志的操作redo log(重做…

python的街道办管理系统flask-django-php-nodejs

随着世界经济信息化、全球化的到来和互联网的飞速发展,推动了各行业的改革。若想达到安全,快捷的目的,就需要拥有信息化的组织和管理模式,建立一套合理、动态的、交互友好的、高效的街道办管理系统。当前的信息管理存在工作效率低…

3.22Code

基于邻接矩阵的新顶点的增加 #include<iostream>using namespace std;#define MAXVNUM 100typedef struct{int vexsNum;int arcsNum;int arcs[MAXVNUM][MAXVNUM];int vexs[MAXVNUM]; }AdjMatrixG;void InitGraph(AdjMatrixG &G){for(int k0;k<MAXVNUM;k)G.vexs[k…

webgl浏览器渲染设置

在浏览器中程序图形化webgl渲染时&#xff0c;有时候发现代码没有问题&#xff0c;但是就是无法渲染或者渲染报错&#xff0c;此时可以尝试如下的设置&#xff1a; 通过在chrome浏览器输入chrome&#xff1a;//flags打开扩展 设置一&#xff08;webgl开发者扩展&#xff09; 设…

计算机网络——数据链路层(数据链路层功能概述)

计算机网络——数据链路层&#xff08;数据链路层功能概述&#xff09; 数据链路层的功能数据链路层的基本概念封装成帧和透明传输 我们之前已经学完了物理层的所有内容&#xff0c;今天开始我们要进入数据链路层的学习&#xff0c;如果有小伙伴对物理层的内容感兴趣的话&#…

Xilinx FPGA 远程升级时bin和bit文件使用注意

以Spartan-6 ISE开发环境为例。 ISE开发环境支持生成bit和bin格式的程序文件&#xff0c;可以在生成选项进行配置&#xff1a; 把生成的bit文件和bin文件进行二进制比较&#xff0c;发现bit比bin文件头部多了一些内容&#xff08;头部信息&#xff09;&#xff0c;剩余部分完…

苹果电脑不能删除移动硬盘文件 苹果电脑移动硬盘只读模式如何更改 移动硬盘文件或目录损坏且无法读取怎么办

当我们将移动硬盘插入苹果电脑后&#xff0c;发现无法对移动硬盘中的文件进行编辑该怎么办&#xff1f;相信有不少网友遇到过这类情况。苹果电脑不能删除移动硬盘文件&#xff0c;或无法拷贝硬盘里的文件。今天我为大家解决苹果电脑移动硬盘只读模式如何更改的问题&#xff0c;…

GraphPad Prism 10:一站式数据分析解决方案

GraphPad Prism 10是一款功能强大的数据分析和可视化软件&#xff0c;广泛应用于生命科学研究、医学、生物、化学等多个领域。以下是对其详细功能的介绍&#xff1a; 首先&#xff0c;GraphPad Prism 10具有出色的数据可视化功能。它支持各种类型的图表和图形&#xff0c;包括…

C++ 侯捷 程序设计(Ⅱ)兼谈对象模型 笔记

Conversion function 转换函数 侯捷老师使用分数 Fraction举例&#xff0c;分数理应可以被看作是小数 提供了Fraction类对象一个转换为double的方法&#xff0c;当碰到需要转换为double的情况下&#xff0c;会调用该方法。 黄色的就是转换函数&#xff0c;没有return type&am…

高手勿入:连问chatGPT九个问题,解决个简单前端问题,无剪辑。

将layui弹窗button默认文字&#xff1a;确定&#xff0c;修改为其他文字&#xff0c;就这么个简单问题&#xff0c;把前端妹子&#xff08;新手&#xff09;难坏了&#xff0c;向我求助&#xff0c;我没有像以往一样直接给答案&#xff0c;而是以新手的方式求助chatgpt&#xf…

Vue3快速上手(十七)Vue3之状态管理Pinia

一、简介 Pinia官网:https://pinia.vuejs.org/zh/ 从官网截图里可以直接看到,pinia是一个vuejs的状态(数据)管理工具。功能性同vuex。logo是小菠萝。它是一个集中式状态管理工具。就是将多个组件共用的数据管理起来,重复利用。有点类似缓存的意思。 二、Pinia环境搭建 …

vCenter 6.5为虚拟机添加GPU直通

参考&#xff1a;Dell文档 如何为GPU直通启用VMware虚拟机。 | Dell 中国

【计算机视觉】Gaussian Splatting源码解读补充(一)

本文旨在补充gwpscut创作的博文学习笔记之——3D Gaussian Splatting源码解读。 Gaussian Splatting Github地址&#xff1a;https://github.com/graphdeco-inria/gaussian-splatting 论文地址&#xff1a;https://repo-sam.inria.fr/fungraph/3d-gaussian-splatting/3d_gauss…

redis功能点

一、redis简介 概述 Redis 是速度非常快的非关系型&#xff08;NoSQL&#xff09;内存键值数据库&#xff0c;可以存储键和五种不同类型的值之间的映射。键的类型只能为字符串&#xff0c;值支持五种数据类型&#xff1a;字符串、列表、集合、散列表、有序集合。 Redis 支持很…

深入理解指针03

1. 字符指针变量 在指针的类型中我们知道有⼀种指针类型为字符指针char*; ⼀般使⽤: int main(){char ch w;char *pc &ch;*pc w;return 0;} 还有⼀种使⽤⽅式如下: int main() {const char* pstr "hello world";//这⾥是把⼀个字符串放到pstr指针变量⾥了…

【并查集专题】【蓝桥杯备考训练】:网络分析、奶酪、合并集合、连通块中点的数量、格子游戏【已更新完成】

目录 1、网络分析&#xff08;第十一届蓝桥杯省赛第一场C A组/B组&#xff09; 2、奶酪&#xff08;NOIP2017提高组&#xff09; 3、合并集合&#xff08;模板&#xff09; 4、连通块中点的数量&#xff08;模板&#xff09; 5、格子游戏&#xff08;《信息学奥赛一本通》…

win10下自由切换多版本JDK操作

1.在window 系统变量 path路径追加%JAVA_HOME%\bin;%JAVA_HOME%\jre\bin; 2.下载多版本jdk zip文件解压到到C:\Program Files\Java\目录下 3.定义切换Java版本的bat文件,内容如下 @echo off @echo -------------------welcome to use Java version switch service------------…

设计模式 之 简单工厂模式+工厂模式

简单工厂模式 创建一个工厂类&#xff0c;对实现了同一个接口的多个类进行实例的创建。 //抽象类 人 public abstract class HuMan {public abstract void Talk(); } //黑人实现类 public class BlackHuman : HuMan {public override void Talk(){Console.WriteLine("I a…

python的ITS 信息平台的设计与实现flask-django-nodejs-php

第二&#xff0c;陈列说明该系统实现所采用的架构、系统搭建采用的服务器、系统开发环境和使用的工具&#xff0c;以及系统后台采用的数据库。 最后&#xff0c;对系统进行全面测试&#xff0c;主要包括功能测试、查询性能测试、安全性能测试。 分析系统存在的不足以及将来改进…