生产问题(十四)K8S抢占CPU导致数据库链接池打爆

这篇具有很好参考价值的文章主要介绍了生产问题(十四)K8S抢占CPU导致数据库链接池打爆。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、引言

        线上一天出现了两次数据库连接失败的大量报错,一开始以为是数据库的问题,但是想了想如果是数据库的问题,应该会有大量的应用问题

        具体分析之后,发现其实是容器cpu出现了Throttled,导致大量线程阻塞

生产问题(十四)K8S抢占CPU导致数据库链接池打爆,数据库

 二、分析

1、堆栈

        既然出现了报错,又没有发布,先看看堆栈里面报错的地方

        追踪到最底层的堆栈,显示的是数据库链接池耗尽[size:100; busy:1; idle:0; lastwait:44],100个链接只有4个在处理,没有一个空闲的,那么很明显其他的链接所在的线程都被阻塞了

生产问题(十四)K8S抢占CPU导致数据库链接池打爆,数据库

2、源码

        虽然说不太可能,但是还是要看看源码。大多数同学都觉得框架里面是不可能有错的,所以也不会去看源码,基本上都是去其他方向分析,这个在99%的情况下没有问题,但是作者不止一次遇到框架本身是有问题的,只是遇到了一些极端情况。比如:

Mybatis拼接sql出错及源码解析_<if test="month!=null">-CSDN博客

生产问题(九)Mysql8.0 ddl问题_mysql8 default_authentication_plugin-CSDN博客

生产问题(十一)日志JavaAgent-NoClassDefFoundError_-javaagent 找不到报错-CSDN博客

生产问题(十三)谷歌Protobuf误修改系统全局时区-CSDN博客

        这些问题,如果不往框架的方向上走是找不到问题原因的,那么言归正传,我们看源码,这是tomcat进行线程链接的地方,超时的话就会报

throw new PoolExhaustedException("[" + Thread.currentThread().getName()+"] " +
    "Timeout: Pool empty. Unable to fetch a connection in " + (maxWait / 1000) +
    " seconds, none available[size:"+size.get() +"; busy:"+busy.size()+"; idle:"+idle.size()+"; lastwait:"+timetowait+"].");

        和lastwait:44的现象也对的上,给我们进一步指明了方向,有很多占据数据库链接的线程不做事,其他很多线程在等

private PooledConnection borrowConnection(int wait, String username, String password) throws SQLException {

        if (isClosed()) {
            throw new SQLException("Connection pool closed.");
        } //end if

        //get the current time stamp
        long now = System.currentTimeMillis();
        //see if there is one available immediately
        PooledConnection con = idle.poll();

        while (true) {
            if (con!=null) {
                //configure the connection and return it
                PooledConnection result = borrowConnection(now, con, username, password);
                if (result!=null) return result;
            }

            //if we get here, see if we need to create one
            //this is not 100% accurate since it doesn't use a shared
            //atomic variable - a connection can become idle while we are creating
            //a new connection
            if (size.get() < getPoolProperties().getMaxActive()) {
                //atomic duplicate check
                if (size.addAndGet(1) > getPoolProperties().getMaxActive()) {
                    //if we got here, two threads passed through the first if
                    size.decrementAndGet();
                } else {
                    //create a connection, we're below the limit
                    return createConnection(now, con, username, password);
                }
            } //end if

            //calculate wait time for this iteration
            long maxWait = wait;
            //if the passed in wait time is -1, means we should use the pool property value
            if (wait==-1) {
                maxWait = (getPoolProperties().getMaxWait()<=0)?Long.MAX_VALUE:getPoolProperties().getMaxWait();
            }

            long timetowait = Math.max(0, maxWait - (System.currentTimeMillis() - now));
            waitcount.incrementAndGet();
            try {
                //retrieve an existing connection
                con = idle.poll(timetowait, TimeUnit.MILLISECONDS);
            } catch (InterruptedException ex) {
                if (getPoolProperties().getPropagateInterruptState()) {
                    Thread.currentThread().interrupt();
                }
                SQLException sx = new SQLException("Pool wait interrupted.");
                sx.initCause(ex);
                throw sx;
            } finally {
                waitcount.decrementAndGet();
            }
            if (maxWait==0 && con == null) { //no wait, return one if we have one
                if (jmxPool!=null) {
                    jmxPool.notify(org.apache.tomcat.jdbc.pool.jmx.ConnectionPool.POOL_EMPTY, "Pool empty - no wait.");
                }
                throw new PoolExhaustedException("[" + Thread.currentThread().getName()+"] " +
                        "NoWait: Pool empty. Unable to fetch a connection, none available["+busy.size()+" in use].");
            }
            //we didn't get a connection, lets see if we timed out
            if (con == null) {
                if ((System.currentTimeMillis() - now) >= maxWait) {
                    if (jmxPool!=null) {
                        jmxPool.notify(org.apache.tomcat.jdbc.pool.jmx.ConnectionPool.POOL_EMPTY, "Pool empty - timeout.");
                    }
                    throw new PoolExhaustedException("[" + Thread.currentThread().getName()+"] " +
                        "Timeout: Pool empty. Unable to fetch a connection in " + (maxWait / 1000) +
                        " seconds, none available[size:"+size.get() +"; busy:"+busy.size()+"; idle:"+idle.size()+"; lastwait:"+timetowait+"].");
                } else {
                    //no timeout, lets try again
                    continue;
                }
            }
        } //while
    }

3、监控

        既然分析出有很多占据数据库链接的线程不做事,其他线程在等,那么在cpu、线程方面就应该有所体现,接下来看监控。

        监控显示对应问题的时间点发生了CPU Throttled,他会限制应用程序或进程的CPU使用率,一度达到十几秒,等待io的task线程激增,很明显这就是具体的原因,接下来需要找寻的是,为什么会产生Throttled。

生产问题(十四)K8S抢占CPU导致数据库链接池打爆,数据库

生产问题(十四)K8S抢占CPU导致数据库链接池打爆,数据库

        jvm的内存和线程激增也侧面验证了cpu阻塞,大量的线程处理不了任务

生产问题(十四)K8S抢占CPU导致数据库链接池打爆,数据库

生产问题(十四)K8S抢占CPU导致数据库链接池打爆,数据库

三、cpu抢占

        通过上述分析,接下来需要找寻为什么会产生Throttled?

        一般来说有两种原因:

1、瞬时流量激增

        这种一般就是入口流量突然增加,已经分配的cpu处理不过来,需要按照K8S的动态分配继续申请cpu,但是来不及

        看看请求数量,整体变化不大,但是监控采集是一分钟一次,所以这个方向不能被排除,但是没有其他方面的证据协助分析

生产问题(十四)K8S抢占CPU导致数据库链接池打爆,数据库

2、宿主机cpu被抢占

        这段监控是下午出问题时候的,上午找不到是哪个宿主机了

        可以看到这个宿主机不太对,他这个0.7是每个cpu维度的,对应时间从0.3到0.7,相当于原来用三个,突然用到了7个,所以宿主机争抢的概率还是大一些        生产问题(十四)K8S抢占CPU导致数据库链接池打爆,数据库

        再看看具体每个容器,明显黄色容器的cpu有一段飙升,倒数第三个是出问题的机器       ,上面的几个容器都有增加,一方面也代表了cpu被其他容器抢占的可能 

生产问题(十四)K8S抢占CPU导致数据库链接池打爆,数据库

四、解决

        由于瞬时流量激增、宿主机cpu被抢占两个方向的解决方案是完全不同的,所以我们需要先快速处理。

1、升配、增加机器数量

        这样瞬时流量就会大幅减小,由于流量小了,cpu被抢占也就不会有太大影响

2、设置cpu set模式

        K8S给予linux的cgroups默认的一般都是share模式,就是给你分这么多cpu但是后续可以被抢占。

        设置set模式,就是不给别人抢占,当然你忙的时候也抢不了别人

        作者认为对于核心应用来说隔离是必须的,如果他隔离的情况下处理不了就应该升配或者扩容,而不是抢别人的

3、增加秒级监控

        一分钟一次的监控属实是坑,基本上辅助不了确定的方向,只能是结合监控数据去猜一下,之前出现过很多问题也因为监控确定不了具体原因,只是锁定几个方向

        但是怎么增加,增加有没有影响都是件麻烦事,因为框架组那边很直白的说了一分钟一次对于目前的监控都有压力,秒级的要自己暴露出prometheus规范的指标,通过加env的方式暴露出来,框架的监控agent会去采集。

        那他为什么不去全部都加一下呢,还是内部有一些资源或者风险存在的,这就很顶了。

五、总结

        这一次的问题被锁定在了瞬时流量激增、宿主机cpu被抢占两个方向,宿主机cpu被抢占概率大一些,监控分钟级别的采集不能确定唯一方向,只能沿着两个方向一起解决。

        这也是个好事,起码知道了监控的机制,后续需要改进的方向。文章来源地址https://www.toymoban.com/news/detail-824212.html

到了这里,关于生产问题(十四)K8S抢占CPU导致数据库链接池打爆的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • k8s学习(三十四)飞腾2000+麒麟V10离线部署k8s

    需要在飞腾芯片和麒麟V10操作系统下安装k8s,目前在飞腾2000下安装成功,飞腾2500还没有成功,现介绍飞腾2000下的安装流程。 准备多台机器,其中一台作为K8S主节点,其他作为从节点。 这里只准备了2台机器,一台作为主节点,一台作为从节点。 k8s版本为1.20.2 功能 IP k8s主节

    2024年02月01日
    浏览(89)
  • K8S学习指南(10)-k8s中为pod分配CPU和内存资源

    Kubernetes(简称K8s)是一种开源的容器编排平台,广泛用于构建、部署和管理容器化应用。在Kubernetes中,Pod是最小的可部署单元,而资源分配是确保Pod正常运行的关键因素之一。本文将深入探讨如何在Kubernetes中为Pod分配CPU和内存资源,并提供详细的示例。 在容器化环境中,多

    2024年02月04日
    浏览(57)
  • 商城-学习整理-集群-K8S-集群环境部署(二十四)

    MySQL-MMM是Master-MasterReplicationManagerforMySQL(mysql主主复制管理器)的简称,是Google的开源项目。 (Perl脚本)。MMM基于MySQL Replication做的扩展架构,主要用来监控mysql主主复制并做失败转移。其原理是将真实数据库节点的IP(RIP)映射为虚拟IP(VIP)集。mysql-mmm的监管端会提供多个

    2024年02月11日
    浏览(48)
  • 学习笔记十四:K8S最小调度单元POD概述

    K8s官方文档:https://kubernetes.io/ K8s中文官方文档: https://kubernetes.io/zh/ K8s Github地址:https://github.com/kubernetes/kubernetes Pod资源对应的官方文档:https://kubernetes.io/zh-cn/docs/concepts/workloads/pods/ Pod是Kubernetes中的最小调度单元,k8s是通过定义一个Pod的资源,然后在Pod里面运行容器,容器

    2024年02月12日
    浏览(36)
  • k8s pod “cpu和内存“ 资源限制

    转载用于收藏学习:原文 为了保证充分利用集群资源,且确保重要容器在运行周期内能够分配到足够的资源稳定运行,因此平台需要具备 Pod的资源限制的能力。 对于一个pod来说,资源最基础的2个的指标就是:CPU和内存。 Kubernetes提供了个采用requests和limits 两种类型参数对资

    2024年02月13日
    浏览(68)
  • Kubernetes实战(十四)-k8s高可用集群扩容master节点

    k8s 集群是由一组运行 k8s 的节点组成的,节点可以是物理机、虚拟机或者云服务器。k8s 集群中的节点分为两种角色:master 和 node。 master 节点:master 节点负责控制和管理整个集群,它运行着一些关键的组件,如 kube-apiserver、kube-scheduler、kube-controller-manager 等。master 节点可以有

    2024年02月03日
    浏览(53)
  • K8s攻击案例:RBAC配置不当导致集群接管

    01、概述 Service Account本质是服务账号,是Pod连接K8s集群的凭证。在默认情况下,系统会为创建的Pod提供一个默认的Service Account,用户也可以自定义Service Account,与Service Account关联的凭证会自动挂载到Pod的文件系统中。 当攻击者通过某个web应用获取到一个Pod权限时,如果RBAC权

    2024年02月02日
    浏览(46)
  • K8s攻击案例:组件未授权访问导致集群入侵

    K8s集群往往会因为组件的不安全配置存在未授权访问的情况,如果攻击者能够进行未授权访问,可能导致集群节点遭受入侵。比较常见的的组件未授权访问漏洞,主要包括 API Server 未授权访问、kubelet 未授权访问、etcd 未授权访问、kube-proxy 不安全配置、Dashboard未授权访问。

    2024年02月03日
    浏览(39)
  • Kubernetes(K8s)从入门到精通系列之十四:安装工具

    Kubernetes 命令行工具 kubectl, 让你可以对 Kubernetes 集群运行命令。 你可以使用 kubectl 来部署应用、监测和管理集群资源以及查看日志。 kind 让你能够在本地计算机上运行 Kubernetes。 使用这个工具需要你安装 Docker 或者 Podman。 与 kind 类似,minikube 是一个工具, 能让你在本地运

    2024年02月14日
    浏览(44)
  • 案例分享-full gc导致k8s pod重启

     在之前的记一次k8s pod频繁重启的优化之旅中分享过对于pod频繁重启的一些案例,最近又遇到一例,继续分享出来希望能给大家带来些许收获。 报警群里突然显示某pod频繁重启,我随即上去查看日志,主要分这么几步:   1.查看pod重启的原因,kubectl descirbe pod 上面的Reason:

    2024年02月02日
    浏览(53)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包