管中窺豹-ssh連結過多的問題分析及覆盤

AoGang發表於2021-06-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操作監控)

相關文章