ss命令結合zabbix對socket做監控

福伴發表於2020-12-18

本文為部落格園作者所寫: 一寸HUI,個人部落格地址:https://www.cnblogs.com/zsql/

最近天冷了,socket也出問題了,一直沒有做監控,現在就把監控加起來,目前我們使用的有zabbix和prometheus兩種監控,這裡我們使用zabbix對其進行監控,這裡使用的是ss命令,不使用netstat命令,因為ss的速度快很多,不信的話可以去測一下哈,一臺機器的socket越多,對比越明顯。而且ss命令能顯示更多的內容,其實我對這兩個命令不是特別的熟悉,通過man ss可以看到:

 因為個人也不熟,所以大家一起學一下吧,這篇文章主要分為三塊進行說明,第一就是介紹這個ss命令,第二就是某及機器總體的socket進行監控,第三個就是某些機器連線該機器的數量進行監控,比如機器A-->(連線)機器B的次數,這樣就可以看出來是哪個機器頻繁傳送請求了。天氣真冷,敲敲鍵盤,搓搓腿,哈哈

一、ss命令

ss命令用於顯示socket狀態. 他可以顯示PACKET sockets, TCP sockets, UDP sockets, DCCP sockets, RAW sockets, Unix domain sockets等等統計. 它比其他工具展示等多tcp和state資訊. 它是一個非常實用、快速、有效的跟蹤IP連線和sockets的新工具.SS命令可以提供如下資訊:

  • 所有的TCP sockets
  • 所有的UDP sockets
  • 所有ssh/ftp/ttp/https持久連線
  • 所有連線到Xserver的本地程式
  • 使用state(例如:connected, synchronized, SYN-RECV, SYN-SENT,TIME-WAIT)、地址、埠過濾
  • 所有的state FIN-WAIT-1 tcpsocket連線以及更多

很多流行的Linux發行版都支援ss以及很多監控工具使用ss命令.熟悉這個工具有助於您更好的發現與解決系統效能問題.本人強烈建議使用ss命令替代netstat部分命令,例如netsat -ant/lnt等

直接ss命令

 對上面解釋一波:

Netid #socket型別,在上面的例子中,有 TCP、u_str(unix流)等套接字
State #套接字處於什麼狀態,下面是TCP套接字的所有狀態及說明, 實際上就是TCP的三次握手和四次揮手的所有狀態
Recv-Q #在 ESTAB 狀態下,表示核心中還有多少位元組的資料沒有被上層應用讀取,如果這裡數值很大,應用程式可能發生了阻塞
Send-Q #在 ESTAB 狀態下,表示核心傳送佇列中還有多少位元組的資料沒有收到確認的ACK,如果這個數值很大,表明接收端的接收以及處理需要加強
Local Address:Port #本地地址和埠
Peer Address:Port #遠端地址和埠

然後我們接著看上面的state有哪些呢,如果特別熟悉網路的人應該很懂,至少我現在是不是特別熟悉,三次握手和四次揮手的狀態:

LISTEN #服務端偵聽套接字等待客戶端的連線
SYN-SENT #客戶端已傳送套接字連線請求報文,等待連線被伺服器接收
SYN-RECEIVED #伺服器端接收連線請求報文後,等待客戶端的確認連線的回覆報文
ESTABLISHED #服務端和客戶端之間成功建立了一條有效的連線,可以互相傳輸資料
FIN-WAIT-1 #伺服器或客戶端呼叫close函式主動向對方發出終止連線的請求報文,同時等待對方確認終止連線的回覆報文
FIN-WAIT-2 #主動關閉連線端收到對方確認終止連線的回覆報文,同時等待對方連線終止的請求報文,這時的狀態是TCP連線的半關閉狀態,可以接受資料,但是不能傳送資料
CLOSE-WAIT #被動關閉端收到主動關閉端終止連線的請求報文後,向主動關閉端傳送確認終止連線的回覆報文,同時被動關閉端等待本地使用者終止連線,這時被動關閉端的狀態是TCP連線的半關閉狀態,可以傳送資料,但是不能接收資料
CLOSING #伺服器和客戶端同時向對方傳送終止連線(呼叫close函式)請求報文,並且雙方都是在收到對方傳送的終止連線回復報文之前收到了對方的傳送的終止連線請求報文,這個時候雙方都進入了CLOSING狀態,進入CLOSING狀態之後,只要收到了對方對自己終止連線的回覆報文,就會進入TIME-WAIT狀態,所以CLOSING狀態的持續時間會特別短,一般很難捕獲到
LAST-ACK #被動關閉端傳送完全部資料之後,向主動關閉端傳送終止連線的請求報文,等待主動關閉端傳送終止連線的回覆報文
TIME-WAIT #主動關閉端收到被動關閉端終止連線的請求報文後,給被動關閉端傳送終止連線的回覆報文,等待足夠時間以確保被動關閉端收到了主動關閉段傳送的終止連線的回覆報文
CLOSED #完全沒有連線,套接字連線已經終止了

那麼這些狀態ss命令又怎麼對應呢?(後面的是ss命令顯示的狀態資訊)

[TCP_ESTABLISHED] = "ESTAB",
[TCP_SYN_SENT] = "SYN-SENT",
[TCP_SYN_RECV] = "SYN-RECV",
[TCP_FIN_WAIT1] = "FIN-WAIT-1",
[TCP_FIN_WAIT2] = "FIN-WAIT-2",
[TCP_TIME_WAIT] = "TIME-WAIT",
[TCP_CLOSE] = "UNCONN",
[TCP_CLOSE_WAIT] = "CLOSE-WAIT",
[TCP_LAST_ACK] = "LAST-ACK",
[TCP_LISTEN] =  "LISTEN",
[TCP_CLOSING] = "CLOSING",

降到這裡其實就可以去做下面的監控了,但是我還是想從別的部落格裡在copy一些東西過來完善下這個命令,繼續往下看ss命令的使用。好像還沒說命令的使用,,,

Usage: ss [ OPTIONS ]
       ss [ OPTIONS ] [ FILTER ]

其實這個命令不難,難的是會不會網路。。。

-h, --help 幫助資訊
-V, --version 程式版本資訊
-n, --numeric 不解析服務名稱
-r, --resolve 解析主機名
-a, --all 顯示所有套接字(sockets)
-l, --listening 顯示監聽狀態的套接字(sockets)
-o, --options 顯示計時器資訊
-e, --extended 顯示詳細的套接字(sockets)資訊
-m, --memory 顯示套接字(socket)的記憶體使用情況
-p, --processes 顯示使用套接字(socket)的程式
-i, --info 顯示 TCP內部資訊
-s, --summary 顯示套接字(socket)使用概況
-4, --ipv4 僅顯示IPv4的套接字(sockets)
-6, --ipv6 僅顯示IPv6的套接字(sockets)
-0, --packet 顯示 PACKET 套接字(socket)
-t, --tcp 僅顯示 TCP套接字(sockets)
-u, --udp 僅顯示 UCP套接字(sockets)
-d, --dccp 僅顯示 DCCP套接字(sockets)
-w, --raw 僅顯示 RAW套接字(sockets)
-x, --unix 僅顯示 Unix套接字(sockets)
-f, --family=FAMILY 顯示 FAMILY型別的套接字(sockets),FAMILY可選,支援 unix, inet, inet6, link, netlink
-A, --query=QUERY, --socket=QUERY
QUERY := {all|inet|tcp|udp|raw|unix|packet|netlink}[,QUERY]
-D, --diag=FILE 將原始TCP套接字(sockets)資訊轉儲到檔案
-F, --filter=FILE 從檔案中都去過濾器資訊
FILTER := [ state TCP-STATE ] [ EXPRESSION ]

到此為止,如上的基本都是copy(引用)過來的,地址見文末的參考文獻,重點在下面的監控

二、zabbix監控機器總體的socket情況

做這個監控前可以熟悉下awk命令,很好玩的,可以參考個人博文:"三劍客"之awk心中無劍

這是使用的監控系統為zabbix,我們這裡會結合zabbix的模板(這裡選擇模板是為了後期擴充),和自定義指令碼的方式進行監控。

第一步(寫指令碼):二話不多說上指令碼先:

vim tcp_status.sh
#################指令碼內容#################
#!/bin/bash
if [ $# -ne 1 ];then
    echo "Follow the script name with an argument "
fi

case $1 in

    LISTEN)
        result=`ss -ant | awk 'NR>1 {a[$1]++} END {for (b in a) print b,a[b]}' | awk '/LISTEN/{print $2}'`
        if [ "$result" == "" ];then
               echo 0
        else
           echo $result
        fi
        ;;

    ESTAB)
        result=`ss -ant | awk 'NR>1 {a[$1]++} END {for (b in a) print b,a[b]}' | awk '/ESTAB/{print $2}'`
        if [ "$result" == "" ];then
               echo 0
        else
           echo $result
        fi
        ;;


    CLOSE-WAIT)
        result=`ss -ant | awk 'NR>1 {a[$1]++} END {for (b in a) print b,a[b]}' | awk '/CLOSE-WAIT/{print $2}'`
        if [ "$result" == "" ];then
               echo 0
        else
           echo $result
        fi
        ;;

    TIME-WAIT)
        result=`ss -ant | awk 'NR>1 {a[$1]++} END {for (b in a) print b,a[b]}' | awk '/TIME-WAIT/{print $2}'`
        if [ "$result" == "" ];then
               echo 0
        else
           echo $result
        fi
        ;;

    SYN-SENT)
        result=`ss -ant | awk 'NR>1 {a[$1]++} END {for (b in a) print b,a[b]}' | awk '/SYN-SENT/{print $2}'`
        if [ "$result" == "" ];then
               echo 0
        else
           echo $result
        fi
        ;;

    SYN-RECV)
        result=`ss -ant | awk 'NR>1 {a[$1]++} END {for (b in a) print b,a[b]}' | awk '/SYN-RECV/{print $2}'`
        if [ "$result" == "" ];then
               echo 0
        else
           echo $result
        fi
        ;;

    FIN-WAIT-1)
        result=`ss -ant | awk 'NR>1 {a[$1]++} END {for (b in a) print b,a[b]}' | awk '/FIN-WAIT-1/{print $2}'`
        if [ "$result" == "" ];then
               echo 0
        else
           echo $result
        fi
        ;;

    FIN-WAIT-2)
        result=`ss -ant | awk 'NR>1 {a[$1]++} END {for (b in a) print b,a[b]}' | awk '/FIN-WAIT-2/{print $2}'`
        if [ "$result" == "" ];then
               echo 0
        else
           echo $result
        fi
        ;;

    UNCONN)
        result=`ss -ant | awk 'NR>1 {a[$1]++} END {for (b in a) print b,a[b]}' | awk '/UNCONN/{print $2}'`
        if [ "$result" == "" ];then
               echo 0
        else
           echo $result
        fi
        ;;

    LAST-ACK)
        result=`ss -ant | awk 'NR>1 {a[$1]++} END {for (b in a) print b,a[b]}' | awk '/LAST-ACK/{print $2}'`
        if [ "$result" == "" ];then
               echo 0
        else
           echo $result
        fi
        ;;

    CLOSING)
        result=`ss -ant | awk 'NR>1 {a[$1]++} END {for (b in a) print b,a[b]}' | awk '/CLOSING/{print $2}'`
        if [ "$result" == "" ];then
               echo 0
        else
           echo $result
        fi
        ;;
 esac

第二步(配置zabbix agent的配置檔案)

vim  zabbix_agent.conf
##############新增如下內容#################
UnsafeUserParameters=1   #有點忘記了,這個引數是自定義指令碼需要配置的,有疑問的可以去查哈
UserParameter=tcp.status[*],sh /home/zabbix/tcp_status.sh $1  #這裡就是用來指定剛剛寫的指令碼,後面傳一個引數

配置好了以後記得重啟zabbix agent

第三步(配置zabbix的模板,往其中新增item,trigger,graph)

新增模板,然後往其中新增item,如下圖所示(具體步驟不嘮叨,太簡單)

 

 

 上圖中key中的tcp.status指的是剛剛在第二步中的配置UserParameter=tcp.status[*],sh /home/zabbix/tcp_status.sh \$1 

然後中括號裡面的內容就是\$1進行傳參的引數,具體的引數就是[UNCONN]裡面的UNCONN,這些值對應第一步監控指令碼中的case中的每一種情況,到這裡基本上完成了,不,還是畫個圖吧,在模板中新增graph,如下圖所示:

 

 還有最重要的一步就是把配置好監控指令碼的(第一步)的主機新增到該模板,到此為止這個監控就做完了,看個結果圖吧

 

三、zabbix監控機器來源於各個機器的請求數

這個監控的目的就是看看到底是哪些機器訪問目標機器比較頻繁。

這個監控採用自發現的監控,比上面那個會難一點哈,為啥要選擇自發現的監控呢,因為item不是確定的,這裡選擇:原地址ip和目的ip地址作為item,我們在目的ip地址進行監控,這個是不變的,所以原地址ip值會發生變化,所以這裡採用的是自動生成item的方式進行監控,自動新增和刪除item,其實挺好用的,只要學會了,超級簡單,可以參看個人博文:zabbix自發現item監控

這裡也是分為三步,寫指令碼,配置zabbix_agent.conf檔案,配置Discovery

第一步(寫指令碼,這裡需要兩個指令碼,一個用來做自發現(需要輸出json格式),一個用來做item的)

vim tcp_monitory.sh
##################tcp_monitor.sh##################
#!/bin/bash
#獲取資料輸出到data.txt檔案中,格式為:原地址ip:count:目標地址ip
#並且過濾掉count小於200的資料,這裡沒有分socket的狀態,眉毛鬍子一把抓了,個人可以根據具體的需求改進
ip_addr=`ip addr | grep -w inet | grep -v  "127.0.0.1" | awk '{print $2}'| awk -F "/" '{print $1}'`
ss -ant | awk '{ print $5}'|grep -Ev '127.0.0.1' | cut -d ':' -f4 | awk -v ip_addr=$ip_addr 'NR>1 {++s[$1]} END {for(k in s)if(s[k]>=200){print k,s[k],ip_addr}}' | grep -E  "^([0-9]{1,3}\.){3}[0-9]" > /home/zabbix/data.txt

#執行Python指令碼,這是為了輸出json格式,
python /home/zabbix/get_json.py

#####################################
#如下是get_json.py的內容
##############get_json.py################
#!/usr/bin/env python
#coding=utf-8
import json

def create_json(path):
    json_list = []
    with open(path) as f:
        for line in f.readlines():
            dict = {}
            split = line.split(" ")
            dict["{#DES_IP}"] = split[0]
            //dict["{#LINK_COUNT}"] = split[1] //這個是可以不要的
            dict["{#SOU_IP}"] = split[2][:-1]
            json_list.append(dict)
    sum = {}
    sum["data"] = json_list
    sum = json.dumps(sum)
    print sum


if __name__ == '__main__':
    path = "/home/zabbix/data.txt"
    create_json(path)

##############分割線:上面的是自發現的指令碼###############
##############分割線:下面的是item相關指令碼###############
vim  tcp_item.sh
##################tcp_item.sh####################
#!/bin/bash
export LANG="en_US.UTF-8"
path=/home/zabbix/data.txt
count=`cat $path | grep $1 | grep $2 | awk '{print $2}'`
[ 1"$count" -eq 1 ] && echo 0 || echo $count

兩個指令碼都搞定了,就可以進行zabbix_agent.conf的配置了

第二步(配置zabbix_agent.conf檔案)

在配置檔案中新增如下內容:

UnsafeUserParameters=1 #如果已經配置就不需要配置了

UserParameter=discovery.tcp_monitor[*],sh /home/zabbix/tcp_monitor.sh #自發現
UserParameter=alert.tcp_count[*],sh /home/zabbix/tcp_item.sh $1 $2 #item,其中$1,$2為item中的傳遞引數,用來區別item的不同

第三步:(配置Discovery,配置item,trigger,graph)

這裡還是選擇在zabbix的模板上進行配置,現在新增一個Discovery

   然後在Discovery上配置item,trigger,graph

   配置item:

 

  上面的DES_IP,SOU_IP來源於自發現指令碼中的Python指令碼,用於輸出的格式。alter.tcp_count是UserParameter=alert.tcp_count[*],sh /home/zabbix/tcp_item.sh \$1 \$2,後面的\$1,\$2與DES_IP,SOU_IP相對應生成唯一確定的item。

item配置完畢後就可以配置trigger了:

 

  接下來繼續配置graph了

 

最後把機器新增到模板(是不是說反了?),然後看結果

 大功告成!!!!!!!

參考網址:

http://www.ttlsa.com/linux-command/ss-replace-netstat/

https://blog.csdn.net/fengye_csdn/article/details/109212726

https://blog.csdn.net/m0_37814112/article/details/80701909

http://www.xitongzhijia.net/xtjc/20141230/33791.html

相關文章