当前位置 博文首页 > AoGang:管中窥豹-ssh链接过多的问题分析及复盘

    AoGang:管中窥豹-ssh链接过多的问题分析及复盘

    作者:AoGang 时间:2021-06-22 18:22

    缘起

    某一天,产品侧同事联系过来,反馈话单传输程序报错,现象如下:

    实际上,该节点仅提供了一个sftp服务,供产品侧传输话单过来进行临时存储,由计费部门取走而已。

    分析

    于是找运维同事上服务器看了下情况,发现有以下几个问题:

    1. ssh进程过高(由于前期给各个部门分配的sftp账号不同,正好可以以账号名辨别来源)

    2. 根据以上信息,检查了TCP链接状态,发现绝大多数都是ESTABLISHED连接:

    3. 于是统计了一下TCP链接来源

    #/bin/bash
    for i in `netstat  -ant | grep ESTABLISHED  | awk '{print $5}' | awk  -F: '{print $1}' | sort |uniq`
    do
        count=`netstat  -ant | grep ESTABLISHED  | grep $i|wc -l`
          if [[ ${count} -ge 30 ]] ;then
             echo "$i的连接数是$count"
          #else
          #   continue
          fi
    done
    
    1. 发现链接主要集中于部分IP:

    2. 由于链接持续上涨,结合业务场景推测为ssh连接未正常释放的问题引发。

    拓展

    首先,链接在多天的时间内积攒到几万,并且不进行自动释放,几乎可以断定是由于客户端未释放,原因如下:
    image

    根据TCP请求的状态机,ESTABLISHED状态的链接,只有在发送FIN,或者收到FIN的时候,才会主动断开TCP链接(也就是意味着没有人发FIN);

    而假设一种场景,客户端发送了FIN,但服务端因为网络或者某种原因未收到的话,TCP keepalive机制会进行多次探测,将其断开(也就是意味着keepalive机制一直存在响应)。

    补充:下一篇将对TCP请求的keepalive机制做一个介绍。

    根因

    于是组织产品侧进行排查,最终找到原因:

    原代码中,在退出sftp任务之后,只关闭了channel,而未关闭对应的session(缺少红框内容)

    根据官方example:http://www.jcraft.com/jsch/examples/Sftp.java.html ,最终关闭的应该是session,这样就不会有残留了。

    复盘

    至此问题解决,那么需要复盘分析了:

    前期是否有做过sshd服务的性能限制配置?

    经了解,前期为了限制产品侧的链接个数,已经有过配置对应的限制配置:

    这里解释下这两个配置:
    MaxSessions 1000 限制最大会话个数;
    MaxStartups 1000:30:1200 会话个数达到1000之后的链接,有30%几率失败;会话个数达到1200后,全部失败;

    可是为何实际生产中连接数达到了几十万都没有释放呢?

    经过一番调查最终找到原因:每次登录会在ssh服务新建一个连接,每次代码层面进行sftp操作,会生成一个新的会话/session。
    所以这个maxsession,实际上限制的是每一个连接能新建出多少个会话的个数。
    对应到各种ssh工具上,有一个复制会话,有一个复制渠道(channel),对应的即是这两个概念。

    因此配置是存在的,只是没有起到预期的效果。

    那么应该如何正确的配置呢?

    实际上,在ulimit里面是可以配置针对用户级别的登录连接个数限制的:
    /etc/security/limits.conf 文件实际是 Linux PAM(插入式认证模块,Pluggable Authentication Modules)中 pam_limits.so 的配置文件,而且只针对于单个会话。

    # /etc/security/limits.conf
    #
    #This file sets the resource limits for the users logged in via PAM.
    该文件为通过PAM登录的用户设置资源限制。
    #It does not affect resource limits of the system services.
    #它不影响系统服务的资源限制。
    #Also note that configuration files in /etc/security/limits.d directory,
    #which are read in alphabetical order, override the settings in this
    #file in case the domain is the same or more specific.
    请注意/etc/security/limits.d下按照字母顺序排列的配置文件会覆盖 /etc/security/limits.conf中的
    domain相同的的配置
    #That means for example that setting a limit for wildcard domain here
    #can be overriden with a wildcard setting in a config file in the
    #subdirectory, but a user specific setting here can be overriden only
    #with a user specific setting in the subdirectory.
    这意味着,例如使用通配符的domain会被子目录中相同的通配符配置所覆盖,但是某一用户的特定配置
    只能被字母路中用户的配置所覆盖。其实就是某一用户A如果在/etc/security/limits.conf有配置,当
    /etc/security/limits.d子目录下配置文件也有用户A的配置时,那么A中某些配置会被覆盖。最终取的值是 /etc/security/limits.d 下的配置文件的配置。
    
    #
    #Each line describes a limit for a user in the form:
    #每一行描述一个用户配置,配置格式如下:
    #<domain> <type> <item> <value>
    
    #Where:
    #<domain> can be:
    # - a user name    一个用户名
    # - a group name, with @group syntax    用户组格式为@GROUP_NAME
    # - the wildcard *, for default entry    默认配置为*,代表所有用户
    # - the wildcard %, can be also used with %group syntax,
    # for maxlogin limit 
    #
    #<type> can have the two values:
    # - "soft" for enforcing the soft limits 
    # - "hard" for enforcing hard limits
    有soft,hard和-,soft指的是当前系统生效的设置值,软限制也可以理解为警告值。
    hard表名系统中所能设定的最大值。soft的限制不能比hard限制高,用-表名同时设置了soft和hard的值。
    #<item> can be one of the following:    <item>可以使以下选项中的一个
    # - core - limits the core file size (KB)    限制内核文件的大小。
    # - data - max data size (KB)    最大数据大小
    # - fsize - maximum filesize (KB)    最大文件大小
    # - memlock - max locked-in-memory address space (KB)    最大锁定内存地址空间
    # - nofile - max number of open file descriptors 最大打开的文件数(以文件描叙符,file descripter计数) 
    # - rss - max resident set size (KB) 最大持久设置大小
    # - stack - max stack size (KB) 最大栈大小
    # - cpu - max CPU time (MIN)    最多CPU占用时间,单位为MIN分钟
    # - nproc - max number of processes 进程的最大数目
    # - as - address space limit (KB) 地址空间限制 
    # - maxlogins - max number of logins for this user    此用户允许登录的最大数目
    # - maxsyslogins - max number of logins on the system    系统最大同时在线用户数
    # - priority - the priority to run user process with    运行用户进程的优先级
    # - locks - max number of file locks the user can hold    用户可以持有的文件锁的最大数量
    # - sigpending - max number of pending signals
    # - msgqueue - max memory used by POSIX message queues (bytes)
    # - nice - max nice priority allowed to raise to values: [-20, 19] max nice优先级允许提升到值
    # - rtprio - max realtime pr iority
    

    因此解就很明显了,只要在limit中配置

    testssh - maxlogins 1

    即可,即时生效。

    etc. 可以结合以上的maxsession,为某个用户的连接/session数做一个更精确的控制。

    前期是如何测试上线的呢?

    此处不作展开,测试工作之重要不容质疑;然而也不能为了测试而测试,上一个小功能就需要把所有的测试run一遍;
    个中文章摊开来说恐怕小小篇幅不足讨论。

    为何没有提前发现呢?

    前期曾做过监控告警的接入,但接入内容仅限于openssh进程消失,22端口丢失的场景;
    很显然,此处缺少进程数量,TCP链接数量,端口消耗数量监控;
    一言以蔽之,也就是只有1/0监控,没有health/err的监控;
    同样是以上的道理,既不能被动等待故障发现后做单个的补充,也不能因为过多细节监控项带来整体运维的复杂度;
    因此个人的理解是要加上通用指标的监控项,即监控指标需要反应系统级的普遍问题或者服务级的健康度,落实到ssh服务,建议如下:
    进程数量监控(no),TCP链接数量监控(yes,百分比监控),端口消耗数量监控(yes,百分比),ssh服务响应时长拨测(yes,模拟ssh登录及scp操作监控)

    bk
    下一篇:没有了